@lemoncat7/dsh-knowledge 2.2.10 → 2.2.13
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/README.md +3 -3
- package/docs/architecture.md +6 -2
- package/lib/api.d.ts.map +1 -1
- package/lib/api.js +20 -0
- package/lib/api.js.map +1 -1
- package/lib/client.js +22 -22
- package/lib/client.js.map +2 -2
- package/lib/local-provider.d.ts +8 -1
- package/lib/local-provider.d.ts.map +1 -1
- package/lib/local-provider.js +12 -0
- package/lib/local-provider.js.map +1 -1
- package/lib/management-proxy.js +1 -1
- package/lib/management-proxy.js.map +1 -1
- package/lib/notes/domain.d.ts +10 -0
- package/lib/notes/domain.d.ts.map +1 -1
- package/lib/notes/domain.js.map +1 -1
- package/lib/notes/store.d.ts +15 -1
- package/lib/notes/store.d.ts.map +1 -1
- package/lib/notes/store.js +277 -28
- package/lib/notes/store.js.map +1 -1
- package/lib/provider.d.ts +8 -1
- package/lib/provider.d.ts.map +1 -1
- package/lib/remote-provider.d.ts +8 -1
- package/lib/remote-provider.d.ts.map +1 -1
- package/lib/remote-provider.js +22 -0
- package/lib/remote-provider.js.map +1 -1
- package/lib/theme-bridge.js +7 -7
- package/lib/theme-bridge.js.map +1 -1
- package/lib/tool-authorization.js +11 -16
- package/lib/tool-authorization.js.map +1 -1
- package/lib/web-note-editor-chrome.d.ts +17 -0
- package/lib/web-note-editor-chrome.d.ts.map +1 -0
- package/lib/web-note-editor-chrome.js +38 -0
- package/lib/web-note-editor-chrome.js.map +1 -0
- package/lib/web-note-editor-find.d.ts +17 -0
- package/lib/web-note-editor-find.d.ts.map +1 -0
- package/lib/web-note-editor-find.js +225 -0
- package/lib/web-note-editor-find.js.map +1 -0
- package/lib/web-note-editor-outline.d.ts +15 -0
- package/lib/web-note-editor-outline.d.ts.map +1 -0
- package/lib/web-note-editor-outline.js +169 -0
- package/lib/web-note-editor-outline.js.map +1 -0
- package/lib/web-note-editor-search.d.ts +30 -0
- package/lib/web-note-editor-search.d.ts.map +1 -0
- package/lib/web-note-editor-search.js +143 -0
- package/lib/web-note-editor-search.js.map +1 -0
- package/lib/web-note-editor-selection.d.ts +15 -0
- package/lib/web-note-editor-selection.d.ts.map +1 -0
- package/lib/web-note-editor-selection.js +255 -0
- package/lib/web-note-editor-selection.js.map +1 -0
- package/lib/web-note-editor.d.ts +7 -0
- package/lib/web-note-editor.d.ts.map +1 -1
- package/lib/web-note-editor.js +36 -6
- package/lib/web-note-editor.js.map +1 -1
- package/lib/web-note-history.d.ts +28 -0
- package/lib/web-note-history.d.ts.map +1 -0
- package/lib/web-note-history.js +163 -0
- package/lib/web-note-history.js.map +1 -0
- package/lib/web-workspace-effects.d.ts.map +1 -1
- package/lib/web-workspace-effects.js +1 -2
- package/lib/web-workspace-effects.js.map +1 -1
- package/lib/web.d.ts.map +1 -1
- package/lib/web.js +4 -0
- package/lib/web.js.map +1 -1
- package/package.json +1 -1
- package/web/api-client.js +94 -0
- package/web/app.js +248 -230
- package/web/host-theme.js +63 -0
- package/web/index.html +1 -0
- package/web/note-editor.js +70 -70
- package/web/note-history.js +1 -0
- package/web/styles.css +353 -154
- package/web/ui-primitives.js +77 -0
- package/web/workspace-effects.js +1 -1
package/web/app.js
CHANGED
|
@@ -2,16 +2,15 @@ const API_BASE = document.querySelector('meta[name="dsh-knowledge-api"]')?.conte
|
|
|
2
2
|
const AUTH_MODE = document.querySelector('meta[name="dsh-knowledge-auth-mode"]')?.content || 'bearer'
|
|
3
3
|
const WEB_PATH = document.querySelector('meta[name="dsh-knowledge-web"]')?.content || '/knowledge'
|
|
4
4
|
const ASSET_VERSION = document.querySelector('meta[name="dsh-knowledge-asset-version"]')?.content || ''
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
'
|
|
10
|
-
'--text', '--text-secondary', '--text-tertiary', '--border', '--border-strong',
|
|
11
|
-
'--accent', '--accent-hover', '--accent-soft', '--on-accent',
|
|
12
|
-
'--success', '--success-soft', '--warning', '--warning-soft', '--danger', '--danger-soft',
|
|
5
|
+
const moduleUrl = name => `./${name}.js${ASSET_VERSION ? `?v=${encodeURIComponent(ASSET_VERSION)}` : ''}`
|
|
6
|
+
const [apiModule, themeModule, uiModule] = await Promise.all([
|
|
7
|
+
import(moduleUrl('api-client')),
|
|
8
|
+
import(moduleUrl('host-theme')),
|
|
9
|
+
import(moduleUrl('ui-primitives')),
|
|
13
10
|
])
|
|
14
|
-
const
|
|
11
|
+
const { createApiClient } = apiModule
|
|
12
|
+
const { installHostThemeBridge } = themeModule
|
|
13
|
+
const { actionButton, badge, createToastPresenter, element, interfaceIcon, paneToggleButton } = uiModule
|
|
15
14
|
const TOKEN_KEY = 'dsh-knowledge.session-token'
|
|
16
15
|
const TYPES = ['preference', 'fact', 'decision', 'procedure', 'lesson']
|
|
17
16
|
const TYPE_LABELS = { preference: '偏好', fact: '事实', decision: '决策', procedure: '流程', lesson: '经验' }
|
|
@@ -33,6 +32,7 @@ const mountContext = {
|
|
|
33
32
|
}
|
|
34
33
|
const app = document.querySelector('#app')
|
|
35
34
|
const toastRegion = document.querySelector('#toast-region')
|
|
35
|
+
const showToast = createToastPresenter(toastRegion)
|
|
36
36
|
const savedDocumentLayout = readDocumentLayout()
|
|
37
37
|
|
|
38
38
|
function createDocumentViewState(overrides = {}) {
|
|
@@ -121,62 +121,10 @@ let noteSelectionRequest = 0
|
|
|
121
121
|
let knowledgeDocumentDrag = null
|
|
122
122
|
let movingDocumentId = ''
|
|
123
123
|
|
|
124
|
-
function installHostThemeBridge() {
|
|
125
|
-
if (window.parent === window) return Promise.resolve()
|
|
126
|
-
const parentOrigin = referrerOrigin()
|
|
127
|
-
return new Promise(resolve => {
|
|
128
|
-
let initialThemeSettled = false
|
|
129
|
-
const settleInitialTheme = () => {
|
|
130
|
-
if (initialThemeSettled) return
|
|
131
|
-
initialThemeSettled = true
|
|
132
|
-
window.clearTimeout(fallback)
|
|
133
|
-
resolve()
|
|
134
|
-
}
|
|
135
|
-
const fallback = window.setTimeout(settleInitialTheme, 160)
|
|
136
|
-
window.addEventListener('message', event => {
|
|
137
|
-
if (event.source !== window.parent) return
|
|
138
|
-
if (parentOrigin && event.origin !== parentOrigin) return
|
|
139
|
-
const message = event.data
|
|
140
|
-
if (!message || message.type !== HOST_THEME_MESSAGE || message.version !== HOST_THEME_PROTOCOL_VERSION) return
|
|
141
|
-
if (message.colorScheme !== 'light' && message.colorScheme !== 'dark') return
|
|
142
|
-
if (!message.tokens || typeof message.tokens !== 'object' || Array.isArray(message.tokens)) return
|
|
143
|
-
|
|
144
|
-
const root = document.documentElement
|
|
145
|
-
for (const name of [...HOST_THEME_COLOR_TOKENS, ...HOST_THEME_STYLE_TOKENS]) root.style.removeProperty(name)
|
|
146
|
-
for (const [name, value] of Object.entries(message.tokens)) {
|
|
147
|
-
if (typeof value !== 'string' || value.length === 0 || value.length > 512) continue
|
|
148
|
-
if (HOST_THEME_COLOR_TOKENS.has(name) && CSS.supports('color', value)) root.style.setProperty(name, value)
|
|
149
|
-
else if (HOST_THEME_STYLE_TOKENS.has(name) && CSS.supports('box-shadow', value)) root.style.setProperty(name, value)
|
|
150
|
-
}
|
|
151
|
-
root.dataset.dshHostTheme = 'true'
|
|
152
|
-
root.dataset.colorScheme = message.colorScheme
|
|
153
|
-
root.style.colorScheme = message.colorScheme
|
|
154
|
-
document.querySelector('meta[name="color-scheme"]')?.setAttribute('content', message.colorScheme)
|
|
155
|
-
const background = root.style.getPropertyValue('--bg')
|
|
156
|
-
if (background) document.querySelector('meta[name="theme-color"]')?.setAttribute('content', background)
|
|
157
|
-
settleInitialTheme()
|
|
158
|
-
})
|
|
159
|
-
window.parent.postMessage({
|
|
160
|
-
type: HOST_THEME_READY_MESSAGE,
|
|
161
|
-
version: HOST_THEME_PROTOCOL_VERSION,
|
|
162
|
-
}, parentOrigin || '*')
|
|
163
|
-
})
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function referrerOrigin() {
|
|
167
|
-
if (!document.referrer) return ''
|
|
168
|
-
try {
|
|
169
|
-
const origin = new URL(document.referrer).origin
|
|
170
|
-
return origin === 'null' ? '' : origin
|
|
171
|
-
} catch {
|
|
172
|
-
return ''
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
124
|
function readDocumentLayout() {
|
|
177
125
|
const fallback = {
|
|
178
126
|
sidebarHidden: false,
|
|
179
|
-
sidebarWidth:
|
|
127
|
+
sidebarWidth: 214,
|
|
180
128
|
}
|
|
181
129
|
try {
|
|
182
130
|
const value = JSON.parse(localStorage.getItem(DOCUMENT_LAYOUT_KEY) || '{}')
|
|
@@ -236,153 +184,15 @@ function restoreScrollPosition(view) {
|
|
|
236
184
|
})
|
|
237
185
|
}
|
|
238
186
|
|
|
239
|
-
function element(tag, attributes = {}, ...children) {
|
|
240
|
-
const node = document.createElement(tag)
|
|
241
|
-
for (const [key, value] of Object.entries(attributes)) {
|
|
242
|
-
if (value === undefined || value === null || value === false) continue
|
|
243
|
-
if (key === 'class') node.className = value
|
|
244
|
-
else if (key === 'text') node.textContent = value
|
|
245
|
-
else if (key.startsWith('on') && typeof value === 'function') node.addEventListener(key.slice(2).toLowerCase(), value)
|
|
246
|
-
else if (key === 'checked' || key === 'selected' || key === 'disabled') node[key] = Boolean(value)
|
|
247
|
-
else node.setAttribute(key, String(value))
|
|
248
|
-
}
|
|
249
|
-
for (const child of children.flat(Infinity)) {
|
|
250
|
-
if (child === undefined || child === null || child === false) continue
|
|
251
|
-
node.append(child instanceof Node ? child : document.createTextNode(String(child)))
|
|
252
|
-
}
|
|
253
|
-
return node
|
|
254
|
-
}
|
|
255
|
-
|
|
256
187
|
function refreshWorkspaceEffects(root = document) {
|
|
257
188
|
window.DshKnowledgeEffects?.refresh(root)
|
|
258
189
|
}
|
|
259
190
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const action = `${visible ? '隐藏' : '显示'}${label}`
|
|
266
|
-
return element('button', {
|
|
267
|
-
type: 'button', class: 'pane-toggle-button', 'data-pane': pane,
|
|
268
|
-
'aria-label': action, 'aria-pressed': String(visible), title: action, onClick,
|
|
269
|
-
}, element('span', { class: `pane-icon pane-icon-${pane}`, 'aria-hidden': 'true' }))
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function interfaceIcon(name, className = 'interface-icon') {
|
|
273
|
-
const paths = {
|
|
274
|
-
search: 'M10.8 4.5a6.3 6.3 0 1 0 0 12.6 6.3 6.3 0 0 0 0-12.6Zm4.6 11 4.1 4',
|
|
275
|
-
}
|
|
276
|
-
return element('svg', {
|
|
277
|
-
class: className, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor',
|
|
278
|
-
'stroke-width': '1.8', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'aria-hidden': 'true',
|
|
279
|
-
}, element('path', { d: paths[name] }))
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function badge(label, variant = '') {
|
|
283
|
-
return element('span', { class: `badge ${variant}`.trim() }, label)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function showToast(message, kind = '') {
|
|
287
|
-
const toast = element('div', { class: `toast ${kind}`.trim(), role: kind === 'error' ? 'alert' : 'status' },
|
|
288
|
-
element('span', {}, message),
|
|
289
|
-
kind === 'error' ? actionButton('关闭', () => toast.remove(), 'ghost small toast-close', { 'aria-label': '关闭错误提示' }) : null,
|
|
290
|
-
)
|
|
291
|
-
toastRegion.append(toast)
|
|
292
|
-
if (kind !== 'error') window.setTimeout(() => toast.remove(), 4200)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
async function api(path, options = {}) {
|
|
296
|
-
const headers = { accept: 'application/json', ...(options.body === undefined ? {} : { 'content-type': 'application/json' }) }
|
|
297
|
-
if (AUTH_MODE === 'same-origin') headers['x-dsh-knowledge-client'] = 'management-web'
|
|
298
|
-
if (state.token) headers.authorization = `Bearer ${state.token}`
|
|
299
|
-
const response = await fetch(`${API_BASE}/${path.replace(/^\/+/, '')}`, {
|
|
300
|
-
method: options.method || 'GET',
|
|
301
|
-
headers,
|
|
302
|
-
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
303
|
-
signal: options.signal,
|
|
304
|
-
})
|
|
305
|
-
const text = await response.text()
|
|
306
|
-
let payload
|
|
307
|
-
if (text) {
|
|
308
|
-
try { payload = JSON.parse(text) } catch { throw new Error('服务返回了无法识别的数据') }
|
|
309
|
-
}
|
|
310
|
-
if (!response.ok) {
|
|
311
|
-
const error = new Error(payload?.error || `请求失败(HTTP ${response.status})`)
|
|
312
|
-
error.status = response.status
|
|
313
|
-
throw error
|
|
314
|
-
}
|
|
315
|
-
return payload
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
async function binaryRequest(path, options = {}) {
|
|
319
|
-
const headers = { accept: options.accept || 'application/json' }
|
|
320
|
-
if (AUTH_MODE === 'same-origin') headers['x-dsh-knowledge-client'] = 'management-web'
|
|
321
|
-
if (state.token) headers.authorization = `Bearer ${state.token}`
|
|
322
|
-
if (options.contentType) headers['content-type'] = options.contentType
|
|
323
|
-
const response = await fetch(`${API_BASE}/${path.replace(/^\/+/, '')}`, {
|
|
324
|
-
method: options.method || 'GET',
|
|
325
|
-
headers,
|
|
326
|
-
body: options.body,
|
|
327
|
-
signal: options.signal,
|
|
328
|
-
})
|
|
329
|
-
if (options.responseType === 'blob') {
|
|
330
|
-
if (!response.ok) throw await responseError(response)
|
|
331
|
-
return response.blob()
|
|
332
|
-
}
|
|
333
|
-
const text = await response.text()
|
|
334
|
-
let payload
|
|
335
|
-
if (text) {
|
|
336
|
-
try { payload = JSON.parse(text) } catch { throw new Error('服务返回了无法识别的数据') }
|
|
337
|
-
}
|
|
338
|
-
if (!response.ok) {
|
|
339
|
-
const error = new Error(payload?.error || `请求失败(HTTP ${response.status})`)
|
|
340
|
-
error.status = response.status
|
|
341
|
-
throw error
|
|
342
|
-
}
|
|
343
|
-
return payload
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function binaryUploadRequest(path, body, options = {}) {
|
|
347
|
-
return new Promise((resolve, reject) => {
|
|
348
|
-
const request = new XMLHttpRequest()
|
|
349
|
-
request.open(options.method || 'POST', `${API_BASE}/${path.replace(/^\/+/, '')}`)
|
|
350
|
-
request.responseType = 'text'
|
|
351
|
-
request.setRequestHeader('accept', 'application/json')
|
|
352
|
-
if (AUTH_MODE === 'same-origin') request.setRequestHeader('x-dsh-knowledge-client', 'management-web')
|
|
353
|
-
if (state.token) request.setRequestHeader('authorization', `Bearer ${state.token}`)
|
|
354
|
-
if (options.contentType) request.setRequestHeader('content-type', options.contentType)
|
|
355
|
-
request.upload.addEventListener('progress', event => {
|
|
356
|
-
if (event.lengthComputable) options.onProgress?.(event.loaded, event.total)
|
|
357
|
-
})
|
|
358
|
-
request.addEventListener('load', () => {
|
|
359
|
-
let payload
|
|
360
|
-
if (request.responseText) {
|
|
361
|
-
try { payload = JSON.parse(request.responseText) }
|
|
362
|
-
catch { return reject(new Error('服务返回了无法识别的数据')) }
|
|
363
|
-
}
|
|
364
|
-
if (request.status < 200 || request.status >= 300) {
|
|
365
|
-
const error = new Error(payload?.error || `请求失败(HTTP ${request.status})`)
|
|
366
|
-
error.status = request.status
|
|
367
|
-
reject(error)
|
|
368
|
-
return
|
|
369
|
-
}
|
|
370
|
-
resolve(payload)
|
|
371
|
-
})
|
|
372
|
-
request.addEventListener('error', () => reject(new Error('上传连接中断,请检查网络后重试')))
|
|
373
|
-
request.addEventListener('abort', () => reject(new DOMException('上传已取消', 'AbortError')))
|
|
374
|
-
request.send(body)
|
|
375
|
-
})
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
async function responseError(response) {
|
|
379
|
-
let message = `请求失败(HTTP ${response.status})`
|
|
380
|
-
try {
|
|
381
|
-
const payload = await response.json()
|
|
382
|
-
if (payload?.error) message = payload.error
|
|
383
|
-
} catch {}
|
|
384
|
-
return Object.assign(new Error(message), { status: response.status })
|
|
385
|
-
}
|
|
191
|
+
const { api, binaryRequest, binaryUploadRequest } = createApiClient({
|
|
192
|
+
apiBase: API_BASE,
|
|
193
|
+
authMode: AUTH_MODE,
|
|
194
|
+
getToken: () => state.token,
|
|
195
|
+
})
|
|
386
196
|
|
|
387
197
|
async function boot() {
|
|
388
198
|
if (AUTH_MODE === 'same-origin') {
|
|
@@ -2223,8 +2033,8 @@ function openNoteAgentGuide() {
|
|
|
2223
2033
|
element('section', { class: 'notes-agent-guide-rule' },
|
|
2224
2034
|
element('span', { class: 'notes-agent-guide-index', 'aria-hidden': 'true' }, '01'),
|
|
2225
2035
|
element('div', {},
|
|
2226
|
-
element('h3', {}, '
|
|
2227
|
-
element('p', {}, '
|
|
2036
|
+
element('h3', {}, '在当前消息里指定笔记文档'),
|
|
2037
|
+
element('p', {}, '只要当前消息明确提到“笔记文档”“笔记目录”或“笔记工作区”,会话就可以按你的要求查看和维护;无需套用固定句式。为了避免误操作,删除仍需明确说出删除,授权也不会从上一轮延续。'))),
|
|
2228
2038
|
element('section', { class: 'notes-agent-guide-rule' },
|
|
2229
2039
|
element('span', { class: 'notes-agent-guide-index', 'aria-hidden': 'true' }, '02'),
|
|
2230
2040
|
element('div', {},
|
|
@@ -2234,7 +2044,7 @@ function openNoteAgentGuide() {
|
|
|
2234
2044
|
element('section', { class: 'notes-agent-examples' },
|
|
2235
2045
|
element('h3', {}, '可以直接这样说'),
|
|
2236
2046
|
element('ul', {}, examples)),
|
|
2237
|
-
element('p', { class: 'notes-agent-guide-note' }, '只说“新建 Markdown
|
|
2047
|
+
element('p', { class: 'notes-agent-guide-note' }, '只说“新建 Markdown”或“创建本地目录”仍不会获得笔记权限;在当前消息中带上“笔记文档”即可。'))
|
|
2238
2048
|
return openSheet({
|
|
2239
2049
|
title: '让会话整理笔记',
|
|
2240
2050
|
description: '会话可以操作笔记工作区,但每次写入都需要当前用户消息明确授权。',
|
|
@@ -2367,9 +2177,10 @@ function renderNoteFolderContent(folder) {
|
|
|
2367
2177
|
renderNoteIcon(node, true),
|
|
2368
2178
|
element('span', {}, element('strong', {}, node.name), element('small', {}, noteKindLabel(node))),
|
|
2369
2179
|
),
|
|
2370
|
-
element('time', { datetime: node.updatedAt }, formatDate(node.updatedAt)),
|
|
2371
|
-
element('span', { class: 'notes-file-size' }, node.kind === 'folder' ? '' : formatBytes(node.size)),
|
|
2180
|
+
element('time', { datetime: node.updatedAt, 'data-label': '更新' }, formatDate(node.updatedAt)),
|
|
2181
|
+
element('span', { class: 'notes-file-size', 'data-label': node.kind === 'folder' ? '类型' : '大小' }, node.kind === 'folder' ? '目录' : formatBytes(node.size)),
|
|
2372
2182
|
element('div', { class: 'notes-file-actions' },
|
|
2183
|
+
node.kind !== 'folder' ? noteDownloadButton(node, 'ghost tiny') : null,
|
|
2373
2184
|
actionButton('重命名', () => openRenameNoteNode(node), 'ghost tiny'),
|
|
2374
2185
|
actionButton('复制', () => { void copyNoteNode(node) }, 'ghost tiny'),
|
|
2375
2186
|
actionButton('删除', () => { void confirmDeleteNoteNode(node) }, 'ghost tiny danger-text'),
|
|
@@ -2412,10 +2223,7 @@ function renderEditableNote(node) {
|
|
|
2412
2223
|
? element('div', { class: 'notes-live-editor', role: 'status', 'aria-label': `正在打开 ${node.name}` },
|
|
2413
2224
|
element('div', { class: 'notes-editor-loading' }, '正在打开文档…'))
|
|
2414
2225
|
: createPlainTextNoteEditor(node)
|
|
2415
|
-
|
|
2416
|
-
else mountPlainTextNoteEditor(editor, node)
|
|
2417
|
-
return element('main', { class: `notes-content is-document${markdown ? '' : ' has-line-numbers'}` },
|
|
2418
|
-
renderNoteFileToolbar(node, { editable: true }),
|
|
2226
|
+
const scrollHost = element('div', { class: 'notes-document-scroll', 'data-scroll-key': `notes-document:${node.id}` },
|
|
2419
2227
|
element('h1', {
|
|
2420
2228
|
class: 'notes-document-title', contenteditable: 'plaintext-only', spellcheck: 'false',
|
|
2421
2229
|
'aria-label': `修改 ${node.name} 的标题`, title: '点击修改标题',
|
|
@@ -2427,6 +2235,17 @@ function renderEditableNote(node) {
|
|
|
2427
2235
|
}, title),
|
|
2428
2236
|
editor,
|
|
2429
2237
|
)
|
|
2238
|
+
const outlineHost = markdown ? element('aside', { id: 'dsh-note-outline', class: 'notes-editor-outline', 'aria-label': '文档大纲', 'aria-hidden': 'true' }) : null
|
|
2239
|
+
const editorFrame = markdown
|
|
2240
|
+
? element('div', { class: 'notes-editor-frame', 'data-outline-open': 'false', 'data-find-open': 'false' }, scrollHost, outlineHost)
|
|
2241
|
+
: scrollHost
|
|
2242
|
+
if (markdown) mountMarkdownNoteEditor(editor, node, editorFrame, scrollHost, outlineHost)
|
|
2243
|
+
else mountPlainTextNoteEditor(editor, node)
|
|
2244
|
+
return element('main', { class: `notes-content is-document${markdown ? '' : ' has-line-numbers'}` },
|
|
2245
|
+
renderNoteFileToolbar(node, { editable: true, enhanced: markdown }),
|
|
2246
|
+
editorFrame,
|
|
2247
|
+
renderNoteFileStatusbar(node),
|
|
2248
|
+
)
|
|
2430
2249
|
}
|
|
2431
2250
|
|
|
2432
2251
|
function editableNoteTitle(node) {
|
|
@@ -2479,8 +2298,11 @@ function updateNoteDraft(value) {
|
|
|
2479
2298
|
syncNoteEditorChrome()
|
|
2480
2299
|
}
|
|
2481
2300
|
|
|
2482
|
-
function mountMarkdownNoteEditor(host, node) {
|
|
2301
|
+
function mountMarkdownNoteEditor(host, node, frame, scrollHost, outlineHost) {
|
|
2483
2302
|
mountMarkdownEditor(host, {
|
|
2303
|
+
frame,
|
|
2304
|
+
scrollHost,
|
|
2305
|
+
outlineHost,
|
|
2484
2306
|
markdown: state.notes.draft,
|
|
2485
2307
|
label: `编辑 ${node.name}`,
|
|
2486
2308
|
isCurrent: () => state.notes.selectedNode?.id === node.id,
|
|
@@ -2526,6 +2348,13 @@ function mountMarkdownEditor(host, options) {
|
|
|
2526
2348
|
host.removeAttribute('role')
|
|
2527
2349
|
markdownEditorHandle = runtime.createMarkdownEditor({
|
|
2528
2350
|
host,
|
|
2351
|
+
...(options.frame ? {
|
|
2352
|
+
frame: options.frame,
|
|
2353
|
+
scrollHost: options.scrollHost,
|
|
2354
|
+
outlineHost: options.outlineHost,
|
|
2355
|
+
findButton: host.closest('.notes-content')?.querySelector('[data-note-find]') || null,
|
|
2356
|
+
outlineButton: host.closest('.notes-content')?.querySelector('[data-note-outline]') || null,
|
|
2357
|
+
} : {}),
|
|
2529
2358
|
markdown: options.markdown,
|
|
2530
2359
|
label: options.label,
|
|
2531
2360
|
onChange: options.onChange,
|
|
@@ -2601,12 +2430,13 @@ function renderNoteFile(node) {
|
|
|
2601
2430
|
renderNoteIcon(node, true),
|
|
2602
2431
|
element('h2', {}, node.name),
|
|
2603
2432
|
element('p', {}, '该格式不能在浏览器中直接编辑,可以下载后使用本地应用打开。'),
|
|
2604
|
-
|
|
2433
|
+
noteDownloadButton(node, 'primary small', '下载文件'),
|
|
2605
2434
|
)
|
|
2606
2435
|
}
|
|
2607
2436
|
return element('main', { class: 'notes-content is-file' },
|
|
2608
2437
|
renderNoteFileToolbar(node),
|
|
2609
2438
|
element('section', { class: `notes-inline-viewer is-${previewKind}`, 'aria-label': `${node.name}内容` }, preview),
|
|
2439
|
+
renderNoteFileStatusbar(node),
|
|
2610
2440
|
)
|
|
2611
2441
|
}
|
|
2612
2442
|
|
|
@@ -2614,19 +2444,89 @@ function renderNoteFileToolbar(node, options = {}) {
|
|
|
2614
2444
|
return element('header', { class: 'notes-document-toolbar' },
|
|
2615
2445
|
element('div', { class: 'notes-toolbar-leading' },
|
|
2616
2446
|
renderNoteDocumentBreadcrumb(node),
|
|
2617
|
-
element('div', { class: 'notes-file-info', 'aria-label': '文件信息' },
|
|
2618
|
-
noteInfoItem('编号', shortNoteId(node.id), node.id),
|
|
2619
|
-
noteInfoItem('类型', node.kind === 'document' ? 'Markdown' : node.mediaType || '未知'),
|
|
2620
|
-
noteInfoItem('大小', formatBytes(node.size), '', 'size'),
|
|
2621
|
-
noteInfoItem('更新', formatDate(node.updatedAt), '', 'updated'),
|
|
2622
|
-
),
|
|
2623
2447
|
),
|
|
2624
2448
|
element('div', { class: 'notes-document-actions' },
|
|
2625
2449
|
options.editable ? element('span', { class: 'notes-save-state', role: 'status', 'data-note-save-state': '', 'data-dirty': String(state.notes.dirty) }, state.notes.dirty ? '未保存' : '已保存') : null,
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2450
|
+
options.enhanced ? actionButton('查找', () => markdownEditorHandle?.openFind(), 'ghost small', { 'data-note-find': '', 'data-note-mobile-overflow': '', 'aria-keyshortcuts': 'Control+F Meta+F', 'aria-pressed': 'false' }) : null,
|
|
2451
|
+
options.enhanced ? actionButton([
|
|
2452
|
+
interfaceIcon('outline', 'notes-toolbar-action-icon'),
|
|
2453
|
+
element('span', {}, '大纲'),
|
|
2454
|
+
], () => markdownEditorHandle?.toggleOutline(), 'ghost small notes-outline-action', { 'data-note-outline': '', 'aria-label': '打开标题大纲', 'aria-controls': 'dsh-note-outline', 'aria-expanded': 'false', 'aria-pressed': 'false' }) : null,
|
|
2455
|
+
options.editable ? actionButton('历史', () => { void openNoteHistory(node) }, 'ghost small', { 'data-note-history': '', 'data-note-mobile-overflow': '' }) : null,
|
|
2456
|
+
noteDownloadButton(node, 'ghost small', '下载', { 'data-note-mobile-overflow': '' }),
|
|
2457
|
+
actionButton('复制引用', () => { void copyNoteReference(node) }, 'ghost small', { 'data-note-mobile-overflow': '' }),
|
|
2458
|
+
actionButton('重命名', () => openRenameNoteNode(node), 'ghost small', { 'data-note-mobile-overflow': '' }),
|
|
2459
|
+
options.editable ? actionButton('保存', () => { void saveNoteDocument() }, 'primary small', { 'data-note-save': '', 'data-note-mobile-overflow': '', disabled: !state.notes.dirty }) : null,
|
|
2460
|
+
renderNoteFileOverflowMenu(node, options),
|
|
2461
|
+
),
|
|
2462
|
+
)
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
function renderNoteFileOverflowMenu(node, options) {
|
|
2466
|
+
const details = element('details', {
|
|
2467
|
+
class: 'notes-document-more',
|
|
2468
|
+
onKeyDown: event => {
|
|
2469
|
+
if (event.key !== 'Escape') return
|
|
2470
|
+
event.preventDefault()
|
|
2471
|
+
details.open = false
|
|
2472
|
+
summary.focus()
|
|
2473
|
+
},
|
|
2474
|
+
onFocusOut: () => {
|
|
2475
|
+
window.setTimeout(() => {
|
|
2476
|
+
if (!details.contains(document.activeElement)) details.open = false
|
|
2477
|
+
}, 0)
|
|
2478
|
+
},
|
|
2479
|
+
})
|
|
2480
|
+
const summary = element('summary', { class: 'button ghost small', title: '更多操作', 'aria-label': `打开 ${node.name} 的更多操作` },
|
|
2481
|
+
interfaceIcon('more', 'notes-document-more-icon'),
|
|
2482
|
+
)
|
|
2483
|
+
details.addEventListener('toggle', () => {
|
|
2484
|
+
summary.setAttribute('aria-expanded', String(details.open))
|
|
2485
|
+
summary.setAttribute('aria-label', `${details.open ? '关闭' : '打开'} ${node.name} 的更多操作`)
|
|
2486
|
+
})
|
|
2487
|
+
const closeThen = action => event => {
|
|
2488
|
+
details.open = false
|
|
2489
|
+
action(event)
|
|
2490
|
+
}
|
|
2491
|
+
const menuItems = []
|
|
2492
|
+
if (options.editable && state.notes.dirty) {
|
|
2493
|
+
menuItems.push(noteMenuAction('保存修改', 'save', closeThen(() => { void saveNoteDocument() })))
|
|
2494
|
+
menuItems.push(noteMenuDivider())
|
|
2495
|
+
}
|
|
2496
|
+
if (options.enhanced) {
|
|
2497
|
+
menuItems.push(noteMenuAction('查找', 'search', closeThen(() => markdownEditorHandle?.openFind())))
|
|
2498
|
+
}
|
|
2499
|
+
if (options.editable) menuItems.push(noteMenuAction('页面历史', 'history', closeThen(() => { void openNoteHistory(node) })))
|
|
2500
|
+
if (options.enhanced || options.editable) menuItems.push(noteMenuDivider())
|
|
2501
|
+
menuItems.push(
|
|
2502
|
+
noteMenuAction('下载', 'download', closeThen(event => { void downloadNoteFile(node, event.currentTarget) })),
|
|
2503
|
+
noteMenuAction('复制引用', 'link', closeThen(() => { void copyNoteReference(node) })),
|
|
2504
|
+
noteMenuAction('重命名', 'rename', closeThen(() => openRenameNoteNode(node))),
|
|
2505
|
+
)
|
|
2506
|
+
const menu = element('div', { class: 'notes-document-more-menu', role: 'menu', 'aria-label': `${node.name} 的更多操作` }, menuItems)
|
|
2507
|
+
summary.setAttribute('aria-expanded', 'false')
|
|
2508
|
+
details.append(summary, menu)
|
|
2509
|
+
return details
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
function noteMenuAction(label, icon, onClick, attributes = {}) {
|
|
2513
|
+
return actionButton([
|
|
2514
|
+
interfaceIcon(icon, 'notes-document-menu-icon'),
|
|
2515
|
+
element('span', { class: 'notes-document-menu-label' }, label),
|
|
2516
|
+
], onClick, 'ghost small notes-document-menu-item', { role: 'menuitem', ...attributes })
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
function noteMenuDivider() {
|
|
2520
|
+
return element('div', { class: 'notes-document-menu-divider', role: 'separator' })
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
function renderNoteFileStatusbar(node) {
|
|
2524
|
+
return element('footer', { class: 'notes-document-statusbar', 'aria-label': '文档信息' },
|
|
2525
|
+
element('div', { class: 'notes-file-info' },
|
|
2526
|
+
noteInfoItem('编号', shortNoteId(node.id), node.id, 'id'),
|
|
2527
|
+
noteInfoItem('类型', node.kind === 'document' ? 'Markdown' : node.mediaType || '未知'),
|
|
2528
|
+
noteInfoItem('大小', formatBytes(node.size), '', 'size'),
|
|
2529
|
+
noteInfoItem('更新', formatDate(node.updatedAt), '', 'updated'),
|
|
2630
2530
|
),
|
|
2631
2531
|
)
|
|
2632
2532
|
}
|
|
@@ -3154,14 +3054,132 @@ async function saveNoteDocument() {
|
|
|
3154
3054
|
}
|
|
3155
3055
|
}
|
|
3156
3056
|
|
|
3157
|
-
async function
|
|
3057
|
+
async function openNoteHistory(node) {
|
|
3058
|
+
if (state.notes.selectedNode?.id === node.id && state.notes.dirty && !await saveNoteDocument()) return
|
|
3059
|
+
try {
|
|
3060
|
+
const current = state.notes.selectedNode?.id === node.id ? state.notes.selectedNode : await api(`notes/${encodeURIComponent(node.id)}`)
|
|
3061
|
+
if (!current?.editable) throw new Error('只有可编辑的笔记文档支持页面历史。')
|
|
3062
|
+
const versions = await api(`notes/${encodeURIComponent(node.id)}/versions?limit=100`)
|
|
3063
|
+
if (!versions.length) {
|
|
3064
|
+
showToast('这个文档还没有可查看的保存记录。')
|
|
3065
|
+
return
|
|
3066
|
+
}
|
|
3067
|
+
let modal
|
|
3068
|
+
const view = window.DshKnowledgeNoteHistory.createNoteHistoryView({
|
|
3069
|
+
versions,
|
|
3070
|
+
currentVersion: current.version,
|
|
3071
|
+
currentContent: state.notes.selectedNode?.id === node.id ? state.notes.content : '',
|
|
3072
|
+
loadContent: async (version, signal) => {
|
|
3073
|
+
const blob = await binaryRequest(`notes/${encodeURIComponent(node.id)}/versions/${version.version}/content`, {
|
|
3074
|
+
responseType: 'blob', accept: version.mediaType || 'text/plain', signal,
|
|
3075
|
+
})
|
|
3076
|
+
return blob.text()
|
|
3077
|
+
},
|
|
3078
|
+
renderPreview: content => renderHistoricalNotePreview(content, isMarkdownNote(current)),
|
|
3079
|
+
renderDiff: renderNoteHistoryDiff,
|
|
3080
|
+
formatDate,
|
|
3081
|
+
formatBytes,
|
|
3082
|
+
onRestore: async (version, content) => {
|
|
3083
|
+
const updated = await api(`notes/${encodeURIComponent(node.id)}/versions/${version.version}/restore`, {
|
|
3084
|
+
method: 'POST', body: { expectedVersion: current.version },
|
|
3085
|
+
})
|
|
3086
|
+
if (state.notes.selectedNode?.id === node.id) {
|
|
3087
|
+
state.notes.selectedNode = updated
|
|
3088
|
+
state.notes.content = content
|
|
3089
|
+
state.notes.draft = content
|
|
3090
|
+
state.notes.dirty = false
|
|
3091
|
+
await loadNoteChildren(updated.parentId, true)
|
|
3092
|
+
}
|
|
3093
|
+
modal?.close(true)
|
|
3094
|
+
renderShell()
|
|
3095
|
+
showToast(`已将版本 ${version.version} 恢复为新的版本 ${updated.version}。`)
|
|
3096
|
+
},
|
|
3097
|
+
onError: error => showToast(friendlyError(error), 'error'),
|
|
3098
|
+
})
|
|
3099
|
+
modal = openModal({
|
|
3100
|
+
title: `${current.name} · 页面历史`,
|
|
3101
|
+
description: '每次内容保存都会形成不可变快照;恢复历史不会删除当前版本。',
|
|
3102
|
+
body: view.element,
|
|
3103
|
+
cancelLabel: '关闭',
|
|
3104
|
+
onClose: () => view.destroy(),
|
|
3105
|
+
})
|
|
3106
|
+
modal.dialog.classList.add('note-history-dialog')
|
|
3107
|
+
modal.dialog.classList.remove('narrow')
|
|
3108
|
+
} catch (error) {
|
|
3109
|
+
showToast(friendlyError(error), 'error')
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
function renderHistoricalNotePreview(content, markdown) {
|
|
3114
|
+
if (!markdown) return element('pre', { class: 'note-history-plain-preview', role: 'document' }, content || '(空文档)')
|
|
3115
|
+
const rendered = renderMarkdownPreview(content).cloneNode(true)
|
|
3116
|
+
rendered.classList.add('note-history-markdown-preview')
|
|
3117
|
+
rendered.querySelectorAll('a').forEach(link => {
|
|
3118
|
+
link.removeAttribute('href')
|
|
3119
|
+
link.removeAttribute('role')
|
|
3120
|
+
link.removeAttribute('tabindex')
|
|
3121
|
+
link.removeAttribute('title')
|
|
3122
|
+
})
|
|
3123
|
+
return rendered
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
function renderNoteHistoryDiff(historical, current) {
|
|
3127
|
+
const diff = window.DshKnowledgeReview.createLineDiff(historical, current)
|
|
3128
|
+
const lines = window.DshKnowledgeReview.compactDiffLines(diff.lines, 3)
|
|
3129
|
+
return element('section', { class: 'note-history-diff', 'aria-label': '历史版本与当前版本的逐行差异' },
|
|
3130
|
+
element('div', { class: 'note-history-diff-summary' },
|
|
3131
|
+
element('strong', {}, '历史版本 → 当前版本'),
|
|
3132
|
+
element('div', { class: 'diff-summary', 'aria-label': `新增 ${diff.additions} 行,删除 ${diff.deletions} 行` },
|
|
3133
|
+
element('span', { class: 'diff-stat additions' }, `+${diff.additions}`),
|
|
3134
|
+
element('span', { class: 'diff-stat deletions' }, `-${diff.deletions}`),
|
|
3135
|
+
),
|
|
3136
|
+
),
|
|
3137
|
+
diff.simplified ? element('div', { class: 'diff-notice' }, '内容较长,已使用有界的简化差异视图。') : null,
|
|
3138
|
+
element('div', { class: 'diff-viewer', role: 'table' },
|
|
3139
|
+
element('div', { class: 'diff-column-headings', role: 'row' },
|
|
3140
|
+
element('span', { role: 'columnheader' }, '旧'),
|
|
3141
|
+
element('span', { role: 'columnheader' }, '新'),
|
|
3142
|
+
element('span', { 'aria-hidden': 'true' }),
|
|
3143
|
+
element('span', { role: 'columnheader' }, '正文'),
|
|
3144
|
+
),
|
|
3145
|
+
lines.length ? lines.map(renderDiffLine) : element('div', { class: 'diff-empty' }, '所选版本与当前内容相同。'),
|
|
3146
|
+
),
|
|
3147
|
+
)
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
function noteDownloadButton(node, variant = 'ghost small', label = '下载', attributes = {}) {
|
|
3151
|
+
return actionButton(label, event => { void downloadNoteFile(node, event.currentTarget) }, variant, {
|
|
3152
|
+
'aria-label': `下载 ${node.name}`,
|
|
3153
|
+
title: `下载 ${node.name}`,
|
|
3154
|
+
...attributes,
|
|
3155
|
+
})
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
async function downloadNoteFile(node, button) {
|
|
3159
|
+
const originalLabel = button?.textContent || ''
|
|
3160
|
+
if (button) {
|
|
3161
|
+
button.disabled = true
|
|
3162
|
+
button.setAttribute('aria-busy', 'true')
|
|
3163
|
+
button.textContent = '下载中'
|
|
3164
|
+
}
|
|
3158
3165
|
try {
|
|
3159
|
-
|
|
3166
|
+
if (state.notes.selectedNode?.id === node.id && state.notes.dirty && !await saveNoteDocument()) return
|
|
3167
|
+
const current = state.notes.selectedNode?.id === node.id ? state.notes.selectedNode : node
|
|
3168
|
+
const blob = await binaryRequest(`notes/${encodeURIComponent(current.id)}/content?download=1`, { responseType: 'blob', accept: current.mediaType || 'application/octet-stream' })
|
|
3160
3169
|
const url = URL.createObjectURL(blob)
|
|
3161
|
-
const anchor = element('a', { href: url, download:
|
|
3170
|
+
const anchor = element('a', { href: url, download: current.name })
|
|
3162
3171
|
document.body.append(anchor); anchor.click(); anchor.remove()
|
|
3163
3172
|
window.setTimeout(() => URL.revokeObjectURL(url), 1000)
|
|
3164
|
-
|
|
3173
|
+
showToast(`已下载 ${current.name}。`)
|
|
3174
|
+
} catch (error) {
|
|
3175
|
+
showToast(`下载失败:${friendlyError(error)}`, 'error')
|
|
3176
|
+
} finally {
|
|
3177
|
+
if (button?.isConnected) {
|
|
3178
|
+
button.disabled = false
|
|
3179
|
+
button.removeAttribute('aria-busy')
|
|
3180
|
+
button.textContent = originalLabel
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3165
3183
|
}
|
|
3166
3184
|
|
|
3167
3185
|
async function copyNoteReference(node) {
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const HOST_THEME_MESSAGE = '@lemoncat7/dsh-knowledge/host-theme'
|
|
2
|
+
const HOST_THEME_READY_MESSAGE = '@lemoncat7/dsh-knowledge/host-theme-ready'
|
|
3
|
+
const HOST_THEME_PROTOCOL_VERSION = 1
|
|
4
|
+
const COLOR_TOKENS = new Set([
|
|
5
|
+
'--bg', '--surface', '--surface-raised', '--surface-soft', '--surface-hover', '--dialog-surface',
|
|
6
|
+
'--text', '--text-secondary', '--text-tertiary', '--border', '--border-strong',
|
|
7
|
+
'--accent', '--accent-hover', '--accent-soft', '--on-accent',
|
|
8
|
+
'--success', '--success-soft', '--warning', '--warning-soft', '--danger', '--danger-soft',
|
|
9
|
+
])
|
|
10
|
+
const STYLE_TOKENS = new Set(['--shadow'])
|
|
11
|
+
|
|
12
|
+
function referrerOrigin() {
|
|
13
|
+
if (!document.referrer) return ''
|
|
14
|
+
try {
|
|
15
|
+
const origin = new URL(document.referrer).origin
|
|
16
|
+
return origin === 'null' ? '' : origin
|
|
17
|
+
} catch {
|
|
18
|
+
return ''
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Synchronise only the host colour scheme and the explicitly allowed tokens. */
|
|
23
|
+
export function installHostThemeBridge() {
|
|
24
|
+
if (window.parent === window) return Promise.resolve()
|
|
25
|
+
const parentOrigin = referrerOrigin()
|
|
26
|
+
return new Promise(resolve => {
|
|
27
|
+
let settled = false
|
|
28
|
+
const settle = () => {
|
|
29
|
+
if (settled) return
|
|
30
|
+
settled = true
|
|
31
|
+
window.clearTimeout(fallback)
|
|
32
|
+
resolve()
|
|
33
|
+
}
|
|
34
|
+
const fallback = window.setTimeout(settle, 160)
|
|
35
|
+
window.addEventListener('message', event => {
|
|
36
|
+
if (event.source !== window.parent || (parentOrigin && event.origin !== parentOrigin)) return
|
|
37
|
+
const message = event.data
|
|
38
|
+
if (!message || message.type !== HOST_THEME_MESSAGE || message.version !== HOST_THEME_PROTOCOL_VERSION) return
|
|
39
|
+
if (message.colorScheme !== 'light' && message.colorScheme !== 'dark') return
|
|
40
|
+
if (!message.tokens || typeof message.tokens !== 'object' || Array.isArray(message.tokens)) return
|
|
41
|
+
|
|
42
|
+
const root = document.documentElement
|
|
43
|
+
for (const name of [...COLOR_TOKENS, ...STYLE_TOKENS]) root.style.removeProperty(name)
|
|
44
|
+
for (const [name, value] of Object.entries(message.tokens)) {
|
|
45
|
+
if (typeof value !== 'string' || value.length === 0 || value.length > 512) continue
|
|
46
|
+
if (COLOR_TOKENS.has(name) && CSS.supports('color', value)) root.style.setProperty(name, value)
|
|
47
|
+
else if (STYLE_TOKENS.has(name) && CSS.supports('box-shadow', value)) root.style.setProperty(name, value)
|
|
48
|
+
}
|
|
49
|
+
root.dataset.dshHostTheme = 'true'
|
|
50
|
+
root.dataset.colorScheme = message.colorScheme
|
|
51
|
+
root.style.colorScheme = message.colorScheme
|
|
52
|
+
document.querySelector('meta[name="color-scheme"]')?.setAttribute('content', message.colorScheme)
|
|
53
|
+
const background = root.style.getPropertyValue('--bg')
|
|
54
|
+
if (background) document.querySelector('meta[name="theme-color"]')?.setAttribute('content', background)
|
|
55
|
+
settle()
|
|
56
|
+
})
|
|
57
|
+
window.parent.postMessage({
|
|
58
|
+
type: HOST_THEME_READY_MESSAGE,
|
|
59
|
+
version: HOST_THEME_PROTOCOL_VERSION,
|
|
60
|
+
}, parentOrigin || '*')
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|