dsh-sidenote 0.8.4 → 0.8.5

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/lib/client.js CHANGED
@@ -766,7 +766,7 @@ window.__ModuleLoader__.load({
766
766
  let dockRegistered = false;
767
767
  let report = {
768
768
  versions: {
769
- sidenote: "0.8.4",
769
+ sidenote: "0.8.5",
770
770
  dsh: "unknown",
771
771
  betterSidebar: null
772
772
  },
@@ -966,6 +966,8 @@ window.__ModuleLoader__.load({
966
966
  settingsDescription: "Choose the actions available when you select conversation text.",
967
967
  hideNativeQuote: "Hide native quote",
968
968
  hideNativeQuoteHint: "Hide the Quote action in main and side chats.",
969
+ showSideChatNote: "Add a note before asking in side chat",
970
+ showSideChatNoteHint: "Turn off to add selected text directly to side chat and write your question there.",
969
971
  settingsSaving: "Saving…",
970
972
  settingsLoading: "Loading preferences…",
971
973
  settingsUnavailable: "Preferences are unavailable. Reload the page to reconnect.",
@@ -1141,6 +1143,8 @@ window.__ModuleLoader__.load({
1141
1143
  settingsDescription: "调整划选对话文字时显示的操作。",
1142
1144
  hideNativeQuote: "隐藏原生引用",
1143
1145
  hideNativeQuoteHint: "在主对话和侧边聊天中隐藏“引用”按钮。",
1146
+ showSideChatNote: "侧边提问前填写注解",
1147
+ showSideChatNoteHint: "关闭后,划选内容直接添加到侧边聊天,可在侧聊输入框中继续提问。",
1144
1148
  settingsSaving: "正在保存…",
1145
1149
  settingsLoading: "正在加载偏好设置…",
1146
1150
  settingsUnavailable: "暂时无法读取设置,请刷新页面重新连接。",
@@ -1420,13 +1424,13 @@ window.__ModuleLoader__.load({
1420
1424
  document.head.appendChild(tag);
1421
1425
  }
1422
1426
  var diagnostics_module_css_default = {
1423
- "versions": "tGe0LW_versions",
1427
+ "trigger": "tGe0LW_trigger",
1428
+ "list": "tGe0LW_list",
1424
1429
  "header": "tGe0LW_header",
1425
1430
  "panel": "tGe0LW_panel",
1426
- "trigger": "tGe0LW_trigger",
1427
1431
  "actions": "tGe0LW_actions",
1428
- "list": "tGe0LW_list",
1429
- "description": "tGe0LW_description"
1432
+ "description": "tGe0LW_description",
1433
+ "versions": "tGe0LW_versions"
1430
1434
  };
1431
1435
  //#endregion
1432
1436
  //#region src/client/diagnostics/index.tsx
@@ -2349,32 +2353,32 @@ window.__ModuleLoader__.load({
2349
2353
  }
2350
2354
  var annotations_module_css_default = {
2351
2355
  "contextNumber": "SVnebq_contextNumber",
2352
- "contextActions": "SVnebq_contextActions",
2353
- "chipClearAll": "SVnebq_chipClearAll",
2354
- "sentChips": "SVnebq_sentChips",
2355
- "contextText": "SVnebq_contextText",
2356
- "chipWrap": "SVnebq_chipWrap",
2357
- "contextFooter": "SVnebq_contextFooter",
2358
- "userContextHost": "SVnebq_userContextHost",
2359
- "chip": "SVnebq_chip",
2356
+ "contextLabel": "SVnebq_contextLabel",
2357
+ "contextEditor": "SVnebq_contextEditor",
2360
2358
  "sentChipGroup": "SVnebq_sentChipGroup",
2361
- "sentChevron": "SVnebq_sentChevron",
2362
- "sentPreview": "SVnebq_sentPreview",
2363
- "contextList": "SVnebq_contextList",
2364
- "chipDismissShade": "SVnebq_chipDismissShade",
2365
- "chipDismiss": "SVnebq_chipDismiss",
2366
2359
  "sentChip": "SVnebq_sentChip",
2360
+ "chipMain": "SVnebq_chipMain",
2361
+ "contextText": "SVnebq_contextText",
2362
+ "chipClearAll": "SVnebq_chipClearAll",
2367
2363
  "storageNotice": "SVnebq_storageNotice",
2368
- "contextLabel": "SVnebq_contextLabel",
2364
+ "contextContent": "SVnebq_contextContent",
2365
+ "sentChevron": "SVnebq_sentChevron",
2366
+ "sentChevronOpen": "SVnebq_sentChevronOpen",
2367
+ "userContextHost": "SVnebq_userContextHost",
2368
+ "sentPreview": "SVnebq_sentPreview",
2369
+ "contextActions": "SVnebq_contextActions",
2370
+ "contextFooter": "SVnebq_contextFooter",
2369
2371
  "contextAppear": "SVnebq_contextAppear",
2372
+ "contextList": "SVnebq_contextList",
2370
2373
  "contextPanel": "SVnebq_contextPanel",
2371
- "contextContent": "SVnebq_contextContent",
2374
+ "chip": "SVnebq_chip",
2372
2375
  "contextAction": "SVnebq_contextAction",
2373
- "contextEditor": "SVnebq_contextEditor",
2374
- "chipMain": "SVnebq_chipMain",
2376
+ "sentChips": "SVnebq_sentChips",
2377
+ "chipWrap": "SVnebq_chipWrap",
2378
+ "chipDismiss": "SVnebq_chipDismiss",
2375
2379
  "contextSource": "SVnebq_contextSource",
2376
- "sentChevronOpen": "SVnebq_sentChevronOpen",
2377
- "contextItem": "SVnebq_contextItem"
2380
+ "contextItem": "SVnebq_contextItem",
2381
+ "chipDismissShade": "SVnebq_chipDismissShade"
2378
2382
  };
2379
2383
  //#endregion
2380
2384
  //#region src/client/annotations/NativeUserContext.tsx
@@ -2848,38 +2852,38 @@ window.__ModuleLoader__.load({
2848
2852
  document.head.appendChild(tag);
2849
2853
  }
2850
2854
  var sidechat_module_css_default = {
2851
- "contextColumn": "_5KEEsG_contextColumn",
2852
- "progressDetail": "_5KEEsG_progressDetail",
2853
- "progressPreview": "_5KEEsG_progressPreview",
2854
- "headerSide": "_5KEEsG_headerSide",
2855
- "emptyTitle": "_5KEEsG_emptyTitle",
2856
- "progressContent": "_5KEEsG_progressContent",
2857
- "nativeToolbar": "_5KEEsG_nativeToolbar",
2858
- "contextUserRow": "_5KEEsG_contextUserRow",
2855
+ "root": "_5KEEsG_root",
2859
2856
  "progress": "_5KEEsG_progress",
2860
- "chatView": "_5KEEsG_chatView",
2861
- "contextChevron": "_5KEEsG_contextChevron",
2862
- "nativeConversation": "_5KEEsG_nativeConversation",
2863
- "contextBubble": "_5KEEsG_contextBubble",
2864
- "nativeHost": "_5KEEsG_nativeHost",
2865
2857
  "progressProcess": "_5KEEsG_progressProcess",
2866
- "emptyIcon": "_5KEEsG_emptyIcon",
2867
- "root": "_5KEEsG_root",
2868
- "emptyText": "_5KEEsG_emptyText",
2869
- "contextImage": "_5KEEsG_contextImage",
2870
2858
  "nativeToolbarActions": "_5KEEsG_nativeToolbarActions",
2871
- "progressEntries": "_5KEEsG_progressEntries",
2872
- "mainContext": "_5KEEsG_mainContext",
2873
- "contextChevronOpen": "_5KEEsG_contextChevronOpen",
2874
- "progressEntry": "_5KEEsG_progressEntry",
2859
+ "nativeToolbar": "_5KEEsG_nativeToolbar",
2875
2860
  "stateDetail": "_5KEEsG_stateDetail",
2876
- "progressLatest": "_5KEEsG_progressLatest",
2861
+ "headerSide": "_5KEEsG_headerSide",
2862
+ "nativeConversation": "_5KEEsG_nativeConversation",
2863
+ "emptyText": "_5KEEsG_emptyText",
2864
+ "contextChevron": "_5KEEsG_contextChevron",
2865
+ "contextHint": "_5KEEsG_contextHint",
2866
+ "progressEntry": "_5KEEsG_progressEntry",
2867
+ "progressPreview": "_5KEEsG_progressPreview",
2877
2868
  "nativeAction": "_5KEEsG_nativeAction",
2869
+ "reflowAction": "_5KEEsG_reflowAction",
2870
+ "chatView": "_5KEEsG_chatView",
2871
+ "contextColumn": "_5KEEsG_contextColumn",
2872
+ "contextChevronOpen": "_5KEEsG_contextChevronOpen",
2873
+ "mainContext": "_5KEEsG_mainContext",
2874
+ "emptyTitle": "_5KEEsG_emptyTitle",
2878
2875
  "errorRow": "_5KEEsG_errorRow",
2879
- "contextHint": "_5KEEsG_contextHint",
2876
+ "progressDetail": "_5KEEsG_progressDetail",
2880
2877
  "historyToggle": "_5KEEsG_historyToggle",
2878
+ "nativeHost": "_5KEEsG_nativeHost",
2879
+ "progressEntries": "_5KEEsG_progressEntries",
2880
+ "progressLatest": "_5KEEsG_progressLatest",
2881
+ "progressContent": "_5KEEsG_progressContent",
2881
2882
  "stateScreen": "_5KEEsG_stateScreen",
2882
- "reflowAction": "_5KEEsG_reflowAction"
2883
+ "contextImage": "_5KEEsG_contextImage",
2884
+ "contextBubble": "_5KEEsG_contextBubble",
2885
+ "emptyIcon": "_5KEEsG_emptyIcon",
2886
+ "contextUserRow": "_5KEEsG_contextUserRow"
2883
2887
  };
2884
2888
  //#endregion
2885
2889
  //#region src/client/sidechat/ContextProgress.tsx
@@ -6684,35 +6688,35 @@ ${body}
6684
6688
  document.head.appendChild(tag);
6685
6689
  }
6686
6690
  var annotate_module_css_default = {
6687
- "saveButton": "_SVViG_saveButton",
6688
- "hintClose": "_SVViG_hintClose",
6689
- "badgeSent": "_SVViG_badgeSent",
6690
- "deleteButton": "_SVViG_deleteButton",
6691
- "highlight": "_SVViG_highlight",
6691
+ "badge": "_SVViG_badge",
6692
+ "editorEdit": "_SVViG_editorEdit",
6693
+ "sentCardQuote": "_SVViG_sentCardQuote",
6692
6694
  "confirmButton": "_SVViG_confirmButton",
6693
- "editorFooter": "_SVViG_editorFooter",
6694
- "editorAskRow": "_SVViG_editorAskRow",
6695
- "sentCardNote": "_SVViG_sentCardNote",
6695
+ "saveButton": "_SVViG_saveButton",
6696
6696
  "cancelButton": "_SVViG_cancelButton",
6697
+ "sentCardNote": "_SVViG_sentCardNote",
6697
6698
  "toolbarBelow": "_SVViG_toolbarBelow",
6698
- "sentChipRow": "_SVViG_sentChipRow",
6699
+ "sentCardTitle": "_SVViG_sentCardTitle",
6700
+ "editorError": "_SVViG_editorError",
6701
+ "sentChip": "_SVViG_sentChip",
6702
+ "highlight": "_SVViG_highlight",
6703
+ "editorTarget": "_SVViG_editorTarget",
6704
+ "toolbarStatus": "_SVViG_toolbarStatus",
6705
+ "dshSidenoteHintIn": "_SVViG_dshSidenoteHintIn",
6706
+ "toolbarButton": "_SVViG_toolbarButton",
6707
+ "editorAskRow": "_SVViG_editorAskRow",
6699
6708
  "editorInput": "_SVViG_editorInput",
6709
+ "deleteButton": "_SVViG_deleteButton",
6710
+ "hintClose": "_SVViG_hintClose",
6700
6711
  "toolbar": "_SVViG_toolbar",
6701
6712
  "editorAsk": "_SVViG_editorAsk",
6702
- "editorError": "_SVViG_editorError",
6703
- "sentCardTitle": "_SVViG_sentCardTitle",
6704
- "editorTextarea": "_SVViG_editorTextarea",
6705
- "dshSidenoteHintIn": "_SVViG_dshSidenoteHintIn",
6706
- "editorTarget": "_SVViG_editorTarget",
6707
- "sentChip": "_SVViG_sentChip",
6708
- "sentCardQuote": "_SVViG_sentCardQuote",
6709
- "editorEdit": "_SVViG_editorEdit",
6710
6713
  "editorSpacer": "_SVViG_editorSpacer",
6714
+ "badgeSent": "_SVViG_badgeSent",
6715
+ "editorFooter": "_SVViG_editorFooter",
6711
6716
  "hint": "_SVViG_hint",
6712
- "toolbarStatus": "_SVViG_toolbarStatus",
6713
6717
  "editorNew": "_SVViG_editorNew",
6714
- "badge": "_SVViG_badge",
6715
- "toolbarButton": "_SVViG_toolbarButton"
6718
+ "sentChipRow": "_SVViG_sentChipRow",
6719
+ "editorTextarea": "_SVViG_editorTextarea"
6716
6720
  };
6717
6721
  //#endregion
6718
6722
  //#region src/client/annotate/hint.tsx
@@ -7025,6 +7029,7 @@ ${body}
7025
7029
  y: anchor.y,
7026
7030
  abort: new AbortController(),
7027
7031
  target,
7032
+ skipNote: !preference.showSideChatNote,
7028
7033
  ...targetTitle !== void 0 ? { targetTitle } : {}
7029
7034
  });
7030
7035
  controller.clear();
@@ -7053,6 +7058,10 @@ ${body}
7053
7058
  view: snapshot.source.view
7054
7059
  });
7055
7060
  setSideDraft((current) => current === sideDraft ? null : current);
7061
+ if (sideDraft.skipNote) {
7062
+ const view = [...document.querySelectorAll("[data-sidenote-chat-session]")].find((element) => element.dataset.sidenoteChatSession === target.childId);
7063
+ if (view) focusSideComposer(view);
7064
+ }
7056
7065
  return true;
7057
7066
  } catch {
7058
7067
  return false;
@@ -7136,6 +7145,7 @@ ${body}
7136
7145
  x: sideDraft.x,
7137
7146
  y: sideDraft.y,
7138
7147
  targetTitle: sideDraft.targetTitle,
7148
+ skipNote: sideDraft.skipNote,
7139
7149
  bounds: sourceClip(sideDraft.snapshot.source),
7140
7150
  onSave: commitSideDraft,
7141
7151
  onCancel: () => {
@@ -7232,6 +7242,7 @@ ${body}
7232
7242
  function SideChatNoteEditor(props) {
7233
7243
  useLocaleTick();
7234
7244
  const [note, setNote] = (0, react.useState)("");
7245
+ const [showNote, setShowNote] = (0, react.useState)(!props.skipNote);
7235
7246
  const noteInputRef = useAutoGrowingNote(note);
7236
7247
  const [failed, setFailed] = (0, react.useState)(false);
7237
7248
  const [pending, setPending] = (0, react.useState)(false);
@@ -7262,6 +7273,7 @@ ${body}
7262
7273
  if (cancelled.current) return;
7263
7274
  saved.current = ok;
7264
7275
  failedRef.current = !ok;
7276
+ if (!ok) setShowNote(true);
7265
7277
  setFailed(!ok);
7266
7278
  setPending(false);
7267
7279
  };
@@ -7280,6 +7292,9 @@ ${body}
7280
7292
  };
7281
7293
  const saveRef = (0, react.useRef)(save);
7282
7294
  saveRef.current = save;
7295
+ (0, react.useEffect)(() => {
7296
+ if (props.skipNote) saveRef.current();
7297
+ }, [props.skipNote]);
7283
7298
  (0, react.useEffect)(() => {
7284
7299
  const onKeyDown = (event) => {
7285
7300
  if (event.key === "Escape") {
@@ -7320,7 +7335,7 @@ ${body}
7320
7335
  className: annotate_module_css_default.editorTarget,
7321
7336
  children: t("sendToTarget", { title: props.targetTitle })
7322
7337
  }),
7323
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7338
+ showNote && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7324
7339
  className: annotate_module_css_default.editorAskRow,
7325
7340
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
7326
7341
  ref: noteInputRef,
@@ -8339,7 +8354,7 @@ ${body}
8339
8354
  }
8340
8355
  //#endregion
8341
8356
  //#region src/client/preferences.ts
8342
- /** Share one host-backed value between every main and side conversation. */
8357
+ /** Share host-backed preferences between every main and side conversation. */
8343
8358
  function createSidenotePreferences(form) {
8344
8359
  const listeners = /* @__PURE__ */ new Set();
8345
8360
  let saving = false;
@@ -8348,6 +8363,7 @@ ${body}
8348
8363
  const state = form.getSnapshot();
8349
8364
  return {
8350
8365
  hidden: state.value?.hideNativeQuote ?? true,
8366
+ showSideChatNote: state.value?.showSideChatNote ?? true,
8351
8367
  status: state.status,
8352
8368
  writable: state.writable,
8353
8369
  saving,
@@ -8360,6 +8376,20 @@ ${body}
8360
8376
  for (const listener of listeners) listener();
8361
8377
  };
8362
8378
  const unsubscribe = form.subscribe(publish);
8379
+ const set = async (field, value) => {
8380
+ if (saving || snapshot.status !== "ready" || !snapshot.writable) return;
8381
+ saving = true;
8382
+ failed = false;
8383
+ publish();
8384
+ try {
8385
+ failed = !await form.set(field, value);
8386
+ } catch {
8387
+ failed = true;
8388
+ } finally {
8389
+ saving = false;
8390
+ publish();
8391
+ }
8392
+ };
8363
8393
  return {
8364
8394
  getSnapshot: () => snapshot,
8365
8395
  subscribe(listener) {
@@ -8368,20 +8398,8 @@ ${body}
8368
8398
  listeners.delete(listener);
8369
8399
  };
8370
8400
  },
8371
- async setHidden(hidden) {
8372
- if (saving || snapshot.status !== "ready" || !snapshot.writable) return;
8373
- saving = true;
8374
- failed = false;
8375
- publish();
8376
- try {
8377
- failed = !await form.set("hideNativeQuote", hidden);
8378
- } catch {
8379
- failed = true;
8380
- } finally {
8381
- saving = false;
8382
- publish();
8383
- }
8384
- },
8401
+ setHidden: (hidden) => set("hideNativeQuote", hidden),
8402
+ setShowSideChatNote: (show) => set("showSideChatNote", show),
8385
8403
  dispose() {
8386
8404
  unsubscribe();
8387
8405
  listeners.clear();
@@ -8400,15 +8418,15 @@ ${body}
8400
8418
  document.head.appendChild(tag);
8401
8419
  }
8402
8420
  var settings_module_css_default = {
8403
- "description": "_2xJwMG_description",
8404
- "error": "_2xJwMG_error",
8405
8421
  "hint": "_2xJwMG_hint",
8406
- "label": "_2xJwMG_label",
8407
- "title": "_2xJwMG_title",
8408
- "copy": "_2xJwMG_copy",
8422
+ "description": "_2xJwMG_description",
8409
8423
  "status": "_2xJwMG_status",
8410
8424
  "row": "_2xJwMG_row",
8411
- "page": "_2xJwMG_page"
8425
+ "page": "_2xJwMG_page",
8426
+ "copy": "_2xJwMG_copy",
8427
+ "title": "_2xJwMG_title",
8428
+ "label": "_2xJwMG_label",
8429
+ "error": "_2xJwMG_error"
8412
8430
  };
8413
8431
  //#endregion
8414
8432
  //#region src/client/settings.tsx
@@ -8455,6 +8473,32 @@ ${body}
8455
8473
  }
8456
8474
  })]
8457
8475
  }),
8476
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8477
+ className: settings_module_css_default.row,
8478
+ role: "group",
8479
+ "aria-labelledby": `${id}-side-note-label`,
8480
+ "aria-describedby": `${id}-side-note-description`,
8481
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8482
+ className: settings_module_css_default.copy,
8483
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8484
+ id: `${id}-side-note-label`,
8485
+ className: settings_module_css_default.label,
8486
+ children: t("showSideChatNote")
8487
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8488
+ id: `${id}-side-note-description`,
8489
+ className: settings_module_css_default.hint,
8490
+ children: t("showSideChatNoteHint")
8491
+ })]
8492
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
8493
+ checked: state.showSideChatNote,
8494
+ label: t("showSideChatNote"),
8495
+ title: t("showSideChatNoteHint"),
8496
+ disabled: state.status !== "ready" || !state.writable || state.saving,
8497
+ onChange: (show) => {
8498
+ preferences.setShowSideChatNote(show);
8499
+ }
8500
+ })]
8501
+ }),
8458
8502
  state.saving && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8459
8503
  className: settings_module_css_default.status,
8460
8504
  role: "status",
package/lib/index.js CHANGED
@@ -1094,6 +1094,7 @@ defineMethod("transform", [
1094
1094
  /** Live presentation preference stored in the host's profile document. */
1095
1095
  const Config = Schema.object({
1096
1096
  hideNativeQuote: Schema.boolean().default(true).volatile(),
1097
+ showSideChatNote: Schema.boolean().default(true).volatile(),
1097
1098
  finalRepliesDefaultAttempted: Schema.boolean().default(false).volatile()
1098
1099
  });
1099
1100
  //#endregion
@@ -23,6 +23,7 @@ export declare function SideChatNoteEditor(props: {
23
23
  y: number;
24
24
  targetTitle?: string;
25
25
  bounds?: Bounds;
26
+ skipNote?: boolean;
26
27
  onSave: (note: string) => boolean | Promise<boolean>;
27
28
  onCancel: () => void;
28
29
  }): ReactNode;
@@ -69,6 +69,8 @@ export declare const en: {
69
69
  settingsDescription: string;
70
70
  hideNativeQuote: string;
71
71
  hideNativeQuoteHint: string;
72
+ showSideChatNote: string;
73
+ showSideChatNoteHint: string;
72
74
  settingsSaving: string;
73
75
  settingsLoading: string;
74
76
  settingsUnavailable: string;
@@ -1,23 +1,26 @@
1
1
  import type { ObservableSnapshot } from './host/contracts.ts';
2
- /** The host ConfigForm subset used by the single Sidenote preference. */
2
+ /** The host ConfigForm subset used by Sidenote preferences. */
3
3
  export interface SidenoteConfigForm extends ObservableSnapshot<{
4
4
  status: 'loading' | 'ready' | 'unavailable';
5
5
  value: {
6
6
  hideNativeQuote: boolean;
7
+ showSideChatNote?: boolean;
7
8
  } | undefined;
8
9
  writable: boolean;
9
10
  }> {
10
- set(field: 'hideNativeQuote', value: boolean): Promise<boolean>;
11
+ set(field: 'hideNativeQuote' | 'showSideChatNote', value: boolean): Promise<boolean>;
11
12
  }
12
13
  export interface SidenotePreferences extends ObservableSnapshot<{
13
14
  hidden: boolean;
15
+ showSideChatNote: boolean;
14
16
  status: 'loading' | 'ready' | 'unavailable';
15
17
  writable: boolean;
16
18
  saving: boolean;
17
19
  failed: boolean;
18
20
  }> {
19
21
  setHidden(hidden: boolean): Promise<void>;
22
+ setShowSideChatNote(show: boolean): Promise<void>;
20
23
  dispose(): void;
21
24
  }
22
- /** Share one host-backed value between every main and side conversation. */
25
+ /** Share host-backed preferences between every main and side conversation. */
23
26
  export declare function createSidenotePreferences(form: SidenoteConfigForm): SidenotePreferences;
@@ -2,8 +2,10 @@ import z from '@deepseek-ai/schemastery';
2
2
  /** Live presentation preference stored in the host's profile document. */
3
3
  export declare const Config: z<Schemastery.ObjectS<NoInfer<{
4
4
  hideNativeQuote: z<boolean, boolean, "volatile-defined">;
5
+ showSideChatNote: z<boolean, boolean, "volatile-defined">;
5
6
  finalRepliesDefaultAttempted: z<boolean, boolean, "volatile-defined">;
6
7
  }>>, Schemastery.ObjectT<NoInfer<{
7
8
  hideNativeQuote: z<boolean, boolean, "volatile-defined">;
9
+ showSideChatNote: z<boolean, boolean, "volatile-defined">;
8
10
  finalRepliesDefaultAttempted: z<boolean, boolean, "volatile-defined">;
9
11
  }>>, "plain">;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-sidenote",
3
- "version": "0.8.4",
3
+ "version": "0.8.5",
4
4
  "description": "Follow the tangent. Keep the thread. Annotate, explore on the side, and bring answers back to DeepSeek Harness. / 思路可以分岔,主线始终向前。划选注释、侧边追问,再把结论带回正在推进的任务。",
5
5
  "icon": "docs/assets/logo.svg",
6
6
  "type": "module",
@@ -17,6 +17,7 @@ import type { ReactNode } from 'react'
17
17
  import { IconCheckOutline16, IconTrashOutline16 } from '../host/icons.ts'
18
18
  import type { Context } from '../host/contracts.ts'
19
19
  import { currentSessionIdOf } from '../host/currentSession.ts'
20
+ import { focusSideComposer } from '../host/dom.ts'
20
21
  import { hostDiagnostics } from '../host/probes.ts'
21
22
  import { insertNativeQuote, probeInput, resolveInput } from '../host/input.ts'
22
23
  import { sideChatBridge, type SideChatBridge } from '../bridge.ts'
@@ -56,8 +57,8 @@ function useAutoGrowingNote(note: string) {
56
57
  return input
57
58
  }
58
59
 
59
- /** 「Ask in side chat」的注解收集态:先弹编辑器(与「Add to conversation」一致,
60
- * 允许空注解),保存后经 bridge 等待真实子会话,创建共享注释对象;正文保持干净。 */
60
+ /** 「Ask in side chat」的投递草稿:按偏好填写或跳过注解,
61
+ * 经 bridge 等待真实子会话后创建共享注释对象;正文保持干净。 */
61
62
  interface SideDraftState {
62
63
  readonly snapshot: SelectionSnapshot
63
64
  readonly x: number
@@ -66,6 +67,7 @@ interface SideDraftState {
66
67
  readonly targetTitle?: string
67
68
  readonly abort: AbortController
68
69
  readonly target: ReturnType<SideChatBridge['captureTarget']> | undefined
70
+ readonly skipNote: boolean
69
71
  }
70
72
 
71
73
  interface OverlayProps {
@@ -188,11 +190,12 @@ function AnnotateOverlayInner({ ctx, store, controller, preferences }: OverlayPr
188
190
  }
189
191
 
190
192
  const askInSideChat = (snapshot: SelectionSnapshot): void => {
191
- // 收集注解(可空),等选定的实际目标就绪后递交共享对象。
193
+ // 按偏好收集注解,等选定的实际目标就绪后递交共享对象。
192
194
  const anchor = selectionBadgePoint(snapshot)
193
195
  const target = sideChatBridge.current?.captureTarget(snapshot.sessionId)
194
196
  const targetTitle = target?.title
195
- setSideDraft({ snapshot, x: anchor.x, y: anchor.y, abort: new AbortController(), target, ...(targetTitle !== undefined ? { targetTitle } : {}) })
197
+ setSideDraft({ snapshot, x: anchor.x, y: anchor.y, abort: new AbortController(), target, skipNote: !preference.showSideChatNote,
198
+ ...(targetTitle !== undefined ? { targetTitle } : {}) })
196
199
  controller.clear()
197
200
  window.getSelection()?.removeAllRanges()
198
201
  }
@@ -211,6 +214,11 @@ function AnnotateOverlayInner({ ctx, store, controller, preferences }: OverlayPr
211
214
  })
212
215
  rangeCache.current.set(annotation.id, { ranges: snapshot.ranges.map(range => range.cloneRange()), view: snapshot.source.view })
213
216
  setSideDraft(current => current === sideDraft ? null : current)
217
+ if (sideDraft.skipNote) {
218
+ const view = [...document.querySelectorAll<HTMLElement>('[data-sidenote-chat-session]')]
219
+ .find(element => element.dataset.sidenoteChatSession === target.childId)
220
+ if (view) focusSideComposer(view)
221
+ }
214
222
  return true
215
223
  } catch { return false }
216
224
  }
@@ -298,6 +306,7 @@ function AnnotateOverlayInner({ ctx, store, controller, preferences }: OverlayPr
298
306
  x={sideDraft.x}
299
307
  y={sideDraft.y}
300
308
  targetTitle={sideDraft.targetTitle}
309
+ skipNote={sideDraft.skipNote}
301
310
  bounds={sourceClip(sideDraft.snapshot.source)}
302
311
  onSave={commitSideDraft}
303
312
  onCancel={() => { sideDraft.abort.abort(); setSideDraft(null) }}
@@ -392,11 +401,13 @@ export function SideChatNoteEditor(props: {
392
401
  y: number
393
402
  targetTitle?: string
394
403
  bounds?: Bounds
404
+ skipNote?: boolean
395
405
  onSave: (note: string) => boolean | Promise<boolean>
396
406
  onCancel: () => void
397
407
  }): ReactNode {
398
408
  useLocaleTick()
399
409
  const [note, setNote] = useState('')
410
+ const [showNote, setShowNote] = useState(!props.skipNote)
400
411
  const noteInputRef = useAutoGrowingNote(note)
401
412
  const [failed, setFailed] = useState(false)
402
413
  const [pending, setPending] = useState(false)
@@ -415,6 +426,7 @@ export function SideChatNoteEditor(props: {
415
426
  if (cancelled.current) return
416
427
  saved.current = ok
417
428
  failedRef.current = !ok
429
+ if (!ok) setShowNote(true)
418
430
  setFailed(!ok)
419
431
  setPending(false)
420
432
  }
@@ -426,6 +438,9 @@ export function SideChatNoteEditor(props: {
426
438
  }
427
439
  const saveRef = useRef(save)
428
440
  saveRef.current = save
441
+ useEffect(() => {
442
+ if (props.skipNote) saveRef.current()
443
+ }, [props.skipNote])
429
444
 
430
445
  useEffect(() => {
431
446
  const onKeyDown = (event: KeyboardEvent): void => {
@@ -460,40 +475,42 @@ export function SideChatNoteEditor(props: {
460
475
  {props.targetTitle !== undefined && props.targetTitle !== '' && (
461
476
  <div className={css.editorTarget}>{t('sendToTarget', { title: props.targetTitle })}</div>
462
477
  )}
463
- <div className={css.editorAskRow}>
464
- <textarea
465
- ref={noteInputRef}
466
- rows={1}
467
- className={css.editorInput}
468
- value={note}
469
- placeholder={t('sideNotePlaceholder')}
470
- aria-label={t('sideNoteAria')}
471
- autoFocus
472
- disabled={pending}
473
- onChange={(event) => {
474
- setNote(event.target.value)
475
- failedRef.current = false
476
- setFailed(false)
477
- }}
478
- onKeyDown={(event) => {
479
- if (event.key !== 'Enter' || event.shiftKey) return
480
- // IME 保护:组合中(候选窗未提交)的 Enter 属于输入法,不能算确认。
481
- if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return
482
- event.preventDefault()
483
- save()
484
- }}
485
- />
486
- <button
487
- type="button"
488
- className={css.confirmButton}
489
- title={t('confirmTitle')}
490
- aria-label={t('confirmAskAria')}
491
- onClick={() => { save() }}
492
- disabled={pending}
493
- >
494
- <IconCheckOutline16 size={14} />
495
- </button>
496
- </div>
478
+ {showNote && (
479
+ <div className={css.editorAskRow}>
480
+ <textarea
481
+ ref={noteInputRef}
482
+ rows={1}
483
+ className={css.editorInput}
484
+ value={note}
485
+ placeholder={t('sideNotePlaceholder')}
486
+ aria-label={t('sideNoteAria')}
487
+ autoFocus
488
+ disabled={pending}
489
+ onChange={(event) => {
490
+ setNote(event.target.value)
491
+ failedRef.current = false
492
+ setFailed(false)
493
+ }}
494
+ onKeyDown={(event) => {
495
+ if (event.key !== 'Enter' || event.shiftKey) return
496
+ // IME 保护:组合中(候选窗未提交)的 Enter 属于输入法,不能算确认。
497
+ if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return
498
+ event.preventDefault()
499
+ save()
500
+ }}
501
+ />
502
+ <button
503
+ type="button"
504
+ className={css.confirmButton}
505
+ title={t('confirmTitle')}
506
+ aria-label={t('confirmAskAria')}
507
+ onClick={() => { save() }}
508
+ disabled={pending}
509
+ >
510
+ <IconCheckOutline16 size={14} />
511
+ </button>
512
+ </div>
513
+ )}
497
514
  {failed && <div className={css.editorError}>{t('openSideFailed')}</div>}
498
515
  {pending && <div className={css.editorTarget} role="status">{t('sideTargetWaiting')}</div>}
499
516
  </div>
@@ -62,6 +62,8 @@ export const en = {
62
62
  settingsDescription: 'Choose the actions available when you select conversation text.',
63
63
  hideNativeQuote: 'Hide native quote',
64
64
  hideNativeQuoteHint: 'Hide the Quote action in main and side chats.',
65
+ showSideChatNote: 'Add a note before asking in side chat',
66
+ showSideChatNoteHint: 'Turn off to add selected text directly to side chat and write your question there.',
65
67
  settingsSaving: 'Saving…',
66
68
  settingsLoading: 'Loading preferences…',
67
69
  settingsUnavailable: 'Preferences are unavailable. Reload the page to reconnect.',
@@ -271,6 +273,8 @@ export const zh: Record<CopyKey, string> = {
271
273
  settingsDescription: '调整划选对话文字时显示的操作。',
272
274
  hideNativeQuote: '隐藏原生引用',
273
275
  hideNativeQuoteHint: '在主对话和侧边聊天中隐藏“引用”按钮。',
276
+ showSideChatNote: '侧边提问前填写注解',
277
+ showSideChatNoteHint: '关闭后,划选内容直接添加到侧边聊天,可在侧聊输入框中继续提问。',
274
278
  settingsSaving: '正在保存…',
275
279
  settingsLoading: '正在加载偏好设置…',
276
280
  settingsUnavailable: '暂时无法读取设置,请刷新页面重新连接。',
@@ -1,33 +1,36 @@
1
1
  import type { ObservableSnapshot } from './host/contracts.ts'
2
2
 
3
- /** The host ConfigForm subset used by the single Sidenote preference. */
3
+ /** The host ConfigForm subset used by Sidenote preferences. */
4
4
  export interface SidenoteConfigForm extends ObservableSnapshot<{
5
5
  status: 'loading' | 'ready' | 'unavailable'
6
- value: { hideNativeQuote: boolean } | undefined
6
+ value: { hideNativeQuote: boolean; showSideChatNote?: boolean } | undefined
7
7
  writable: boolean
8
8
  }> {
9
- set(field: 'hideNativeQuote', value: boolean): Promise<boolean>
9
+ set(field: 'hideNativeQuote' | 'showSideChatNote', value: boolean): Promise<boolean>
10
10
  }
11
11
 
12
12
  export interface SidenotePreferences extends ObservableSnapshot<{
13
13
  hidden: boolean
14
+ showSideChatNote: boolean
14
15
  status: 'loading' | 'ready' | 'unavailable'
15
16
  writable: boolean
16
17
  saving: boolean
17
18
  failed: boolean
18
19
  }> {
19
20
  setHidden(hidden: boolean): Promise<void>
21
+ setShowSideChatNote(show: boolean): Promise<void>
20
22
  dispose(): void
21
23
  }
22
24
 
23
- /** Share one host-backed value between every main and side conversation. */
25
+ /** Share host-backed preferences between every main and side conversation. */
24
26
  export function createSidenotePreferences(form: SidenoteConfigForm): SidenotePreferences {
25
27
  const listeners = new Set<() => void>()
26
28
  let saving = false
27
29
  let failed = false
28
30
  const read = () => {
29
31
  const state = form.getSnapshot()
30
- return { hidden: state.value?.hideNativeQuote ?? true, status: state.status, writable: state.writable, saving, failed }
32
+ return { hidden: state.value?.hideNativeQuote ?? true, showSideChatNote: state.value?.showSideChatNote ?? true,
33
+ status: state.status, writable: state.writable, saving, failed }
31
34
  }
32
35
  let snapshot = read()
33
36
  const publish = (): void => {
@@ -35,18 +38,20 @@ export function createSidenotePreferences(form: SidenoteConfigForm): SidenotePre
35
38
  for (const listener of listeners) listener()
36
39
  }
37
40
  const unsubscribe = form.subscribe(publish)
41
+ const set = async (field: 'hideNativeQuote' | 'showSideChatNote', value: boolean): Promise<void> => {
42
+ if (saving || snapshot.status !== 'ready' || !snapshot.writable) return
43
+ saving = true
44
+ failed = false
45
+ publish()
46
+ try { failed = !await form.set(field, value) }
47
+ catch { failed = true }
48
+ finally { saving = false; publish() }
49
+ }
38
50
  return {
39
51
  getSnapshot: () => snapshot,
40
52
  subscribe(listener) { listeners.add(listener); return () => { listeners.delete(listener) } },
41
- async setHidden(hidden) {
42
- if (saving || snapshot.status !== 'ready' || !snapshot.writable) return
43
- saving = true
44
- failed = false
45
- publish()
46
- try { failed = !await form.set('hideNativeQuote', hidden) }
47
- catch { failed = true }
48
- finally { saving = false; publish() }
49
- },
53
+ setHidden: hidden => set('hideNativeQuote', hidden),
54
+ setShowSideChatNote: show => set('showSideChatNote', show),
50
55
  dispose() { unsubscribe(); listeners.clear() },
51
56
  }
52
57
  }
@@ -24,6 +24,15 @@ function SidenoteSettings({ preferences }: { preferences: SidenotePreferences })
24
24
  disabled={state.status !== 'ready' || !state.writable || state.saving}
25
25
  onChange={hidden => { void preferences.setHidden(hidden) }} />
26
26
  </div>
27
+ <div className={css.row} role="group" aria-labelledby={`${id}-side-note-label`} aria-describedby={`${id}-side-note-description`}>
28
+ <div className={css.copy}>
29
+ <span id={`${id}-side-note-label`} className={css.label}>{t('showSideChatNote')}</span>
30
+ <p id={`${id}-side-note-description`} className={css.hint}>{t('showSideChatNoteHint')}</p>
31
+ </div>
32
+ <Switch checked={state.showSideChatNote} label={t('showSideChatNote')} title={t('showSideChatNoteHint')}
33
+ disabled={state.status !== 'ready' || !state.writable || state.saving}
34
+ onChange={show => { void preferences.setShowSideChatNote(show) }} />
35
+ </div>
27
36
  {state.saving && <p className={css.status} role="status">{t('settingsSaving')}</p>}
28
37
  {state.status === 'loading' && <p className={css.status} role="status">{t('settingsLoading')}</p>}
29
38
  {state.status === 'unavailable' && <p className={css.error} role="alert">{t('settingsUnavailable')}</p>}
package/src/settings.ts CHANGED
@@ -3,5 +3,6 @@ import z from '@deepseek-ai/schemastery'
3
3
  /** Live presentation preference stored in the host's profile document. */
4
4
  export const Config = z.object({
5
5
  hideNativeQuote: z.boolean().default(true).volatile(),
6
+ showSideChatNote: z.boolean().default(true).volatile(),
6
7
  finalRepliesDefaultAttempted: z.boolean().default(false).volatile(),
7
8
  })