@aipanel/dsh-client 1.2.8 → 1.2.9

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 +265 -224
  2. package/package.json +8 -2
package/lib/client.js CHANGED
@@ -24,32 +24,70 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
24
24
  // dsh-client/src/client/index.ts
25
25
  var index_exports = {};
26
26
  __export(index_exports, {
27
- INSERT_ELEMENT_EVENT: () => INSERT_ELEMENT_EVENT,
28
- SESSION_READY_EVENT: () => SESSION_READY_EVENT,
29
27
  apply: () => apply,
30
28
  inject: () => inject
31
29
  });
32
30
  module.exports = __toCommonJS(index_exports);
33
31
 
32
+ // ../../core/es/common/constants.mjs
33
+ var EXT_BROADCAST = {
34
+ PAGE_CONTEXT: "PAGE_CONTEXT",
35
+ THEME_CHANGE: "THEME_CHANGE",
36
+ SERVICE_APPEARED: "SERVICE_APPEARED",
37
+ SERVICE_GONE: "SERVICE_GONE"
38
+ };
39
+ var EXT_MSG = {
40
+ ...EXT_BROADCAST,
41
+ GET_PORT_INFO: "GET_PORT_INFO",
42
+ TAB_SWITCHED: "TAB_SWITCHED",
43
+ REQUEST_PAGE_CONTEXT: "REQUEST_PAGE_CONTEXT",
44
+ SELECTION_START: "SELECTION_START",
45
+ SELECTION_STOP: "SELECTION_STOP",
46
+ CS_QUERY_WINDOW: "__CS_QUERY_WINDOW__",
47
+ /** Side Panel → Background:立即轮询一次并回传当前服务信息 */
48
+ FORCE_POLL: "FORCE_POLL"
49
+ };
50
+ var WIDGET_MSG = {
51
+ READY: "AIPANEL_READY",
52
+ KEYDOWN: "AIPANEL_KEYDOWN",
53
+ SET_THEME: "AIPANEL_SET_THEME",
54
+ INSERT_FILE_PART: "AIPANEL_INSERT_FILE_PART",
55
+ SELECT_MODE_CHANGE: "AIPANEL_SELECT_MODE_CHANGE",
56
+ ELEMENT_SELECTED: "AIPANEL_ELEMENT_SELECTED",
57
+ SELECTION_CANCELLED: "AIPANEL_SELECTION_CANCELLED",
58
+ SELECTOR_START: "AIPANEL_SELECTOR_START",
59
+ SELECTOR_STOP: "AIPANEL_SELECTOR_STOP",
60
+ SERVICE_INFO: "AIPANEL_SERVICE_INFO",
61
+ MINIMIZE_STATE: "MINIMIZE_STATE_CHANGE",
62
+ PROMPT_DOCK_VISIBILITY: "PROMPT_DOCK_VISIBILITY_CHANGE",
63
+ REVIEW_PANEL_TOGGLE: "REVIEW_PANEL_TOGGLE",
64
+ /** 无 deepLink 能力的 Provider:通知 iframe 聚焦指定会话 */
65
+ FOCUS_SESSION: "AIPANEL_FOCUS_SESSION",
66
+ /** 无 deepLink 能力的 Provider:iframe 确认目标会话已激活且渲染稳定(携带 sessionId) */
67
+ SESSION_READY: "AIPANEL_SESSION_READY"
68
+ };
69
+
34
70
  // dsh-client/src/client/diagnostics-view.tsx
35
71
  var import_react = require("react");
36
72
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
37
73
  var import_jsx_runtime = require("react/jsx-runtime");
38
- var DIAGNOSTICS_STORAGE_KEY = "dsh.bridge.diagnostics.enabled";
74
+ function isSettledToolResult(block) {
75
+ return typeof block === "object" && block !== null && "kind" in block && block.kind === "tool-result";
76
+ }
39
77
  function readDiagnostics(block) {
40
- if (typeof block !== "object" || block === null) return void 0;
41
- const settled = block;
42
- if (settled.kind !== "tool-result") return void 0;
43
- const meta = settled.meta;
78
+ if (!isSettledToolResult(block)) return void 0;
79
+ const meta = block.meta;
44
80
  const diagnostics = meta?.diagnostics;
45
81
  if (!Array.isArray(diagnostics)) return void 0;
46
82
  return diagnostics.every(isEntry) ? diagnostics : void 0;
47
83
  }
48
84
  function extractBlockText(block) {
49
- if (typeof block !== "object" || block === null) return "";
50
- const settled = block;
51
- if (!Array.isArray(settled.content)) return "";
52
- return (settled.content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n");
85
+ if (!isSettledToolResult(block)) return "";
86
+ const content = block.content;
87
+ if (!Array.isArray(content)) return "";
88
+ return content.filter(
89
+ (b) => typeof b === "object" && b !== null && b.type === "text" && typeof b.text === "string"
90
+ ).map((b) => b.text).join("\n");
53
91
  }
54
92
  function isEntry(d) {
55
93
  const e = d;
@@ -171,7 +209,7 @@ var styles = {
171
209
  function DiagnosticsRow({ block, cwd, toolName, openFile }) {
172
210
  const diagnostics = readDiagnostics(block);
173
211
  const [expanded, setExpanded] = (0, import_react.useState)(false);
174
- const isSettled = block?.kind === "tool-result";
212
+ const isSettled = isSettledToolResult(block);
175
213
  const hasDiagnostics = typeof diagnostics !== "undefined" && diagnostics.length > 0;
176
214
  const errors = (diagnostics ?? []).filter((d) => d.severity === "error");
177
215
  const warnings = (diagnostics ?? []).filter((d) => d.severity === "warning");
@@ -270,12 +308,8 @@ function DiagnosticsRow({ block, cwd, toolName, openFile }) {
270
308
  }
271
309
  );
272
310
  }
273
- function registerDiagnosticsView(ctx) {
274
- try {
275
- if (localStorage.getItem(DIAGNOSTICS_STORAGE_KEY) !== "1") return;
276
- } catch {
277
- return;
278
- }
311
+ function registerDiagnosticsView(ctx, enabled = true) {
312
+ if (!enabled) return;
279
313
  const slots = ctx.slots;
280
314
  if (!slots) return;
281
315
  slots.inject("tool.call.toolview", function* () {
@@ -284,23 +318,16 @@ function registerDiagnosticsView(ctx) {
284
318
  }
285
319
 
286
320
  // dsh-client/src/client/index.ts
287
- var SELECTION_STORAGE_KEY = "dsh.bridge.selection";
321
+ var MSG = WIDGET_MSG;
288
322
  var inject = ["slots", "sessions", "inputTriggers", "conversation"];
289
- var SESSION_READY_EVENT = "aipanel:session-ready";
290
- var INSERT_ELEMENT_EVENT = "aipanel:insert-element";
291
323
  var SESSION_SETTLE_MS = 400;
324
+ var FOCUS_OPEN_MAX_ATTEMPTS = 3;
292
325
  function ensureNodeId(e) {
293
326
  if (e.id) return e.id;
294
327
  const random = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID().replace(/-/g, "").slice(0, 8) : Math.random().toString(36).slice(2, 10);
295
328
  e.id = `n${random}`;
296
329
  return e.id;
297
330
  }
298
- function elementLabel(e) {
299
- if (e.description) return e.description;
300
- const text = e.innerText?.trim();
301
- if (text) return text.slice(0, 40);
302
- return "\u5143\u7D20";
303
- }
304
331
  function elementContextRef(e) {
305
332
  return JSON.stringify(e);
306
333
  }
@@ -323,90 +350,91 @@ function toReference(e) {
323
350
  clipboardText: `@${mark}`
324
351
  };
325
352
  }
326
- function toCandidate(e) {
327
- return {
328
- name: elementLabel(e),
329
- description: e.description || e.innerText || void 0,
330
- value: elementContextRef(e)
331
- };
353
+ function isEmbedded() {
354
+ try {
355
+ return window.parent !== window;
356
+ } catch {
357
+ return false;
358
+ }
332
359
  }
333
- function readSelectionCandidates() {
360
+ function postToHost(type, data = {}) {
361
+ if (!isEmbedded()) return;
334
362
  try {
335
- const raw = localStorage.getItem(SELECTION_STORAGE_KEY);
336
- if (!raw) return [];
337
- const elements = JSON.parse(raw);
338
- if (!Array.isArray(elements)) return [];
339
- return elements.filter((e) => !!e && typeof e === "object").slice(0, 20).map(toCandidate);
363
+ window.parent.postMessage({ type, ...data }, "*");
340
364
  } catch {
341
- return [];
342
365
  }
343
366
  }
344
- function apply(ctx) {
345
- registerDiagnosticsView(ctx);
346
- const inputTriggers = ctx.get("inputTriggers");
347
- if (!inputTriggers) return;
348
- const source = {
349
- trigger: "@",
350
- name: "aipanel",
351
- order: 300,
352
- showGroupTitle: false,
353
- // 候选 = 最近选中元素(bridge 写入),按输入查询过滤
354
- candidates: async (_session, req) => {
355
- const all = readSelectionCandidates();
356
- const query = req.query.trim().toLowerCase();
357
- if (!query) return all;
358
- return all.filter(
359
- (c) => c.name.toLowerCase().includes(query) || (c.description ?? "").toLowerCase().includes(query)
360
- );
361
- },
362
- // 选定 → 铸造 file 引用:ref/label 分离(label 短,ref 携带完整元素 + 节点 id)
363
- onPick: (pick) => {
364
- try {
365
- const parsed = JSON.parse(pick.candidate.value ?? "null");
366
- if (parsed) {
367
- ensureNodeId(parsed);
368
- return {
369
- insert: {
370
- source: "aipanel",
371
- ref: JSON.stringify(parsed),
372
- label: pick.candidate.name,
373
- appearance: "file",
374
- // clipboardText 以 @ 开头(dsh backdrop 取首字符作 chip 触发 glyph)
375
- clipboardText: `@${pick.candidate.name}`
376
- }
377
- };
378
- }
379
- } catch {
380
- }
381
- return {
382
- insert: {
383
- source: "aipanel",
384
- ref: pick.candidate.value ?? pick.candidate.name,
385
- label: pick.candidate.name,
386
- appearance: "file",
387
- clipboardText: `@${pick.candidate.name}`
388
- }
389
- };
390
- },
391
- // 模型投影
392
- codec: {
393
- clipboardText: (ref) => ref,
394
- serialize: async (ref) => serializeElement(ref)
395
- }
396
- };
397
- ctx.effect(() => inputTriggers.registerSource(source), "aipanel: @ source");
367
+ function mapAipanelTheme(t) {
368
+ if (t === "light" || t === "dark") return t;
369
+ if (t === "system" || t === "auto") return "system";
370
+ return null;
371
+ }
372
+ function apply(ctx, config = {}) {
373
+ registerDiagnosticsView(ctx, config.enableDiagnostics !== false);
398
374
  const sessions = ctx.get("sessions");
399
- if (sessions && sessions.list) {
375
+ if (sessions) {
400
376
  let lastCurrent;
401
377
  let settleTimer = null;
402
- const notifyBridge = (sessionId) => {
378
+ let targetSessionId;
379
+ let pendingFocusId;
380
+ let focusAttempts = 0;
381
+ let refreshing = false;
382
+ const notifyReady = (sessionId) => {
383
+ postToHost(MSG.SESSION_READY, { sessionId });
384
+ };
385
+ const hasBaseline = (snap) => {
386
+ if (!snap) return false;
387
+ return !!snap.current || !!snap.ids?.length || Object.keys(snap.byId ?? {}).length > 0;
388
+ };
389
+ const listContains = (snap, id) => {
390
+ if (!snap) return false;
391
+ return !!snap.byId?.[id] || snap.ids?.includes(id) === true;
392
+ };
393
+ const drainPendingFocus = () => {
394
+ const id = pendingFocusId;
395
+ if (!id) return;
396
+ const snap = sessions.list.getSnapshot();
397
+ if (!hasBaseline(snap)) return;
398
+ pendingFocusId = void 0;
399
+ focusAttempts = 0;
400
+ void tryOpenTarget(id);
401
+ };
402
+ const tryOpenTarget = async (id) => {
403
+ if (focusAttempts >= FOCUS_OPEN_MAX_ATTEMPTS) return;
404
+ focusAttempts += 1;
405
+ const snap = sessions.list.getSnapshot();
406
+ if (snap.current === id) return;
407
+ if (!listContains(snap, id) && !refreshing) {
408
+ refreshing = true;
409
+ try {
410
+ await sessions.refresh();
411
+ } catch {
412
+ } finally {
413
+ refreshing = false;
414
+ }
415
+ const fresh = sessions.list.getSnapshot();
416
+ if (!listContains(fresh, id)) {
417
+ setTimeout(() => void tryOpenTarget(id), 500);
418
+ return;
419
+ }
420
+ }
403
421
  try {
404
- window.dispatchEvent(new CustomEvent(SESSION_READY_EVENT, { detail: { sessionId } }));
422
+ sessions.open(id);
405
423
  } catch {
406
424
  }
407
425
  };
426
+ const handleFocus = (sessionId) => {
427
+ targetSessionId = sessionId;
428
+ const snap = sessions.list.getSnapshot();
429
+ if (!hasBaseline(snap)) {
430
+ pendingFocusId = sessionId;
431
+ return;
432
+ }
433
+ focusAttempts = 0;
434
+ void tryOpenTarget(sessionId);
435
+ };
408
436
  const probe = () => {
409
- const snapshot = sessions.list.getSnapshot();
437
+ const snapshot = sessions.list?.getSnapshot?.();
410
438
  const current = snapshot?.current;
411
439
  if (!current) {
412
440
  lastCurrent = void 0;
@@ -421,148 +449,161 @@ function apply(ctx) {
421
449
  if (settleTimer) clearTimeout(settleTimer);
422
450
  settleTimer = setTimeout(() => {
423
451
  settleTimer = null;
424
- if (sessions.list.getSnapshot()?.current === current) {
425
- notifyBridge(current);
452
+ if (sessions.list?.getSnapshot?.()?.current === current) {
453
+ notifyReady(current);
454
+ if (targetSessionId && targetSessionId !== current) {
455
+ handleFocus(targetSessionId);
456
+ }
426
457
  }
427
458
  }, SESSION_SETTLE_MS);
459
+ if (hasBaseline(snapshot)) drainPendingFocus();
428
460
  };
429
461
  probe();
430
- const dispose = sessions.list.subscribe(probe);
431
- ctx.effect(() => dispose, "aipanel: session-ready watcher");
432
- }
433
- const conversation = ctx.get("conversation");
434
- const insertElementListener = (event) => {
435
- const detail = event.detail;
436
- const element = detail?.element;
437
- if (!element) return;
438
- const current = sessions?.list?.getSnapshot()?.current;
439
- if (!current) return;
440
- try {
441
- const actx = sessions?.scope?.(current);
442
- if (!actx) return;
443
- const input = conversation?.input?.for?.(actx);
444
- if (!input || typeof input.insertReference !== "function") return;
445
- const snapshot = input.state?.getSnapshot?.();
446
- if (!snapshot) return;
447
- let start = snapshot.draft.length;
448
- let end = snapshot.draft.length;
462
+ const unsubscribe = sessions.list.subscribe?.(probe);
463
+ ctx.effect(() => unsubscribe ?? (() => {
464
+ }), "aipanel: session-ready watcher");
465
+ const embedded = isEmbedded();
466
+ let selectModeActive = false;
467
+ const applyThemeFromHost = (theme) => {
468
+ const id = typeof theme === "string" ? mapAipanelTheme(theme) : null;
469
+ if (!id) return;
449
470
  try {
450
- const composer = document.querySelector(
471
+ const themeService = ctx.get("theme");
472
+ themeService?.setTheme(id);
473
+ } catch {
474
+ }
475
+ };
476
+ const LAYOUT_STYLE_ID = "aipanel-layout-overrides";
477
+ const injectLayoutOverrides = () => {
478
+ if (!embedded) return;
479
+ try {
480
+ if (document.getElementById(LAYOUT_STYLE_ID)) return;
481
+ const style = document.createElement("style");
482
+ style.id = LAYOUT_STYLE_ID;
483
+ style.textContent = [
484
+ "[data-sidebar-collapsed] {",
485
+ " grid-template-columns: auto !important;",
486
+ "}",
487
+ "[data-sidebar-collapsed] > :first-child {",
488
+ " display: none !important;",
489
+ "}",
490
+ '[aria-label="\u9009\u62E9\u5DE5\u4F5C\u533A"] {',
491
+ " display: none !important;",
492
+ "}"
493
+ ].join("\n");
494
+ document.head.appendChild(style);
495
+ } catch {
496
+ }
497
+ };
498
+ const onKeydownCapture = (event) => {
499
+ if (event.key !== "Escape" && !(event.ctrlKey && event.key.toLowerCase() === "p")) return;
500
+ if (selectModeActive) {
501
+ event.preventDefault();
502
+ event.stopPropagation();
503
+ }
504
+ postToHost(MSG.KEYDOWN, {
505
+ key: event.key,
506
+ ctrlKey: event.ctrlKey,
507
+ metaKey: event.metaKey,
508
+ shiftKey: event.shiftKey,
509
+ altKey: event.altKey
510
+ });
511
+ };
512
+ const focusComposer = () => {
513
+ try {
514
+ const el = document.querySelector(
451
515
  '[role="textbox"][contenteditable="true"], textarea[data-phase]'
452
516
  );
453
- if (composer) {
454
- if (composer.isContentEditable) {
455
- const draftInComposer = (composer.innerText ?? "").replace(/\n$/, "");
456
- if (draftInComposer === snapshot.draft) {
457
- const sel = window.getSelection();
458
- if (sel && sel.rangeCount > 0 && sel.anchorNode && sel.focusNode && composer.contains(sel.anchorNode) && composer.contains(sel.focusNode)) {
459
- const textNodes = [];
460
- const walker = document.createTreeWalker(composer, NodeFilter.SHOW_TEXT);
461
- let n = walker.nextNode();
462
- while (n) {
463
- textNodes.push(n);
464
- n = walker.nextNode();
465
- }
466
- const posOf = (node, off) => {
467
- const idx = textNodes.indexOf(node);
468
- if (idx < 0) return null;
469
- let acc = 0;
470
- for (let i = 0; i < idx; i++) acc += textNodes[i].data.length;
471
- return acc + Math.min(off, textNodes[idx].data.length);
472
- };
473
- const s = posOf(sel.anchorNode, sel.anchorOffset);
474
- const e = posOf(sel.focusNode, sel.focusOffset);
475
- if (s !== null && e !== null) {
476
- start = Math.min(Math.max(s, 0), snapshot.draft.length);
477
- end = Math.min(Math.max(e, start), snapshot.draft.length);
478
- }
479
- }
480
- }
481
- } else {
482
- const ta = composer;
483
- if (ta.value === snapshot.draft) {
484
- const s = ta.selectionStart ?? snapshot.draft.length;
485
- const e = ta.selectionEnd ?? s;
486
- start = Math.min(s, snapshot.draft.length);
487
- end = Math.min(e, snapshot.draft.length);
488
- }
489
- }
490
- }
517
+ el?.focus();
518
+ } catch {
519
+ }
520
+ };
521
+ const detectSpan = (inputFor, snapshot) => {
522
+ if (!snapshot) return null;
523
+ const caret = inputFor.caretSpan?.();
524
+ return caret ? { start: caret.start, end: caret.end, draftRev: snapshot.draftRev } : null;
525
+ };
526
+ const INSERT_RETRY_MAX = 5;
527
+ const INSERT_RETRY_DELAY_MS = 80;
528
+ const insertElement = (element) => {
529
+ if (!element) return;
530
+ const current = sessions.list.getSnapshot().current;
531
+ if (!current) return;
532
+ let inputFor;
533
+ try {
534
+ const actx = sessions.scope(current);
535
+ if (!actx) return;
536
+ const conversation = ctx.get("conversation");
537
+ if (!conversation) return;
538
+ inputFor = conversation.input.for(actx);
491
539
  } catch {
540
+ return;
492
541
  }
542
+ if (!inputFor) return;
493
543
  const reference = toReference(element);
494
- const gap = snapshot.draft.slice(end)[0] === " " ? 0 : 1;
495
- const insertedLen = reference.clipboardText.length + gap;
496
- let leadingSpace = 0;
497
- let rev = snapshot.draftRev;
498
- const insertable = input;
499
- if (start > 0 && !/\s/.test(snapshot.draft[start - 1])) {
544
+ const attempt = (left) => {
545
+ let applied = false;
500
546
  try {
501
- if (insertable.insertText?.(" ", { start, end, draftRev: rev })) {
502
- const next = input.state?.getSnapshot?.();
503
- if (next) {
504
- leadingSpace = 1;
505
- start += 1;
506
- end += 1;
507
- rev = next.draftRev;
508
- }
547
+ const snap = inputFor.state.getSnapshot();
548
+ if (snap) {
549
+ const span = detectSpan(inputFor, snap);
550
+ if (span) applied = inputFor.insertReference(reference, span);
509
551
  }
510
552
  } catch {
553
+ applied = false;
511
554
  }
512
- }
513
- input.insertReference(reference, { start, end, draftRev: rev });
514
- requestAnimationFrame(() => {
515
- try {
516
- const el = document.querySelector(
517
- '[role="textbox"][contenteditable="true"], textarea[data-phase]'
518
- );
519
- if (!el) return;
520
- const caret = start + insertedLen + leadingSpace;
521
- if (el.isContentEditable) {
522
- const textNodes = [];
523
- const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
524
- let n = walker.nextNode();
525
- while (n) {
526
- textNodes.push(n);
527
- n = walker.nextNode();
528
- }
529
- let target = textNodes[textNodes.length - 1] ?? null;
530
- let off = target ? target.data.length : 0;
531
- let remaining = Math.max(0, caret);
532
- if (target) {
533
- for (const t of textNodes) {
534
- if (remaining <= t.data.length) {
535
- target = t;
536
- off = remaining;
537
- break;
538
- }
539
- remaining -= t.data.length;
540
- }
541
- }
542
- if (target) {
543
- const range = document.createRange();
544
- range.setStart(target, Math.min(off, target.data.length));
545
- range.collapse(true);
546
- const sel = window.getSelection();
547
- sel?.removeAllRanges();
548
- sel?.addRange(range);
549
- }
550
- el.focus();
551
- } else {
552
- const ta = el;
553
- ta.focus();
554
- ta.setSelectionRange(Math.min(caret, ta.value.length), Math.min(caret, ta.value.length));
555
- }
556
- } catch {
555
+ if (applied) {
556
+ focusComposer();
557
+ return;
557
558
  }
558
- });
559
- } catch {
559
+ if (left > 0) {
560
+ setTimeout(() => attempt(left - 1), INSERT_RETRY_DELAY_MS);
561
+ }
562
+ };
563
+ attempt(INSERT_RETRY_MAX);
564
+ };
565
+ const onWindowMessage = (event) => {
566
+ const data = event.data;
567
+ if (!data || typeof data.type !== "string") return;
568
+ if (data.type === MSG.SET_THEME && typeof data.theme === "string") {
569
+ applyThemeFromHost(data.theme);
570
+ } else if (data.type === MSG.FOCUS_SESSION && typeof data.sessionId === "string") {
571
+ handleFocus(data.sessionId);
572
+ } else if (data.type === MSG.INSERT_FILE_PART && data.element) {
573
+ insertElement(data.element);
574
+ } else if (data.type === MSG.SELECT_MODE_CHANGE) {
575
+ selectModeActive = data.selectMode === true;
576
+ }
577
+ };
578
+ window.addEventListener("message", onWindowMessage);
579
+ ctx.effect(
580
+ () => () => window.removeEventListener("message", onWindowMessage),
581
+ "aipanel: host message listener"
582
+ );
583
+ injectLayoutOverrides();
584
+ if (embedded) {
585
+ window.addEventListener("keydown", onKeydownCapture, true);
586
+ ctx.effect(
587
+ () => () => window.removeEventListener("keydown", onKeydownCapture, true),
588
+ "aipanel: keydown capture"
589
+ );
590
+ }
591
+ if (typeof config.theme === "string") {
592
+ applyThemeFromHost(config.theme);
593
+ }
594
+ }
595
+ const inputTriggers = ctx.get("inputTriggers");
596
+ if (!inputTriggers) return;
597
+ const source = {
598
+ trigger: "@",
599
+ name: "aipanel",
600
+ candidates: async () => [],
601
+ onPick: () => void 0,
602
+ codec: {
603
+ clipboardText: (ref) => ref,
604
+ serialize: async (ref) => serializeElement(ref)
560
605
  }
561
606
  };
562
- window.addEventListener(INSERT_ELEMENT_EVENT, insertElementListener);
563
- ctx.effect(
564
- () => () => window.removeEventListener(INSERT_ELEMENT_EVENT, insertElementListener),
565
- "aipanel: insert-element listener"
566
- );
607
+ ctx.effect(() => inputTriggers.registerSource(source), "aipanel: @ codec source");
567
608
  }
568
609
  return module.exports; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipanel/dsh-client",
3
- "version": "1.2.8",
3
+ "version": "1.2.9",
4
4
  "type": "module",
5
5
  "description": "AIPanel 浏览器侧插件(dsh web Web Client):注册 @aipanel 文件引用 source,选中元素以 file chip 高亮插入并可供模型解析。",
6
6
  "publishConfig": {
@@ -12,11 +12,17 @@
12
12
  ],
13
13
  "devDependencies": {
14
14
  "@deepseek-ai/cordis": "^4.0.2",
15
+ "@deepseek-ai/dsh-api-session-controller": "^0.1.2-rc.1",
16
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-rc.1",
15
17
  "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-rc.1",
16
18
  "@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-rc.1",
19
+ "@deepseek-ai/dsh-client-ui-theme": "^0.1.2-rc.1",
20
+ "@deepseek-ai/dsh-client-ui-tool": "^0.1.2-rc.1",
21
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
17
22
  "@types/react": "^18.3.0",
18
23
  "esbuild": "^0.25.0",
19
- "react": "^18.3.0"
24
+ "react": "^18.3.0",
25
+ "@aipanel/core": "1.2.9"
20
26
  },
21
27
  "exports": {
22
28
  ".": "./lib/index.js",