2026-04-05 23:22:00 +08:00
|
|
|
|
<script setup>
|
2026-04-07 23:38:23 +08:00
|
|
|
|
import { computed, onMounted, ref } from 'vue'
|
2026-04-05 23:30:01 +08:00
|
|
|
|
import FileTree from '../components/FileTree.vue'
|
|
|
|
|
|
import FileContent from '../components/FileContent.vue'
|
|
|
|
|
|
import ContextMenu from '../components/ContextMenu.vue'
|
2026-04-07 23:38:23 +08:00
|
|
|
|
import { useFileSystem } from '../composables/useFileSystem'
|
2026-04-05 23:22:00 +08:00
|
|
|
|
|
2026-04-05 23:30:01 +08:00
|
|
|
|
const fs = useFileSystem()
|
|
|
|
|
|
const sidebarCollapsed = ref(false)
|
|
|
|
|
|
const confirmDialog = ref(null)
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
fs.load()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const selectedNode = computed(() => fs.getSelectedNode())
|
2026-04-07 23:38:23 +08:00
|
|
|
|
const breadcrumb = computed(() => (fs.selectedId.value ? fs.getBreadcrumbPath(fs.selectedId.value) : []))
|
|
|
|
|
|
const storageSummary = computed(() => {
|
|
|
|
|
|
const bytes = fs.stats.value.usedBytes
|
|
|
|
|
|
if (!bytes) return '0 B'
|
|
|
|
|
|
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
|
|
|
|
|
let value = bytes
|
|
|
|
|
|
let index = 0
|
|
|
|
|
|
while (value >= 1024 && index < units.length - 1) {
|
|
|
|
|
|
value /= 1024
|
|
|
|
|
|
index += 1
|
|
|
|
|
|
}
|
|
|
|
|
|
return `${value >= 100 || index === 0 ? value.toFixed(0) : value.toFixed(1)} ${units[index]}`
|
2026-04-05 23:30:01 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2026-06-06 17:18:15 +08:00
|
|
|
|
async function handleCreateFile(parentId, name = 'untitled.md') {
|
|
|
|
|
|
await fs.createFile(parentId, name)
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-06 17:18:15 +08:00
|
|
|
|
async function handleCreateFolder(parentId, name = '新建文件夹') {
|
|
|
|
|
|
await fs.createFolder(parentId, name)
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-06 17:18:15 +08:00
|
|
|
|
async function handleRename(id, newName) {
|
|
|
|
|
|
await fs.rename(id, newName)
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function findNode(nodes, id) {
|
|
|
|
|
|
for (const node of nodes) {
|
|
|
|
|
|
if (node.id === id) return node
|
|
|
|
|
|
if (node.type === 'folder') {
|
|
|
|
|
|
const found = findNode(node.children || [], id)
|
|
|
|
|
|
if (found) return found
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
function handleDelete(id) {
|
|
|
|
|
|
const node = findNode(fs.tree.value, id)
|
|
|
|
|
|
if (!node) return
|
|
|
|
|
|
confirmDialog.value = {
|
|
|
|
|
|
id,
|
|
|
|
|
|
name: node.name,
|
|
|
|
|
|
type: node.type
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-06 17:18:15 +08:00
|
|
|
|
async function confirmDelete() {
|
2026-04-07 23:38:23 +08:00
|
|
|
|
if (!confirmDialog.value) return
|
2026-06-06 17:18:15 +08:00
|
|
|
|
await fs.remove(confirmDialog.value.id)
|
2026-04-07 23:38:23 +08:00
|
|
|
|
confirmDialog.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-05 23:30:01 +08:00
|
|
|
|
function handleContextMenu(x, y, node) {
|
|
|
|
|
|
fs.showContextMenu(x, y, node)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-06 17:18:15 +08:00
|
|
|
|
async function handleDrop(draggedId, targetParentId) {
|
2026-04-05 23:30:01 +08:00
|
|
|
|
if (draggedId === targetParentId) return
|
2026-04-06 11:14:09 +08:00
|
|
|
|
fs.cut(draggedId)
|
2026-06-06 17:18:15 +08:00
|
|
|
|
await fs.paste(targetParentId)
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
async function handleUploadFiles(files) {
|
|
|
|
|
|
const parentId = selectedNode.value?.type === 'folder' ? selectedNode.value.id : selectedNode.value?.parentId || null
|
|
|
|
|
|
const result = await fs.uploadFiles(files, parentId)
|
|
|
|
|
|
if (result.failed.length > 0) {
|
|
|
|
|
|
fs.error.value = result.failed.map((item) => `${item.name}:${item.reason}`).join(';')
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
function closeConfirm() {
|
|
|
|
|
|
confirmDialog.value = null
|
2026-04-06 11:14:09 +08:00
|
|
|
|
}
|
2026-04-05 23:22:00 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="docs-view">
|
2026-04-05 23:30:01 +08:00
|
|
|
|
<div class="docs-layout">
|
2026-04-07 23:38:23 +08:00
|
|
|
|
<aside v-show="!sidebarCollapsed" class="docs-sidebar">
|
2026-04-05 23:30:01 +08:00
|
|
|
|
<FileTree
|
|
|
|
|
|
:nodes="fs.tree.value"
|
|
|
|
|
|
:selected-id="fs.selectedId.value"
|
|
|
|
|
|
:expanded-ids="fs.expandedIds.value"
|
|
|
|
|
|
:clipboard="fs.clipboard.value"
|
|
|
|
|
|
:get-file-icon="fs.getFileIcon"
|
2026-04-07 23:38:23 +08:00
|
|
|
|
:loading="fs.loading.value"
|
|
|
|
|
|
:stats="fs.stats.value"
|
2026-04-05 23:30:01 +08:00
|
|
|
|
@select="fs.select"
|
|
|
|
|
|
@toggle="fs.toggleFolder"
|
|
|
|
|
|
@create-file="handleCreateFile"
|
|
|
|
|
|
@create-folder="handleCreateFolder"
|
|
|
|
|
|
@rename="handleRename"
|
|
|
|
|
|
@remove="handleDelete"
|
|
|
|
|
|
@copy="fs.copy"
|
|
|
|
|
|
@cut="fs.cut"
|
|
|
|
|
|
@paste="fs.paste"
|
|
|
|
|
|
@context-menu="handleContextMenu"
|
|
|
|
|
|
@drop="handleDrop"
|
2026-04-07 23:38:23 +08:00
|
|
|
|
@drag-start="() => {}"
|
|
|
|
|
|
@drag-over="() => {}"
|
|
|
|
|
|
@upload-files="handleUploadFiles"
|
2026-04-05 23:30:01 +08:00
|
|
|
|
/>
|
2026-04-07 23:38:23 +08:00
|
|
|
|
</aside>
|
|
|
|
|
|
|
|
|
|
|
|
<section class="docs-main">
|
2026-05-01 20:55:02 +08:00
|
|
|
|
<header v-if="selectedNode?.type !== 'file'" class="docs-toolbar">
|
2026-04-07 23:38:23 +08:00
|
|
|
|
<div class="toolbar-left">
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="sidebar-toggle"
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
:title="sidebarCollapsed ? '展开左侧栏' : '收起左侧栏'"
|
|
|
|
|
|
@click="sidebarCollapsed = !sidebarCollapsed"
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg viewBox="0 0 16 16" width="15" height="15" fill="currentColor"><path d="M2.75 2A1.75 1.75 0 001 3.75v8.5C1 13.216 1.784 14 2.75 14h10.5A1.75 1.75 0 0015 12.25v-8.5A1.75 1.75 0 0013.25 2H2.75zm0 1.5h2.5v9h-2.5a.25.25 0 01-.25-.25v-8.5a.25.25 0 01.25-.25zm4 9v-9h6.5a.25.25 0 01.25.25v8.5a.25.25 0 01-.25.25h-6.5z"/></svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<div class="toolbar-breadcrumb">
|
|
|
|
|
|
<button class="crumb-link" type="button" @click="fs.select(null)">workspace</button>
|
|
|
|
|
|
<template v-for="item in breadcrumb" :key="item.id">
|
|
|
|
|
|
<span class="crumb-sep">/</span>
|
|
|
|
|
|
<button
|
|
|
|
|
|
v-if="item.id !== selectedNode?.id"
|
|
|
|
|
|
class="crumb-link"
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
@click="fs.select(item.id)"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ item.name }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<span v-else class="crumb-current">{{ item.name }}</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-04-05 23:30:01 +08:00
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
<div class="toolbar-right">
|
2026-06-06 17:18:15 +08:00
|
|
|
|
<span class="storage-pill">文档空间 {{ storageSummary }}</span>
|
2026-04-07 23:38:23 +08:00
|
|
|
|
<button
|
|
|
|
|
|
v-if="fs.canPaste()"
|
|
|
|
|
|
class="toolbar-btn"
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
@click="fs.paste(selectedNode?.type === 'folder' ? selectedNode.id : selectedNode?.parentId || null)"
|
|
|
|
|
|
>
|
|
|
|
|
|
粘贴
|
|
|
|
|
|
</button>
|
2026-04-05 23:30:01 +08:00
|
|
|
|
</div>
|
2026-04-07 23:38:23 +08:00
|
|
|
|
</header>
|
2026-04-05 23:30:01 +08:00
|
|
|
|
|
|
|
|
|
|
<FileContent
|
|
|
|
|
|
:node="selectedNode"
|
|
|
|
|
|
:breadcrumb="breadcrumb"
|
2026-04-06 11:14:09 +08:00
|
|
|
|
:root-nodes="fs.tree.value"
|
|
|
|
|
|
:get-file-icon="fs.getFileIcon"
|
2026-04-07 23:38:23 +08:00
|
|
|
|
:get-file-blob="fs.getFileBlob"
|
2026-05-01 20:55:02 +08:00
|
|
|
|
:update-file="fs.updateFile"
|
|
|
|
|
|
:show-sidebar-toggle="selectedNode?.type === 'file'"
|
2026-04-05 23:30:01 +08:00
|
|
|
|
@navigate="fs.select"
|
2026-05-01 20:55:02 +08:00
|
|
|
|
@toggle-sidebar="sidebarCollapsed = !sidebarCollapsed"
|
2026-04-05 23:30:01 +08:00
|
|
|
|
/>
|
2026-04-07 23:38:23 +08:00
|
|
|
|
</section>
|
2026-04-05 23:22:00 +08:00
|
|
|
|
</div>
|
2026-04-05 23:30:01 +08:00
|
|
|
|
|
|
|
|
|
|
<ContextMenu
|
|
|
|
|
|
:visible="!!fs.contextMenu.value"
|
|
|
|
|
|
:x="fs.contextMenu.value?.x || 0"
|
|
|
|
|
|
:y="fs.contextMenu.value?.y || 0"
|
|
|
|
|
|
:node="fs.contextMenu.value?.node || null"
|
|
|
|
|
|
:can-paste="fs.canPaste()"
|
|
|
|
|
|
@close="fs.hideContextMenu()"
|
2026-04-07 23:38:23 +08:00
|
|
|
|
@rename="fs.hideContextMenu()"
|
2026-04-05 23:30:01 +08:00
|
|
|
|
@delete="(id) => { fs.hideContextMenu(); handleDelete(id) }"
|
|
|
|
|
|
@copy="(id) => { fs.hideContextMenu(); fs.copy(id) }"
|
|
|
|
|
|
@cut="(id) => { fs.hideContextMenu(); fs.cut(id) }"
|
|
|
|
|
|
@paste="(parentId) => { fs.hideContextMenu(); fs.paste(parentId) }"
|
|
|
|
|
|
@new-file="(parentId) => { fs.hideContextMenu(); handleCreateFile(parentId) }"
|
|
|
|
|
|
@new-folder="(parentId) => { fs.hideContextMenu(); handleCreateFolder(parentId) }"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<Teleport to="body">
|
|
|
|
|
|
<div v-if="confirmDialog" class="confirm-overlay">
|
|
|
|
|
|
<div class="confirm-dialog">
|
|
|
|
|
|
<h3>确认删除</h3>
|
2026-04-07 23:38:23 +08:00
|
|
|
|
<p>
|
|
|
|
|
|
确定要删除 <strong>{{ confirmDialog.name }}</strong>
|
|
|
|
|
|
{{ confirmDialog.type === 'folder' ? ' 以及其中的全部内容' : '' }} 吗?
|
|
|
|
|
|
</p>
|
2026-04-05 23:30:01 +08:00
|
|
|
|
<div class="confirm-actions">
|
2026-04-07 23:38:23 +08:00
|
|
|
|
<button class="btn btn-secondary" type="button" @click="closeConfirm">取消</button>
|
|
|
|
|
|
<button class="btn btn-danger" type="button" @click="confirmDelete">删除</button>
|
2026-04-05 23:30:01 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Teleport>
|
|
|
|
|
|
|
|
|
|
|
|
<Teleport to="body">
|
2026-04-07 23:38:23 +08:00
|
|
|
|
<div v-if="fs.error.value" class="error-toast">
|
2026-04-05 23:30:01 +08:00
|
|
|
|
<div class="error-content">
|
2026-04-07 23:38:23 +08:00
|
|
|
|
<span>{{ fs.error.value }}</span>
|
|
|
|
|
|
<button class="error-close" type="button" @click="fs.error.value = null">×</button>
|
2026-04-05 23:30:01 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Teleport>
|
2026-04-05 23:22:00 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.docs-view {
|
|
|
|
|
|
width: 100%;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
height: 100vh;
|
|
|
|
|
|
overflow: hidden;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
background: var(--github-bg);
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.docs-layout {
|
2026-04-07 23:38:23 +08:00
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 320px minmax(0, 1fr);
|
|
|
|
|
|
height: 100%;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.docs-sidebar {
|
2026-04-07 23:38:23 +08:00
|
|
|
|
min-width: 0;
|
2026-04-07 12:46:00 +08:00
|
|
|
|
border-right: 1px solid var(--github-border);
|
|
|
|
|
|
background: var(--github-bg);
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.docs-main {
|
2026-04-07 23:38:23 +08:00
|
|
|
|
min-width: 0;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.docs-toolbar {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
min-height: 56px;
|
|
|
|
|
|
padding: 0 18px;
|
2026-04-07 12:46:00 +08:00
|
|
|
|
border-bottom: 1px solid var(--github-border);
|
2026-04-07 23:38:23 +08:00
|
|
|
|
background: var(--github-bg);
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.toolbar-left,
|
|
|
|
|
|
.toolbar-right,
|
|
|
|
|
|
.toolbar-breadcrumb {
|
2026-04-05 23:22:00 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
gap: 10px;
|
2026-04-05 23:22:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.toolbar-breadcrumb {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
flex-wrap: wrap;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.sidebar-toggle,
|
|
|
|
|
|
.toolbar-btn,
|
|
|
|
|
|
.crumb-link {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
background: transparent;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.sidebar-toggle {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 34px;
|
|
|
|
|
|
height: 34px;
|
|
|
|
|
|
border: 1px solid var(--github-border);
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
color: var(--github-text-secondary);
|
|
|
|
|
|
background: var(--github-bg);
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.sidebar-toggle:hover,
|
|
|
|
|
|
.toolbar-btn:hover {
|
|
|
|
|
|
background: var(--github-hover);
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.crumb-link {
|
|
|
|
|
|
color: #0969da;
|
|
|
|
|
|
font-size: 14px;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.crumb-current {
|
|
|
|
|
|
color: var(--github-text);
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 700;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.crumb-sep {
|
|
|
|
|
|
color: var(--github-text-secondary);
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.storage-pill {
|
|
|
|
|
|
display: inline-flex;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
align-items: center;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
height: 32px;
|
|
|
|
|
|
padding: 0 12px;
|
|
|
|
|
|
border: 1px solid var(--github-border);
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: var(--github-hover);
|
|
|
|
|
|
color: var(--github-text-secondary);
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
font-weight: 700;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.toolbar-btn {
|
|
|
|
|
|
height: 32px;
|
|
|
|
|
|
padding: 0 12px;
|
|
|
|
|
|
border: 1px solid var(--github-border);
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
background: var(--github-bg);
|
|
|
|
|
|
color: var(--github-text);
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-weight: 600;
|
2026-04-06 11:14:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-05 23:30:01 +08:00
|
|
|
|
.confirm-overlay {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
inset: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
background: rgba(15, 23, 42, 0.35);
|
|
|
|
|
|
z-index: 10000;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.confirm-dialog {
|
2026-04-07 23:38:23 +08:00
|
|
|
|
width: min(420px, calc(100vw - 32px));
|
2026-04-05 23:30:01 +08:00
|
|
|
|
padding: 24px;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
border: 1px solid var(--github-border);
|
|
|
|
|
|
border-radius: 16px;
|
|
|
|
|
|
background: var(--github-bg);
|
|
|
|
|
|
box-shadow: 0 28px 60px rgba(15, 23, 42, 0.22);
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.confirm-dialog h3 {
|
2026-04-07 23:38:23 +08:00
|
|
|
|
margin: 0 0 10px;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
font-size: 1.1rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.confirm-dialog p {
|
2026-04-07 23:38:23 +08:00
|
|
|
|
margin: 0;
|
|
|
|
|
|
color: var(--github-text-secondary);
|
|
|
|
|
|
line-height: 1.7;
|
2026-04-05 23:22:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-05 23:30:01 +08:00
|
|
|
|
.confirm-actions {
|
|
|
|
|
|
display: flex;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
margin-top: 18px;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.btn {
|
|
|
|
|
|
height: 36px;
|
|
|
|
|
|
padding: 0 14px;
|
|
|
|
|
|
border: 1px solid var(--github-border);
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
font-weight: 700;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.btn-secondary {
|
|
|
|
|
|
background: var(--github-bg);
|
|
|
|
|
|
color: var(--github-text);
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
.btn-danger {
|
|
|
|
|
|
border-color: #cf222e;
|
|
|
|
|
|
background: #cf222e;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.error-toast {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
left: 50%;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
bottom: 24px;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
transform: translateX(-50%);
|
2026-04-07 23:38:23 +08:00
|
|
|
|
z-index: 10001;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.error-content {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
gap: 12px;
|
|
|
|
|
|
max-width: min(720px, calc(100vw - 32px));
|
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
background: #cf222e;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
color: #fff;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
box-shadow: 0 18px 40px rgba(207, 34, 46, 0.28);
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.error-close {
|
|
|
|
|
|
border: none;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
background: transparent;
|
|
|
|
|
|
color: inherit;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 23:38:23 +08:00
|
|
|
|
@media (max-width: 900px) {
|
|
|
|
|
|
.docs-layout {
|
|
|
|
|
|
grid-template-columns: minmax(0, 1fr);
|
|
|
|
|
|
}
|
2026-04-05 23:30:01 +08:00
|
|
|
|
|
|
|
|
|
|
.docs-sidebar {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 0;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
left: 0;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
bottom: 0;
|
2026-04-07 23:38:23 +08:00
|
|
|
|
z-index: 999;
|
|
|
|
|
|
width: min(88vw, 320px);
|
|
|
|
|
|
box-shadow: 20px 0 40px rgba(15, 23, 42, 0.16);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.docs-toolbar {
|
|
|
|
|
|
padding: 10px 14px;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
flex-direction: column;
|
2026-04-05 23:30:01 +08:00
|
|
|
|
}
|
2026-04-05 23:22:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|