diff --git a/index.html b/index.html index 84ea6c1..4d59ba3 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,21 @@ DevFlow + -
+
+
+
diff --git a/src/App.vue b/src/App.vue index 95cd342..696df47 100644 --- a/src/App.vue +++ b/src/App.vue @@ -213,7 +213,12 @@ async function activateForPopup() { onMounted(async () => { // 1. 先把 SQLite 全表 KV 填进 appSettings 缓存(后续 get/useSetting 才能读到真实值) - await appSettings.loadAll() + // 包 try/catch 防 loadAll reject 中断 onMounted 后续初始化(对齐下方各步 try/catch 模式) + try { + await appSettings.loadAll() + } catch (e) { + console.error('启动加载应用设置失败:', e) + } // 2. 一次性迁移:把旧 localStorage 的 11 个 key 导入 SQLite(仅首次跑,之后 localStorage 清净) await migrateLegacyLocalStorage() // 3. 缓存已就绪,初始化主题/locale diff --git a/src/components/OffloadedResultBar.vue b/src/components/OffloadedResultBar.vue index f7a7609..ec5cc45 100644 --- a/src/components/OffloadedResultBar.vue +++ b/src/components/OffloadedResultBar.vue @@ -51,7 +51,7 @@ const sizeText = computed(() => align-items: center; gap: 8px; padding: 6px 10px; - background: var(--df-warning-bg, rgba(240,199,94,0.08)); + background: var(--df-warning-bg); } .offload-badge { flex-shrink: 0; @@ -59,8 +59,8 @@ const sizeText = computed(() => font-weight: 600; padding: 1px 7px; border-radius: var(--df-radius-sm); - background: var(--df-warning-bg, rgba(240,199,94,0.14)); - color: var(--df-warning, #f0c75e); + background: var(--df-warning-bg); + color: var(--df-warning); } .offload-meta { flex-shrink: 0; diff --git a/src/components/Paginator.vue b/src/components/Paginator.vue index 565c777..81d2415 100644 --- a/src/components/Paginator.vue +++ b/src/components/Paginator.vue @@ -102,7 +102,7 @@ function onPageSizeChange(val: string) { align-items: center; flex-wrap: wrap; gap: 8px; - margin-top: var(--df-gap-grid, 16px); + margin-top: var(--df-gap-grid); padding: 8px 4px; font-size: 12px; color: var(--df-text-dim); diff --git a/src/components/ai-chat/shortcuts-overlay.vue b/src/components/ai-chat/shortcuts-overlay.vue index 8cdcaeb..d2c0760 100644 --- a/src/components/ai-chat/shortcuts-overlay.vue +++ b/src/components/ai-chat/shortcuts-overlay.vue @@ -87,7 +87,7 @@ const shortcuts = computed(() => [ cursor: pointer; font-size: 12px; padding: 2px 4px; - border-radius: 4px; + border-radius: var(--df-radius-sm); } .ai-shortcuts-close:hover { color: var(--df-text); background: var(--df-bg-card); } .ai-shortcuts-list { @@ -109,7 +109,7 @@ const shortcuts = computed(() => [ font-size: 11px; background: var(--df-bg-card); border: 0.5px solid var(--df-border); - border-radius: 4px; + border-radius: var(--df-radius-sm); padding: 2px 8px; color: var(--df-text); } diff --git a/src/components/ideas/IdeaDetail.vue b/src/components/ideas/IdeaDetail.vue index 57f1e34..4218db2 100644 --- a/src/components/ideas/IdeaDetail.vue +++ b/src/components/ideas/IdeaDetail.vue @@ -1213,4 +1213,10 @@ watch(() => props.idea.id, loadHistory) grid-template-columns: 1fr; } } +/* 容器闸:详情栏窄于 480px 时辩论双栏堆叠(父级 .ideas 已声明 container-type) */ +@container (max-width: 480px) { + .debate-container { + grid-template-columns: 1fr; + } +} diff --git a/src/components/knowledge/KnowledgeDetail.vue b/src/components/knowledge/KnowledgeDetail.vue index 14379fc..6dc673f 100644 --- a/src/components/knowledge/KnowledgeDetail.vue +++ b/src/components/knowledge/KnowledgeDetail.vue @@ -110,6 +110,36 @@ +
+
+ {{ t('knowledge.relatedTitle', { n: relatedItems.length }) }} + {{ sectionOpen.related ? '▾' : '▸' }} +
+
+
+ +
+
+
+ +
- +
() const { t } = useI18n() @@ -246,7 +277,10 @@ const refLimit = ref(10) // ===== 折叠态(P0 溯源/引用/生命周期段折叠 + P1 内容/reasoning 超长折叠)===== // 段落默认开合:溯源/引用默认展开(常用),生命周期默认收起(回顾性,通常无需常驻) -const sectionOpen = ref({ trace: true, refs: true, lifecycle: false }) +const sectionOpen = ref({ trace: true, refs: true, lifecycle: false, related: true }) + +// 关联知识(阶段1 连边):detail payload 直带,空数组隐藏区块 +const relatedItems = computed(() => props.detail?.related ?? []) // 内容/reasoning 折叠:超阈值(300px / 120px)才显示「展开/收起」按钮 const CONTENT_FOLD_THRESHOLD = 300 @@ -363,7 +397,7 @@ function relativeTime(millisStr: string): string { // 切换知识时重置引用列表「加载更多」上限 + 折叠态(对齐原 Knowledge.vue selectKnowledge 内 refLimit=10 行为) watch(() => props.detail.knowledge.id, () => { refLimit.value = 10 - sectionOpen.value = { trace: true, refs: true, lifecycle: false } + sectionOpen.value = { trace: true, refs: true, lifecycle: false, related: true } contentExpanded.value = false reasoningExpanded.value = false measureOverflow() @@ -381,9 +415,11 @@ watch(renderedContent, () => { .detail-section:last-child { border-bottom: none; margin-bottom: 0; } .detail-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 12px; min-width: 0; flex-wrap: wrap; } -.detail-title-row { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; overflow: hidden; } +/* flex-basis 用 auto(非 1 简写的 0%):basis=0 会让 flex-wrap 换行判定视标题行不占宽, + 中间断点带(视口>760 且面板>420)按钮组永远挤进首行,标题被压成一字一行(2026-10-23 用户实证) */ +.detail-title-row { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1 1 auto; overflow: hidden; } .detail-kind-icon { font-size: 20px; flex-shrink: 0; } -.detail-title { font-size: 18px; font-weight: 500; color: var(--df-text); word-break: break-word; overflow-wrap: break-word; min-width: 0; } +.detail-title { font-size: var(--df-fs-title); font-weight: 500; color: var(--df-text); word-break: break-word; overflow-wrap: break-word; min-width: 0; } .detail-actions { display: flex; gap: 6px; flex-shrink: 0; flex-wrap: wrap; } .detail-badges { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; font-size: 11px; } @@ -457,6 +493,14 @@ watch(renderedContent, () => { .ref-query { color: var(--df-text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .load-more { align-self: flex-start; margin-top: 4px; } +/* 关联知识(阶段1 连边):复用 ref-item 版式,点击跳对端 */ +.related-item { cursor: pointer; text-align: left; border: none; width: 100%; } +.related-item:hover { background: var(--df-hover); } +.related-dir { color: var(--df-text-dim); flex-shrink: 0; } +.related-kind { + margin-left: auto; color: var(--df-text-dim); font-size: 11px; white-space: nowrap; +} + /* 生命周期时间线 */ .timeline { display: flex; flex-direction: column; } .tl-node { display: flex; gap: 12px; position: relative; padding-bottom: 4px; } @@ -490,11 +534,14 @@ watch(renderedContent, () => { @media (max-width: 760px) { .detail-head { flex-direction: column; align-items: flex-start; } .detail-actions { width: 100%; } - .detail-title { font-size: 15px; } + /* 15px 字面量归档 --df-fs-* 谱:base 已是 --df-fs-title(16),降档取谱内下一档 --df-fs-body(13),保留防竖线化作用 */ + .detail-title { font-size: var(--df-fs-body); } } -@container (max-width: 420px) { +/* 620 断点带补全(2026-10-23):kn-layout 切单列后详情面板即整宽,标题/按钮改堆叠, + 与 420 断点同款;缺此条则 620~420 带内标题行与按钮组同行硬挤(用户窄屏实证) */ +@container (max-width: 620px) { .detail-head { flex-direction: column; align-items: flex-start; } .detail-actions { width: 100%; } - .detail-title { font-size: 15px; } + .detail-title { font-size: var(--df-fs-body); } } diff --git a/src/components/layout/AppSidebar.vue b/src/components/layout/AppSidebar.vue index d0c61b6..e39c26d 100644 --- a/src/components/layout/AppSidebar.vue +++ b/src/components/layout/AppSidebar.vue @@ -11,7 +11,7 @@
DevFlow - v0.1 + v{{ appVersion }} @@ -87,12 +87,16 @@ import { computed, ref, onMounted, onBeforeUnmount } from 'vue' import { useAiStore } from '@/stores/ai' import { useKnowledgeStore } from '@/stores/knowledge' import { useAppSettingsStore } from '@/stores/appSettings' +import { settingsApi } from '@/api' import { convStates } from '@/composables/ai/aiShared' const aiStore = useAiStore() const knowledgeStore = useKnowledgeStore() const appSettings = useAppSettingsStore() +// 侧栏品牌区版本号(onMounted 拉真实版本;取失败回退为不显示,禁写死假版本) +const appVersion = ref('') + // ── 侧边栏收缩状态(持久化到 appSettings,小窗口自动收缩) ── // 展开 220px(文字+图标) / 收缩 56px(仅图标,hover tooltip 显示文字) // 优先级:用户手动设置 > 小窗口自动收缩(≤768px) @@ -130,6 +134,11 @@ onMounted(async () => { window.addEventListener('resize', checkWindowWidth) // Ctrl+B 切换快捷键 window.addEventListener('keydown', handleCollapseKeydown) + // 真实版本号(编译期注入,与 AboutPanel 同源;失败静默 → 版本号不显示) + try { + const info = await settingsApi.getVersion() + appVersion.value = info.version + } catch { /* 静默:取版本失败不阻断侧栏渲染 */ } }) onBeforeUnmount(() => { diff --git a/src/components/layout/UpdateBadge.vue b/src/components/layout/UpdateBadge.vue index 458bf55..4e6487e 100644 --- a/src/components/layout/UpdateBadge.vue +++ b/src/components/layout/UpdateBadge.vue @@ -189,6 +189,7 @@ async function onInstall() { top: 36px; right: 0; width: 320px; + max-width: calc(100vw - 24px); padding: 14px 16px; border: 0.5px solid var(--df-border); border-radius: var(--df-radius-lg); @@ -217,7 +218,7 @@ async function onInstall() { .update-pop-bar { width: 100%; height: 6px; - border-radius: 3px; + border-radius: var(--df-radius-xs); background: var(--df-neutral-bg); overflow: hidden; } @@ -229,7 +230,7 @@ async function onInstall() { .update-pop-progress-text { margin-top: 6px; font-size: 12px; - font-family: var(--df-font-mono, monospace); + font-family: var(--df-font-mono); color: var(--df-text-dim); } .update-pop-error { diff --git a/src/components/project/ApprovalDialog.vue b/src/components/project/ApprovalDialog.vue index 7e2e93e..2dd8efb 100644 --- a/src/components/project/ApprovalDialog.vue +++ b/src/components/project/ApprovalDialog.vue @@ -1,7 +1,7 @@