dsh-pictor 0.2.1 → 0.2.3
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 +1 -1
- package/lib/client.js +81 -67
- package/lib/index.js +29 -13
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Replace `<profile>` with the target profile name (e.g. `desktop`, `web`) and sta
|
|
|
22
22
|
|
|
23
23
|
## Supported dsh version
|
|
24
24
|
|
|
25
|
-
Pictor is built and tested against **DSH Desktop 0.
|
|
25
|
+
Pictor is built and tested against **DSH Desktop 0.9.0** (bundled Harness **0.1.5-rc.2**). The DeepSeek Harness client API moves quickly — the workspace/session services it exposes keep changing — so this plugin intentionally tracks one harness generation. Other versions (older or newer) may work or may break without notice, and are outside our support scope. If something stops working after a DSH update, update dsh to the version above rather than debugging the mismatch.
|
|
26
26
|
|
|
27
27
|
## Usage
|
|
28
28
|
|
package/lib/client.js
CHANGED
|
@@ -53,11 +53,11 @@ const STYLE = `
|
|
|
53
53
|
background: var(--dsw-alias-bg-layer-1, #fbfbfa); display: flex; flex-direction: column; overflow-y: auto; }
|
|
54
54
|
.pt-nav-head { padding: 18px 16px 12px; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
55
55
|
.pt-nav-titles { flex: 1; min-width: 0; }
|
|
56
|
-
.pt-nav-title { font-size:
|
|
56
|
+
.pt-nav-title { font-size: 15px; font-weight: 650; letter-spacing: -0.15px; margin: 0; }
|
|
57
57
|
.pt-nav-sub { font-size: 13px; color: var(--dsw-alias-label-caption, #8a8a8a); margin: 2px 0 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
58
|
-
.pt-nav-new { border:
|
|
59
|
-
border-radius:
|
|
60
|
-
.pt-nav-new:hover {
|
|
58
|
+
.pt-root .pt-nav-new { border: 1px solid transparent; background: var(--dsw-alias-button-primary-fill, #4f7cff); color: var(--dsw-alias-label-primary-foreground, #fff);
|
|
59
|
+
border-radius: 8px; padding: 5px 12px; font-size: 13.5px; font-weight: 550; cursor: pointer; white-space: nowrap; flex: none; box-shadow: 0 1px 2px rgba(0,0,0,0.12); transition: background 140ms ease; }
|
|
60
|
+
.pt-root .pt-nav-new:hover { background: var(--dsw-alias-button-primary-hover, #3a6ae0); }
|
|
61
61
|
.pt-nav-list { flex: 1; overflow-y: auto; padding: 4px 8px 12px; min-height: 0; }
|
|
62
62
|
.pt-nav-item { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
|
|
63
63
|
padding: 9px 10px; border-radius: 10px; border: 0; background: transparent; cursor: pointer;
|
|
@@ -78,13 +78,6 @@ const STYLE = `
|
|
|
78
78
|
.pt-nav-settings:hover { border-color: var(--dsw-alias-border-l1, #b0b0b0); filter: brightness(.98); }
|
|
79
79
|
.pt-nav-settings .glyph { color: var(--dsw-alias-state-business-primary, #4f7cff); font-size: 16px; }
|
|
80
80
|
.pt-nav-settings:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,.05)); }
|
|
81
|
-
.pt-lang { display: flex; align-items: center; gap: 8px; padding: 8px 10px 0; font-size: 13px;
|
|
82
|
-
color: var(--dsw-alias-label-caption, #888); }
|
|
83
|
-
.pt-lang-opts { display: inline-flex; border: 1px solid var(--dsw-alias-border-l2, #d8d8d8);
|
|
84
|
-
border-radius: 999px; overflow: hidden; }
|
|
85
|
-
.pt-lang-opt { border: 0; background: transparent; padding: 2px 10px; font-size: 12.5px; cursor: pointer;
|
|
86
|
-
color: var(--dsw-alias-label-secondary, #777); }
|
|
87
|
-
.pt-lang-opt.on { background: var(--dsw-alias-state-business-primary, #4f7cff); color: #fff; font-weight: 600; }
|
|
88
81
|
|
|
89
82
|
/* ---- 主区 ---- */
|
|
90
83
|
.pt-main { flex: 1; min-width: 0; overflow-y: auto; padding: 22px 28px 40px; }
|
|
@@ -92,12 +85,16 @@ const STYLE = `
|
|
|
92
85
|
.pt-title { font-size: 28px; font-weight: 700; letter-spacing: -0.01em; margin: 0 0 4px; }
|
|
93
86
|
.pt-subtitle { margin: 0 0 24px; font-size: 15px; color: var(--dsw-alias-label-secondary, #666); }
|
|
94
87
|
.pt-btn { border: 1px solid var(--dsw-alias-border-l2, #d8d8d8); background: var(--dsw-alias-bg-layer-2, #fff);
|
|
95
|
-
color: var(--dsw-alias-label-primary, #1a1a1a); border-radius:
|
|
96
|
-
font-weight:
|
|
97
|
-
.pt-btn:hover {
|
|
98
|
-
.pt-btn:
|
|
99
|
-
.pt-btn-
|
|
100
|
-
.pt-btn
|
|
88
|
+
color: var(--dsw-alias-label-primary, #1a1a1a); border-radius: 8px; padding: 7px 14px; font-size: 14px;
|
|
89
|
+
font-weight: 500; cursor: pointer; transition: background 140ms ease, border-color 140ms ease, color 140ms ease, box-shadow 140ms ease; user-select: none; white-space: nowrap; }
|
|
90
|
+
.pt-btn:hover:not(:disabled) { background: var(--dsw-alias-bg-layer-3, var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,0.05))); border-color: var(--dsw-alias-border-l3, #ccc); }
|
|
91
|
+
.pt-btn:active { transform: translateY(0.5px); }
|
|
92
|
+
.pt-btn:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #4f7cff); outline-offset: 2px; }
|
|
93
|
+
.pt-btn:disabled { opacity: 0.45; cursor: not-allowed; box-shadow: none; }
|
|
94
|
+
.pt-btn-primary { background: var(--dsw-alias-button-primary-fill, #4f7cff); border-color: transparent; color: var(--dsw-alias-label-primary-foreground, #fff); font-weight: 550; box-shadow: 0 1px 2px rgba(0,0,0,0.12); }
|
|
95
|
+
.pt-btn-primary:hover:not(:disabled) { background: var(--dsw-alias-button-primary-hover, #3a6ae0); }
|
|
96
|
+
.pt-btn-ghost { background: transparent; border-color: transparent; color: var(--dsw-alias-label-dimmed, #777); }
|
|
97
|
+
.pt-btn-ghost:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,0.05)); color: var(--dsw-alias-label-primary, #1a1a1a); border-color: transparent; }
|
|
101
98
|
.pt-card { background: var(--dsw-alias-bg-layer-1, #fff); border: 1px solid var(--dsw-alias-border-l2, #e2e2e2);
|
|
102
99
|
border-radius: 16px; padding: 18px 20px; }
|
|
103
100
|
.pt-card-row { display: flex; align-items: center; gap: 14px; justify-content: space-between; }
|
|
@@ -120,10 +117,12 @@ const STYLE = `
|
|
|
120
117
|
.pt-badge-gated { color: #2563eb; }
|
|
121
118
|
.pt-badge-done { color: #15803d; }
|
|
122
119
|
.pt-badge-idle { color: var(--dsw-alias-label-secondary, #777); }
|
|
123
|
-
.pt-input { width: 100%; border: 1px solid var(--dsw-alias-border-l2, #d8d8d8); border-radius:
|
|
120
|
+
.pt-input { width: 100%; border: 1px solid var(--dsw-alias-border-l2, #d8d8d8); border-radius: 8px;
|
|
124
121
|
background: var(--dsw-alias-bg-layer-2, #fff); color: var(--dsw-alias-label-primary, #1a1a1a);
|
|
125
|
-
padding:
|
|
126
|
-
.pt-input
|
|
122
|
+
padding: 8px 12px; font-size: 14px; outline: none; transition: border-color 140ms ease, box-shadow 140ms ease; }
|
|
123
|
+
.pt-input::placeholder { color: var(--dsw-alias-label-caption, #888); }
|
|
124
|
+
.pt-input:hover { border-color: var(--dsw-alias-border-l3, #c5c5c5); }
|
|
125
|
+
.pt-input:focus, .pt-rich:focus { border-color: var(--dsw-alias-brand-primary, #4f7cff); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary, #4f7cff) 18%, transparent); }
|
|
127
126
|
.pt-textarea { min-height: 90px; resize: vertical; }
|
|
128
127
|
.pt-rich { min-height: 180px; max-height: 320px; overflow-y: auto; padding: 12px 14px; }
|
|
129
128
|
.pt-rich:empty::before { content: attr(data-placeholder); color: var(--dsw-alias-label-caption, #999); }
|
|
@@ -206,7 +205,7 @@ const STYLE = `
|
|
|
206
205
|
.pt-tab.active { background: var(--dsw-alias-bg-layer-2, #fff); color: var(--dsw-alias-label-primary, #1a1a1a);
|
|
207
206
|
border-color: var(--dsw-alias-accent, #4f7cff); }
|
|
208
207
|
.pt-field { margin-bottom: 14px; }
|
|
209
|
-
.pt-field-label { display: block; font-size: 13.5px; font-weight:
|
|
208
|
+
.pt-field-label { display: block; font-size: 13.5px; font-weight: 500; color: var(--dsw-alias-label-primary, #1a1a1a);
|
|
210
209
|
margin-bottom: 6px; }
|
|
211
210
|
.pt-modal-backdrop { position: fixed; inset: 0; z-index: 60; background: rgba(0,0,0,.38);
|
|
212
211
|
display: flex; align-items: center; justify-content: center; padding: 24px; }
|
|
@@ -368,13 +367,22 @@ const I18N = {
|
|
|
368
367
|
send: 'Send', langLabel: 'Language', loading: 'Loading…',
|
|
369
368
|
},
|
|
370
369
|
}
|
|
371
|
-
|
|
372
|
-
try {
|
|
370
|
+
function readBandungLang() {
|
|
371
|
+
try {
|
|
372
|
+
if (typeof window !== 'undefined' && window.__dshAppDock__ && window.__dshAppDock__.lang) return window.__dshAppDock__.lang.get()
|
|
373
|
+
return typeof localStorage !== 'undefined' ? (localStorage.getItem('bandung-lang') === 'en' ? 'en' : 'zh') : 'zh'
|
|
374
|
+
} catch { return 'zh' }
|
|
375
|
+
}
|
|
373
376
|
const langStore = {
|
|
374
|
-
val:
|
|
377
|
+
val: readBandungLang(),
|
|
375
378
|
subs: new Set(),
|
|
376
379
|
emit() { for (const f of this.subs) f() },
|
|
377
|
-
set(v) {
|
|
380
|
+
set(v) {
|
|
381
|
+
this.val = v === 'en' ? 'en' : 'zh'
|
|
382
|
+
try { localStorage.setItem('bandung-lang', this.val) } catch { /* ignore */ }
|
|
383
|
+
if (typeof window !== 'undefined' && window.__dshAppDock__ && window.__dshAppDock__.lang) window.__dshAppDock__.lang.set(this.val)
|
|
384
|
+
this.emit()
|
|
385
|
+
},
|
|
378
386
|
subscribe(f) { this.subs.add(f); return () => { this.subs.delete(f) } },
|
|
379
387
|
}
|
|
380
388
|
function useLang() {
|
|
@@ -387,22 +395,17 @@ function t(key, vars) {
|
|
|
387
395
|
if (vars) { for (const k of Object.keys(vars)) text = text.replace('{' + k + '}', String(vars[k])) }
|
|
388
396
|
return text
|
|
389
397
|
}
|
|
390
|
-
function LangSwitch() {
|
|
391
|
-
const lang = useLang()
|
|
392
|
-
const opt = (code, label) => h('button', {
|
|
393
|
-
className: 'pt-lang-opt' + (lang === code ? ' on' : ''),
|
|
394
|
-
onClick: () => langStore.set(code),
|
|
395
|
-
}, label)
|
|
396
|
-
return h('div', { className: 'pt-lang' },
|
|
397
|
-
h('span', { className: 'pt-lang-label' }, t('langLabel')),
|
|
398
|
-
h('div', { className: 'pt-lang-opts' }, opt('zh', '中文'), opt('en', 'English')))
|
|
399
|
-
}
|
|
400
398
|
|
|
401
399
|
// ---------- 工具 ----------
|
|
402
400
|
|
|
401
|
+
// RPC 走 webServer 的 /pictor/rpc/* prefix 路由(同源 HTTP,不再经 connection.rpc.call)。
|
|
403
402
|
function rpc(ctx, endpoint, payload) {
|
|
404
403
|
// 服务端 schema 要求 payload 字段必须存在(对象);缺省补 {},否则序列化丢键被拒。
|
|
405
|
-
return
|
|
404
|
+
return fetch(RPC + '/rpc/' + encodeURIComponent(endpoint), {
|
|
405
|
+
method: 'POST',
|
|
406
|
+
headers: { 'content-type': 'application/json' },
|
|
407
|
+
body: JSON.stringify(payload === undefined ? {} : payload),
|
|
408
|
+
}).then((r) => r.json()).then((result) => {
|
|
406
409
|
if (result && result.ok === false) {
|
|
407
410
|
const msg = result.error && result.error.message ? result.error.message : String(result.error || 'RPC 调用失败')
|
|
408
411
|
throw new Error(msg)
|
|
@@ -492,14 +495,27 @@ function sanitizePaste(html) {
|
|
|
492
495
|
// 在就调哪个,老版本前端不受影响。
|
|
493
496
|
function sessionConnectFn(ctx, ws) {
|
|
494
497
|
let uiWs = null
|
|
495
|
-
try { uiWs =
|
|
498
|
+
try { uiWs = (ctx.get && ctx.get('uiWorkspace')) || null } catch { /* ignore */ }
|
|
496
499
|
return (uiWs && typeof uiWs.connectWorkspace === 'function')
|
|
497
500
|
? uiWs.connectWorkspace.bind(uiWs)
|
|
498
501
|
: (ws && typeof ws.connectWorkspace === 'function') ? ws.connectWorkspace.bind(ws) : null
|
|
499
502
|
}
|
|
500
503
|
|
|
504
|
+
/**
|
|
505
|
+
* 从 workspaces.connectWorkspace / sessions.create 的返回里取 sessionId。
|
|
506
|
+
* 0.1.5 起 create/connectWorkspace 返回 {ok, value:{sessionId}} 信封;
|
|
507
|
+
* 早版本直接返回 id 字符串或 {id}。这里全兼容。
|
|
508
|
+
*/
|
|
509
|
+
function extractSid(created) {
|
|
510
|
+
if (!created) return null
|
|
511
|
+
if (typeof created === 'string') return created
|
|
512
|
+
return created.sessionId || created.id
|
|
513
|
+
|| (created.value && (created.value.sessionId || created.value.id))
|
|
514
|
+
|| null
|
|
515
|
+
}
|
|
516
|
+
|
|
501
517
|
async function pictorWorkspace(ctx) {
|
|
502
|
-
const ws =
|
|
518
|
+
const ws = (ctx.get && ctx.get('workspaces'))
|
|
503
519
|
const base = await rpc(ctx, 'config.get').then((c) => (c && c.dataRoot) || '')
|
|
504
520
|
// 这里只用 list/create/delete/rename,新旧 services 都有;connectWorkspace
|
|
505
521
|
// 的可用性由 driveProjectSession 检查。
|
|
@@ -542,13 +558,13 @@ async function pictorWorkspace(ctx) {
|
|
|
542
558
|
}
|
|
543
559
|
|
|
544
560
|
async function driveProjectSession(ctx, prompt) {
|
|
545
|
-
const ws =
|
|
546
|
-
const sessionsSvc =
|
|
561
|
+
const ws = (ctx.get && ctx.get('workspaces'))
|
|
562
|
+
const sessionsSvc = (ctx.get && ctx.get('sessions'))
|
|
547
563
|
const connect = sessionConnectFn(ctx, ws)
|
|
548
564
|
const canCreate = !!(sessionsSvc && typeof sessionsSvc.create === 'function')
|
|
549
565
|
if (!(connect || canCreate) || !(sessionsSvc && typeof sessionsSvc.binding === 'function')) {
|
|
550
566
|
let uiWs = null
|
|
551
|
-
try { uiWs =
|
|
567
|
+
try { uiWs = (ctx.get && ctx.get('uiWorkspace')) || null } catch { /* ignore */ }
|
|
552
568
|
const diag = { ws: !!ws, ses: !!sessionsSvc && typeof sessionsSvc.binding === 'function', uiws: !!uiWs, conn: !!connect, create: canCreate }
|
|
553
569
|
console.warn('[pictor] session services unavailable', diag)
|
|
554
570
|
try { window.__pictor_diag = diag } catch { /* ignore */ }
|
|
@@ -559,7 +575,7 @@ async function driveProjectSession(ctx, prompt) {
|
|
|
559
575
|
let sessionId
|
|
560
576
|
try {
|
|
561
577
|
const created = connect ? await connect(wid) : await sessionsSvc.create({ workspaceId: wid })
|
|
562
|
-
sessionId = created
|
|
578
|
+
sessionId = extractSid(created)
|
|
563
579
|
}
|
|
564
580
|
catch (e) { throw new Error('创建会话失败:' + String((e && e.message) || e)) }
|
|
565
581
|
const bound = sessionsSvc && typeof sessionsSvc.binding === 'function' ? sessionsSvc.binding(sessionId) : null
|
|
@@ -575,7 +591,7 @@ async function driveProjectSession(ctx, prompt) {
|
|
|
575
591
|
async function drivePrompt(ctx, id, message) {
|
|
576
592
|
const p = await rpc(ctx, 'project.prompt', { id, message })
|
|
577
593
|
if (p && p.sessionId) {
|
|
578
|
-
const sessionsSvc =
|
|
594
|
+
const sessionsSvc = (ctx.get && ctx.get('sessions'))
|
|
579
595
|
if (sessionsSvc && typeof sessionsSvc.binding === 'function') {
|
|
580
596
|
try {
|
|
581
597
|
const bound = sessionsSvc.binding(p.sessionId)
|
|
@@ -1666,7 +1682,7 @@ function Workbench(props) {
|
|
|
1666
1682
|
h('button', { className: 'pt-nav-settings', onClick: () => setView('settings') },
|
|
1667
1683
|
h('span', { className: 'glyph' }, '⚙'),
|
|
1668
1684
|
t('navSettings')),
|
|
1669
|
-
|
|
1685
|
+
))
|
|
1670
1686
|
|
|
1671
1687
|
return h('div', { className: 'pt-root pt-workbench' },
|
|
1672
1688
|
nav,
|
|
@@ -1712,9 +1728,9 @@ function selectedLabel(ids) {
|
|
|
1712
1728
|
// 调整了客户端 API),host 落盘 ~/.pictor/diag.jsonl,无需 DevTools 即可排查。
|
|
1713
1729
|
function pictorDiag(ctx, phase) {
|
|
1714
1730
|
let ws = null, ses = null, ui = null
|
|
1715
|
-
try { ws = ctx && (ctx.
|
|
1716
|
-
try { ses = ctx && (ctx.
|
|
1717
|
-
try { ui = ctx && (ctx.
|
|
1731
|
+
try { ws = ctx && (ctx.get && ctx.get('workspaces')) } catch { /* ignore */ }
|
|
1732
|
+
try { ses = ctx && (ctx.get && ctx.get('sessions')) } catch { /* ignore */ }
|
|
1733
|
+
try { ui = ctx && (ctx.get && ctx.get('uiWorkspace')) } catch { /* ignore */ }
|
|
1718
1734
|
const body = {
|
|
1719
1735
|
phase,
|
|
1720
1736
|
ws: !!ws, ses: !!ses, ui: !!ui,
|
|
@@ -1732,7 +1748,7 @@ function pictorDiag(ctx, phase) {
|
|
|
1732
1748
|
|
|
1733
1749
|
function apply(ctx) {
|
|
1734
1750
|
try { pictorDiag(ctx, 'apply') } catch { /* ignore */ }
|
|
1735
|
-
console.info('[dsh-pictor] apply 被调用', { hasSlots: Boolean(ctx && ctx.slots) })
|
|
1751
|
+
console.info('[dsh-pictor] apply 被调用', { hasSlots: Boolean(ctx.get && ctx.get('slots')) })
|
|
1736
1752
|
if (typeof document !== 'undefined') {
|
|
1737
1753
|
const styleId = 'dsh-pictor-style'
|
|
1738
1754
|
if (!document.getElementById(styleId)) {
|
|
@@ -1742,7 +1758,7 @@ function apply(ctx) {
|
|
|
1742
1758
|
document.head.appendChild(style)
|
|
1743
1759
|
}
|
|
1744
1760
|
}
|
|
1745
|
-
const slots =
|
|
1761
|
+
const slots = (ctx.get && ctx.get('slots'))
|
|
1746
1762
|
if (!slots) return
|
|
1747
1763
|
|
|
1748
1764
|
const panel = { open: false, subs: new Set() }
|
|
@@ -1751,12 +1767,12 @@ function apply(ctx) {
|
|
|
1751
1767
|
panel.close = () => { if (panel.open) { panel.open = false; panel.emit() } }
|
|
1752
1768
|
panel.subscribe = (fn) => { panel.subs.add(fn); return () => { panel.subs.delete(fn) } }
|
|
1753
1769
|
// 点选 dsh 左侧会话(或面板外任意处)自动收起工作台:
|
|
1754
|
-
//
|
|
1770
|
+
// 只排除面板本身;侧栏会话此刻在面板左侧始终可点(入口已交给应用坞)。
|
|
1755
1771
|
if (typeof document !== 'undefined') {
|
|
1756
1772
|
document.addEventListener('mousedown', (e) => {
|
|
1757
1773
|
if (!panel.open) return
|
|
1758
1774
|
const t = e.target
|
|
1759
|
-
if (t && typeof t.closest === 'function' &&
|
|
1775
|
+
if (t && typeof t.closest === 'function' && t.closest('.pt-shell-panel')) return
|
|
1760
1776
|
panel.close()
|
|
1761
1777
|
})
|
|
1762
1778
|
}
|
|
@@ -1798,24 +1814,22 @@ function apply(ctx) {
|
|
|
1798
1814
|
h(Workbench, { ctx, key: 'shell' })))
|
|
1799
1815
|
}
|
|
1800
1816
|
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
'aria-expanded': open ? 'true' : 'false',
|
|
1812
|
-
}, h('span', { className: 'glyph' }, '◈'), 'Pictor')
|
|
1817
|
+
// 入坞:dsh-app-dock 是依赖,入口交给坞(含 ready 延迟注册),自占 footer 槽移除。
|
|
1818
|
+
const registerWithDock = () => {
|
|
1819
|
+
if (typeof window === 'undefined' || !window.__dshAppDock__) return
|
|
1820
|
+
window.__dshAppDock__.register({ id: 'dsh-pictor', label: 'Pictor', icon: '◈', order: 20, onToggle: () => panel.toggle() })
|
|
1821
|
+
if (window.__dshAppDock__.lang) {
|
|
1822
|
+
window.__dshAppDock__.lang.subscribe(() => {
|
|
1823
|
+
const v = window.__dshAppDock__.lang.get()
|
|
1824
|
+
if (langStore.val !== v) { langStore.val = v; langStore.emit() }
|
|
1825
|
+
})
|
|
1826
|
+
}
|
|
1813
1827
|
}
|
|
1828
|
+
if (typeof window !== 'undefined' && !window.__dshAppDock__) {
|
|
1829
|
+
window.addEventListener('dsh-app-dock:ready', registerWithDock, { once: true })
|
|
1830
|
+
}
|
|
1831
|
+
registerWithDock()
|
|
1814
1832
|
|
|
1815
|
-
slots.inject('sidebar.footer.action', () => slots.register(
|
|
1816
|
-
{ name: 'sidebar.footer.action', id: 'dsh-pictor', order: 20, label: 'Pictor' },
|
|
1817
|
-
() => h(FooterAction, null),
|
|
1818
|
-
))
|
|
1819
1833
|
slots.inject('shell.overlay', () => slots.register(
|
|
1820
1834
|
{ name: 'shell.overlay', id: 'dsh-pictor', order: 10, label: 'Pictor' },
|
|
1821
1835
|
() => h(WorkbenchPanel, null),
|
package/lib/index.js
CHANGED
|
@@ -106,7 +106,7 @@ function initialPrompt(dir) {
|
|
|
106
106
|
return prompts.join("\n");
|
|
107
107
|
}
|
|
108
108
|
function apply(ctx, config = {}) {
|
|
109
|
-
const connection = ctx.connection;
|
|
109
|
+
const connection = ctx.get?.("connection");
|
|
110
110
|
const base = config.dataDir || process.env.PICTOR_HOME || join(homedir(), ".pictor");
|
|
111
111
|
const INDEX_JSON = join(base, "index.json");
|
|
112
112
|
const CONFIG_JSON = join(base, "pictor-config.json");
|
|
@@ -171,7 +171,7 @@ function apply(ctx, config = {}) {
|
|
|
171
171
|
}
|
|
172
172
|
function credentialsService() {
|
|
173
173
|
try {
|
|
174
|
-
return ctx.
|
|
174
|
+
return ctx.get?.("credentials");
|
|
175
175
|
} catch {
|
|
176
176
|
return null;
|
|
177
177
|
}
|
|
@@ -676,20 +676,36 @@ function apply(ctx, config = {}) {
|
|
|
676
676
|
throw new Error(`dsh-pictor: \u672A\u77E5\u7AEF\u70B9 ${endpoint}`);
|
|
677
677
|
}
|
|
678
678
|
}
|
|
679
|
-
|
|
680
|
-
connection.rpc.handle("/pictor", async (endpoint, payload) => {
|
|
681
|
-
try {
|
|
682
|
-
return { ok: true, value: await handle(endpoint, payload || {}) };
|
|
683
|
-
} catch (e) {
|
|
684
|
-
logError(`rpc:${endpoint}`, e);
|
|
685
|
-
return { ok: false, error: { code: "internal", message: String(e?.message || e), details: {} } };
|
|
686
|
-
}
|
|
687
|
-
}, { authority: "loopback" });
|
|
688
|
-
}
|
|
689
|
-
const wsA = ctx.webServer || ctx.get?.("webServer");
|
|
679
|
+
const wsA = ctx.get?.("webServer");
|
|
690
680
|
if (wsA && typeof wsA.register === "function") {
|
|
691
681
|
const IMG_CACHE = { "content-type": "image/png", "cache-control": "public, max-age=3600" };
|
|
692
682
|
const WEBP_CACHE = { "content-type": "image/webp", "cache-control": "public, max-age=3600" };
|
|
683
|
+
const RPC_PREFIX = "/pictor/rpc";
|
|
684
|
+
wsA.register({ kind: "prefix", path: RPC_PREFIX, handler: async (req, res) => {
|
|
685
|
+
try {
|
|
686
|
+
const pathname = new URL(req.url || "/", "http://x").pathname;
|
|
687
|
+
const endpoint = decodeURIComponent(pathname.slice(RPC_PREFIX.length + 1));
|
|
688
|
+
let payload = {};
|
|
689
|
+
if (req.method === "POST" || req.method === "PUT") {
|
|
690
|
+
const chunks = [];
|
|
691
|
+
for await (const c of req) chunks.push(Buffer.isBuffer(c) ? c : Buffer.from(c));
|
|
692
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
693
|
+
if (raw) payload = JSON.parse(raw);
|
|
694
|
+
}
|
|
695
|
+
try {
|
|
696
|
+
const value = await handle(endpoint, payload || {});
|
|
697
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
698
|
+
res.end(JSON.stringify({ ok: true, value }));
|
|
699
|
+
} catch (e) {
|
|
700
|
+
logError(`rpc:${endpoint}`, e);
|
|
701
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
702
|
+
res.end(JSON.stringify({ ok: false, error: { code: "internal", message: String(e?.message || e), details: {} } }));
|
|
703
|
+
}
|
|
704
|
+
} catch {
|
|
705
|
+
res.writeHead(400);
|
|
706
|
+
res.end("bad request");
|
|
707
|
+
}
|
|
708
|
+
} });
|
|
693
709
|
wsA.register({ kind: "exact", path: "/pictor/asset/empty-state.png", handler: (_req, res) => {
|
|
694
710
|
res.writeHead(200, IMG_CACHE);
|
|
695
711
|
res.end(readFileSync(join(PLUGIN_ROOT, "assets", "empty-state.png")));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-pictor",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Pictor:文档转信息图工作台。每项目一个连贯 dsh 会话,GUI 为 shell.overlay 工作台(左栏项目、右栏三步:提取/方案/渲染),行为全部交给 DSH 智能运行时。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
},
|
|
31
31
|
"client": {
|
|
32
32
|
"inject": [
|
|
33
|
-
"@deepseek-ai/dsh-client-runtime",
|
|
34
33
|
"@deepseek-ai/dsh-client-ui-theme",
|
|
35
34
|
"@deepseek-ai/dsh-client-ui-conversation",
|
|
36
|
-
"@deepseek-ai/dsh-client-ui-settings"
|
|
35
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
36
|
+
"@deepseek-ai/dsh-client-ui-workspace"
|
|
37
37
|
],
|
|
38
38
|
"platform": "web"
|
|
39
39
|
}
|
|
@@ -61,11 +61,15 @@
|
|
|
61
61
|
"prepare": "node scripts/build.mjs"
|
|
62
62
|
},
|
|
63
63
|
"license": "MIT",
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"dsh-app-dock": "^0.1.2"
|
|
66
|
+
},
|
|
64
67
|
"peerDependencies": {
|
|
65
|
-
"@deepseek-ai/
|
|
68
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
66
69
|
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
|
|
67
70
|
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6",
|
|
68
71
|
"@deepseek-ai/dsh-client-ui-theme": "^0.1.0-rc.6",
|
|
72
|
+
"@deepseek-ai/dsh-client-ui-workspace": "^0.1.0-rc.6",
|
|
69
73
|
"react": "^18.2.0",
|
|
70
74
|
"react-dom": "^18.2.0"
|
|
71
75
|
},
|
|
@@ -81,4 +85,4 @@
|
|
|
81
85
|
"@playwright/test": "^1.62.1",
|
|
82
86
|
"esbuild": "^0.28.2"
|
|
83
87
|
}
|
|
84
|
-
}
|
|
88
|
+
}
|