dsh-knowledge-library 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- <!doctype html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>拾页 · 本地资料库</title><link rel="stylesheet" href="/app.css"></head><body><div id="root"></div><script src="/app.js"></script></body></html>
1
+ <!doctype html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>拾页 · 本地资料库</title><link rel="stylesheet" href="/app.css"></head><body class="kl-preview"><div id="root"></div><script src="/app.js"></script></body></html>
package/dist/ui/App.js CHANGED
@@ -30,6 +30,7 @@ export function App() {
30
30
  const [jobs, setJobs] = useState([]);
31
31
  const [showJobs, setShowJobs] = useState(false);
32
32
  const [showInfo, setShowInfo] = useState(false);
33
+ const [showNavigation, setShowNavigation] = useState(false);
33
34
  const [error, setError] = useState('');
34
35
  const [toast, setToast] = useState('');
35
36
  const [loading, setLoading] = useState(true);
@@ -40,7 +41,7 @@ export function App() {
40
41
  const detailRef = useRef(null);
41
42
  const refresh = useCallback(() => setRevision(v => v + 1), []);
42
43
  const notify = (message) => setToast(message);
43
- const chooseFilter = (next) => { setFilter(next); setLimit(40); setSelected(undefined); setAnchor(undefined); setDetail(undefined); };
44
+ const chooseFilter = (next) => { setFilter(next); setLimit(40); setSelected(undefined); setAnchor(undefined); setDetail(undefined); setShowNavigation(false); };
44
45
  const refreshMeta = useCallback(async () => {
45
46
  const [s, j] = await Promise.all([api('/stats'), api('/jobs')]);
46
47
  setStats(s);
@@ -118,7 +119,7 @@ export function App() {
118
119
  }
119
120
  }
120
121
  const currentTitle = filter.category || filter.collection || (filter.tag ? `标签 · ${filter.tag}` : filter.kind === 'note' ? '知识笔记' : '全部资料');
121
- return _jsxs("div", { className: "kl-app", children: [_jsxs("aside", { className: "kl-nav", children: [_jsxs("div", { className: "kl-brand", children: [_jsx("span", { className: "kl-mark", children: _jsx(Icon, { name: "library", size: 24 }) }), _jsxs("div", { children: [_jsx("strong", { children: "\u62FE\u9875" }), _jsx("span", { children: "\u672C\u5730\u8D44\u6599\u5E93" })] }), _jsx("span", { className: "kl-version", children: "01" })] }), _jsxs("button", { className: "kl-primary kl-add", onClick: () => setEditor({ mode: 'capture' }), children: [_jsx(Icon, { name: "plus", size: 18 }), "\u6536\u96C6\u8D44\u6599"] }), _jsxs("nav", { "aria-label": "\u8D44\u6599\u5BFC\u822A", children: [_jsxs("button", { className: !Object.values(filter).some(Boolean) ? 'active' : '', onClick: () => chooseFilter({}), children: [_jsx(Icon, { name: "library", size: 18 }), _jsx("span", { children: "\u5168\u90E8\u8D44\u6599" }), _jsx("small", { children: stats?.total ?? '—' })] }), _jsxs("button", { className: filter.category === '收件箱' ? 'active' : '', onClick: () => chooseFilter({ category: '收件箱' }), children: [_jsx(Icon, { name: "inbox", size: 18 }), _jsx("span", { children: "\u6536\u4EF6\u7BB1" }), _jsx("small", { children: stats?.inbox ?? '—' })] }), _jsxs("button", { className: filter.kind === 'note' ? 'active' : '', onClick: () => chooseFilter({ kind: 'note' }), children: [_jsx(Icon, { name: "note", size: 18 }), _jsx("span", { children: "\u77E5\u8BC6\u7B14\u8BB0" }), _jsx("small", { children: stats?.notes ?? '—' })] })] }), _jsxs("div", { className: "kl-nav-section", children: [_jsxs("div", { className: "kl-nav-label", children: ["\u5206\u7C7B ", _jsx("span", { children: "\u6309\u4E3B\u9898\u5F52\u6863" })] }), stats?.categories.filter(c => c.name !== '收件箱').map(c => _jsxs("button", { className: `kl-nav-row ${filter.category === c.name ? 'active' : ''}`, onClick: () => chooseFilter({ category: c.name }), children: [_jsx(Icon, { name: "folder", size: 16 }), _jsx("span", { title: c.name, children: c.name }), _jsx("small", { children: c.count })] }, c.name)), !stats?.categories.some(c => c.name !== '收件箱') && _jsxs("p", { className: "kl-nav-hint", children: ["\u6536\u96C6\u65F6\u586B\u5199\u5206\u7C7B\uFF0C", _jsx("br", {}), "\u5728\u8FD9\u91CC\u627E\u5230\u5B83\u4EEC\u3002"] })] }), _jsxs("div", { className: "kl-nav-section", children: [_jsxs("div", { className: "kl-nav-label", children: ["\u4E13\u9898 ", _jsx("span", { children: "\u56F4\u7ED5\u4E00\u4E2A\u95EE\u9898" })] }), stats?.collections.map(c => _jsxs("button", { className: `kl-nav-row ${filter.collection === c.name ? 'active' : ''}`, onClick: () => chooseFilter({ collection: c.name }), children: [_jsx("span", { className: "kl-collection-dot" }), _jsx("span", { children: c.name }), _jsx("small", { children: c.count })] }, c.name)), !stats?.collections.length && _jsx("p", { className: "kl-nav-hint", children: "\u628A\u4E0D\u540C\u6765\u6E90\u653E\u8FDB\u540C\u4E00\u4E13\u9898\u3002" })] }), !!stats?.tags.length && _jsxs("div", { className: "kl-nav-section", children: [_jsx("div", { className: "kl-nav-label", children: "\u6807\u7B7E" }), _jsx("div", { className: "kl-tag-cloud", children: stats.tags.slice(0, 15).map(tag => _jsxs("button", { className: filter.tag === tag.name ? 'active' : '', onClick: () => chooseFilter({ tag: tag.name }), children: ["#", tag.name] }, tag.name)) })] }), _jsxs("div", { className: "kl-nav-bottom", children: [_jsxs("button", { onClick: () => setShowInfo(true), children: [_jsx("span", { className: "kl-live-dot" }), _jsx("span", { children: "\u4FDD\u5B58\u5728\u8FD9\u53F0\u7535\u8111" }), _jsx(Icon, { name: "settings", size: 16 })] }), _jsx("p", { children: "\u6765\u6E90\u53EF\u8FFD\u6EAF \u00B7 \u5185\u5BB9\u53EF\u5E26\u8D70" })] })] }), _jsxs("main", { className: "kl-workspace", children: [_jsxs("header", { className: "kl-header", children: [_jsxs("div", { children: [_jsx("div", { className: "kl-eyebrow", children: "COLLECT / CONNECT / UNDERSTAND" }), _jsx("h1", { children: currentTitle }), _jsx("p", { children: "\u7559\u4F4F\u503C\u5F97\u518D\u8BFB\u7684\u5185\u5BB9\uFF0C\u8BA9\u6BCF\u4E00\u6B21\u601D\u8003\u90FD\u6709\u51FA\u5904\u3002" })] }), _jsxs("button", { className: "kl-secondary", onClick: () => setEditor({ mode: 'note' }), children: [_jsx(Icon, { name: "note", size: 17 }), "\u5199\u7B14\u8BB0"] })] }), _jsxs("div", { className: "kl-toolbar", children: [_jsxs("label", { className: "kl-search", children: [_jsx(Icon, { name: "search", size: 19 }), _jsx("input", { ref: searchRef, "aria-label": "\u641C\u7D22\u8D44\u6599", placeholder: "\u641C\u7D22\u6B63\u6587\u3001\u6807\u9898\u3001\u6807\u7B7E\u2026", value: query, onChange: e => { setQuery(e.target.value); setLimit(40); } }), _jsx("kbd", { children: "Ctrl K" })] }), _jsxs("button", { className: `kl-job-button ${pending ? 'has-jobs' : ''}`, onClick: () => setShowJobs(true), children: [_jsx(Icon, { name: "clock", size: 17 }), _jsx("span", { children: pending ? `${pending} 项处理中` : '采集任务' }), jobs.some(j => j.status === 'failed') && _jsx("i", {})] })] }), !!error && _jsxs("div", { className: "kl-error", role: "alert", children: [_jsx("span", { children: error }), _jsx("button", { "aria-label": "\u5173\u95ED\u9519\u8BEF", onClick: () => setError(''), children: _jsx(Icon, { name: "close", size: 16 }) })] }), _jsxs("div", { className: "kl-filterbar", children: [_jsxs("span", { children: [_jsx("b", { children: total }), " \u4EFD\u8D44\u6599", query && ` · “${query}”`] }), _jsxs("div", { children: [_jsxs("select", { "aria-label": "\u6309\u7C7B\u578B\u7B5B\u9009", value: filter.kind || '', onChange: e => { setFilter({ ...filter, kind: e.target.value || undefined }); setLimit(40); }, children: [_jsx("option", { value: "", children: "\u6240\u6709\u7C7B\u578B" }), Object.entries(kinds).map(([value, kind]) => _jsx("option", { value: value, children: kind.label }, value))] }), _jsx("span", { className: "kl-sort-label", children: "\u6700\u8FD1\u66F4\u65B0 \u2193" })] })] }), _jsxs("div", { className: `kl-body ${detail || detailLoading ? 'with-detail' : ''}`, children: [_jsxs("section", { className: "kl-list", "aria-label": "\u8D44\u6599\u5217\u8868", "aria-busy": loading, children: [loading && !items.length ? _jsxs("div", { className: "kl-empty", children: [_jsx("span", { className: "kl-spinner" }), "\u6B63\u5728\u8BFB\u53D6\u8D44\u6599\u5E93\u2026"] }) : !items.length ? _jsxs("div", { className: "kl-empty", children: [_jsxs("div", { className: "kl-empty-art", children: [_jsx("span", {}), _jsx(Icon, { name: "file", size: 46 }), _jsx("span", {})] }), _jsx("h2", { children: query || Object.values(filter).some(Boolean) ? '这里还没有匹配的资料' : '从第一份值得保存的资料开始' }), _jsx("p", { children: query ? '试试更短的关键词,或切换分类。' : '一篇网页、一段摘录,或一份视频字幕。' }), _jsxs("button", { className: "kl-secondary", onClick: () => query || Object.values(filter).some(Boolean) ? (setQuery(''), chooseFilter({})) : setEditor({ mode: 'capture' }), children: [query || Object.values(filter).some(Boolean) ? '查看全部资料' : '收集第一份资料', _jsx(Icon, { name: "arrow", size: 16 })] })] }) : items.map(item => _jsxs("button", { className: `kl-item ${selected === item.id ? 'selected' : ''}`, onClick: () => { setAnchor(undefined); setSelected(item.id); }, children: [_jsxs("div", { className: "kl-item-top", children: [_jsxs("span", { className: `kl-type kl-type-${item.kind}`, children: [_jsx(Icon, { name: kinds[item.kind].icon, size: 15 }), kinds[item.kind].label] }), _jsx("time", { children: date(item.createdAt) })] }), _jsx("h2", { children: item.title }), _jsx("p", { children: item.excerpt || item.extraction }), _jsxs("div", { className: "kl-item-bottom", children: [_jsxs("span", { className: "kl-category", children: [_jsx(Icon, { name: "folder", size: 13 }), item.category] }), item.tags.slice(0, 2).map(tag => _jsx("span", { className: "kl-chip", children: tag }, tag)), item.status === 'stored' && _jsx("span", { className: "kl-stored", children: "\u4EC5\u5F52\u6863" }), _jsx(Icon, { name: "arrow", size: 15 })] })] }, item.id)), items.length < total && _jsx("button", { className: "kl-load-more", onClick: () => setLimit(v => Math.min(100, v + 40)), disabled: limit >= 100, children: limit >= 100 ? '请缩小搜索范围(最多显示 100 条)' : '加载更多' })] }), !selected && !!items.length && _jsxs("aside", { className: "kl-reading-placeholder", children: [_jsx("div", { className: "kl-placeholder-number", children: "\u62FE / \u9875" }), _jsxs("h2", { children: ["\u6536\u96C6\u4E4B\u540E\uFF0C", _jsx("br", {}), "\u7ED9\u77E5\u8BC6\u4E00\u70B9\u751F\u957F\u7684\u7A7A\u95F4\u3002"] }), _jsxs("p", { children: ["\u9009\u62E9\u4E00\u4EFD\u8D44\u6599\u5F00\u59CB\u9605\u8BFB\u3002", _jsx("br", {}), "\u6458\u5F55\u3001\u5173\u8054\uFF0C\u518D\u4EA4\u7ED9 Agent \u6DF1\u5165\u5206\u6790\u3002"] }), _jsxs("div", { children: [_jsxs("span", { children: [stats?.ready ?? 0, _jsx("small", { children: "\u53EF\u68C0\u7D22\u8D44\u6599" })] }), _jsxs("span", { children: [stats?.notes ?? 0, _jsx("small", { children: "\u77E5\u8BC6\u7B14\u8BB0" })] })] })] }), selected && _jsxs("section", { className: "kl-detail", "aria-label": "\u8D44\u6599\u8BE6\u60C5", ref: detailRef, children: [_jsxs("div", { className: "kl-detail-toolbar", children: [_jsxs("button", { className: "kl-back", onClick: () => { setSelected(undefined); setDetail(undefined); }, children: [_jsx(Icon, { name: "close", size: 18 }), _jsx("span", { children: "\u5173\u95ED\u9605\u8BFB" })] }), detail && _jsxs("div", { children: [_jsx("button", { title: "\u590D\u5236\u8D44\u6599\u5F15\u7528", "aria-label": "\u590D\u5236\u8D44\u6599\u5F15\u7528", onClick: () => void copy(`library://${detail.id}`), children: _jsx(Icon, { name: "copy", size: 17 }) }), _jsx("a", { title: "\u5BFC\u51FA Markdown", "aria-label": "\u5BFC\u51FA Markdown", href: `${API}/materials/${detail.id}/export`, children: _jsx(Icon, { name: "download", size: 17 }) }), _jsx("button", { onClick: () => setEditor({ mode: 'edit', source: detail }), children: "\u7F16\u8F91\u4FE1\u606F" })] })] }), detailLoading ? _jsxs("div", { className: "kl-empty", children: [_jsx("span", { className: "kl-spinner" }), "\u8BFB\u53D6\u4E2D\u2026"] }) : detail && _jsxs("article", { children: [_jsxs("div", { className: "kl-detail-kicker", children: [_jsx("span", { children: kinds[detail.kind].label }), _jsx("span", { children: detail.category })] }), _jsx("h2", { children: detail.title }), _jsxs("div", { className: "kl-source", children: [detail.sourceUrl ? _jsxs("a", { href: detail.sourceUrl, target: "_blank", rel: "noreferrer", children: [new URL(detail.sourceUrl).hostname, _jsx(Icon, { name: "arrow", size: 12 })] }) : _jsx("span", { children: detail.originalName || '本地收集' }), _jsx("span", { children: new Date(detail.createdAt).toLocaleDateString('zh-CN') }), detail.author && _jsx("span", { children: detail.author })] }), !!detail.tags.length && _jsx("div", { className: "kl-detail-tags", children: detail.tags.map(t => _jsxs("span", { className: "kl-chip", children: ["#", t] }, t)) }), detail.sourceNote && _jsx("aside", { className: "kl-source-note", children: detail.sourceNote }), detail.status === 'stored' && _jsxs("div", { className: "kl-notice", children: [_jsx(Icon, { name: "file" }), _jsxs("div", { children: [_jsx("strong", { children: "\u539F\u6587\u4EF6\u5DF2\u5B89\u5168\u5F52\u6863" }), _jsx("p", { children: detail.extraction })] })] }), _jsx("div", { className: "kl-content", children: detail.segments.map(segment => _jsxs("div", { className: `kl-segment ${anchor === segment.id ? 'kl-cited' : ''}`, id: segment.id, "data-segment-id": segment.id, children: [_jsxs("div", { className: "kl-segment-line", children: [_jsx("span", { children: segment.startSeconds !== undefined ? time(segment.startSeconds) : segment.id }), _jsx("button", { title: "\u6458\u5F55\u4E3A\u7B14\u8BB0", "aria-label": `摘录 ${segment.id} 为笔记`, onClick: () => setEditor({ mode: 'note', source: detail, segment }), children: _jsx(Icon, { name: "note", size: 14 }) }), _jsx("button", { title: "\u590D\u5236\u6BB5\u843D\u5F15\u7528", "aria-label": `复制 ${segment.id} 引用`, onClick: () => void copy(`library://${detail.id}/${segment.id}`), children: _jsx(Icon, { name: "copy", size: 14 }) })] }), /^#{1,6}\s/.test(segment.text) ? _jsx("h3", { children: segment.text.replace(/^#{1,6}\s+/, '') }) : _jsx("p", { children: segment.text })] }, segment.id)) }), !!detail.citations.length && _jsxs("div", { className: "kl-references", children: [_jsx("h3", { children: "\u6765\u6E90\u5F15\u7528" }), detail.citations.map((c, i) => _jsxs("button", { "aria-label": `查看引用 ${i + 1}`, onClick: () => { setAnchor(c.segmentId); setSelected(c.materialId); }, children: [_jsx(Icon, { name: "file", size: 15 }), _jsxs("span", { children: [c.materialId.slice(0, 15), "\u2026", c.segmentId ? ` / ${c.segmentId}` : ''] }), _jsx(Icon, { name: "arrow", size: 14 })] }, i))] }), _jsxs("footer", { className: "kl-detail-footer", children: [_jsxs("div", { children: [_jsx("span", { className: "kl-live-dot" }), detail.extraction] }), detail.originalFile && _jsxs("a", { href: `${API}/materials/${detail.id}/original`, children: ["\u4E0B\u8F7D\u539F\u59CB\u6587\u4EF6", _jsx(Icon, { name: "download", size: 14 })] }), _jsx("code", { children: detail.id })] })] })] })] })] }), editor && _jsx(EditorDialog, { editor: editor, categories: stats?.categories.map(c => c.name) || [], onClose: () => setEditor(undefined), onSaved: result => {
122
+ return _jsxs("div", { className: "kl-app", children: [_jsxs("main", { className: "kl-workspace", children: [_jsxs("header", { className: "kl-header", children: [_jsx("h1", { children: currentTitle }), _jsxs("div", { className: "kl-header-actions", children: [_jsxs("button", { className: "kl-secondary", onClick: () => setEditor({ mode: 'note' }), children: [_jsx(Icon, { name: "note", size: 16 }), "\u5199\u7B14\u8BB0"] }), _jsxs("button", { className: "kl-primary", onClick: () => setEditor({ mode: 'capture' }), children: [_jsx(Icon, { name: "plus", size: 16 }), "\u6536\u96C6\u8D44\u6599"] })] })] }), _jsxs("div", { className: "kl-navigation", children: [_jsxs("nav", { "aria-label": "\u8D44\u6599\u5BFC\u822A", children: [_jsx("button", { "aria-pressed": !Object.values(filter).some(Boolean), onClick: () => chooseFilter({}), children: "\u5168\u90E8\u8D44\u6599" }), _jsx("button", { "aria-pressed": filter.category === '收件箱', onClick: () => chooseFilter({ category: '收件箱' }), children: "\u6536\u4EF6\u7BB1" }), _jsx("button", { "aria-pressed": filter.kind === 'note', onClick: () => chooseFilter({ kind: 'note' }), children: "\u77E5\u8BC6\u7B14\u8BB0" })] }), _jsxs("button", { className: "kl-directory-toggle", "aria-expanded": showNavigation, "aria-controls": "kl-directory", onClick: () => setShowNavigation(v => !v), children: [_jsx(Icon, { name: "folder", size: 16 }), "\u5206\u7C7B\u4E0E\u4E13\u9898"] })] }), _jsxs("div", { className: "kl-toolbar", children: [_jsxs("label", { className: "kl-search", children: [_jsx(Icon, { name: "search", size: 19 }), _jsx("input", { ref: searchRef, "aria-label": "\u641C\u7D22\u8D44\u6599", placeholder: "\u641C\u7D22\u6B63\u6587\u3001\u6807\u9898\u3001\u6807\u7B7E\u2026", value: query, onChange: e => { setQuery(e.target.value); setLimit(40); } }), _jsx("kbd", { children: "Ctrl K" })] }), _jsxs("button", { "aria-label": pending ? `采集任务,${pending} 项处理中` : '采集任务', className: `kl-job-button ${pending ? 'has-jobs' : ''}`, onClick: () => setShowJobs(true), children: [_jsx(Icon, { name: "clock", size: 17 }), _jsx("span", { children: pending ? `${pending} 项处理中` : '采集任务' }), jobs.some(j => j.status === 'failed') && _jsx("i", {})] }), _jsx("button", { className: "kl-icon-button", "aria-label": "\u8D44\u6599\u5E93\u8BBE\u7F6E", title: "\u8D44\u6599\u5E93\u8BBE\u7F6E", onClick: () => setShowInfo(true), children: _jsx(Icon, { name: "settings", size: 18 }) })] }), !!error && _jsxs("div", { className: "kl-error", role: "alert", children: [_jsx("span", { children: error }), _jsx("button", { "aria-label": "\u5173\u95ED\u9519\u8BEF", onClick: () => setError(''), children: _jsx(Icon, { name: "close", size: 16 }) })] }), _jsxs("div", { className: "kl-filterbar", children: [_jsxs("span", { children: [_jsx("b", { children: total }), " \u4EFD\u8D44\u6599", query && ` · “${query}”`] }), _jsxs("div", { children: [_jsxs("select", { "aria-label": "\u6309\u7C7B\u578B\u7B5B\u9009", value: filter.kind || '', onChange: e => { setFilter({ ...filter, kind: e.target.value || undefined }); setLimit(40); }, children: [_jsx("option", { value: "", children: "\u6240\u6709\u7C7B\u578B" }), Object.entries(kinds).map(([value, kind]) => _jsx("option", { value: value, children: kind.label }, value))] }), _jsx("span", { className: "kl-sort-label", children: "\u6700\u8FD1\u66F4\u65B0 \u2193" })] })] }), _jsxs("div", { className: `kl-body ${detail || detailLoading ? 'with-detail' : ''}`, children: [showNavigation && _jsxs("aside", { className: "kl-nav", id: "kl-directory", "aria-label": "\u5206\u7C7B\u4E0E\u4E13\u9898", children: [_jsxs("div", { className: "kl-directory-title", children: [_jsx("span", { children: "\u5206\u7C7B\u4E0E\u4E13\u9898" }), _jsx("button", { className: "kl-icon-button", "aria-label": "\u5173\u95ED\u5206\u7C7B\u5BFC\u822A", onClick: () => setShowNavigation(false), children: _jsx(Icon, { name: "close", size: 16 }) })] }), _jsxs("div", { className: "kl-nav-section", children: [_jsx("div", { className: "kl-nav-label", children: "\u5206\u7C7B" }), stats?.categories.filter(c => c.name !== '收件箱').map(c => _jsxs("button", { "aria-pressed": filter.category === c.name, className: "kl-nav-row", onClick: () => chooseFilter({ category: c.name }), children: [_jsx(Icon, { name: "folder", size: 16 }), _jsx("span", { title: c.name, children: c.name }), _jsx("small", { children: c.count })] }, c.name)), !stats?.categories.some(c => c.name !== '收件箱') && _jsx("p", { className: "kl-nav-hint", children: "\u6536\u96C6\u8D44\u6599\u65F6\u6DFB\u52A0\u5206\u7C7B" })] }), _jsxs("div", { className: "kl-nav-section", children: [_jsx("div", { className: "kl-nav-label", children: "\u4E13\u9898" }), stats?.collections.map(c => _jsxs("button", { "aria-pressed": filter.collection === c.name, className: "kl-nav-row", onClick: () => chooseFilter({ collection: c.name }), children: [_jsx("span", { className: "kl-collection-dot" }), _jsx("span", { title: c.name, children: c.name }), _jsx("small", { children: c.count })] }, c.name)), !stats?.collections.length && _jsx("p", { className: "kl-nav-hint", children: "\u6536\u96C6\u8D44\u6599\u65F6\u6DFB\u52A0\u4E13\u9898" })] }), !!stats?.tags.length && _jsxs("div", { className: "kl-nav-section", children: [_jsx("div", { className: "kl-nav-label", children: "\u6807\u7B7E" }), _jsx("div", { className: "kl-tag-cloud", children: stats.tags.slice(0, 15).map(tag => _jsxs("button", { "aria-pressed": filter.tag === tag.name, onClick: () => chooseFilter({ tag: tag.name }), children: ["#", tag.name] }, tag.name)) })] }), _jsxs("div", { className: "kl-nav-bottom", children: [_jsx("span", { className: "kl-live-dot" }), "\u4FDD\u5B58\u5728\u8FD9\u53F0\u7535\u8111"] })] }), _jsxs("section", { className: "kl-list", "aria-label": "\u8D44\u6599\u5217\u8868", "aria-busy": loading, children: [loading && !items.length ? _jsxs("div", { className: "kl-empty", children: [_jsx("span", { className: "kl-spinner" }), "\u6B63\u5728\u8BFB\u53D6\u8D44\u6599\u5E93\u2026"] }) : !items.length ? _jsxs("div", { className: "kl-empty", children: [_jsx(Icon, { name: "inbox", size: 28 }), _jsx("h2", { children: query || Object.values(filter).some(Boolean) ? '没有找到匹配的资料' : '还没有资料,收集的内容会显示在这里' }), query && _jsx("p", { children: "\u8BD5\u8BD5\u5176\u4ED6\u5173\u952E\u8BCD\uFF0C\u6216\u5207\u6362\u5206\u7C7B\u3002" }), _jsx("button", { className: "kl-secondary", onClick: () => query || Object.values(filter).some(Boolean) ? (setQuery(''), chooseFilter({})) : setEditor({ mode: 'capture' }), children: query || Object.values(filter).some(Boolean) ? '查看全部资料' : '收集第一份资料' })] }) : items.map(item => _jsxs("button", { className: `kl-item ${selected === item.id ? 'selected' : ''}`, onClick: () => { setAnchor(undefined); setSelected(item.id); }, children: [_jsxs("div", { className: "kl-item-top", children: [_jsxs("span", { className: `kl-type kl-type-${item.kind}`, children: [_jsx(Icon, { name: kinds[item.kind].icon, size: 15 }), kinds[item.kind].label] }), _jsx("time", { children: date(item.createdAt) })] }), _jsx("h2", { children: item.title }), _jsx("p", { children: item.excerpt || item.extraction }), _jsxs("div", { className: "kl-item-bottom", children: [_jsxs("span", { className: "kl-category", children: [_jsx(Icon, { name: "folder", size: 13 }), item.category] }), item.tags.slice(0, 2).map(tag => _jsx("span", { className: "kl-chip", children: tag }, tag)), item.status === 'stored' && _jsx("span", { className: "kl-stored", children: "\u4EC5\u5F52\u6863" }), _jsx(Icon, { name: "arrow", size: 15 })] })] }, item.id)), items.length < total && _jsx("button", { className: "kl-load-more", onClick: () => setLimit(v => Math.min(100, v + 40)), disabled: limit >= 100, children: limit >= 100 ? '请缩小搜索范围(最多显示 100 条)' : '加载更多' })] }), selected && _jsxs("section", { className: "kl-detail", "aria-label": "\u8D44\u6599\u8BE6\u60C5", ref: detailRef, children: [_jsxs("div", { className: "kl-detail-toolbar", children: [_jsxs("button", { className: "kl-back", onClick: () => { setSelected(undefined); setDetail(undefined); }, children: [_jsx(Icon, { name: "close", size: 18 }), _jsx("span", { children: "\u5173\u95ED\u9605\u8BFB" })] }), detail && _jsxs("div", { children: [_jsx("button", { title: "\u590D\u5236\u8D44\u6599\u5F15\u7528", "aria-label": "\u590D\u5236\u8D44\u6599\u5F15\u7528", onClick: () => void copy(`library://${detail.id}`), children: _jsx(Icon, { name: "copy", size: 17 }) }), _jsx("a", { title: "\u5BFC\u51FA Markdown", "aria-label": "\u5BFC\u51FA Markdown", href: `${API}/materials/${detail.id}/export`, children: _jsx(Icon, { name: "download", size: 17 }) }), _jsx("button", { onClick: () => setEditor({ mode: 'edit', source: detail }), children: "\u7F16\u8F91\u4FE1\u606F" })] })] }), detailLoading ? _jsxs("div", { className: "kl-empty", children: [_jsx("span", { className: "kl-spinner" }), "\u8BFB\u53D6\u4E2D\u2026"] }) : detail && _jsxs("article", { children: [_jsxs("div", { className: "kl-detail-kicker", children: [_jsx("span", { children: kinds[detail.kind].label }), _jsx("span", { children: detail.category })] }), _jsx("h2", { children: detail.title }), _jsxs("div", { className: "kl-source", children: [detail.sourceUrl ? _jsxs("a", { href: detail.sourceUrl, target: "_blank", rel: "noreferrer", children: [new URL(detail.sourceUrl).hostname, _jsx(Icon, { name: "arrow", size: 12 })] }) : _jsx("span", { children: detail.originalName || '本地收集' }), _jsx("span", { children: new Date(detail.createdAt).toLocaleDateString('zh-CN') }), detail.author && _jsx("span", { children: detail.author })] }), !!detail.tags.length && _jsx("div", { className: "kl-detail-tags", children: detail.tags.map(t => _jsxs("span", { className: "kl-chip", children: ["#", t] }, t)) }), detail.sourceNote && _jsx("aside", { className: "kl-source-note", children: detail.sourceNote }), detail.status === 'stored' && _jsxs("div", { className: "kl-notice", children: [_jsx(Icon, { name: "file" }), _jsxs("div", { children: [_jsx("strong", { children: "\u539F\u6587\u4EF6\u5DF2\u5B89\u5168\u5F52\u6863" }), _jsx("p", { children: detail.extraction })] })] }), _jsx("div", { className: "kl-content", children: detail.segments.map(segment => _jsxs("div", { className: `kl-segment ${anchor === segment.id ? 'kl-cited' : ''}`, id: segment.id, "data-segment-id": segment.id, children: [_jsxs("div", { className: "kl-segment-line", children: [_jsx("span", { children: segment.startSeconds !== undefined ? time(segment.startSeconds) : segment.id }), _jsx("button", { title: "\u6458\u5F55\u4E3A\u7B14\u8BB0", "aria-label": `摘录 ${segment.id} 为笔记`, onClick: () => setEditor({ mode: 'note', source: detail, segment }), children: _jsx(Icon, { name: "note", size: 14 }) }), _jsx("button", { title: "\u590D\u5236\u6BB5\u843D\u5F15\u7528", "aria-label": `复制 ${segment.id} 引用`, onClick: () => void copy(`library://${detail.id}/${segment.id}`), children: _jsx(Icon, { name: "copy", size: 14 }) })] }), /^#{1,6}\s/.test(segment.text) ? _jsx("h3", { children: segment.text.replace(/^#{1,6}\s+/, '') }) : _jsx("p", { children: segment.text })] }, segment.id)) }), !!detail.citations.length && _jsxs("div", { className: "kl-references", children: [_jsx("h3", { children: "\u6765\u6E90\u5F15\u7528" }), detail.citations.map((c, i) => _jsxs("button", { "aria-label": `查看引用 ${i + 1}`, onClick: () => { setAnchor(c.segmentId); setSelected(c.materialId); }, children: [_jsx(Icon, { name: "file", size: 15 }), _jsxs("span", { children: [c.materialId.slice(0, 15), "\u2026", c.segmentId ? ` / ${c.segmentId}` : ''] }), _jsx(Icon, { name: "arrow", size: 14 })] }, i))] }), _jsxs("footer", { className: "kl-detail-footer", children: [_jsxs("div", { children: [_jsx("span", { className: "kl-live-dot" }), detail.extraction] }), detail.originalFile && _jsxs("a", { href: `${API}/materials/${detail.id}/original`, children: ["\u4E0B\u8F7D\u539F\u59CB\u6587\u4EF6", _jsx(Icon, { name: "download", size: 14 })] }), _jsx("code", { children: detail.id })] })] })] })] })] }), editor && _jsx(EditorDialog, { editor: editor, categories: stats?.categories.map(c => c.name) || [], onClose: () => setEditor(undefined), onSaved: result => {
122
123
  setEditor(undefined);
123
124
  refresh();
124
125
  if (result.material) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-knowledge-library",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "本地优先的 DeepSeek Harness 资料库:采集、分类、检索与可追溯知识笔记",
5
5
  "type": "module",
6
6
  "license": "MIT",