@lijian-ui/dsh-file-manager 0.2.2 → 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.
@@ -33,17 +33,13 @@ export interface ConversationService {
33
33
  export interface FileReference {
34
34
  /** Workspace-relative path (what the agent will read). */
35
35
  path: string;
36
- /** First line of the referenced range (1-based, inclusive). */
37
- startLine: number;
38
- /** Last line of the referenced range (1-based, inclusive). */
39
- endLine: number;
36
+ /** First line of the referenced range (1-based, inclusive). Omit for whole-file references. */
37
+ startLine?: number;
38
+ /** Last line of the referenced range (1-based, inclusive). Omit for whole-file references. */
39
+ endLine?: number;
40
40
  }
41
- /** The `@path:lines` model form (what the codec serializes to on submit). */
42
- export declare function formatFileRefToken(ref: {
43
- path: string;
44
- startLine: number;
45
- endLine: number;
46
- }): string;
41
+ /** The `@path` / `@path:lines` model form (what the codec serializes to on submit). */
42
+ export declare function formatFileRefToken(ref: FileReference): string;
47
43
  /** The DRAFT-REV+CAS span the conversation machine expects for chip inserts. */
48
44
  export interface RefSpan {
49
45
  start: number;
@@ -1 +1 @@
1
- {"version":3,"file":"reference.d.ts","sourceRoot":"","sources":["../../../src/client/reference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAA;AAEtF;;;;;;;;;;GAUG;AAEH;;;;GAIG;AACH,UAAU,UAAU;IAClB,KAAK,EAAE;QAAE,WAAW,IAAI;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AACD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,aAAa,GAAG,UAAU,CAAA;KAAE,CAAA;CAChD;AAED,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAA;IACZ,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAA;IACjB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAA;CAChB;AA0BD,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAGpG;AAUD,gFAAgF;AAChF,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAezG;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,aAAa,EAClB,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,OAAO,GACZ,OAAO,CAkBT;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,aAAa,EAClB,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,GAAG,EAAE,aAAa,GACjB,OAAO,CAcT"}
1
+ {"version":3,"file":"reference.d.ts","sourceRoot":"","sources":["../../../src/client/reference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAA;AAEtF;;;;;;;;;;GAUG;AAEH;;;;GAIG;AACH,UAAU,UAAU;IAClB,KAAK,EAAE;QAAE,WAAW,IAAI;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AACD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,aAAa,GAAG,UAAU,CAAA;KAAE,CAAA;CAChD;AAED,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAA;IACZ,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AA2BD,uFAAuF;AACvF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAI7D;AAYD,gFAAgF;AAChF,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAezG;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,aAAa,EAClB,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,OAAO,GACZ,OAAO,CAmBT;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,aAAa,EAClB,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,GAAG,EAAE,aAAa,GACjB,OAAO,CAcT"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lijian-ui/dsh-file-manager",
3
3
  "description": "DSH web GUI right-panel system: a pixel-faithful re-implementation of FileManager's Explorer + Preview columns (file tree, filename search, git changes, multi-tab preview of 10+ formats) driven by real host-side fs/git services, hot-pluggable via a profile bundle without any dsh source changes",
4
- "version": "0.2.2",
4
+ "version": "0.2.3",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -14,7 +14,7 @@ function ensureStyle(): void {
14
14
  if (document.getElementById(STYLE_ID) !== null) return
15
15
  const tag = document.createElement('style')
16
16
  tag.id = STYLE_ID
17
- tag.textContent = '.dsh-dock-item{width:32px;height:32px;border:none;border-radius:9999px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);cursor:pointer;position:relative;transition:transform .15s ease,background .18s ease;display:inline-flex;align-items:center;justify-content:center}.dsh-dock-item:hover{transform:scale(1.15);background:var(--dsw-alias-interactive-bg-hover)}.dsh-dock-item-active{background:var(--dsw-alias-state-business-primary)}.dsh-dock-tooltip{position:absolute;top:calc(100% + 6px);left:50%;transform:translateX(-50%);padding:2px 8px;font-size:12px;line-height:1.4;white-space:nowrap;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;opacity:0;pointer-events:none;transition:opacity .15s}.dsh-dock-item:hover .dsh-dock-tooltip{opacity:1}'
17
+ tag.textContent = '.dsh-dock-item{width:32px;height:32px;border:none;border-radius:9999px;background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-base);cursor:pointer;position:relative;transition:transform .15s ease,background .18s ease;display:inline-flex;align-items:center;justify-content:center}.dsh-dock-item:hover{transform:scale(1.15);background:color-mix(in srgb,var(--dsw-alias-label-primary) 85%,var(--dsw-alias-bg-base))}.dsh-dock-item-active{background:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-bg-base)}.dsh-dock-tooltip{position:absolute;top:calc(100% + 6px);left:50%;transform:translateX(-50%);padding:2px 8px;font-size:12px;line-height:1.4;white-space:nowrap;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;opacity:0;pointer-events:none;transition:opacity .15s}.dsh-dock-item:hover .dsh-dock-tooltip{opacity:1}'
18
18
  document.head.appendChild(tag)
19
19
  }
20
20
 
@@ -37,7 +37,6 @@ import { handleFileRefClick } from './chat/file-ref.ts'
37
37
  import { appendToDraft, appendFileReferenceChip } from './reference.ts'
38
38
  import { registerFileReferenceSource } from './file-source.ts'
39
39
  import type { FileReferenceSelection } from './preview/selection.ts'
40
- import './styles/chip.module.css'
41
40
  import { FilePanelDockItem } from './FilePanelDockItem.tsx'
42
41
 
43
42
  declare module '@deepseek-ai/dsh-client-ui-slots' {
@@ -223,9 +222,8 @@ export function apply(ctx: ClientContext): void {
223
222
  let lastPreviewOpen = false
224
223
 
225
224
  // The Explorer row '@' button: insert a file-reference chip for files
226
- // (path + 1..N) or plain `@<rel-path>/` text for folders. File lines
227
- // are fetched from the host so the chip can carry an accurate line
228
- // range; failures fall back to plain text.
225
+ // (plain `@<rel-path>`, no line range) or plain `@<rel-path>/` text for
226
+ // folders.
229
227
  const onReference = (path: string, isDir: boolean): void => {
230
228
  const sessionId = ctx.sessions.list.getSnapshot().current as SessionId | undefined
231
229
  if (sessionId === undefined) {
@@ -236,16 +234,7 @@ export function apply(ctx: ClientContext): void {
236
234
  appendToDraft(ctx, sessionId, `@${path}/ `)
237
235
  return
238
236
  }
239
- void panelApi.read(currentRoot, path, false).then((result) => {
240
- if (!result.ok) {
241
- appendToDraft(ctx, sessionId, `@${path} `)
242
- return
243
- }
244
- const text = result.value.content
245
- const trimmed = text.endsWith('\n') ? text.slice(0, -1) : text
246
- const lines = trimmed === '' ? 1 : trimmed.split('\n').length
247
- appendFileReferenceChip(ctx, sessionId, { path, startLine: 1, endLine: lines })
248
- })
237
+ appendFileReferenceChip(ctx, sessionId, { path })
249
238
  }
250
239
 
251
240
  // The preview "添加到对话" action: insert a file-reference chip
@@ -4,13 +4,15 @@
4
4
  *
5
5
  * Trigger: typing `@` (with nothing after it) in the composer opens this
6
6
  * modal directly — no inline trigger menu (the shell's trigger menu is a
7
- * flat candidate list that cannot host a tree). The modal owns a recursive
8
- * tree with lazy expansion, a workspace filename search, per-row checkboxes
9
- * for multi-selection, and a chip row of the picked paths.
7
+ * flat candidate list that cannot host a tree). On open the just-typed `@`
8
+ * is stripped from the composer draft, which permanently silences the
9
+ * shell's own draft-driven `@` menu while the picker is up. The modal owns
10
+ * a recursive tree with lazy expansion, a workspace filename search,
11
+ * per-row checkboxes for multi-selection, and a chip row of the picked
12
+ * paths.
10
13
  *
11
- * Insertion replaces the `@` token the picker was opened from with the
12
- * picked references joined by spaces (`@a.ts @src/ @b.md`); cancel drops
13
- * the stray `@`.
14
+ * Insertion lands the picked references at the recorded anchor position
15
+ * (`@a.ts @src/ @b.md`); cancel drops the stray `@`.
14
16
  * @module dsh-filemgr/client/picker/FilePickerModal
15
17
  */
16
18
 
@@ -399,6 +401,28 @@ export function FilePickerHost({ ctx, api }: { ctx: ClientContext; api: PanelApi
399
401
  initialDir: '',
400
402
  span: { start: mention.start, end: mention.start + 1 },
401
403
  })
404
+ // Strip the just-typed `@` from the composer draft. Ordering note: this
405
+ // listener runs on the textarea (target phase) BEFORE the shell's
406
+ // root-delegated handler commits the keystroke into the machine, so a
407
+ // synchronous strip would read a STALE snapshot (guard fails, `@`
408
+ // survives). Defer to a microtask — by then the machine has committed
409
+ // the draft with the `@`, the guard holds, and the shell's draft-driven
410
+ // `@` menu loses its anchor for good. insert() re-checks the anchor
411
+ // idempotently in case this race ever lands the other way.
412
+ queueMicrotask(() => {
413
+ try {
414
+ const actx = ctx.sessions.scope(sessionId)
415
+ const conversation = (ctx as unknown as { conversation?: ConversationService }).conversation
416
+ if (actx === undefined || conversation === undefined) return
417
+ const shellInput = conversation.input.for(actx)
418
+ const snap = shellInput.state.getSnapshot()
419
+ if (snap.draft.charAt(mention.start) === '@') {
420
+ shellInput.setDraft(snap.draft.slice(0, mention.start) + snap.draft.slice(mention.start + 1))
421
+ }
422
+ } catch {
423
+ // Best-effort: insert() re-checks the anchor idempotently.
424
+ }
425
+ })
402
426
  }
403
427
  const attach = (): void => {
404
428
  for (const ta of document.querySelectorAll<HTMLTextAreaElement>('textarea')) {
@@ -421,58 +445,53 @@ export function FilePickerHost({ ctx, api }: { ctx: ClientContext; api: PanelApi
421
445
  /**
422
446
  * Insert the picked references into the composer draft as file-reference
423
447
  * chips (one `slash/input-insert-reference` bail per file; the `file`
424
- * source codec serializes each to `@path:lines` on submit). Folders fall
425
- * back to plain `@path/ ` text since the chip has no line concept.
426
- * Multiple selections land as adjacent chips.
448
+ * source codec serializes each to `@path` on submit). Whole-file picks
449
+ * carry no line range. Folders fall back to plain `@path/ ` text since
450
+ * the chip has no directory concept. Multiple selections land as
451
+ * adjacent chips. Since open stripped the bare `@` from the draft, every
452
+ * chip inserts at the recorded anchor as a zero-length span.
427
453
  */
428
454
  const insert = async (paths: PickedPath[]): Promise<void> => {
429
455
  const current = filePicker.getSnapshot()
430
456
  if (!current.open || current.req === null || paths.length === 0) return
431
- const { sessionId, root, span } = current.req
457
+ const { sessionId, span } = current.req
432
458
  try {
433
459
  const actx = ctx.sessions.scope(sessionId)
434
460
  if (actx === undefined) return
435
461
  const conversation = (ctx as unknown as { conversation?: ConversationService }).conversation
436
462
  if (conversation === undefined) return
437
463
  const shell = conversation.input.for(actx)
438
- // Pre-fetch every file's line count in parallel (bails are serial so
439
- // they share the same draft; reads are independent).
440
- const lineCounts = await Promise.all(paths.map(async (pick) => {
441
- if (pick.isDir) return { pick, lines: null }
442
- const result = await api.read(root, pick.path, false)
443
- if (!result.ok) return { pick, lines: null }
444
- const text = result.value.content
445
- const trimmed = text.endsWith('\n') ? text.slice(0, -1) : text
446
- const lines = trimmed === '' ? 1 : trimmed.split('\n').length
447
- return { pick, lines }
448
- }))
449
- // Walk in original order. The first chip replaces the `@` token; the
450
- // rest insert at the current cursor (0-length span) so the machine
451
- // appends each one in turn.
452
- let cursor = span.end
453
- let firstSpan = true
454
- for (const { pick, lines } of lineCounts) {
455
- if (pick.isDir) {
456
- appendToDraft(ctx, sessionId, `@${pick.path}/ `)
457
- continue
464
+ // Idempotent anchor cleanup: the open-time microtask strip usually
465
+ // removed the bare `@` already; if that race ever landed the other way
466
+ // (or was skipped), remove it now so no stray `@` survives next to the
467
+ // inserted chips.
468
+ {
469
+ const snap0 = shell.state.getSnapshot()
470
+ if (snap0.draft.charAt(span.start) === '@') {
471
+ shell.setDraft(snap0.draft.slice(0, span.start) + snap0.draft.slice(span.start + 1))
458
472
  }
459
- if (lines === null) continue
473
+ }
474
+ // Walk in REVERSE pick order, every file chip at the SAME zero-length
475
+ // anchor: each machine insert splices the chip's DISPLAY TEXT (`@label`)
476
+ // plus a gap at span.start — the chip look itself is a minted scan
477
+ // decoration, never special characters — pushing earlier-inserted text
478
+ // to the right, so the draft reads in original pick order with no
479
+ // cursor bookkeeping. (A fixed advance of 2 would land the next chip
480
+ // mid-text of the previous one.) Folders keep the plain-text append at
481
+ // the draft tail.
482
+ for (let i = paths.length - 1; i >= 0; i--) {
483
+ const pick = paths[i]
484
+ if (pick.isDir) continue
460
485
  const snap = shell.state.getSnapshot()
461
- const currentSpan: RefSpan = firstSpan
462
- ? { start: span.start, end: span.end, draftRev: snap.draftRev }
463
- : { start: cursor, end: cursor, draftRev: snap.draftRev }
464
- const ok = insertFileReference(
486
+ insertFileReference(
465
487
  ctx,
466
488
  sessionId,
467
- { path: pick.path, startLine: 1, endLine: lines },
468
- currentSpan,
489
+ { path: pick.path },
490
+ { start: span.start, end: span.start, draftRev: snap.draftRev },
469
491
  )
470
- if (ok) {
471
- firstSpan = false
472
- // The machine replaces [start,end) with U+FFFC and an optional
473
- // trailing space; the cursor advances by 2 for the new characters.
474
- cursor += 2
475
- }
492
+ }
493
+ for (const pick of paths) {
494
+ if (pick.isDir) appendToDraft(ctx, sessionId, `@${pick.path}/ `)
476
495
  }
477
496
  filePicker.close()
478
497
  } catch (error) {
@@ -70,6 +70,7 @@ export function PreviewPanel({ stores, onAddFileReference }: { stores: PanelStor
70
70
  }
71
71
  const ref = findFileReferenceSelection(el, selection, activeTab)
72
72
  if (ref === null) {
73
+
73
74
  setAddToChat(null)
74
75
  return
75
76
  }
@@ -29,10 +29,10 @@ export interface ConversationService {
29
29
  export interface FileReference {
30
30
  /** Workspace-relative path (what the agent will read). */
31
31
  path: string
32
- /** First line of the referenced range (1-based, inclusive). */
33
- startLine: number
34
- /** Last line of the referenced range (1-based, inclusive). */
35
- endLine: number
32
+ /** First line of the referenced range (1-based, inclusive). Omit for whole-file references. */
33
+ startLine?: number
34
+ /** Last line of the referenced range (1-based, inclusive). Omit for whole-file references. */
35
+ endLine?: number
36
36
  }
37
37
 
38
38
  /** Basename of a `/`-separated relative path ('' when empty). */
@@ -56,11 +56,13 @@ function formatRefLabel(ref: FileReference): string {
56
56
  const basename = basenameOf(ref.path)
57
57
  const dirname = dirnameOf(ref.path)
58
58
  const name = dirname === '' ? basename : `${basename} - ${dirname}`
59
+ if (ref.startLine === undefined || ref.endLine === undefined) return name
59
60
  return ref.endLine > ref.startLine ? `${name} ${ref.startLine}-${ref.endLine}` : `${name} ${ref.startLine}`
60
61
  }
61
62
 
62
- /** The `@path:lines` model form (what the codec serializes to on submit). */
63
- export function formatFileRefToken(ref: { path: string; startLine: number; endLine: number }): string {
63
+ /** The `@path` / `@path:lines` model form (what the codec serializes to on submit). */
64
+ export function formatFileRefToken(ref: FileReference): string {
65
+ if (ref.startLine === undefined || ref.endLine === undefined) return `@${ref.path}`
64
66
  const range = ref.endLine > ref.startLine ? `${ref.startLine}-${ref.endLine}` : `${ref.startLine}`
65
67
  return `@${ref.path}:${range}`
66
68
  }
@@ -71,6 +73,8 @@ interface ConversationReference {
71
73
  ref: string
72
74
  label: string
73
75
  clipboardText: string
76
+ /** Official chip appearance: renders the reference icon over the `@` glyph. */
77
+ appearance: 'file'
74
78
  }
75
79
 
76
80
  /** The DRAFT-REV+CAS span the conversation machine expects for chip inserts. */
@@ -86,7 +90,7 @@ export function appendToDraft(ctx: ClientContext, sessionId: SessionId | undefin
86
90
  if (sessionId === undefined) return false
87
91
  const actx = ctx.sessions.scope(sessionId)
88
92
  if (actx === undefined) return false
89
- const conversation = (ctx as unknown as { conversation?: ConversationService }).conversation
93
+ const conversation = (ctx as unknown as { get(name: string): unknown }).get('conversation') as ConversationService | undefined
90
94
  if (conversation === undefined) return false
91
95
  const input = conversation.input.for(actx as unknown as ClientContext)
92
96
  const draft = input.state.getSnapshot().draft
@@ -122,6 +126,7 @@ export function insertFileReference(
122
126
  ref: formatFileRefToken(ref),
123
127
  label: formatRefLabel(ref),
124
128
  clipboardText: formatFileRefToken(ref),
129
+ appearance: 'file',
125
130
  }
126
131
  return (actx as unknown as {
127
132
  bail(target: unknown, event: string, payload: unknown): boolean
@@ -148,7 +153,7 @@ export function appendFileReferenceChip(
148
153
  if (sessionId === undefined) return false
149
154
  const actx = ctx.sessions.scope(sessionId)
150
155
  if (actx === undefined) return false
151
- const conversation = (ctx as unknown as { conversation?: ConversationService }).conversation
156
+ const conversation = (ctx as unknown as { get(name: string): unknown }).get('conversation') as ConversationService | undefined
152
157
  if (conversation === undefined) return false
153
158
  const shell = conversation.input.for(actx as unknown as ClientContext)
154
159
  const snap = shell.state.getSnapshot()
@@ -8,7 +8,7 @@
8
8
  .overlay {
9
9
  position: fixed;
10
10
  inset: 0;
11
- background: rgba(0, 0, 0, 0.45);
11
+ background: var(--dsw-alias-bg-mask-1);
12
12
  display: flex;
13
13
  align-items: center;
14
14
  justify-content: center;
@@ -24,7 +24,7 @@
24
24
  background: var(--aion-bg-base);
25
25
  border: 1px solid var(--aion-border-base);
26
26
  border-radius: 10px;
27
- box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
27
+ box-shadow: var(--dsw-shadow-lv3);
28
28
  overflow: hidden;
29
29
  font-family: var(--aion-font-sans);
30
30
  color: var(--aion-text-primary);
@@ -158,11 +158,11 @@
158
158
  }
159
159
 
160
160
  .rowSelected {
161
- background: rgba(22, 93, 255, 0.12);
161
+ background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 12%, transparent);
162
162
  }
163
163
 
164
164
  .rowSelected:hover {
165
- background: rgba(22, 93, 255, 0.18);
165
+ background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 18%, transparent);
166
166
  }
167
167
 
168
168
  .chev {
@@ -220,7 +220,7 @@
220
220
  height: 16px;
221
221
  border: 1px solid var(--aion-border-base);
222
222
  border-radius: 4px;
223
- color: #fff;
223
+ color: var(--dsw-alias-label-primary-foreground);
224
224
  flex-shrink: 0;
225
225
  background: transparent;
226
226
  }
@@ -294,7 +294,7 @@
294
294
  }
295
295
 
296
296
  .chipX:hover {
297
- background: rgba(0, 0, 0, 0.12);
297
+ background: var(--dsw-alias-interactive-bg-hover);
298
298
  color: var(--aion-text-primary);
299
299
  }
300
300
 
@@ -325,8 +325,8 @@
325
325
  height: 32px;
326
326
  padding: 0 16px;
327
327
  border: none;
328
- background: var(--aion-primary);
329
- color: #fff;
328
+ background: var(--dsw-alias-button-primary-fill);
329
+ color: var(--dsw-alias-label-primary-foreground);
330
330
  border-radius: 6px;
331
331
  font-size: 13px;
332
332
  font-weight: 500;
@@ -335,7 +335,7 @@
335
335
  }
336
336
 
337
337
  .insertBtn:hover:not(:disabled) {
338
- filter: brightness(1.1);
338
+ background: var(--dsw-alias-button-primary-hover);
339
339
  }
340
340
 
341
341
  .insertBtn:disabled {
@@ -504,25 +504,30 @@
504
504
  border-radius: 4px;
505
505
  }
506
506
 
507
- /* The "添加到对话" floating action over a preview selection. */
507
+ /* The "添加到对话" floating action over a preview selection.
508
+ * Mirrors the official primitives Button primary variant (.sm capsule):
509
+ * geometry h28/r14/p10, fill = button-primary-fill, text = label-primary-foreground,
510
+ * hover = button-primary-hover, shadow = --dsw-shadow-lv1 (floating layer). */
508
511
  .addToChat {
509
512
  position: fixed;
510
513
  z-index: 1000;
511
- padding: 6px 12px;
512
- background: var(--aion-primary);
513
- color: #fff;
514
- border-radius: 6px;
515
- font-size: 13px;
516
- font-weight: 500;
517
- font-family: var(--aion-font-sans);
514
+ display: inline-flex;
515
+ align-items: center;
516
+ height: 28px;
517
+ padding: 0 10px;
518
+ background: var(--dsw-alias-button-primary-fill);
519
+ color: var(--dsw-alias-label-primary-foreground);
520
+ border-radius: 14px;
521
+ font-size: 12px;
522
+ line-height: 18px;
518
523
  cursor: pointer;
519
- box-shadow: var(--aion-overlay-shadow);
524
+ box-shadow: var(--dsw-shadow-lv1);
520
525
  user-select: none;
521
526
  white-space: nowrap;
522
527
  }
523
528
 
524
529
  .addToChat:hover {
525
- filter: brightness(1.1);
530
+ background: var(--dsw-alias-button-primary-hover);
526
531
  }
527
532
 
528
533
  /* Code viewer shell: line gutter + code body in one scroll container. */
@@ -5,13 +5,15 @@
5
5
  * — dark-mode adaptation is handled by `--dsw-*` itself, so no per-theme
6
6
  * override block is needed.
7
7
  *
8
- * The two rules at the top are intentionally GLOBAL (hence the :global()
9
- * wrapper): the token variables must be readable by DOM-created chrome the
10
- * panel columns and drag handles are appended by the layout controller outside
11
- * any React subtree so they live on :root. Every other rule below is still
12
- * scoped to `.filemgr-root` / the plugin's own class names.
8
+ * Scoped to BODY, not :root: since the official theme rc.2 the `--dsw-*`
9
+ * semantic tokens live on `body` (not html), so an alias defined at :root
10
+ * would substitute var() against an undefined name and compute to the
11
+ * guaranteed-invalid value (""), killing every background that consumes it.
12
+ * On body the dsw tokens resolve and the aliases inherit to all visible
13
+ * elements — including the DOM-created chrome (panel columns, drag handles,
14
+ * the '@' picker portal) appended outside any React subtree.
13
15
  */
14
- :global(:root) {
16
+ :global(body) {
15
17
  --aion-bg-base: var(--dsw-alias-bg-base);
16
18
  --aion-bg-1: var(--dsw-alias-bg-layer-1);
17
19
  --aion-bg-2: var(--dsw-alias-bg-layer-2);
@@ -34,9 +36,9 @@
34
36
  --aion-fill-3: var(--dsw-alias-interactive-bg-hover-solid);
35
37
  --aion-border-base: var(--dsw-alias-border-l2);
36
38
  --aion-overlay-shadow: var(--dsw-shadow-lv2);
37
- --aion-font-sans: -apple-system, "system-ui", "Segoe UI", Roboto, "Helvetica Neue",
38
- "PingFang SC", "Microsoft YaHei", sans-serif;
39
- --aion-font-mono: var(--dsw-alias-font-family-mono);
39
+ /* 官方字体 token;mono 用本地栈 —— 当前主题版本尚无 --dsw-alias-font-family-mono */
40
+ --aion-font-sans: var(--dsw-font-family);
41
+ --aion-font-mono: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
40
42
  }
41
43
 
42
44
 
@@ -201,7 +203,7 @@
201
203
  display: flex;
202
204
  align-items: center;
203
205
  justify-content: center;
204
- background: rgba(0, 0, 0, 0.35);
206
+ background: var(--dsw-alias-bg-mask-1);
205
207
  }
206
208
  :global(.filemgr-dialog) {
207
209
  width: 400px;
@@ -252,28 +254,30 @@
252
254
  outline-offset: 2px;
253
255
  }
254
256
  :global(.filemgr-btn-primary) {
255
- background: var(--aion-primary);
256
- border-color: var(--aion-primary);
257
- color: #ffffff;
257
+ background: var(--dsw-alias-button-primary-fill);
258
+ border-color: var(--dsw-alias-button-primary-fill);
259
+ color: var(--dsw-alias-label-primary-foreground);
258
260
  }
259
261
  :global(.filemgr-btn-primary:hover) {
260
- background: var(--aion-primary);
261
- opacity: 0.9;
262
+ background: var(--dsw-alias-button-primary-hover);
263
+ border-color: var(--dsw-alias-button-primary-hover);
262
264
  }
263
265
  :global(.filemgr-btn-primary:active) {
264
- opacity: 0.75;
265
- filter: saturate(0.9);
266
+ background: var(--dsw-alias-button-primary-hover);
267
+ border-color: var(--dsw-alias-button-primary-hover);
266
268
  }
267
269
  :global(.filemgr-btn-danger) {
268
270
  background: var(--aion-danger);
269
271
  border-color: var(--aion-danger);
270
- color: #ffffff;
272
+ color: var(--dsw-alias-label-primary-foreground);
271
273
  }
272
274
  :global(.filemgr-btn-danger:hover) {
273
- opacity: 0.9;
275
+ background: color-mix(in srgb, var(--aion-danger) 88%, black);
276
+ border-color: color-mix(in srgb, var(--aion-danger) 88%, black);
274
277
  }
275
278
  :global(.filemgr-btn-danger:active) {
276
- opacity: 0.75;
279
+ background: color-mix(in srgb, var(--aion-danger) 78%, black);
280
+ border-color: color-mix(in srgb, var(--aion-danger) 78%, black);
277
281
  }
278
282
 
279
283
  /* Context menu (tab right-click / tree right-click). */
@@ -1,38 +0,0 @@
1
- /**
2
- * Global restyle of the shell's reference chips to the file-reference look
3
- * (the user's screenshot): a muted rounded pill with a green `</>` code
4
- * glyph. The shell renders every U+FFFC chip with `data-decoration="chip"`
5
- * (InputBar), so this file overrides its default blue tint through that
6
- * attribute — the hash-scoped `.chip` class stays untouched and alignment
7
- * (the U+FFFC advance cell) is preserved because the glyph comes from the
8
- * label cell's ::before, not the placeholder cell's.
9
- *
10
- * Side effect: chips of other sources (skills, subagents) pick up the same
11
- * pill look — a uniform, muted family, which the shell's default blue
12
- * already intended.
13
- * @module dsh-filemgr/client/styles/chip
14
- */
15
-
16
- :global([data-decoration="chip"]) {
17
- background: rgba(15, 23, 42, 0.06);
18
- border-radius: 6px;
19
- }
20
-
21
- :global(body[data-ds-dark-theme] [data-decoration="chip"]) {
22
- background: rgba(255, 255, 255, 0.1);
23
- }
24
-
25
- /* The label cell keeps the shell's default 0.72 compensated scale (no
26
- override here) — the pill text renders at the composer's native size. */
27
-
28
- /* The green `</>` glyph, drawn inside the label cell (absolute-positioned by
29
- the shell, so this never shifts the placeholder advance). */
30
- :global([data-decoration="chip"] > span::before) {
31
- content: "</>";
32
- color: var(--aion-success);
33
- font-family: var(--aion-font-mono);
34
- font-size: 0.85em;
35
- font-weight: 700;
36
- margin-right: 4px;
37
- letter-spacing: -0.5px;
38
- }