@lemoncat7/dsh-knowledge 2.0.0 → 2.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.
package/web/app.js CHANGED
@@ -6,7 +6,7 @@ const HOST_THEME_MESSAGE = '@lemoncat7/dsh-knowledge/host-theme'
6
6
  const HOST_THEME_READY_MESSAGE = '@lemoncat7/dsh-knowledge/host-theme-ready'
7
7
  const HOST_THEME_PROTOCOL_VERSION = 1
8
8
  const HOST_THEME_COLOR_TOKENS = new Set([
9
- '--bg', '--surface', '--surface-raised', '--surface-soft', '--surface-hover',
9
+ '--bg', '--surface', '--surface-raised', '--surface-soft', '--surface-hover', '--dialog-surface',
10
10
  '--text', '--text-secondary', '--text-tertiary', '--border', '--border-strong',
11
11
  '--accent', '--accent-hover', '--accent-soft', '--on-accent',
12
12
  '--success', '--success-soft', '--warning', '--warning-soft', '--danger', '--danger-soft',
@@ -138,8 +138,8 @@ function installHostThemeBridge() {
138
138
  else if (HOST_THEME_STYLE_TOKENS.has(name) && CSS.supports('box-shadow', value)) root.style.setProperty(name, value)
139
139
  }
140
140
  root.dataset.dshHostTheme = 'true'
141
+ root.dataset.colorScheme = message.colorScheme
141
142
  root.style.colorScheme = message.colorScheme
142
- root.style.setProperty('--dialog-surface', root.style.getPropertyValue('--surface-raised') || root.style.getPropertyValue('--surface'))
143
143
  document.querySelector('meta[name="color-scheme"]')?.setAttribute('content', message.colorScheme)
144
144
  const background = root.style.getPropertyValue('--bg')
145
145
  if (background) document.querySelector('meta[name="theme-color"]')?.setAttribute('content', background)
@@ -242,6 +242,10 @@ function element(tag, attributes = {}, ...children) {
242
242
  return node
243
243
  }
244
244
 
245
+ function refreshWorkspaceEffects(root = document) {
246
+ window.DshKnowledgeEffects?.refresh(root)
247
+ }
248
+
245
249
  function actionButton(label, onClick, variant = '', attributes = {}) {
246
250
  return element('button', { type: 'button', class: `button ${variant}`.trim(), onClick, ...attributes }, label)
247
251
  }
@@ -434,6 +438,7 @@ function renderLogin(message = '') {
434
438
  element('p', {}, '管理对话中沉淀的长期知识与 AI 提取候选。'),
435
439
  form,
436
440
  )))
441
+ refreshWorkspaceEffects(app)
437
442
  }
438
443
 
439
444
  function signOut() {
@@ -1004,6 +1009,11 @@ function renderShell() {
1004
1009
  },
1005
1010
  renderSidebar(),
1006
1011
  renderAppSidebarResizer(),
1012
+ state.menuOpen ? element('button', {
1013
+ type: 'button', class: 'app-sidebar-scrim',
1014
+ 'aria-label': '关闭导航菜单',
1015
+ onClick: () => { state.menuOpen = false; renderShell() },
1016
+ }) : null,
1007
1017
  element('main', { class: 'main' },
1008
1018
  element('header', { class: 'topbar' },
1009
1019
  element('div', { class: 'topbar-title' },
@@ -1031,10 +1041,9 @@ function renderShell() {
1031
1041
  element('div', { class: 'page' }, element('div', { class: 'view-stage', 'data-view-stage': state.view }, renderCurrentView())),
1032
1042
  ),
1033
1043
  )
1034
- if (state.menuOpen) shell.addEventListener('click', (event) => {
1035
- if (event.target === shell) { state.menuOpen = false; renderShell() }
1036
- })
1037
1044
  app.replaceChildren(shell)
1045
+ applySidebarVisibility(shell, state.documentView.sidebarHidden)
1046
+ refreshWorkspaceEffects(shell)
1038
1047
  restoreScrollPosition(currentScrollState())
1039
1048
  }
1040
1049
 
@@ -1100,7 +1109,37 @@ function setSidebarWidth(width, shell, handle) {
1100
1109
  function setSidebarHidden(hidden) {
1101
1110
  state.documentView.sidebarHidden = hidden
1102
1111
  saveDocumentLayout()
1103
- renderShell()
1112
+ const shell = app.querySelector('.app-shell')
1113
+ if (shell) applySidebarVisibility(shell, hidden)
1114
+ }
1115
+
1116
+ function applySidebarVisibility(shell, hidden) {
1117
+ shell.dataset.sidebarHidden = String(hidden)
1118
+ const sidebar = shell.querySelector(':scope > .sidebar')
1119
+ const resizer = shell.querySelector(':scope > .app-sidebar-resizer')
1120
+ const main = shell.querySelector(':scope > .main')
1121
+ const toggle = shell.querySelector('.pane-toggle-button[data-pane="main"]')
1122
+ const compact = window.matchMedia('(max-width: 760px)').matches
1123
+ const sidebarUnavailable = compact ? !state.menuOpen : hidden
1124
+ sidebar?.toggleAttribute('inert', sidebarUnavailable)
1125
+ resizer?.toggleAttribute('inert', hidden)
1126
+ main?.toggleAttribute('inert', compact && state.menuOpen)
1127
+ if (sidebar) {
1128
+ if (sidebarUnavailable) sidebar.setAttribute('aria-hidden', 'true')
1129
+ else sidebar.removeAttribute('aria-hidden')
1130
+ }
1131
+ if (resizer) {
1132
+ resizer.tabIndex = hidden ? -1 : 0
1133
+ if (hidden) resizer.setAttribute('aria-hidden', 'true')
1134
+ else resizer.removeAttribute('aria-hidden')
1135
+ }
1136
+ if (toggle) {
1137
+ const visible = !hidden
1138
+ const action = `${visible ? '隐藏' : '显示'}主导航栏`
1139
+ toggle.setAttribute('aria-pressed', String(visible))
1140
+ toggle.setAttribute('aria-label', action)
1141
+ toggle.title = action
1142
+ }
1104
1143
  }
1105
1144
 
1106
1145
  function renderSidebar() {
@@ -1388,7 +1427,10 @@ function renderKnowledgeBaseCard(base) {
1388
1427
  const archived = base.status === 'archived'
1389
1428
  const visibleTags = base.defaultTags.slice(0, 4)
1390
1429
  const hiddenTagCount = Math.max(0, base.defaultTags.length - visibleTags.length)
1391
- return element('article', { class: `base-card${archived ? ' is-archived' : ''}` },
1430
+ return element('article', {
1431
+ class: `base-card${archived ? ' is-archived' : ''}`,
1432
+ 'data-knowledge-motion-key': `knowledge-library:${base.id}`,
1433
+ },
1392
1434
  element('div', { class: 'base-card-header' },
1393
1435
  element('div', { class: 'base-card-identity' },
1394
1436
  element('span', { class: 'base-symbol', 'aria-hidden': 'true' }, base.name.trim().slice(0, 1).toLocaleUpperCase() || 'K'),
@@ -1520,7 +1562,11 @@ function renderMountListRow(base, view, targetKind, targetId) {
1520
1562
  },
1521
1563
  })
1522
1564
  const modelLabel = writebackRouteLabel(base)
1523
- return element('article', { class: `mount-list-row${selected ? ' is-selected' : ''}`, role: 'listitem' },
1565
+ return element('article', {
1566
+ class: `mount-list-row${selected ? ' is-selected' : ''}`,
1567
+ role: 'listitem',
1568
+ 'data-knowledge-motion-key': `knowledge-mount:${targetKind}:${base.id}`,
1569
+ },
1524
1570
  element('label', { class: 'mount-select' }, checkbox, element('span', { class: 'visually-hidden' }, `选择 ${base.name}`)),
1525
1571
  element('div', { class: 'mount-list-main' },
1526
1572
  element('div', { class: 'mount-list-title' }, element('strong', {}, base.name), badge(view.statusLabel, view.statusVariant), badge(modelLabel)),
@@ -1591,9 +1637,12 @@ function renderDocumentWorkspace(workspace, options = {}) {
1591
1637
  const expanded = view.expandedBases.has(base.id) || Boolean(query)
1592
1638
  const page = documentPageState(workspace, base.id)
1593
1639
  const documents = (query ? view.searchResults : workspaceDocuments).filter(document => document.knowledgeBaseId === base.id)
1594
- return element('section', { class: 'note-tree-group', 'data-expanded': String(expanded) },
1640
+ return element('section', {
1641
+ class: 'note-tree-group', 'data-expanded': String(expanded),
1642
+ },
1595
1643
  element('button', {
1596
1644
  type: 'button', class: 'note-tree-base', 'aria-expanded': String(expanded),
1645
+ 'data-knowledge-motion-key': `knowledge-base:${workspace.kind}:${base.id}`,
1597
1646
  onClick: () => {
1598
1647
  if (query) { view.knowledgeBaseId = base.id; return renderShell() }
1599
1648
  void toggleDocumentBase(workspace, base.id)
@@ -1610,6 +1659,8 @@ function renderDocumentWorkspace(workspace, options = {}) {
1610
1659
  actionButton('重试', () => { void loadDocumentPage(workspace, base.id, { reset: true }).then(renderShell) }, 'ghost small')) : null,
1611
1660
  documents.map(document => element('button', {
1612
1661
  type: 'button', class: 'note-tree-document', 'aria-current': document.id === view.documentId ? 'page' : undefined,
1662
+ 'data-document-id': document.id,
1663
+ 'data-knowledge-motion-key': `knowledge-document:${workspace.kind}:${document.id}`,
1613
1664
  onClick: () => { view.knowledgeBaseId = base.id; void selectDocument(workspace, document.id) },
1614
1665
  }, element('span', { class: 'tree-document-icon', 'aria-hidden': 'true' }), element('span', { class: 'tree-document-copy' },
1615
1666
  element('strong', {}, document.title), element('small', {}, document.relPath)),
@@ -2035,6 +2086,7 @@ function renderNoteTreeBranch(parentId, depth = 0) {
2035
2086
  const row = element('div', {
2036
2087
  class: 'notes-tree-item', 'data-kind': node.kind, 'data-selected': String(selected),
2037
2088
  'data-note-id': node.id,
2089
+ 'data-knowledge-motion-key': `note:${node.id}`,
2038
2090
  draggable: 'true',
2039
2091
  onDragStart: event => {
2040
2092
  event.dataTransfer.effectAllowed = 'move'
@@ -2074,7 +2126,8 @@ function renderNoteTreeBranch(parentId, depth = 0) {
2074
2126
  function renderNoteSearchResults() {
2075
2127
  if (!state.notes.searchResults.length) return element('div', { class: 'notes-tree-empty' }, '没有匹配的笔记文档。')
2076
2128
  return element('div', { class: 'notes-search-results' }, state.notes.searchResults.map(node => element('button', {
2077
- type: 'button', class: 'notes-search-row', onClick: () => { void selectNoteNode(node) },
2129
+ type: 'button', class: 'notes-search-row', 'data-knowledge-motion-key': `note-search:${node.id}`,
2130
+ onClick: () => { void selectNoteNode(node) },
2078
2131
  }, renderNoteIcon(node), element('span', {}, element('strong', {}, node.name), element('small', {}, noteKindLabel(node))))))
2079
2132
  }
2080
2133
 
@@ -3774,6 +3827,7 @@ function openModal({ title, description = '', body, primaryLabel, primaryVariant
3774
3827
  document.removeEventListener('keydown', onKeyDown)
3775
3828
  closed = true
3776
3829
  backdrop.remove()
3830
+ refreshWorkspaceEffects()
3777
3831
  onClose?.()
3778
3832
  if (previouslyFocused instanceof HTMLElement) previouslyFocused.focus()
3779
3833
  }
@@ -3815,6 +3869,7 @@ function openModal({ title, description = '', body, primaryLabel, primaryVariant
3815
3869
  body.addEventListener('change', () => { formDirty = true })
3816
3870
  }
3817
3871
  document.body.append(backdrop)
3872
+ refreshWorkspaceEffects(backdrop)
3818
3873
  document.addEventListener('keydown', onKeyDown)
3819
3874
  window.setTimeout(() => (dialog.querySelector('input, textarea, select, button') || dialog).focus(), 0)
3820
3875
  return { close, dialog }
package/web/index.html CHANGED
@@ -13,6 +13,7 @@
13
13
  <link rel="stylesheet" href="__DSH_KNOWLEDGE_WEB_PATH__/styles.css?v=__DSH_KNOWLEDGE_ASSET_VERSION__">
14
14
  <script src="__DSH_KNOWLEDGE_WEB_PATH__/markdown-preview.js?v=__DSH_KNOWLEDGE_ASSET_VERSION__"></script>
15
15
  <script src="__DSH_KNOWLEDGE_WEB_PATH__/change-review.js?v=__DSH_KNOWLEDGE_ASSET_VERSION__"></script>
16
+ <script src="__DSH_KNOWLEDGE_WEB_PATH__/workspace-effects.js?v=__DSH_KNOWLEDGE_ASSET_VERSION__"></script>
16
17
  <script type="module" src="__DSH_KNOWLEDGE_WEB_PATH__/app.js?v=__DSH_KNOWLEDGE_ASSET_VERSION__"></script>
17
18
  </head>
18
19
  <body>