dsh-pictor 0.2.1 → 0.2.2

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 (2) hide show
  1. package/lib/client.js +50 -54
  2. package/package.json +4 -1
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: 18px; font-weight: 700; letter-spacing: -0.01em; margin: 0; }
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: 0; background: var(--dsw-alias-state-business-primary, #4f7cff); color: #fff;
59
- border-radius: 10px; padding: 7px 12px; font-size: 13px; font-weight: 600; cursor: pointer; white-space: nowrap; flex: none; }
60
- .pt-nav-new:hover { filter: brightness(.95); }
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: 12px; padding: 9px 18px; font-size: 15px;
96
- font-weight: 550; cursor: pointer; transition: filter 150ms, background 150ms; white-space: nowrap; }
97
- .pt-btn:hover { filter: brightness(0.96); }
98
- .pt-btn:disabled { opacity: 0.45; cursor: not-allowed; }
99
- .pt-btn-primary { background: var(--dsw-alias-accent, #4f7cff); border-color: var(--dsw-alias-accent, #4f7cff); color: #fff; }
100
- .pt-btn-ghost { background: transparent; border-color: transparent; color: var(--dsw-alias-label-secondary, #666); }
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: 12px;
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: 10px 14px; font-size: 15px; outline: none; }
126
- .pt-input:focus, .pt-rich:focus { border-color: var(--dsw-alias-accent, #4f7cff); }
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: 550; color: var(--dsw-alias-label-secondary, #666);
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
- let savedLang = 'zh'
372
- try { savedLang = typeof localStorage !== 'undefined' ? localStorage.getItem('pictor-lang') : null } catch { /* 沙箱可能没有 storage */ }
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: savedLang === 'en' ? 'en' : 'zh',
377
+ val: readBandungLang(),
375
378
  subs: new Set(),
376
379
  emit() { for (const f of this.subs) f() },
377
- set(v) { this.val = v === 'en' ? 'en' : 'zh'; try { localStorage.setItem('pictor-lang', this.val) } catch { /* ignore */ } this.emit() },
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,16 +395,6 @@ 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
 
@@ -1666,7 +1664,7 @@ function Workbench(props) {
1666
1664
  h('button', { className: 'pt-nav-settings', onClick: () => setView('settings') },
1667
1665
  h('span', { className: 'glyph' }, '⚙'),
1668
1666
  t('navSettings')),
1669
- h(LangSwitch, null)))
1667
+ ))
1670
1668
 
1671
1669
  return h('div', { className: 'pt-root pt-workbench' },
1672
1670
  nav,
@@ -1751,12 +1749,12 @@ function apply(ctx) {
1751
1749
  panel.close = () => { if (panel.open) { panel.open = false; panel.emit() } }
1752
1750
  panel.subscribe = (fn) => { panel.subs.add(fn); return () => { panel.subs.delete(fn) } }
1753
1751
  // 点选 dsh 左侧会话(或面板外任意处)自动收起工作台:
1754
- // 只排除面板本身与 Pictor 启动按钮;侧栏会话此刻在面板左侧始终可点。
1752
+ // 只排除面板本身;侧栏会话此刻在面板左侧始终可点(入口已交给应用坞)。
1755
1753
  if (typeof document !== 'undefined') {
1756
1754
  document.addEventListener('mousedown', (e) => {
1757
1755
  if (!panel.open) return
1758
1756
  const t = e.target
1759
- if (t && typeof t.closest === 'function' && (t.closest('.pt-shell-panel') || t.closest('.pt-footer-action'))) return
1757
+ if (t && typeof t.closest === 'function' && t.closest('.pt-shell-panel')) return
1760
1758
  panel.close()
1761
1759
  })
1762
1760
  }
@@ -1798,24 +1796,22 @@ function apply(ctx) {
1798
1796
  h(Workbench, { ctx, key: 'shell' })))
1799
1797
  }
1800
1798
 
1801
- function FooterAction() {
1802
- useLang() // footer 文案随语言刷新
1803
- const open = usePanelOpen()
1804
- return h('div', {
1805
- className: 'pt-footer-action' + (open ? ' on' : ''),
1806
- role: 'button',
1807
- tabIndex: 0,
1808
- onClick: () => panel.toggle(),
1809
- onKeyDown: (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); panel.toggle() } },
1810
- title: open ? t('close') : t('open'),
1811
- 'aria-expanded': open ? 'true' : 'false',
1812
- }, h('span', { className: 'glyph' }, '◈'), 'Pictor')
1799
+ // 入坞:dsh-app-dock 是依赖,入口交给坞(含 ready 延迟注册),自占 footer 槽移除。
1800
+ const registerWithDock = () => {
1801
+ if (typeof window === 'undefined' || !window.__dshAppDock__) return
1802
+ window.__dshAppDock__.register({ id: 'dsh-pictor', label: 'Pictor', icon: '◈', order: 20, onToggle: () => panel.toggle() })
1803
+ if (window.__dshAppDock__.lang) {
1804
+ window.__dshAppDock__.lang.subscribe(() => {
1805
+ const v = window.__dshAppDock__.lang.get()
1806
+ if (langStore.val !== v) { langStore.val = v; langStore.emit() }
1807
+ })
1808
+ }
1813
1809
  }
1810
+ if (typeof window !== 'undefined' && !window.__dshAppDock__) {
1811
+ window.addEventListener('dsh-app-dock:ready', registerWithDock, { once: true })
1812
+ }
1813
+ registerWithDock()
1814
1814
 
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
1815
  slots.inject('shell.overlay', () => slots.register(
1820
1816
  { name: 'shell.overlay', id: 'dsh-pictor', order: 10, label: 'Pictor' },
1821
1817
  () => h(WorkbenchPanel, null),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-pictor",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Pictor:文档转信息图工作台。每项目一个连贯 dsh 会话,GUI 为 shell.overlay 工作台(左栏项目、右栏三步:提取/方案/渲染),行为全部交给 DSH 智能运行时。",
5
5
  "type": "module",
6
6
  "repository": {
@@ -61,6 +61,9 @@
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
68
  "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
66
69
  "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",