@lemoncat7/dsh-knowledge 2.2.1 → 2.2.7

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.
Files changed (54) hide show
  1. package/docs/architecture.md +5 -5
  2. package/lib/api.d.ts.map +1 -1
  3. package/lib/api.js +13 -23
  4. package/lib/api.js.map +1 -1
  5. package/lib/async-pool.d.ts +7 -0
  6. package/lib/async-pool.d.ts.map +1 -0
  7. package/lib/async-pool.js +21 -0
  8. package/lib/async-pool.js.map +1 -0
  9. package/lib/client.js +1 -1
  10. package/lib/client.js.map +1 -1
  11. package/lib/extraction.d.ts.map +1 -1
  12. package/lib/extraction.js +3 -2
  13. package/lib/extraction.js.map +1 -1
  14. package/lib/index.d.ts.map +1 -1
  15. package/lib/index.js +13 -6
  16. package/lib/index.js.map +1 -1
  17. package/lib/local-provider.d.ts +11 -0
  18. package/lib/local-provider.d.ts.map +1 -1
  19. package/lib/local-provider.js +223 -68
  20. package/lib/local-provider.js.map +1 -1
  21. package/lib/note-tools.js +4 -4
  22. package/lib/note-tools.js.map +1 -1
  23. package/lib/provider-router.d.ts +6 -2
  24. package/lib/provider-router.d.ts.map +1 -1
  25. package/lib/provider-router.js +10 -6
  26. package/lib/provider-router.js.map +1 -1
  27. package/lib/provider.d.ts +3 -1
  28. package/lib/provider.d.ts.map +1 -1
  29. package/lib/remote-provider.d.ts +3 -1
  30. package/lib/remote-provider.d.ts.map +1 -1
  31. package/lib/remote-provider.js +17 -0
  32. package/lib/remote-provider.js.map +1 -1
  33. package/lib/retrieval.d.ts.map +1 -1
  34. package/lib/retrieval.js +9 -7
  35. package/lib/retrieval.js.map +1 -1
  36. package/lib/tool-authorization.d.ts +3 -1
  37. package/lib/tool-authorization.d.ts.map +1 -1
  38. package/lib/tool-authorization.js +27 -14
  39. package/lib/tool-authorization.js.map +1 -1
  40. package/lib/tools.js +1 -1
  41. package/lib/tools.js.map +1 -1
  42. package/lib/tracking.d.ts.map +1 -1
  43. package/lib/tracking.js +8 -3
  44. package/lib/tracking.js.map +1 -1
  45. package/lib/web-workspace-effects.d.ts.map +1 -1
  46. package/lib/web-workspace-effects.js +10 -1
  47. package/lib/web-workspace-effects.js.map +1 -1
  48. package/lib/web.d.ts.map +1 -1
  49. package/lib/web.js +7 -4
  50. package/lib/web.js.map +1 -1
  51. package/package.json +2 -2
  52. package/web/app.js +215 -21
  53. package/web/styles.css +31 -17
  54. package/web/workspace-effects.js +2 -2
package/web/app.js CHANGED
@@ -22,6 +22,7 @@ const WRITE_MODE_LABELS = { none: '仅召回', audit: '审核写入', direct: '
22
22
  const EVIDENCE_LABELS = { explicit: '用户明确', verified: '结果已验证', inferred: '模型推断' }
23
23
  const DOCUMENT_STATE_LABELS = { open: '进行中', resolved: '已解决', complete: '已收集完成' }
24
24
  const DOCUMENT_LAYOUT_KEY = 'dsh-knowledge.document-layout'
25
+ const KNOWLEDGE_DOCUMENT_DRAG_TYPE = 'application/x-dsh-knowledge-document-id'
25
26
  const NOTE_MAX_FILE_SIZE = 64 * 1024 * 1024
26
27
  const pageParams = new URLSearchParams(location.search)
27
28
  const mountContext = {
@@ -100,7 +101,10 @@ let navigationRequest = 0
100
101
  let navigationSave = Promise.resolve(true)
101
102
  let libraryDetailRequest = 0
102
103
  let documentSearchTimer = 0
104
+ let documentSearchController = null
103
105
  let noteSearchTimer = 0
106
+ let noteSearchController = null
107
+ let noteSearchRequest = 0
104
108
  let markdownEditorHandle = null
105
109
  let noteEditorLoader = null
106
110
  let markdownEditorMountRequest = 0
@@ -109,6 +113,8 @@ let plainTextEditorMountRequest = 0
109
113
  let noteTransferFrame = 0
110
114
  let noteTransferSequence = 0
111
115
  let noteSelectionRequest = 0
116
+ let knowledgeDocumentDrag = null
117
+ let movingDocumentId = ''
112
118
 
113
119
  function installHostThemeBridge() {
114
120
  if (window.parent === window) return Promise.resolve()
@@ -442,6 +448,7 @@ function renderLogin(message = '') {
442
448
  }
443
449
 
444
450
  function signOut() {
451
+ cancelPendingSearches()
445
452
  releaseNoteEditors()
446
453
  releaseNoteAsset()
447
454
  sessionStorage.removeItem(TOKEN_KEY)
@@ -455,6 +462,7 @@ async function navigate(view) {
455
462
  const request = ++navigationRequest
456
463
  navigationSave = navigationSave.then(() => saveBeforeNavigation(), () => saveBeforeNavigation())
457
464
  if (!await navigationSave || request !== navigationRequest) return
465
+ cancelPendingSearches()
458
466
  navigationController?.abort()
459
467
  const controller = new AbortController()
460
468
  navigationController = controller
@@ -489,6 +497,20 @@ async function navigate(view) {
489
497
  }
490
498
  }
491
499
 
500
+ function cancelPendingSearches() {
501
+ state.documentView.searchRequest += 1
502
+ state.libraryDetail.view.searchRequest += 1
503
+ noteSearchRequest += 1
504
+ window.clearTimeout(documentSearchTimer)
505
+ window.clearTimeout(noteSearchTimer)
506
+ documentSearchController?.abort()
507
+ noteSearchController?.abort()
508
+ documentSearchController = null
509
+ noteSearchController = null
510
+ documentSearchTimer = 0
511
+ noteSearchTimer = 0
512
+ }
513
+
492
514
  function loadingPhaseForView(view) {
493
515
  return ({
494
516
  overview: '正在汇总知识活动', bases: '正在读取知识库配置', entries: '正在读取知识目录',
@@ -739,7 +761,10 @@ function resetDocumentSearch(view) {
739
761
 
740
762
  function scheduleDocumentSearch(workspace) {
741
763
  window.clearTimeout(documentSearchTimer)
764
+ documentSearchController?.abort()
765
+ documentSearchController = null
742
766
  const view = workspace.view
767
+ view.searchRequest += 1
743
768
  if (!view.query.trim()) {
744
769
  resetDocumentSearch(view)
745
770
  renderShell()
@@ -747,7 +772,6 @@ function scheduleDocumentSearch(workspace) {
747
772
  }
748
773
  view.searchLoading = true
749
774
  view.searchError = ''
750
- renderShell()
751
775
  documentSearchTimer = window.setTimeout(() => { void loadDocumentSearch(workspace, true) }, 220)
752
776
  }
753
777
 
@@ -765,6 +789,9 @@ async function loadDocumentSearch(workspace, reset = false) {
765
789
  const query = view.query.trim()
766
790
  if (!query) return resetDocumentSearch(view)
767
791
  const request = ++view.searchRequest
792
+ documentSearchController?.abort()
793
+ const controller = new AbortController()
794
+ documentSearchController = controller
768
795
  view.searchLoading = true
769
796
  view.searchError = ''
770
797
  const params = new URLSearchParams({ q: query, limit: '80' })
@@ -783,7 +810,7 @@ async function loadDocumentSearch(workspace, reset = false) {
783
810
  }
784
811
  if (!reset && view.searchNextCursor) params.set('cursor', view.searchNextCursor)
785
812
  try {
786
- const result = await api(`document-index?${params}`)
813
+ const result = await api(`document-index?${params}`, { signal: controller.signal })
787
814
  if (request !== view.searchRequest || query !== view.query.trim()) return
788
815
  const merged = new Map((reset ? [] : view.searchResults).map(document => [document.id, document]))
789
816
  for (const item of result.items) merged.set(item.id, item)
@@ -791,9 +818,11 @@ async function loadDocumentSearch(workspace, reset = false) {
791
818
  view.searchNextCursor = result.nextCursor || ''
792
819
  view.searchTotal = result.total
793
820
  } catch (error) {
821
+ if (controller.signal.aborted) return
794
822
  if (request !== view.searchRequest) return
795
823
  view.searchError = friendlyError(error)
796
824
  } finally {
825
+ if (documentSearchController === controller) documentSearchController = null
797
826
  if (request === view.searchRequest) {
798
827
  view.searchLoading = false
799
828
  renderDocumentSearchState(workspace)
@@ -956,26 +985,119 @@ async function saveDocumentEditor(workspace = activeDocumentWorkspace()) {
956
985
  }
957
986
  }
958
987
 
988
+ function activateKnowledgeBaseDropTarget(event, workspace, baseId) {
989
+ const drag = knowledgeDocumentDrag
990
+ if (!drag || drag.workspaceKind !== workspace.kind || drag.sourceBaseId === baseId) return
991
+ if (!hasDragType(event, KNOWLEDGE_DOCUMENT_DRAG_TYPE)) return
992
+ event.preventDefault()
993
+ event.stopPropagation()
994
+ if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'
995
+ document.querySelectorAll('.note-tree-group[data-drop-target="true"]').forEach(target => {
996
+ if (target !== event.currentTarget) target.dataset.dropTarget = 'false'
997
+ })
998
+ event.currentTarget.dataset.dropTarget = 'true'
999
+ }
1000
+
1001
+ function clearKnowledgeDocumentDragState() {
1002
+ knowledgeDocumentDrag = null
1003
+ document.querySelectorAll('.note-tree-group[data-drop-target="true"]').forEach(node => { node.dataset.dropTarget = 'false' })
1004
+ document.querySelectorAll('.note-tree-document[data-dragging="true"]').forEach(node => { node.dataset.dragging = 'false' })
1005
+ }
1006
+
1007
+ function dropKnowledgeDocument(event, workspace, targetBaseId) {
1008
+ const documentId = event.dataTransfer?.getData(KNOWLEDGE_DOCUMENT_DRAG_TYPE) || ''
1009
+ const source = documentWorkspaceDocuments(workspace).find(document => document.id === documentId)
1010
+ if (!documentId || !source || source.knowledgeBaseId === targetBaseId) return
1011
+ event.preventDefault()
1012
+ event.stopPropagation()
1013
+ clearKnowledgeDocumentDragState()
1014
+ void moveKnowledgeDocument(workspace, documentId, targetBaseId)
1015
+ }
1016
+
1017
+ async function moveKnowledgeDocument(workspace, documentId, targetBaseId) {
1018
+ const bases = documentWorkspaceBases(workspace)
1019
+ const target = bases.find(base => base.id === targetBaseId && base.status === 'active')
1020
+ const summaries = documentWorkspaceDocuments(workspace)
1021
+ const current = summaries.find(document => document.id === documentId)
1022
+ const sourceBaseId = current?.knowledgeBaseId
1023
+ || (workspace.view.editor?.id === documentId ? workspace.view.editor.knowledgeBaseId : '')
1024
+ if (!target || !sourceBaseId || sourceBaseId === targetBaseId || movingDocumentId) return false
1025
+ const editor = workspace.view.editor
1026
+ if (editor?.id === documentId && editor.dirty && !await saveDocumentEditor(workspace)) return false
1027
+
1028
+ movingDocumentId = documentId
1029
+ renderShell()
1030
+ try {
1031
+ const saved = await api(`documents/${encodeURIComponent(documentId)}/move`, {
1032
+ method: 'POST', body: { knowledgeBaseId: targetBaseId },
1033
+ })
1034
+ const latest = documentWorkspaceDocuments(workspace)
1035
+ const summary = latest.find(document => document.id === documentId) || current
1036
+ if (summary) {
1037
+ setDocumentWorkspaceDocuments(workspace, [
1038
+ { ...summary, knowledgeBaseId: targetBaseId, updatedAt: saved.updatedAt },
1039
+ ...latest.filter(document => document.id !== documentId),
1040
+ ])
1041
+ }
1042
+ workspace.view.searchResults = workspace.view.searchResults.map(document => document.id === documentId
1043
+ ? { ...document, knowledgeBaseId: targetBaseId, updatedAt: saved.updatedAt }
1044
+ : document)
1045
+ const sourcePage = documentPageState(workspace, sourceBaseId)
1046
+ const targetPage = documentPageState(workspace, targetBaseId)
1047
+ if (sourcePage.loaded) sourcePage.total = Math.max(0, sourcePage.total - 1)
1048
+ if (targetPage.loaded) targetPage.total += 1
1049
+ workspace.view.knowledgeBaseId = targetBaseId
1050
+ workspace.view.expandedBases.add(targetBaseId)
1051
+ if (workspace.view.documentId === documentId && workspace.view.editor?.id === documentId) {
1052
+ workspace.view.editor = {
1053
+ ...workspace.view.editor,
1054
+ ...saved,
1055
+ tagsText: saved.tags.join(', '),
1056
+ dirty: false,
1057
+ isNew: false,
1058
+ saveState: '已移动',
1059
+ }
1060
+ }
1061
+ showToast(`已移动到“${target.name}”。`)
1062
+ return true
1063
+ } catch (error) {
1064
+ showToast(friendlyError(error), 'error')
1065
+ return false
1066
+ } finally {
1067
+ movingDocumentId = ''
1068
+ renderShell()
1069
+ }
1070
+ }
1071
+
1072
+ function openMoveKnowledgeDocument(workspace, editor) {
1073
+ const targets = documentWorkspaceBases(workspace).filter(base => base.status === 'active' && base.id !== editor.knowledgeBaseId)
1074
+ if (!targets.length) return showToast('当前没有其他可移动到的知识库。', 'error')
1075
+ const form = element('form', { class: 'form-grid' })
1076
+ const destination = selectField('目标知识库', targets.map(base => ({ value: base.id, label: base.name })), targets[0].id)
1077
+ destination.wrapper.classList.add('span-2')
1078
+ form.append(destination.wrapper)
1079
+ return openSheet({
1080
+ title: `移动“${editor.title}”`,
1081
+ description: '文档 ID、版本历史和关联笔记都会保留,Markdown 投影将同步移动。',
1082
+ body: form,
1083
+ primaryLabel: '移动文档',
1084
+ onPrimary: () => moveKnowledgeDocument(workspace, editor.id, destination.input.value),
1085
+ })
1086
+ }
1087
+
959
1088
  function updateEditorSaveState(label) {
960
1089
  const node = document.querySelector('.editor-save-status')
961
1090
  if (node) node.textContent = label
962
1091
  }
963
1092
 
964
1093
  async function loadCandidates(signal) {
965
- const [candidates] = await Promise.all([
966
- api(`candidates?status=${state.candidateStatus}&limit=100`, { signal }),
1094
+ const [payload] = await Promise.all([
1095
+ api(`candidates?status=${state.candidateStatus}&limit=100&includeTargets=1`, { signal }),
967
1096
  ensureKnowledgeBases(false, signal),
968
1097
  ])
1098
+ const candidates = payload.items
969
1099
  state.candidates = candidates
970
- const targetIds = [...new Set(candidates.map(candidate => candidate.targetId).filter(Boolean))]
971
- const targets = await Promise.all(targetIds.map(async id => {
972
- try { return [id, await api(`entries/${encodeURIComponent(id)}`, { signal })] }
973
- catch (error) {
974
- if (signal?.aborted) throw error
975
- return [id, null]
976
- }
977
- }))
978
- state.candidateTargets = new Map(targets)
1100
+ state.candidateTargets = new Map(payload.targets.map(target => [target.id, target]))
979
1101
  if (!state.stats) await refreshStats(signal)
980
1102
  }
981
1103
 
@@ -1616,6 +1738,7 @@ function renderDocumentWorkspace(workspace, options = {}) {
1616
1738
  const activeBases = documentWorkspaceBases(workspace)
1617
1739
  const workspaceDocuments = documentWorkspaceDocuments(workspace)
1618
1740
  const readOnly = documentWorkspaceReadOnly(workspace)
1741
+ const canOrganize = !readOnly && activeBases.length > 1
1619
1742
  const selectedBase = activeBases.find(base => base.id === view.knowledgeBaseId)
1620
1743
  const search = element('input', {
1621
1744
  class: 'note-tree-search', type: 'search', value: view.query, placeholder: '搜索文档', 'aria-label': '搜索知识库文档',
@@ -1639,6 +1762,11 @@ function renderDocumentWorkspace(workspace, options = {}) {
1639
1762
  const documents = (query ? view.searchResults : workspaceDocuments).filter(document => document.knowledgeBaseId === base.id)
1640
1763
  return element('section', {
1641
1764
  class: 'note-tree-group', 'data-expanded': String(expanded),
1765
+ 'data-base-id': base.id, 'data-drop-target': 'false',
1766
+ onDragEnter: event => activateKnowledgeBaseDropTarget(event, workspace, base.id),
1767
+ onDragOver: event => activateKnowledgeBaseDropTarget(event, workspace, base.id),
1768
+ onDragLeave: event => { if (!event.currentTarget.contains(event.relatedTarget)) event.currentTarget.dataset.dropTarget = 'false' },
1769
+ onDrop: event => dropKnowledgeDocument(event, workspace, base.id),
1642
1770
  },
1643
1771
  element('button', {
1644
1772
  type: 'button', class: 'note-tree-base', 'aria-expanded': String(expanded),
@@ -1659,8 +1787,18 @@ function renderDocumentWorkspace(workspace, options = {}) {
1659
1787
  actionButton('重试', () => { void loadDocumentPage(workspace, base.id, { reset: true }).then(renderShell) }, 'ghost small')) : null,
1660
1788
  documents.map(document => element('button', {
1661
1789
  type: 'button', class: 'note-tree-document', 'aria-current': document.id === view.documentId ? 'page' : undefined,
1790
+ draggable: canOrganize && movingDocumentId !== document.id ? 'true' : undefined,
1791
+ 'aria-busy': movingDocumentId === document.id ? 'true' : undefined,
1792
+ title: canOrganize ? `${document.title} · 拖到其他知识库以移动` : document.title,
1662
1793
  'data-document-id': document.id,
1663
1794
  'data-knowledge-motion-key': `knowledge-document:${workspace.kind}:${document.id}`,
1795
+ onDragStart: event => {
1796
+ knowledgeDocumentDrag = { documentId: document.id, sourceBaseId: base.id, workspaceKind: workspace.kind }
1797
+ event.dataTransfer.effectAllowed = 'move'
1798
+ event.dataTransfer.setData(KNOWLEDGE_DOCUMENT_DRAG_TYPE, document.id)
1799
+ event.currentTarget.dataset.dragging = 'true'
1800
+ },
1801
+ onDragEnd: clearKnowledgeDocumentDragState,
1664
1802
  onClick: () => { view.knowledgeBaseId = base.id; void selectDocument(workspace, document.id) },
1665
1803
  }, element('span', { class: 'tree-document-icon', 'aria-hidden': 'true' }), element('span', { class: 'tree-document-copy' },
1666
1804
  element('strong', {}, document.title), element('small', {}, document.relPath)),
@@ -1759,6 +1897,9 @@ function renderNoteEditor(workspace, editor, base) {
1759
1897
  element('div', { class: 'note-editor-actions' },
1760
1898
  finalized ? badge(DOCUMENT_STATE_LABELS[editor.documentState] || '已结束', 'success') : readOnly ? badge('只读') : null,
1761
1899
  element('span', { class: 'editor-save-status', role: 'status' }, editor.saveState),
1900
+ !readOnly && !editor.isNew && documentWorkspaceBases(workspace).some(item => item.id !== editor.knowledgeBaseId)
1901
+ ? actionButton('移动到…', () => openMoveKnowledgeDocument(workspace, editor), 'ghost small')
1902
+ : null,
1762
1903
  finalized && !readOnly ? actionButton('重新打开', () => reopenDocument(workspace, editor), 'small') : null,
1763
1904
  !readOnly && !editor.isNew && !finalized ? actionButton('标记结束', () => openFinalizeDocument(workspace, editor), 'small') : null,
1764
1905
  !readOnly && !editor.isNew && !finalized ? actionButton('删除', () => confirmDeleteDocument(workspace, editor), 'ghost small') : null,
@@ -2024,6 +2165,7 @@ function renderNotes() {
2024
2165
  actionButton('新建', () => openCreateNoteDocument(), 'primary small'),
2025
2166
  actionButton('目录', () => openCreateNoteFolder(), 'small'),
2026
2167
  actionButton('导入', () => fileInput.click(), 'ghost small'),
2168
+ actionButton('会话指令', openNoteAgentGuide, 'ghost small'),
2027
2169
  fileInput,
2028
2170
  ),
2029
2171
  ),
@@ -2048,6 +2190,41 @@ function renderNotes() {
2048
2190
  return element('section', { class: 'notes-page', 'aria-label': '笔记工作区' }, workspace)
2049
2191
  }
2050
2192
 
2193
+ function openNoteAgentGuide() {
2194
+ const capabilityRows = [
2195
+ ['笔记文档', '创建、追加或替换内容、重命名、移动、删除'],
2196
+ ['笔记目录', '创建、重命名、移动、删除'],
2197
+ ].map(([label, detail]) => element('div', { class: 'notes-agent-capability' },
2198
+ element('strong', {}, label), element('span', {}, detail)))
2199
+ const examples = [
2200
+ ['创建', '在笔记工作区的「发布资料」目录里,新建笔记文档「上线检查.md」,内容是:……'],
2201
+ ['更新', '把笔记文档「发布资料/上线检查.md」追加以下内容:……'],
2202
+ ['目录', '把笔记目录「发布资料」重命名为「发布归档」。'],
2203
+ ].map(([label, text]) => element('li', {}, element('span', {}, label), element('code', {}, text)))
2204
+ const body = element('div', { class: 'notes-agent-guide' },
2205
+ element('section', { class: 'notes-agent-guide-rule' },
2206
+ element('span', { class: 'notes-agent-guide-index', 'aria-hidden': 'true' }, '01'),
2207
+ element('div', {},
2208
+ element('h3', {}, '在当前消息里明确授权'),
2209
+ element('p', {}, '写清动作、对象和位置,并明确使用“笔记文档”“笔记目录”或“笔记工作区”。为了避免误写,不能只依赖上一轮说过的授权。'))),
2210
+ element('section', { class: 'notes-agent-guide-rule' },
2211
+ element('span', { class: 'notes-agent-guide-index', 'aria-hidden': 'true' }, '02'),
2212
+ element('div', {},
2213
+ element('h3', {}, '名称足够,重名时补全路径'),
2214
+ element('p', {}, '不需要填写内部编号。会话会先浏览笔记目录定位目标;如果有重名,请写成“目录/子目录/文档名”。'))),
2215
+ element('div', { class: 'notes-agent-capabilities', 'aria-label': '会话可执行的笔记操作' }, capabilityRows),
2216
+ element('section', { class: 'notes-agent-examples' },
2217
+ element('h3', {}, '可以直接这样说'),
2218
+ element('ul', {}, examples)),
2219
+ element('p', { class: 'notes-agent-guide-note' }, '只说“新建 Markdown”或“创建本地目录”不会获得笔记写入权限;请明确指出这是笔记工作区中的文档或目录。'))
2220
+ return openSheet({
2221
+ title: '让会话整理笔记',
2222
+ description: '会话可以操作笔记工作区,但每次写入都需要当前用户消息明确授权。',
2223
+ body,
2224
+ cancelLabel: '知道了',
2225
+ })
2226
+ }
2227
+
2051
2228
  function renderNoteTransfer() {
2052
2229
  const transfer = state.notes.transfer
2053
2230
  if (!transfer) return null
@@ -2563,11 +2740,24 @@ function releaseNoteAsset() {
2563
2740
  function scheduleNoteSearch(value) {
2564
2741
  state.notes.query = value
2565
2742
  window.clearTimeout(noteSearchTimer)
2743
+ noteSearchController?.abort()
2744
+ noteSearchController = null
2745
+ const request = ++noteSearchRequest
2566
2746
  noteSearchTimer = window.setTimeout(async () => {
2747
+ const controller = new AbortController()
2748
+ noteSearchController = controller
2567
2749
  try {
2568
- state.notes.searchResults = value.trim() ? await api(`notes?q=${encodeURIComponent(value.trim())}&limit=200`) : []
2750
+ const results = value.trim()
2751
+ ? await api(`notes?q=${encodeURIComponent(value.trim())}&limit=200`, { signal: controller.signal })
2752
+ : []
2753
+ if (request !== noteSearchRequest || value !== state.notes.query) return
2754
+ state.notes.searchResults = results
2569
2755
  renderShell()
2570
- } catch (error) { showToast(friendlyError(error), 'error') }
2756
+ } catch (error) {
2757
+ if (!controller.signal.aborted && request === noteSearchRequest) showToast(friendlyError(error), 'error')
2758
+ } finally {
2759
+ if (noteSearchController === controller) noteSearchController = null
2760
+ }
2571
2761
  }, 180)
2572
2762
  }
2573
2763
 
@@ -3024,12 +3214,16 @@ function clearNoteDragState() {
3024
3214
  document.querySelectorAll('.notes-tree-item[data-dragging="true"]').forEach(node => { node.dataset.dragging = 'false' })
3025
3215
  }
3026
3216
 
3027
- function installNoteDragRecovery() {
3028
- window.addEventListener('drop', clearNoteDragState, true)
3029
- window.addEventListener('dragend', clearNoteDragState, true)
3030
- window.addEventListener('blur', clearNoteDragState)
3217
+ function installDragRecovery() {
3218
+ const clear = () => {
3219
+ clearNoteDragState()
3220
+ clearKnowledgeDocumentDragState()
3221
+ }
3222
+ window.addEventListener('drop', clear, true)
3223
+ window.addEventListener('dragend', clear, true)
3224
+ window.addEventListener('blur', clear)
3031
3225
  document.addEventListener('dragleave', event => {
3032
- if (event.relatedTarget === null) clearNoteDragState()
3226
+ if (event.relatedTarget === null) clear()
3033
3227
  }, true)
3034
3228
  }
3035
3229
 
@@ -3924,5 +4118,5 @@ window.addEventListener('beforeunload', event => {
3924
4118
  event.returnValue = ''
3925
4119
  })
3926
4120
 
3927
- installNoteDragRecovery()
4121
+ installDragRecovery()
3928
4122
  void installHostThemeBridge().then(() => boot())
package/web/styles.css CHANGED
@@ -433,7 +433,7 @@ html,
433
433
  border-radius: 20px;
434
434
  overflow: hidden;
435
435
  background: var(--material-sidebar);
436
- box-shadow: inset -1px 0 0 var(--glass-rim-shadow), inset 1px 0 0 var(--glass-rim-light), 10px 0 36px rgb(0 0 0 / 3%);
436
+ box-shadow: 10px 0 36px rgb(0 0 0 / 3%);
437
437
  -webkit-backdrop-filter: var(--glass-filter);
438
438
  backdrop-filter: var(--glass-filter);
439
439
  transform-origin: left center;
@@ -562,7 +562,7 @@ html,
562
562
  padding: 14px clamp(18px, 3vw, 38px);
563
563
  overflow: hidden;
564
564
  background: var(--material-toolbar);
565
- box-shadow: inset 0 1px 0 var(--glass-rim-light), inset 0 -1px 0 var(--glass-rim-shadow);
565
+ box-shadow: 0 8px 24px rgb(0 0 0 / 3%);
566
566
  -webkit-backdrop-filter: var(--glass-filter);
567
567
  backdrop-filter: var(--glass-filter);
568
568
  }
@@ -840,7 +840,7 @@ html,
840
840
  overflow: hidden;
841
841
  border-right: 1px solid var(--glass-separator);
842
842
  background: var(--surface-pane);
843
- box-shadow: inset 1px 0 0 var(--glass-rim-light);
843
+ box-shadow: none;
844
844
  -webkit-backdrop-filter: var(--glass-filter);
845
845
  backdrop-filter: var(--glass-filter);
846
846
  }
@@ -862,8 +862,11 @@ html,
862
862
  }
863
863
  .note-tree-group + .note-tree-group { margin-top: 3px; }
864
864
  .note-tree-base, .note-tree-document, .note-tree-new { width: 100%; display: flex; align-items: center; border: 0; background: transparent; color: var(--text-secondary); text-align: left; }
865
- .note-tree-base { min-height: 36px; gap: 7px; border-radius: 9px; padding: 5px 7px; font-weight: 620; transition: background var(--motion-fast) ease, color var(--motion-fast) ease, transform var(--motion-base) var(--ease-out); }
865
+ .note-tree-base { position: relative; min-height: 36px; gap: 7px; border-radius: 9px; padding: 5px 7px; font-weight: 620; transition: background var(--motion-fast) ease, color var(--motion-fast) ease, transform var(--motion-base) var(--ease-out), box-shadow var(--motion-fast) ease; }
866
866
  .note-tree-base:hover, .note-tree-document:hover, .note-tree-new:hover { background: var(--surface-hover); color: var(--text); transform: translateX(2px); }
867
+ .note-tree-group[data-drop-target="true"] > .note-tree-base { background: color-mix(in srgb, var(--accent) 13%, var(--surface)); color: var(--text); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 44%, transparent), var(--shadow-control); transform: translateX(2px); }
868
+ .note-tree-group[data-drop-target="true"] > .note-tree-base .tree-count { visibility: hidden; }
869
+ .note-tree-group[data-drop-target="true"] > .note-tree-base::after { content: "移动到这里"; position: absolute; right: 7px; border-radius: 5px; padding: 2px 6px; background: var(--surface-raised); color: var(--accent); font-size: 9px; font-weight: 650; pointer-events: none; }
867
870
  .tree-disclosure { width: 7px; height: 7px; flex: none; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); transition: transform .16s ease; }
868
871
  .note-tree-group[data-expanded="true"] .tree-disclosure { transform: rotate(45deg) translate(-1px, -1px); }
869
872
  .tree-folder-icon {
@@ -882,6 +885,10 @@ html,
882
885
  .tree-count { min-width: 20px; color: var(--text-tertiary); font-size: 10px; font-variant-numeric: tabular-nums; text-align: right; }
883
886
  .note-tree-documents { display: grid; gap: 1px; margin: 1px 0 5px 18px; padding-left: 10px; border-left: 1px solid color-mix(in srgb, var(--border-strong) 55%, transparent); }
884
887
  .note-tree-document { position: relative; min-height: 40px; gap: 8px; border-radius: 9px; padding: 5px 8px; transition: background var(--motion-fast) ease, color var(--motion-fast) ease, transform var(--motion-base) var(--ease-out), box-shadow var(--motion-base) ease; }
888
+ .note-tree-document[draggable="true"] { cursor: grab; }
889
+ .note-tree-document[draggable="true"]:active { cursor: grabbing; }
890
+ .note-tree-document[data-dragging="true"] { opacity: .42; transform: translateX(5px) scale(.985); }
891
+ .note-tree-document[aria-busy="true"] { opacity: .58; pointer-events: none; }
885
892
  .note-tree-document[aria-current="page"] { background: var(--material-control); color: var(--text); box-shadow: inset 2px 0 0 color-mix(in srgb, var(--text-secondary) 74%, transparent), var(--shadow-control); transform: translateX(2px); }
886
893
  .tree-document-icon {
887
894
  width: 14px;
@@ -920,12 +927,27 @@ html,
920
927
  overflow: hidden;
921
928
  background: var(--surface-editor);
922
929
  }
923
- .notes-browser { min-width: 0; min-height: 0; display: grid; grid-template-rows: auto auto minmax(0, 1fr); overflow: hidden; border-right: 1px solid var(--glass-separator); background: var(--surface-pane); box-shadow: inset 1px 0 0 var(--glass-rim-light); -webkit-backdrop-filter: var(--glass-filter); backdrop-filter: var(--glass-filter); }
930
+ .notes-browser { min-width: 0; min-height: 0; display: grid; grid-template-rows: auto auto minmax(0, 1fr); overflow: hidden; border-right: 1px solid var(--glass-separator); background: var(--surface-pane); box-shadow: none; -webkit-backdrop-filter: var(--glass-filter); backdrop-filter: var(--glass-filter); }
924
931
  .notes-browser-header { min-height: 68px; display: grid; gap: 8px; padding: 10px 10px 7px; }
925
932
  .notes-browser-header > div:first-child { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
926
933
  .notes-browser-header h2 { margin: 0; font-size: 13px; font-weight: 650; }
927
934
  .notes-browser-header span { color: var(--text-tertiary); font-size: 10px; }
928
935
  .notes-browser-actions { display: flex; align-items: center; gap: 4px; }
936
+ .notes-agent-guide { display: grid; gap: 18px; color: var(--text-secondary); }
937
+ .notes-agent-guide-rule { display: grid; grid-template-columns: 30px minmax(0, 1fr); gap: 12px; align-items: start; }
938
+ .notes-agent-guide-index { display: grid; place-items: center; width: 30px; height: 30px; border: 1px solid var(--glass-border); border-radius: 9px; background: var(--material-control); color: var(--text-tertiary); font: 650 9px/1 var(--font-mono); box-shadow: var(--shadow-control); }
939
+ .notes-agent-guide h3 { margin: 0 0 5px; color: var(--text); font-size: 13px; }
940
+ .notes-agent-guide p { margin: 0; font-size: 12px; line-height: 1.65; }
941
+ .notes-agent-capabilities { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
942
+ .notes-agent-capability { min-width: 0; display: grid; gap: 4px; border: 1px solid var(--glass-border); border-radius: 11px; padding: 12px; background: var(--material-panel); box-shadow: var(--shadow-soft); }
943
+ .notes-agent-capability strong { color: var(--text); font-size: 12px; }
944
+ .notes-agent-capability span { color: var(--text-tertiary); font-size: 10px; line-height: 1.55; }
945
+ .notes-agent-examples { display: grid; gap: 8px; }
946
+ .notes-agent-examples ul { display: grid; gap: 7px; margin: 0; padding: 0; list-style: none; }
947
+ .notes-agent-examples li { min-width: 0; display: grid; grid-template-columns: 42px minmax(0, 1fr); gap: 9px; align-items: start; }
948
+ .notes-agent-examples li > span { border-radius: 6px; padding: 4px 5px; background: var(--surface-soft); color: var(--text-tertiary); font-size: 9px; font-weight: 650; text-align: center; }
949
+ .notes-agent-examples code { min-width: 0; border: 1px solid var(--glass-border); border-radius: 8px; padding: 8px 10px; background: var(--surface-editor); color: var(--text-secondary); font: 11px/1.6 var(--font-mono); white-space: normal; overflow-wrap: anywhere; }
950
+ .notes-agent-guide-note { border-left: 2px solid var(--border-strong); padding: 7px 0 7px 11px; color: var(--text-tertiary); }
929
951
  .notes-search { padding: 1px 10px 7px; }
930
952
  .notes-search .input { width: 100%; height: 34px; }
931
953
  .notes-tree { min-width: 0; max-width: 100%; min-height: 0; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; padding: 2px 5px 16px; }
@@ -1533,10 +1555,7 @@ html,
1533
1555
 
1534
1556
  .sidebar.knowledge-glass-surface {
1535
1557
  background: var(--liquid-glass-large);
1536
- box-shadow:
1537
- inset 1px 0 0 var(--glass-rim-light),
1538
- inset -1px 0 0 var(--glass-rim-shadow),
1539
- 10px 0 36px rgb(0 0 0 / 4%);
1558
+ box-shadow: 10px 0 36px rgb(0 0 0 / 4%);
1540
1559
  }
1541
1560
 
1542
1561
  .topbar.knowledge-glass-surface,
@@ -1544,19 +1563,13 @@ html,
1544
1563
  .notes-document-toolbar.knowledge-glass-surface,
1545
1564
  .notes-content-header.knowledge-glass-surface {
1546
1565
  background: var(--liquid-glass-regular);
1547
- box-shadow:
1548
- inset 0 1px 0 var(--glass-rim-light),
1549
- inset 0 -1px 0 var(--glass-rim-shadow),
1550
- 0 8px 24px rgb(0 0 0 / 3%);
1566
+ box-shadow: 0 8px 24px rgb(0 0 0 / 3%);
1551
1567
  }
1552
1568
 
1553
1569
  .note-tree-panel.knowledge-glass-surface,
1554
1570
  .notes-browser.knowledge-glass-surface {
1555
1571
  background: var(--liquid-glass-clear);
1556
- box-shadow:
1557
- inset 1px 0 0 var(--glass-rim-light),
1558
- inset -1px 0 0 var(--glass-rim-shadow),
1559
- 10px 0 28px rgb(0 0 0 / 3.5%);
1572
+ box-shadow: none;
1560
1573
  }
1561
1574
 
1562
1575
  .dialog.knowledge-glass-surface {
@@ -1933,6 +1946,7 @@ html,
1933
1946
  .candidate-actions .button { flex: 1 1 auto; }
1934
1947
  .notes-heading { align-items: flex-start; }
1935
1948
  .notes-browser-actions { flex-wrap: wrap; }
1949
+ .notes-agent-capabilities { grid-template-columns: 1fr; }
1936
1950
  .notes-content-title { align-items: stretch; flex-direction: column; }
1937
1951
  .notes-content-actions .button { flex: 1 1 auto; }
1938
1952
  .notes-document-toolbar { align-items: center; flex-direction: row; overflow-x: auto; }
@@ -1,4 +1,4 @@
1
- "use strict";(()=>{var M="http://www.w3.org/2000/svg",x=[".sidebar",".topbar",".note-tree-panel",".notes-browser",".notes-content-header",".notes-document-toolbar",".note-editor-toolbar",".dialog"].join(","),L=[".login-card",".metric",".panel",".library-detail-header",".mount-manager",".knowledge-card",".candidate",".api-access-card"].join(","),A=[".mount-table",".note-workspace",".notes-workspace",".dialog",L].join(","),R=[".button",".nav-button",".tab",".pane-toggle-button",".note-tree-base",".note-tree-document",".note-tree-new",".notes-tree-row",".notes-search-row",".notes-file-main"].join(","),G=[".note-tree",".notes-tree",".base-grid",".mount-table"].join(","),T="[data-knowledge-motion-key]",C=700,F=38,m=new Map,v=new Map,u=new Map,g=new Map,P=0,d,$=window.matchMedia("(prefers-reduced-motion: reduce)"),O=window.matchMedia("(prefers-reduced-transparency: reduce)");function b(e,t){let n=Array.from(e.querySelectorAll(t));return e instanceof HTMLElement&&e.matches(t)&&n.unshift(e),n}function H(){return d?.isConnected||(d=document.createElementNS(M,"svg"),d.classList.add("knowledge-glass-filter-bank"),d.setAttribute("aria-hidden","true"),d.setAttribute("focusable","false"),document.body.append(d)),d}function h(e,t){let n=document.createElementNS(M,e);for(let[o,s]of Object.entries(t))n.setAttribute(o,s);return n}function _(e){let t=h("filter",{id:e,x:"0%",y:"0%",width:"100%",height:"100%","color-interpolation-filters":"sRGB"}),n=h("feImage",{x:"0",y:"0",width:"100%",height:"100%",preserveAspectRatio:"none",result:"map"});return t.append(n,h("feGaussianBlur",{in:"SourceGraphic",stdDeviation:"6",result:"frosted"})),t.append(h("feDisplacementMap",{in:"frosted",in2:"map",scale:"-16",xChannelSelector:"R",yChannelSelector:"G",result:"refracted"}),h("feGaussianBlur",{in:"refracted",stdDeviation:".12"})),{filter:t,image:n}}function N(){if(O.matches)return!1;let e=navigator.userAgent;return/Firefox/i.test(e)||/Safari/i.test(e)&&!/(Chrome|Chromium|CriOS)/i.test(e)?!1:CSS.supports?.("backdrop-filter","url(#knowledge-glass-support)")||CSS.supports?.("-webkit-backdrop-filter","url(#knowledge-glass-support)")||!1}function I(e,t,n){let o=Number.parseFloat(getComputedStyle(e).borderTopLeftRadius);return Number.isFinite(o)?Math.max(0,Math.min(o,Math.min(t,n)/2)):Math.min(t,n)*.16}function D(e,t,n){let o=Math.min(12,Math.max(4,Math.min(e,t)*.08)),s=`<svg viewBox="0 0 ${e} ${t}" xmlns="${M}">
1
+ "use strict";(()=>{var M="http://www.w3.org/2000/svg",x=[".sidebar",".topbar",".note-tree-panel",".notes-browser",".notes-content-header",".notes-document-toolbar",".note-editor-toolbar",".dialog"].join(","),A=[".sidebar",".topbar",".note-tree-panel",".notes-browser"].join(","),L=[".login-card",".metric",".panel",".library-detail-header",".mount-manager",".knowledge-card",".candidate",".api-access-card"].join(","),R=[".mount-table",".note-workspace",".notes-workspace",".dialog",L].join(","),T=[".button",".nav-button",".tab",".pane-toggle-button",".note-tree-base",".note-tree-document",".note-tree-new",".notes-tree-row",".notes-search-row",".notes-file-main"].join(","),C=[".note-tree",".notes-tree",".base-grid",".mount-table"].join(","),G="[data-knowledge-motion-key]",F=700,P=38,m=new Map,b=new Map,u=new Map,g=new Map,$=0,d,O=window.matchMedia("(prefers-reduced-motion: reduce)"),_=window.matchMedia("(prefers-reduced-transparency: reduce)");function v(e,t){let n=Array.from(e.querySelectorAll(t));return e instanceof HTMLElement&&e.matches(t)&&n.unshift(e),n}function H(){return d?.isConnected||(d=document.createElementNS(M,"svg"),d.classList.add("knowledge-glass-filter-bank"),d.setAttribute("aria-hidden","true"),d.setAttribute("focusable","false"),document.body.append(d)),d}function h(e,t){let n=document.createElementNS(M,e);for(let[o,s]of Object.entries(t))n.setAttribute(o,s);return n}function N(e){let t=h("filter",{id:e,x:"0%",y:"0%",width:"100%",height:"100%","color-interpolation-filters":"sRGB"}),n=h("feImage",{x:"0",y:"0",width:"100%",height:"100%",preserveAspectRatio:"none",result:"map"});return t.append(n,h("feGaussianBlur",{in:"SourceGraphic",stdDeviation:"6",result:"frosted"})),t.append(h("feDisplacementMap",{in:"frosted",in2:"map",scale:"-16",xChannelSelector:"R",yChannelSelector:"G",result:"refracted"}),h("feGaussianBlur",{in:"refracted",stdDeviation:".12"})),{filter:t,image:n}}function I(){if(_.matches)return!1;let e=navigator.userAgent;return/Firefox/i.test(e)||/Safari/i.test(e)&&!/(Chrome|Chromium|CriOS)/i.test(e)?!1:CSS.supports?.("backdrop-filter","url(#knowledge-glass-support)")||CSS.supports?.("-webkit-backdrop-filter","url(#knowledge-glass-support)")||!1}function D(e,t,n){let o=Number.parseFloat(getComputedStyle(e).borderTopLeftRadius);return Number.isFinite(o)?Math.max(0,Math.min(o,Math.min(t,n)/2)):Math.min(t,n)*.16}function B(e,t,n){let o=Math.min(12,Math.max(4,Math.min(e,t)*.08)),s=`<svg viewBox="0 0 ${e} ${t}" xmlns="${M}">
2
2
  <defs>
3
3
  <clipPath id="shape"><rect width="${e}" height="${t}" rx="${n}"/></clipPath>
4
4
  <linearGradient id="left" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="rgb(92 128 128)"/><stop offset="100%" stop-color="rgb(128 128 128)"/></linearGradient>
@@ -13,4 +13,4 @@
13
13
  <rect width="${e}" height="${o}" fill="url(#top)"/>
14
14
  <rect y="${t-o}" width="${e}" height="${o}" fill="url(#bottom)"/>
15
15
  </g>
16
- </svg>`;return`data:image/svg+xml,${encodeURIComponent(s)}`}function B(e){if(m.has(e))return;if(e.classList.add("knowledge-glass-surface"),!N()){e.dataset.knowledgeGlass="fallback",m.set(e,{filter:void 0,observer:void 0,frame:0});return}let t=`knowledge-glass-filter-${++P}`,{filter:n,image:o}=_(t);H().append(n),e.dataset.knowledgeGlass="svg",e.style.setProperty("--knowledge-glass-filter",`url(#${t})`);let s={filter:n,observer:void 0,frame:0},c=()=>{if(s.frame=0,!e.isConnected)return;let a=e.getBoundingClientRect(),l=Math.max(1,Math.round(a.width)),i=Math.max(1,Math.round(a.height));o.setAttribute("href",D(l,i,I(e,l,i)))},r=()=>{s.frame===0&&(s.frame=requestAnimationFrame(c))};s.observer=typeof ResizeObserver>"u"?void 0:new ResizeObserver(r),s.observer?.observe(e),s.frame=requestAnimationFrame(c),m.set(e,s)}function V(e){if(v.has(e))return;e.classList.add("knowledge-border-surface"),e.style.setProperty("--knowledge-pointer-x","50%"),e.style.setProperty("--knowledge-pointer-y","50%");let t=0,n,o=()=>{if(t=0,n===void 0||!e.isConnected)return;let r=e.getBoundingClientRect(),a=Math.min(r.width,Math.max(0,n.x-r.left)),l=Math.min(r.height,Math.max(0,n.y-r.top)),i=Math.min(a,r.width-a,l,r.height-l),y=Math.min(1,Math.max(0,1-i/F)),w=Math.atan2(l-r.height/2,a-r.width/2)*(180/Math.PI)+90;e.style.setProperty("--knowledge-pointer-x",`${(a/Math.max(1,r.width)*100).toFixed(2)}%`),e.style.setProperty("--knowledge-pointer-y",`${(l/Math.max(1,r.height)*100).toFixed(2)}%`),e.style.setProperty("--knowledge-border-proximity",y.toFixed(3)),e.style.setProperty("--knowledge-border-angle",`${w.toFixed(2)}deg`)},s=r=>{n={x:r.clientX,y:r.clientY},t===0&&(t=requestAnimationFrame(o))},c=()=>{n=void 0,e.style.setProperty("--knowledge-border-proximity","0")};e.addEventListener("pointermove",s,{passive:!0}),e.addEventListener("pointerleave",c,{passive:!0}),v.set(e,()=>{t!==0&&cancelAnimationFrame(t),e.removeEventListener("pointermove",s),e.removeEventListener("pointerleave",c)})}function q(e,t){let n=e.dataset.knowledgeMotionKey;if(n===void 0)return!1;let o=g.get(n);return o!==void 0&&t-o<C}function K(e,t){let n=e.dataset.knowledgeMotionKey;if(n===void 0||(g.set(n,t),g.size<=4e3))return;let o=g.keys().next().value;o!==void 0&&g.delete(o)}function k(e){let t=e.scrollHeight-e.clientHeight-e.scrollTop;e.dataset.scrollTop=String(e.scrollTop<=2),e.dataset.scrollBottom=String(t<=2)}function j(e){if(u.has(e))return;let t=e.matches(".base-grid");e.classList.add("knowledge-animated-list"),e.dataset.knowledgeListLayout=t?"grid":"scroll",k(e);let n=()=>k(e);e.addEventListener("scroll",n,{passive:!0});let o=Array.from(e.querySelectorAll(T));if($.matches||typeof IntersectionObserver>"u"){for(let i of o)i.classList.add("knowledge-list-reveal","is-visible");u.set(e,()=>e.removeEventListener("scroll",n));return}let s=e.scrollHeight-e.clientHeight>2?e:null,c=new IntersectionObserver(i=>{let y=i.filter(f=>f.isIntersecting).sort((f,p)=>f.boundingClientRect.top-p.boundingClientRect.top),w=performance.now();y.forEach((f,p)=>{let E=f.target,S=t?52:38;E.style.setProperty("--knowledge-list-delay",`${Math.min(p,7)*S}ms`),E.classList.add("is-visible"),K(E,w)});for(let f of i){if(f.isIntersecting)continue;let p=f.target;p.classList.remove("is-visible"),p.style.setProperty("--knowledge-list-delay","0ms")}},{root:s,threshold:.12,rootMargin:"-4px 0px"}),r=performance.now();for(let i of o)i.classList.add("knowledge-list-reveal"),!t&&q(i,r)&&i.classList.add("is-visible");let a=0,l=0;a=requestAnimationFrame(()=>{a=0,l=requestAnimationFrame(()=>{l=0;for(let i of o)c.observe(i)})}),u.set(e,()=>{a!==0&&cancelAnimationFrame(a),l!==0&&cancelAnimationFrame(l),c.disconnect(),e.removeEventListener("scroll",n)})}function U(){for(let[e,t]of m)e.isConnected||(t.frame!==0&&cancelAnimationFrame(t.frame),t.observer?.disconnect(),t.filter?.remove(),m.delete(e));for(let[e,t]of v)e.isConnected||(t(),v.delete(e));for(let[e,t]of u)e.isConnected||(t(),u.delete(e))}function W(e=document){U();for(let t of b(e,x))B(t);for(let t of b(e,L))t.classList.add("knowledge-card-surface");for(let t of b(e,A))V(t);for(let t of b(e,R))t.classList.add("knowledge-glare-surface");for(let t of b(e,G))j(t)}function z(){for(let[e,t]of m)t.frame!==0&&cancelAnimationFrame(t.frame),t.observer?.disconnect(),t.filter?.remove(),e.classList.remove("knowledge-glass-surface"),e.removeAttribute("data-knowledge-glass"),e.style.removeProperty("--knowledge-glass-filter");for(let[e,t]of v)t(),e.classList.remove("knowledge-border-surface"),e.classList.remove("knowledge-card-surface"),e.style.removeProperty("--knowledge-pointer-x"),e.style.removeProperty("--knowledge-pointer-y");for(let[e,t]of u)t(),e.classList.remove("knowledge-animated-list");m.clear(),v.clear(),u.clear(),g.clear(),d?.remove(),d=void 0}window.DshKnowledgeEffects?.destroy();window.DshKnowledgeEffects={refresh:W,destroy:z};})();
16
+ </svg>`;return`data:image/svg+xml,${encodeURIComponent(s)}`}function V(e){if(m.has(e))return;if(e.classList.add("knowledge-glass-surface"),e.matches(A)||!I()){e.dataset.knowledgeGlass="fallback",m.set(e,{filter:void 0,observer:void 0,frame:0});return}let t=`knowledge-glass-filter-${++$}`,{filter:n,image:o}=N(t);H().append(n),e.dataset.knowledgeGlass="svg",e.style.setProperty("--knowledge-glass-filter",`url(#${t})`);let s={filter:n,observer:void 0,frame:0},c=()=>{if(s.frame=0,!e.isConnected)return;let a=e.getBoundingClientRect(),l=Math.max(1,Math.round(a.width)),i=Math.max(1,Math.round(a.height));o.setAttribute("href",B(l,i,D(e,l,i)))},r=()=>{s.frame===0&&(s.frame=requestAnimationFrame(c))};s.observer=typeof ResizeObserver>"u"?void 0:new ResizeObserver(r),s.observer?.observe(e),s.frame=requestAnimationFrame(c),m.set(e,s)}function q(e){if(b.has(e))return;e.classList.add("knowledge-border-surface"),e.style.setProperty("--knowledge-pointer-x","50%"),e.style.setProperty("--knowledge-pointer-y","50%");let t=0,n,o=()=>{if(t=0,n===void 0||!e.isConnected)return;let r=e.getBoundingClientRect(),a=Math.min(r.width,Math.max(0,n.x-r.left)),l=Math.min(r.height,Math.max(0,n.y-r.top)),i=Math.min(a,r.width-a,l,r.height-l),y=Math.min(1,Math.max(0,1-i/P)),w=Math.atan2(l-r.height/2,a-r.width/2)*(180/Math.PI)+90;e.style.setProperty("--knowledge-pointer-x",`${(a/Math.max(1,r.width)*100).toFixed(2)}%`),e.style.setProperty("--knowledge-pointer-y",`${(l/Math.max(1,r.height)*100).toFixed(2)}%`),e.style.setProperty("--knowledge-border-proximity",y.toFixed(3)),e.style.setProperty("--knowledge-border-angle",`${w.toFixed(2)}deg`)},s=r=>{n={x:r.clientX,y:r.clientY},t===0&&(t=requestAnimationFrame(o))},c=()=>{n=void 0,e.style.setProperty("--knowledge-border-proximity","0")};e.addEventListener("pointermove",s,{passive:!0}),e.addEventListener("pointerleave",c,{passive:!0}),b.set(e,()=>{t!==0&&cancelAnimationFrame(t),e.removeEventListener("pointermove",s),e.removeEventListener("pointerleave",c)})}function K(e,t){let n=e.dataset.knowledgeMotionKey;if(n===void 0)return!1;let o=g.get(n);return o!==void 0&&t-o<F}function U(e,t){let n=e.dataset.knowledgeMotionKey;if(n===void 0||(g.set(n,t),g.size<=4e3))return;let o=g.keys().next().value;o!==void 0&&g.delete(o)}function S(e){let t=e.scrollHeight-e.clientHeight-e.scrollTop;e.dataset.scrollTop=String(e.scrollTop<=2),e.dataset.scrollBottom=String(t<=2)}function j(e){if(u.has(e))return;let t=e.matches(".base-grid");e.classList.add("knowledge-animated-list"),e.dataset.knowledgeListLayout=t?"grid":"scroll",S(e);let n=()=>S(e);e.addEventListener("scroll",n,{passive:!0});let o=Array.from(e.querySelectorAll(G));if(O.matches||typeof IntersectionObserver>"u"){for(let i of o)i.classList.add("knowledge-list-reveal","is-visible");u.set(e,()=>e.removeEventListener("scroll",n));return}let s=e.scrollHeight-e.clientHeight>2?e:null,c=new IntersectionObserver(i=>{let y=i.filter(f=>f.isIntersecting).sort((f,p)=>f.boundingClientRect.top-p.boundingClientRect.top),w=performance.now();y.forEach((f,p)=>{let E=f.target,k=t?52:38;E.style.setProperty("--knowledge-list-delay",`${Math.min(p,7)*k}ms`),E.classList.add("is-visible"),U(E,w)});for(let f of i){if(f.isIntersecting)continue;let p=f.target;p.classList.remove("is-visible"),p.style.setProperty("--knowledge-list-delay","0ms")}},{root:s,threshold:.12,rootMargin:"-4px 0px"}),r=performance.now();for(let i of o)i.classList.add("knowledge-list-reveal"),!t&&K(i,r)&&i.classList.add("is-visible");let a=0,l=0;a=requestAnimationFrame(()=>{a=0,l=requestAnimationFrame(()=>{l=0;for(let i of o)c.observe(i)})}),u.set(e,()=>{a!==0&&cancelAnimationFrame(a),l!==0&&cancelAnimationFrame(l),c.disconnect(),e.removeEventListener("scroll",n)})}function W(){for(let[e,t]of m)e.isConnected||(t.frame!==0&&cancelAnimationFrame(t.frame),t.observer?.disconnect(),t.filter?.remove(),m.delete(e));for(let[e,t]of b)e.isConnected||(t(),b.delete(e));for(let[e,t]of u)e.isConnected||(t(),u.delete(e))}function z(e=document){W();for(let t of v(e,x))V(t);for(let t of v(e,L))t.classList.add("knowledge-card-surface");for(let t of v(e,R))q(t);for(let t of v(e,T))t.classList.add("knowledge-glare-surface");for(let t of v(e,C))j(t)}function Y(){for(let[e,t]of m)t.frame!==0&&cancelAnimationFrame(t.frame),t.observer?.disconnect(),t.filter?.remove(),e.classList.remove("knowledge-glass-surface"),e.removeAttribute("data-knowledge-glass"),e.style.removeProperty("--knowledge-glass-filter");for(let[e,t]of b)t(),e.classList.remove("knowledge-border-surface"),e.classList.remove("knowledge-card-surface"),e.style.removeProperty("--knowledge-pointer-x"),e.style.removeProperty("--knowledge-pointer-y");for(let[e,t]of u)t(),e.classList.remove("knowledge-animated-list");m.clear(),b.clear(),u.clear(),g.clear(),d?.remove(),d=void 0}window.DshKnowledgeEffects?.destroy();window.DshKnowledgeEffects={refresh:z,destroy:Y};})();