@spotpatch/runtime 1.15.2 → 1.15.4

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.
@@ -0,0 +1,183 @@
1
+ import {
2
+ createButton,
3
+ createMarkedElement
4
+ } from "./chunk-P6X75LO5.js";
5
+
6
+ // src/ui/ask-picker.ts
7
+ var SELECT_PICKER_STYLES = `
8
+ .spotpatch-select-picker { position: relative; display: grid; gap: 5px; min-width: 0; }
9
+ .spotpatch-select-trigger { box-sizing: border-box; display: flex; width: 100%; min-height: 38px; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid var(--spotpatch-border); border-radius: 9px; padding: 0 11px; overflow: hidden; color: var(--spotpatch-text); background: var(--spotpatch-bg-input); cursor: pointer; font: inherit; outline: none; text-align: left; }
10
+ .spotpatch-select-trigger > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
11
+ .spotpatch-select-trigger[data-expandable="true"]::after { width: 7px; height: 7px; flex: 0 0 auto; border-right: 1.5px solid #8f96a3; border-bottom: 1.5px solid #8f96a3; content: ""; transform: translateY(-2px) rotate(45deg); }
12
+ .spotpatch-select-trigger[aria-expanded="true"]::after { transform: translateY(2px) rotate(225deg); }
13
+ .spotpatch-select-trigger:focus-visible { border-color: rgb(139 123 255 / 68%); box-shadow: 0 0 0 3px rgb(139 123 255 / 12%); }
14
+ .spotpatch-select-trigger:disabled { color: var(--spotpatch-text); background: rgb(255 255 255 / 3%); cursor: default; opacity: 1; }
15
+ .spotpatch-select-trigger[data-empty="true"]:disabled { color: var(--spotpatch-text-muted); }
16
+ .spotpatch-select-menu { position: relative; z-index: 1; display: grid; gap: 3px; max-height: 224px; overflow-y: auto; overscroll-behavior: contain; border: 1px solid var(--spotpatch-border); border-radius: 9px; padding: 4px; background: var(--spotpatch-bg-input); box-shadow: 0 10px 28px rgb(0 0 0 / 28%); scrollbar-width: thin; }
17
+ .spotpatch-select-menu[hidden] { display: none; }
18
+ .spotpatch-select-option { box-sizing: border-box; min-height: 34px; border: 0; border-radius: 6px; padding: 7px 9px; overflow-wrap: anywhere; color: var(--spotpatch-text-secondary); background: transparent; cursor: pointer; font: inherit; text-align: left; }
19
+ .spotpatch-select-option[data-active="true"], .spotpatch-select-option:hover { color: #fff; background: rgb(139 123 255 / 13%); outline: none; }
20
+ .spotpatch-select-option[aria-selected="true"] { color: #f4f1ff; background: rgb(139 123 255 / 20%); }
21
+ .spotpatch-select-native { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; border: 0; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
22
+ `;
23
+ function createSelectPicker(document, onViewChange) {
24
+ const root = createMarkedElement(document, "div");
25
+ root.className = "spotpatch-select-picker";
26
+ const trigger = createButton(document, "", "spotpatch-select-trigger");
27
+ trigger.id = `spotpatch-select-picker-${Math.random().toString(36).slice(2)}`;
28
+ trigger.setAttribute("role", "combobox");
29
+ trigger.setAttribute("aria-haspopup", "listbox");
30
+ trigger.setAttribute("aria-expanded", "false");
31
+ const text = createMarkedElement(document, "span");
32
+ trigger.append(text);
33
+ const menu = createMarkedElement(document, "div");
34
+ menu.id = `${trigger.id}-menu`;
35
+ menu.className = "spotpatch-select-menu";
36
+ menu.setAttribute("role", "listbox");
37
+ menu.hidden = true;
38
+ trigger.setAttribute("aria-controls", menu.id);
39
+ const select = createMarkedElement(document, "select");
40
+ select.className = "spotpatch-select-native";
41
+ select.tabIndex = -1;
42
+ select.setAttribute("aria-hidden", "true");
43
+ root.append(trigger, menu, select);
44
+ function close(focus = false) {
45
+ if (menu.hidden) return;
46
+ menu.hidden = true;
47
+ trigger.setAttribute("aria-expanded", "false");
48
+ trigger.removeAttribute("aria-activedescendant");
49
+ if (focus) trigger.focus({ preventScroll: true });
50
+ onViewChange();
51
+ }
52
+ function activate(index) {
53
+ const options = [...menu.children];
54
+ const active = options[index];
55
+ if (active === void 0) return;
56
+ for (const option of options) option.dataset.active = String(option === active);
57
+ trigger.setAttribute("aria-activedescendant", active.id);
58
+ const itemRect = active.getBoundingClientRect();
59
+ const menuRect = menu.getBoundingClientRect();
60
+ if (itemRect.top < menuRect.top) menu.scrollTop -= menuRect.top - itemRect.top;
61
+ else if (itemRect.bottom > menuRect.bottom)
62
+ menu.scrollTop += itemRect.bottom - menuRect.bottom;
63
+ }
64
+ function open() {
65
+ if (trigger.disabled || !menu.hidden) return;
66
+ menu.hidden = false;
67
+ trigger.setAttribute("aria-expanded", "true");
68
+ activate(Math.max(0, select.selectedIndex));
69
+ onViewChange();
70
+ }
71
+ function sync() {
72
+ text.textContent = select.selectedOptions[0]?.textContent ?? "";
73
+ trigger.title = text.textContent;
74
+ trigger.dataset.empty = String(select.value.length === 0);
75
+ for (const option of [...menu.children]) {
76
+ option.setAttribute(
77
+ "aria-selected",
78
+ String(option.dataset.value === select.value)
79
+ );
80
+ }
81
+ }
82
+ function choose(value) {
83
+ if (trigger.disabled) return;
84
+ select.value = value;
85
+ sync();
86
+ close(true);
87
+ select.dispatchEvent(new Event("change", { bubbles: true }));
88
+ }
89
+ function rebuild() {
90
+ const expanded = !menu.hidden;
91
+ const nativeOptions = [...select.options];
92
+ menu.replaceChildren();
93
+ for (const [index, option] of nativeOptions.entries()) {
94
+ const item = createMarkedElement(document, "div");
95
+ item.id = `${menu.id}-${String(index)}`;
96
+ item.className = "spotpatch-select-option";
97
+ item.setAttribute("role", "option");
98
+ item.dataset.value = option.value;
99
+ item.textContent = option.textContent;
100
+ item.title = option.textContent;
101
+ item.addEventListener("pointerdown", (event) => {
102
+ event.preventDefault();
103
+ });
104
+ item.addEventListener("click", () => {
105
+ choose(option.value);
106
+ });
107
+ menu.append(item);
108
+ }
109
+ sync();
110
+ if (expanded) {
111
+ activate(Math.max(0, select.selectedIndex));
112
+ onViewChange();
113
+ }
114
+ }
115
+ trigger.addEventListener("click", () => {
116
+ if (menu.hidden) open();
117
+ else close();
118
+ });
119
+ trigger.addEventListener("keydown", (event) => {
120
+ if (event.key === "Escape" && !menu.hidden) {
121
+ event.preventDefault();
122
+ event.stopPropagation();
123
+ close();
124
+ return;
125
+ }
126
+ if (event.key === "Tab") {
127
+ close();
128
+ return;
129
+ }
130
+ if (["Enter", " "].includes(event.key) && !menu.hidden) {
131
+ event.preventDefault();
132
+ const active = [...menu.children].find(
133
+ (item) => item.id === trigger.getAttribute("aria-activedescendant")
134
+ );
135
+ if (active?.dataset.value !== void 0) choose(active.dataset.value);
136
+ return;
137
+ }
138
+ if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
139
+ event.preventDefault();
140
+ if (menu.hidden) {
141
+ open();
142
+ return;
143
+ }
144
+ const options = [...menu.children];
145
+ const index = options.findIndex(
146
+ (item) => item.id === trigger.getAttribute("aria-activedescendant")
147
+ );
148
+ activate(
149
+ event.key === "Home" ? 0 : event.key === "End" ? options.length - 1 : (index + (event.key === "ArrowDown" ? 1 : -1) + options.length) % options.length
150
+ );
151
+ });
152
+ root.addEventListener("focusout", (event) => {
153
+ if (!(event.relatedTarget instanceof Node) || !root.contains(event.relatedTarget))
154
+ close();
155
+ });
156
+ const outside = (event) => {
157
+ if (!event.composedPath().includes(root)) close();
158
+ };
159
+ document.addEventListener("pointerdown", outside);
160
+ select.addEventListener("change", sync);
161
+ return {
162
+ root,
163
+ trigger,
164
+ select,
165
+ rebuild,
166
+ close,
167
+ setDisabled(disabled) {
168
+ select.disabled = disabled;
169
+ trigger.disabled = disabled || select.options.length < 2;
170
+ trigger.dataset.expandable = String(!trigger.disabled);
171
+ if (trigger.disabled) close();
172
+ },
173
+ dispose() {
174
+ document.removeEventListener("pointerdown", outside);
175
+ }
176
+ };
177
+ }
178
+
179
+ export {
180
+ SELECT_PICKER_STYLES,
181
+ createSelectPicker
182
+ };
183
+ //# sourceMappingURL=chunk-ELCIEQGT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui/ask-picker.ts"],"sourcesContent":["import { createButton, createMarkedElement } from \"./dom.js\";\n\nexport const SELECT_PICKER_STYLES = `\n .spotpatch-select-picker { position: relative; display: grid; gap: 5px; min-width: 0; }\n .spotpatch-select-trigger { box-sizing: border-box; display: flex; width: 100%; min-height: 38px; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid var(--spotpatch-border); border-radius: 9px; padding: 0 11px; overflow: hidden; color: var(--spotpatch-text); background: var(--spotpatch-bg-input); cursor: pointer; font: inherit; outline: none; text-align: left; }\n .spotpatch-select-trigger > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .spotpatch-select-trigger[data-expandable=\"true\"]::after { width: 7px; height: 7px; flex: 0 0 auto; border-right: 1.5px solid #8f96a3; border-bottom: 1.5px solid #8f96a3; content: \"\"; transform: translateY(-2px) rotate(45deg); }\n .spotpatch-select-trigger[aria-expanded=\"true\"]::after { transform: translateY(2px) rotate(225deg); }\n .spotpatch-select-trigger:focus-visible { border-color: rgb(139 123 255 / 68%); box-shadow: 0 0 0 3px rgb(139 123 255 / 12%); }\n .spotpatch-select-trigger:disabled { color: var(--spotpatch-text); background: rgb(255 255 255 / 3%); cursor: default; opacity: 1; }\n .spotpatch-select-trigger[data-empty=\"true\"]:disabled { color: var(--spotpatch-text-muted); }\n .spotpatch-select-menu { position: relative; z-index: 1; display: grid; gap: 3px; max-height: 224px; overflow-y: auto; overscroll-behavior: contain; border: 1px solid var(--spotpatch-border); border-radius: 9px; padding: 4px; background: var(--spotpatch-bg-input); box-shadow: 0 10px 28px rgb(0 0 0 / 28%); scrollbar-width: thin; }\n .spotpatch-select-menu[hidden] { display: none; }\n .spotpatch-select-option { box-sizing: border-box; min-height: 34px; border: 0; border-radius: 6px; padding: 7px 9px; overflow-wrap: anywhere; color: var(--spotpatch-text-secondary); background: transparent; cursor: pointer; font: inherit; text-align: left; }\n .spotpatch-select-option[data-active=\"true\"], .spotpatch-select-option:hover { color: #fff; background: rgb(139 123 255 / 13%); outline: none; }\n .spotpatch-select-option[aria-selected=\"true\"] { color: #f4f1ff; background: rgb(139 123 255 / 20%); }\n .spotpatch-select-native { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; border: 0; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }\n`;\n\n/** Shared select-only picker: native value contract, one accessible custom control. */\nexport function createSelectPicker(document: Document, onViewChange: () => void) {\n const root = createMarkedElement(document, \"div\");\n root.className = \"spotpatch-select-picker\";\n const trigger = createButton(document, \"\", \"spotpatch-select-trigger\");\n trigger.id = `spotpatch-select-picker-${Math.random().toString(36).slice(2)}`;\n trigger.setAttribute(\"role\", \"combobox\");\n trigger.setAttribute(\"aria-haspopup\", \"listbox\");\n trigger.setAttribute(\"aria-expanded\", \"false\");\n const text = createMarkedElement(document, \"span\");\n trigger.append(text);\n const menu = createMarkedElement(document, \"div\");\n menu.id = `${trigger.id}-menu`;\n menu.className = \"spotpatch-select-menu\";\n menu.setAttribute(\"role\", \"listbox\");\n menu.hidden = true;\n trigger.setAttribute(\"aria-controls\", menu.id);\n const select = createMarkedElement(document, \"select\");\n select.className = \"spotpatch-select-native\";\n select.tabIndex = -1;\n select.setAttribute(\"aria-hidden\", \"true\");\n root.append(trigger, menu, select);\n\n function close(focus = false): void {\n if (menu.hidden) return;\n menu.hidden = true;\n trigger.setAttribute(\"aria-expanded\", \"false\");\n trigger.removeAttribute(\"aria-activedescendant\");\n if (focus) trigger.focus({ preventScroll: true });\n onViewChange();\n }\n\n function activate(index: number): void {\n const options = [...menu.children] as HTMLElement[];\n const active = options[index];\n if (active === undefined) return;\n for (const option of options) option.dataset.active = String(option === active);\n trigger.setAttribute(\"aria-activedescendant\", active.id);\n const itemRect = active.getBoundingClientRect();\n const menuRect = menu.getBoundingClientRect();\n if (itemRect.top < menuRect.top) menu.scrollTop -= menuRect.top - itemRect.top;\n else if (itemRect.bottom > menuRect.bottom)\n menu.scrollTop += itemRect.bottom - menuRect.bottom;\n }\n\n function open(): void {\n if (trigger.disabled || !menu.hidden) return;\n menu.hidden = false;\n trigger.setAttribute(\"aria-expanded\", \"true\");\n activate(Math.max(0, select.selectedIndex));\n onViewChange();\n }\n\n function sync(): void {\n text.textContent = select.selectedOptions[0]?.textContent ?? \"\";\n trigger.title = text.textContent;\n trigger.dataset.empty = String(select.value.length === 0);\n for (const option of [...menu.children] as HTMLElement[]) {\n option.setAttribute(\n \"aria-selected\",\n String(option.dataset.value === select.value),\n );\n }\n }\n\n function choose(value: string): void {\n if (trigger.disabled) return;\n select.value = value;\n sync();\n close(true);\n select.dispatchEvent(new Event(\"change\", { bubbles: true }));\n }\n\n function rebuild(): void {\n const expanded = !menu.hidden;\n const nativeOptions = [...select.options];\n menu.replaceChildren();\n for (const [index, option] of nativeOptions.entries()) {\n const item = createMarkedElement(document, \"div\");\n item.id = `${menu.id}-${String(index)}`;\n item.className = \"spotpatch-select-option\";\n item.setAttribute(\"role\", \"option\");\n item.dataset.value = option.value;\n item.textContent = option.textContent;\n item.title = option.textContent;\n item.addEventListener(\"pointerdown\", (event) => {\n event.preventDefault();\n });\n item.addEventListener(\"click\", () => {\n choose(option.value);\n });\n menu.append(item);\n }\n sync();\n if (expanded) {\n activate(Math.max(0, select.selectedIndex));\n onViewChange();\n }\n }\n\n trigger.addEventListener(\"click\", () => {\n if (menu.hidden) open();\n else close();\n });\n trigger.addEventListener(\"keydown\", (event) => {\n if (event.key === \"Escape\" && !menu.hidden) {\n event.preventDefault();\n event.stopPropagation();\n close();\n return;\n }\n if (event.key === \"Tab\") {\n close();\n return;\n }\n if ([\"Enter\", \" \"].includes(event.key) && !menu.hidden) {\n event.preventDefault();\n const active = [...menu.children].find(\n (item) => item.id === trigger.getAttribute(\"aria-activedescendant\"),\n ) as HTMLElement | undefined;\n if (active?.dataset.value !== undefined) choose(active.dataset.value);\n return;\n }\n if (![\"ArrowDown\", \"ArrowUp\", \"Home\", \"End\"].includes(event.key)) return;\n event.preventDefault();\n if (menu.hidden) {\n open();\n return;\n }\n const options = [...menu.children];\n const index = options.findIndex(\n (item) => item.id === trigger.getAttribute(\"aria-activedescendant\"),\n );\n activate(\n event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? options.length - 1\n : (index + (event.key === \"ArrowDown\" ? 1 : -1) + options.length) %\n options.length,\n );\n });\n root.addEventListener(\"focusout\", (event) => {\n if (!(event.relatedTarget instanceof Node) || !root.contains(event.relatedTarget))\n close();\n });\n const outside = (event: PointerEvent): void => {\n if (!event.composedPath().includes(root)) close();\n };\n document.addEventListener(\"pointerdown\", outside);\n select.addEventListener(\"change\", sync);\n return {\n root,\n trigger,\n select,\n rebuild,\n close,\n setDisabled(disabled: boolean): void {\n select.disabled = disabled;\n trigger.disabled = disabled || select.options.length < 2;\n trigger.dataset.expandable = String(!trigger.disabled);\n if (trigger.disabled) close();\n },\n dispose(): void {\n document.removeEventListener(\"pointerdown\", outside);\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -806,11 +806,27 @@ function createButton(document, label, className = "") {
806
806
  }
807
807
 
808
808
  // src/ui/ask-picker.ts
809
- function createAskPicker(document, onViewChange) {
809
+ var SELECT_PICKER_STYLES = `
810
+ .spotpatch-select-picker { position: relative; display: grid; gap: 5px; min-width: 0; }
811
+ .spotpatch-select-trigger { box-sizing: border-box; display: flex; width: 100%; min-height: 38px; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid var(--spotpatch-border); border-radius: 9px; padding: 0 11px; overflow: hidden; color: var(--spotpatch-text); background: var(--spotpatch-bg-input); cursor: pointer; font: inherit; outline: none; text-align: left; }
812
+ .spotpatch-select-trigger > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
813
+ .spotpatch-select-trigger[data-expandable="true"]::after { width: 7px; height: 7px; flex: 0 0 auto; border-right: 1.5px solid #8f96a3; border-bottom: 1.5px solid #8f96a3; content: ""; transform: translateY(-2px) rotate(45deg); }
814
+ .spotpatch-select-trigger[aria-expanded="true"]::after { transform: translateY(2px) rotate(225deg); }
815
+ .spotpatch-select-trigger:focus-visible { border-color: rgb(139 123 255 / 68%); box-shadow: 0 0 0 3px rgb(139 123 255 / 12%); }
816
+ .spotpatch-select-trigger:disabled { color: var(--spotpatch-text); background: rgb(255 255 255 / 3%); cursor: default; opacity: 1; }
817
+ .spotpatch-select-trigger[data-empty="true"]:disabled { color: var(--spotpatch-text-muted); }
818
+ .spotpatch-select-menu { position: relative; z-index: 1; display: grid; gap: 3px; max-height: 224px; overflow-y: auto; overscroll-behavior: contain; border: 1px solid var(--spotpatch-border); border-radius: 9px; padding: 4px; background: var(--spotpatch-bg-input); box-shadow: 0 10px 28px rgb(0 0 0 / 28%); scrollbar-width: thin; }
819
+ .spotpatch-select-menu[hidden] { display: none; }
820
+ .spotpatch-select-option { box-sizing: border-box; min-height: 34px; border: 0; border-radius: 6px; padding: 7px 9px; overflow-wrap: anywhere; color: var(--spotpatch-text-secondary); background: transparent; cursor: pointer; font: inherit; text-align: left; }
821
+ .spotpatch-select-option[data-active="true"], .spotpatch-select-option:hover { color: #fff; background: rgb(139 123 255 / 13%); outline: none; }
822
+ .spotpatch-select-option[aria-selected="true"] { color: #f4f1ff; background: rgb(139 123 255 / 20%); }
823
+ .spotpatch-select-native { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; border: 0; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
824
+ `;
825
+ function createSelectPicker(document, onViewChange) {
810
826
  const root = createMarkedElement(document, "div");
811
- root.className = "spotpatch-ask-executor-picker";
812
- const trigger = createButton(document, "", "spotpatch-ask-executor");
813
- trigger.id = `spotpatch-ask-picker-${Math.random().toString(36).slice(2)}`;
827
+ root.className = "spotpatch-select-picker";
828
+ const trigger = createButton(document, "", "spotpatch-select-trigger");
829
+ trigger.id = `spotpatch-select-picker-${Math.random().toString(36).slice(2)}`;
814
830
  trigger.setAttribute("role", "combobox");
815
831
  trigger.setAttribute("aria-haspopup", "listbox");
816
832
  trigger.setAttribute("aria-expanded", "false");
@@ -818,12 +834,12 @@ function createAskPicker(document, onViewChange) {
818
834
  trigger.append(text);
819
835
  const menu = createMarkedElement(document, "div");
820
836
  menu.id = `${trigger.id}-menu`;
821
- menu.className = "spotpatch-ask-executor-menu";
837
+ menu.className = "spotpatch-select-menu";
822
838
  menu.setAttribute("role", "listbox");
823
839
  menu.hidden = true;
824
840
  trigger.setAttribute("aria-controls", menu.id);
825
841
  const select = createMarkedElement(document, "select");
826
- select.className = "spotpatch-ask-executor-native";
842
+ select.className = "spotpatch-select-native";
827
843
  select.tabIndex = -1;
828
844
  select.setAttribute("aria-hidden", "true");
829
845
  root.append(trigger, menu, select);
@@ -873,12 +889,13 @@ function createAskPicker(document, onViewChange) {
873
889
  select.dispatchEvent(new Event("change", { bubbles: true }));
874
890
  }
875
891
  function rebuild() {
876
- close();
892
+ const expanded = !menu.hidden;
893
+ const nativeOptions = [...select.options];
877
894
  menu.replaceChildren();
878
- for (const [index, option] of [...select.options].entries()) {
895
+ for (const [index, option] of nativeOptions.entries()) {
879
896
  const item = createMarkedElement(document, "div");
880
897
  item.id = `${menu.id}-${String(index)}`;
881
- item.className = "spotpatch-ask-executor-option";
898
+ item.className = "spotpatch-select-option";
882
899
  item.setAttribute("role", "option");
883
900
  item.dataset.value = option.value;
884
901
  item.textContent = option.textContent;
@@ -892,6 +909,10 @@ function createAskPicker(document, onViewChange) {
892
909
  menu.append(item);
893
910
  }
894
911
  sync();
912
+ if (expanded) {
913
+ activate(Math.max(0, select.selectedIndex));
914
+ onViewChange();
915
+ }
895
916
  }
896
917
  trigger.addEventListener("click", () => {
897
918
  if (menu.hidden) open();
@@ -972,12 +993,12 @@ var EN = Object.freeze({
972
993
  ]),
973
994
  executorLabel: "Read-only executor",
974
995
  modelLabel: "Model",
975
- loadingExecutors: "Checking available executors\u2026",
996
+ loadingExecutors: "Checking available executors…",
976
997
  noExecutor: "No verified read-only executor is available.",
977
998
  unavailable: "unavailable",
978
999
  consent: "Allow the selected source snapshot to be sent to this provider for this answer.",
979
- dataSummary: (targets, sources) => `${String(targets)} selected element${targets === 1 ? "" : "s"} \xB7 ${String(sources)} source-backed`,
980
- safety: "Single turn \xB7 read-only \xB7 no history",
1000
+ dataSummary: (targets, sources) => `${String(targets)} selected element${targets === 1 ? "" : "s"} · ${String(sources)} source-backed`,
1001
+ safety: "Single turn · read-only · no history",
981
1002
  submit: "Ask",
982
1003
  cancel: "Cancel",
983
1004
  newQuestion: "Start over",
@@ -997,7 +1018,7 @@ var EN = Object.freeze({
997
1018
  cancelled: "Cancelled",
998
1019
  failed: "Could not answer"
999
1020
  }),
1000
- sourceLabel: (path, start, end) => `${path}:${String(start)}${end === start ? "" : `\u2013${String(end)}`}`,
1021
+ sourceLabel: (path, start, end) => `${path}:${String(start)}${end === start ? "" : `–${String(end)}`}`,
1001
1022
  readSource: (path) => `Reading ${path}`,
1002
1023
  readFiles: (bucket) => `Reading ${bucket} files`,
1003
1024
  copied: "Answer copied.",
@@ -1029,74 +1050,74 @@ var EN = Object.freeze({
1029
1050
  warning: (code) => code === "insufficient-evidence" ? "The selected source did not contain enough evidence for a complete answer." : "Some source references were omitted because they could not be verified."
1030
1051
  });
1031
1052
  var ZH = Object.freeze({
1032
- mode: Object.freeze({ ask: "\u95EE\u7B54", change: "\u4FEE\u6539", label: "\u4EFB\u52A1\u6A21\u5F0F" }),
1033
- title: "\u8BE2\u95EE\u6240\u9009\u5143\u7D20",
1034
- subtitle: "\u4E00\u6B21\u63D0\u95EE\uFF0C\u4E00\u6B21\u5E26\u6E90\u7801\u5F15\u7528\u7684\u56DE\u7B54\uFF1BSpotPatch \u4E0D\u4F1A\u5199\u5165\u4EE3\u7801\u3002",
1035
- questionLabel: "\u95EE\u9898",
1036
- questionPlaceholder: "\u8FD9\u4E2A\u7EC4\u4EF6\u662F\u505A\u4EC0\u4E48\u7684\uFF1F",
1037
- suggestionsLabel: "\u63A8\u8350\u95EE\u9898",
1053
+ mode: Object.freeze({ ask: "问答", change: "修改", label: "任务模式" }),
1054
+ title: "询问所选元素",
1055
+ subtitle: "一次提问,一次带源码引用的回答;SpotPatch 不会写入代码。",
1056
+ questionLabel: "问题",
1057
+ questionPlaceholder: "这个组件是做什么的?",
1058
+ suggestionsLabel: "推荐问题",
1038
1059
  suggestions: Object.freeze([
1039
- "\u8FD9\u4E2A\u7EC4\u4EF6\u662F\u505A\u4EC0\u4E48\u7684\uFF1F",
1040
- "\u5B83\u7684\u6570\u636E\u4ECE\u54EA\u91CC\u6765\uFF1F",
1041
- "\u4E3A\u4EC0\u4E48\u8981\u8FD9\u6837\u5B9E\u73B0\uFF1F"
1060
+ "这个组件是做什么的?",
1061
+ "它的数据从哪里来?",
1062
+ "为什么要这样实现?"
1042
1063
  ]),
1043
- executorLabel: "\u53EA\u8BFB\u6267\u884C\u5668",
1044
- modelLabel: "\u6A21\u578B",
1045
- loadingExecutors: "\u6B63\u5728\u68C0\u67E5\u53EF\u7528\u6267\u884C\u5668\u2026",
1046
- noExecutor: "\u76EE\u524D\u6CA1\u6709\u901A\u8FC7\u53EA\u8BFB\u9A8C\u8BC1\u7684\u6267\u884C\u5668\u3002",
1047
- unavailable: "\u4E0D\u53EF\u7528",
1048
- consent: "\u5141\u8BB8\u672C\u6B21\u56DE\u7B54\u5C06\u6240\u9009\u6E90\u7801\u5FEB\u7167\u53D1\u9001\u7ED9\u8BE5\u63D0\u4F9B\u5546\u3002",
1049
- dataSummary: (targets, sources) => `${String(targets)} \u4E2A\u5DF2\u9009\u5143\u7D20 \xB7 ${String(sources)} \u4E2A\u5177\u5907\u6E90\u7801\u4E0A\u4E0B\u6587`,
1050
- safety: "\u5355\u8F6E \xB7 \u53EA\u8BFB \xB7 \u4E0D\u4FDD\u5B58\u5386\u53F2",
1051
- submit: "\u63D0\u95EE",
1052
- cancel: "\u53D6\u6D88",
1053
- newQuestion: "\u91CD\u65B0\u5F00\u59CB",
1054
- copy: "\u590D\u5236\u56DE\u7B54",
1055
- convert: "\u8F6C\u6210\u4FEE\u6539\u8BF7\u6C42",
1056
- answerTitle: "\u56DE\u7B54",
1057
- sourcesTitle: "\u6E90\u7801\u5F15\u7528",
1058
- stale: "\u56DE\u7B54\u540E\u9009\u62E9\u5DF2\u53D1\u751F\u53D8\u5316\u3002\u8BF7\u91CD\u65B0\u63D0\u95EE\u540E\u518D\u91C7\u4FE1\u6B64\u56DE\u7B54\u3002",
1059
- convertedTitle: "\u6765\u81EA\u4E0A\u4E0B\u6587\u95EE\u7B54",
1060
- convertedBody: "\u56DE\u7B54\u4E0E\u6E90\u7801\u5F15\u7528\u4EC5\u4F5C\u4E3A\u672C\u5730\u4E0A\u4E0B\u6587\u4FDD\u7559\uFF0C\u5C1A\u672A\u53D1\u8D77\u4EFB\u4F55\u4FEE\u6539\u3002",
1064
+ executorLabel: "只读执行器",
1065
+ modelLabel: "模型",
1066
+ loadingExecutors: "正在检查可用执行器…",
1067
+ noExecutor: "目前没有通过只读验证的执行器。",
1068
+ unavailable: "不可用",
1069
+ consent: "允许本次回答将所选源码快照发送给该提供商。",
1070
+ dataSummary: (targets, sources) => `${String(targets)} 个已选元素 · ${String(sources)} 个具备源码上下文`,
1071
+ safety: "单轮 · 只读 · 不保存历史",
1072
+ submit: "提问",
1073
+ cancel: "取消",
1074
+ newQuestion: "重新开始",
1075
+ copy: "复制回答",
1076
+ convert: "转成修改请求",
1077
+ answerTitle: "回答",
1078
+ sourcesTitle: "源码引用",
1079
+ stale: "回答后选择已发生变化。请重新提问后再采信此回答。",
1080
+ convertedTitle: "来自上下文问答",
1081
+ convertedBody: "回答与源码引用仅作为本地上下文保留,尚未发起任何修改。",
1061
1082
  status: Object.freeze({
1062
- queued: "\u5DF2\u6392\u961F",
1063
- authorizing: "\u6B63\u5728\u9A8C\u8BC1\u53EA\u8BFB\u6743\u9650",
1064
- running: "\u6B63\u5728\u5206\u6790\u6240\u9009\u6E90\u7801",
1065
- cancelling: "\u6B63\u5728\u53D6\u6D88",
1066
- answered: "\u56DE\u7B54\u5DF2\u5C31\u7EEA",
1067
- cancelled: "\u5DF2\u53D6\u6D88",
1068
- failed: "\u65E0\u6CD5\u56DE\u7B54"
1083
+ queued: "已排队",
1084
+ authorizing: "正在验证只读权限",
1085
+ running: "正在分析所选源码",
1086
+ cancelling: "正在取消",
1087
+ answered: "回答已就绪",
1088
+ cancelled: "已取消",
1089
+ failed: "无法回答"
1069
1090
  }),
1070
- sourceLabel: (path, start, end) => `${path}:${String(start)}${end === start ? "" : `\u2013${String(end)}`}`,
1071
- readSource: (path) => `\u6B63\u5728\u8BFB\u53D6 ${path}`,
1072
- readFiles: (bucket) => `\u6B63\u5728\u8BFB\u53D6 ${bucket} \u4E2A\u6587\u4EF6`,
1073
- copied: "\u56DE\u7B54\u5DF2\u590D\u5236\u3002",
1074
- copyFailed: "\u65E0\u6CD5\u590D\u5236\u56DE\u7B54\u3002",
1075
- sourceOpenFailed: "\u65E0\u6CD5\u6253\u5F00\u6E90\u7801\u3002",
1076
- converted: "\u5DF2\u5207\u6362\u5230\u4FEE\u6539\u6A21\u5F0F\uFF0C\u5E76\u628A\u56DE\u7B54\u4F5C\u4E3A\u672C\u5730\u4E0A\u4E0B\u6587\u9644\u5E26\u3002",
1091
+ sourceLabel: (path, start, end) => `${path}:${String(start)}${end === start ? "" : `–${String(end)}`}`,
1092
+ readSource: (path) => `正在读取 ${path}`,
1093
+ readFiles: (bucket) => `正在读取 ${bucket} 个文件`,
1094
+ copied: "回答已复制。",
1095
+ copyFailed: "无法复制回答。",
1096
+ sourceOpenFailed: "无法打开源码。",
1097
+ converted: "已切换到修改模式,并把回答作为本地上下文附带。",
1077
1098
  error: (code) => {
1078
1099
  const errors = {
1079
- INVALID_TOKEN: "\u672C\u5730 SpotPatch \u4F1A\u8BDD\u5DF2\u5931\u6548\uFF0C\u8BF7\u5237\u65B0\u9875\u9762\u3002",
1080
- ORIGIN_NOT_ALLOWED: "\u672C\u5730\u8BF7\u6C42\u672A\u901A\u8FC7\u540C\u6E90\u5B89\u5168\u6821\u9A8C\uFF0C\u8BF7\u5237\u65B0\u9875\u9762\u6216\u91CD\u542F\u5F00\u53D1\u670D\u52A1\u3002",
1081
- ASK_DISABLED: "\u4E0A\u4E0B\u6587\u95EE\u7B54\u672A\u542F\u7528\u3002",
1082
- ASK_SELECTION_REQUIRED: "\u8BF7\u5148\u9009\u62E9\u81F3\u5C11\u4E00\u4E2A\u5143\u7D20\u3002",
1083
- ASK_SELECTION_STALE: "\u6240\u9009\u6E90\u7801\u5DF2\u7ECF\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u9009\u62E9\u540E\u91CD\u65B0\u63D0\u95EE\u3002",
1084
- ASK_QUESTION_INVALID: "\u8BF7\u8F93\u5165\u4E00\u4E2A\u6709\u6548\u7684\u95EE\u9898\u3002",
1085
- ASK_EXECUTOR_UNAVAILABLE: "\u6240\u9009\u53EA\u8BFB\u6267\u884C\u5668\u5F53\u524D\u4E0D\u53EF\u7528\u3002",
1086
- ASK_TIMEOUT: "\u53EA\u8BFB\u6267\u884C\u5668\u672A\u80FD\u5728\u9650\u5B9A\u65F6\u95F4\u5185\u8FD4\u56DE\u56DE\u7B54\uFF0C\u8BF7\u91CD\u65B0\u63D0\u95EE\u3002",
1087
- ASK_CONSENT_REQUIRED: "\u8BF7\u786E\u8BA4\u672C\u6B21\u56DE\u7B54\u7684\u63D0\u4F9B\u5546\u6570\u636E\u8BBF\u95EE\u3002",
1088
- ASK_BUSY: "\u5DE5\u4F5C\u533A\u6B63\u5728\u6267\u884C\u5176\u4ED6\u4EFB\u52A1\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
1089
- ASK_CANCELLED: "\u672C\u6B21\u63D0\u95EE\u5DF2\u53D6\u6D88\u3002",
1090
- ASK_RESULT_EXPIRED: "\u56DE\u7B54\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u63D0\u95EE\u3002",
1091
- ASK_WRITE_ATTEMPTED: "\u6267\u884C\u5668\u5C1D\u8BD5\u5199\u5165\uFF0C\u5DF2\u88AB\u963B\u6B62\u3002",
1092
- ASK_ANSWER_INVALID: "\u6267\u884C\u5668\u8FD4\u56DE\u7684\u5E26\u5F15\u7528\u56DE\u7B54\u65E0\u6548\u3002",
1093
- ASK_PROTOCOL_INCOMPATIBLE: "\u6267\u884C\u5668\u534F\u8BAE\u4E0D\u517C\u5BB9\u3002",
1094
- ASK_LIMIT_EXCEEDED: "\u6240\u9009\u4E0A\u4E0B\u6587\u8FC7\u5927\u3002",
1095
- ASK_SOURCE_SCOPE_DENIED: "\u8BF7\u6C42\u7684\u6E90\u7801\u8D85\u51FA\u4E86\u6388\u6743\u5FEB\u7167\u8303\u56F4\u3002"
1100
+ INVALID_TOKEN: "本地 SpotPatch 会话已失效,请刷新页面。",
1101
+ ORIGIN_NOT_ALLOWED: "本地请求未通过同源安全校验,请刷新页面或重启开发服务。",
1102
+ ASK_DISABLED: "上下文问答未启用。",
1103
+ ASK_SELECTION_REQUIRED: "请先选择至少一个元素。",
1104
+ ASK_SELECTION_STALE: "所选源码已经变化,请刷新选择后重新提问。",
1105
+ ASK_QUESTION_INVALID: "请输入一个有效的问题。",
1106
+ ASK_EXECUTOR_UNAVAILABLE: "所选只读执行器当前不可用。",
1107
+ ASK_TIMEOUT: "只读执行器未能在限定时间内返回回答,请重新提问。",
1108
+ ASK_CONSENT_REQUIRED: "请确认本次回答的提供商数据访问。",
1109
+ ASK_BUSY: "工作区正在执行其他任务,请稍后重试。",
1110
+ ASK_CANCELLED: "本次提问已取消。",
1111
+ ASK_RESULT_EXPIRED: "回答已过期,请重新提问。",
1112
+ ASK_WRITE_ATTEMPTED: "执行器尝试写入,已被阻止。",
1113
+ ASK_ANSWER_INVALID: "执行器返回的带引用回答无效。",
1114
+ ASK_PROTOCOL_INCOMPATIBLE: "执行器协议不兼容。",
1115
+ ASK_LIMIT_EXCEEDED: "所选上下文过大。",
1116
+ ASK_SOURCE_SCOPE_DENIED: "请求的源码超出了授权快照范围。"
1096
1117
  };
1097
- return code === void 0 ? "SpotPatch \u672A\u80FD\u5B8C\u6210\u672C\u6B21\u63D0\u95EE\u3002" : errors[code] ?? "SpotPatch \u672A\u80FD\u5B8C\u6210\u672C\u6B21\u63D0\u95EE\u3002";
1118
+ return code === void 0 ? "SpotPatch 未能完成本次提问。" : errors[code] ?? "SpotPatch 未能完成本次提问。";
1098
1119
  },
1099
- warning: (code) => code === "insufficient-evidence" ? "\u6240\u9009\u6E90\u7801\u4E0D\u8DB3\u4EE5\u652F\u6301\u5B8C\u6574\u56DE\u7B54\u3002" : "\u90E8\u5206\u6E90\u7801\u5F15\u7528\u56E0\u65E0\u6CD5\u9A8C\u8BC1\u800C\u88AB\u7701\u7565\u3002"
1120
+ warning: (code) => code === "insufficient-evidence" ? "所选源码不足以支持完整回答。" : "部分源码引用因无法验证而被省略。"
1100
1121
  });
1101
1122
  function contextualAskMessages(locale) {
1102
1123
  return locale === "zh-CN" ? ZH : EN;
@@ -1106,6 +1127,7 @@ function contextualAskMessages(locale) {
1106
1127
  function createStyles(document) {
1107
1128
  const style = document.createElement("style");
1108
1129
  style.textContent = `
1130
+ ${SELECT_PICKER_STYLES}
1109
1131
  .spotpatch-ask-mode { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-bottom: 14px; padding: 4px; border: 1px solid var(--spotpatch-border-subtle); border-radius: 10px; background: #0a0a0e; }
1110
1132
  .spotpatch-ask-mode button { min-height: 34px; border: 0; border-radius: 7px; color: var(--spotpatch-text-secondary); background: transparent; cursor: pointer; font-size: 12px; font-weight: 700; }
1111
1133
  .spotpatch-ask-mode button[aria-selected="true"] { color: #f7f7ff; background: linear-gradient(135deg, rgb(139 123 255 / 25%), rgb(82 168 255 / 14%)); box-shadow: inset 0 0 0 1px rgb(139 123 255 / 24%); }
@@ -1118,20 +1140,6 @@ function createStyles(document) {
1118
1140
  .spotpatch-ask-suggestions { display: flex; flex-wrap: wrap; gap: 6px; }
1119
1141
  .spotpatch-ask-suggestions button { border: 1px solid var(--spotpatch-border-subtle); border-radius: 999px; padding: 5px 9px; color: var(--spotpatch-text-secondary); background: rgb(255 255 255 / 2%); cursor: pointer; font-size: 10.5px; }
1120
1142
  .spotpatch-ask-suggestions button:hover { border-color: rgb(139 123 255 / 42%); color: #fff; }
1121
- .spotpatch-ask-executor-picker { display: grid; gap: 5px; min-width: 0; }
1122
- .spotpatch-ask-executor { box-sizing: border-box; display: flex; width: 100%; min-height: 38px; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid var(--spotpatch-border); border-radius: 9px; padding: 0 11px; overflow: hidden; color: var(--spotpatch-text); background: var(--spotpatch-bg-input); cursor: pointer; font: inherit; outline: none; text-align: left; }
1123
- .spotpatch-ask-executor > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1124
- .spotpatch-ask-executor[data-expandable="true"]::after { width: 7px; height: 7px; flex: 0 0 auto; border-right: 1.5px solid #8f96a3; border-bottom: 1.5px solid #8f96a3; content: ""; transform: translateY(-2px) rotate(45deg); }
1125
- .spotpatch-ask-executor[aria-expanded="true"]::after { transform: translateY(2px) rotate(225deg); }
1126
- .spotpatch-ask-executor:focus-visible { border-color: rgb(139 123 255 / 68%); box-shadow: 0 0 0 3px rgb(139 123 255 / 12%); }
1127
- .spotpatch-ask-executor:disabled { color: var(--spotpatch-text); background: rgb(255 255 255 / 3%); cursor: default; opacity: 1; }
1128
- .spotpatch-ask-executor[data-empty="true"]:disabled { color: var(--spotpatch-text-muted); }
1129
- .spotpatch-ask-executor-menu { display: grid; gap: 3px; border: 1px solid var(--spotpatch-border); border-radius: 9px; padding: 4px; background: var(--spotpatch-bg-input); max-height: 224px; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; box-shadow: 0 10px 28px rgb(0 0 0 / 28%); }
1130
- .spotpatch-ask-executor-menu[hidden] { display: none; }
1131
- .spotpatch-ask-executor-option { box-sizing: border-box; min-height: 34px; border: 0; border-radius: 6px; padding: 7px 9px; color: var(--spotpatch-text-secondary); background: transparent; cursor: pointer; font: inherit; text-align: left; overflow-wrap: anywhere; }
1132
- .spotpatch-ask-executor-option[data-active="true"], .spotpatch-ask-executor-option:hover { color: #fff; background: rgb(139 123 255 / 13%); outline: none; }
1133
- .spotpatch-ask-executor-option[aria-selected="true"] { color: #f4f1ff; background: rgb(139 123 255 / 20%); }
1134
- .spotpatch-ask-executor-native { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; border: 0; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
1135
1143
  .spotpatch-ask-executor-status { margin: 0; border-left: 2px solid var(--spotpatch-warning); padding-left: 8px; color: #d8b66c; font-size: 10.5px; line-height: 1.45; }
1136
1144
  .spotpatch-ask-safety { display: grid; gap: 8px; border: 1px solid rgb(82 168 255 / 16%); border-radius: 10px; padding: 10px 11px; background: rgb(82 168 255 / 4%); }
1137
1145
  .spotpatch-ask-data { display: flex; justify-content: space-between; gap: 10px; color: #aab3c2; font-size: 10.5px; }
@@ -1230,13 +1238,13 @@ function createContextualAskPanel(input) {
1230
1238
  const executorField = createMarkedElement(document, "div");
1231
1239
  executorField.className = "spotpatch-ask-field";
1232
1240
  const executorLabel = createMarkedElement(document, "label");
1233
- const executorPicker = createAskPicker(document, input.onViewChange);
1241
+ const executorPicker = createSelectPicker(document, input.onViewChange);
1234
1242
  const executorSelect = executorPicker.select;
1235
1243
  executorLabel.htmlFor = executorPicker.trigger.id;
1236
1244
  const modelField = createMarkedElement(document, "div");
1237
1245
  modelField.className = "spotpatch-ask-field";
1238
1246
  const modelLabel = createMarkedElement(document, "label");
1239
- const modelPicker = createAskPicker(document, input.onViewChange);
1247
+ const modelPicker = createSelectPicker(document, input.onViewChange);
1240
1248
  modelLabel.htmlFor = modelPicker.trigger.id;
1241
1249
  modelField.append(modelLabel, modelPicker.root);
1242
1250
  let modelExecutorId;
@@ -1440,7 +1448,7 @@ function createContextualAskPanel(input) {
1440
1448
  for (const executor of readyExecutors) {
1441
1449
  const option = document.createElement("option");
1442
1450
  option.value = executor.executorId;
1443
- option.textContent = executor.models === void 0 ? `${executor.label} \xB7 ${executor.effectiveModelLabel}` : executor.label;
1451
+ option.textContent = executor.models === void 0 ? `${executor.label} · ${executor.effectiveModelLabel}` : executor.label;
1444
1452
  executorSelect.append(option);
1445
1453
  }
1446
1454
  if (readyExecutors.length === 0) {
@@ -1477,7 +1485,7 @@ function createContextualAskPanel(input) {
1477
1485
  expandedHeadline: messages.status[snapshot.status],
1478
1486
  action: messages.safety,
1479
1487
  expandedAction: messages.safety,
1480
- meta: `${snapshot.executor.label} \xB7 ${snapshot.executor.modelLabel}`,
1488
+ meta: `${snapshot.executor.label} · ${snapshot.executor.modelLabel}`,
1481
1489
  recentActivities: Object.freeze([]),
1482
1490
  startedAt: snapshot.createdAt
1483
1491
  });
@@ -1504,7 +1512,7 @@ function createContextualAskPanel(input) {
1504
1512
  expandedHeadline: messages.status.running,
1505
1513
  action: activityLabel,
1506
1514
  expandedAction: activityLabel,
1507
- meta: `${currentJob.executor.label} \xB7 ${currentJob.executor.modelLabel}`,
1515
+ meta: `${currentJob.executor.label} · ${currentJob.executor.modelLabel}`,
1508
1516
  activity: {
1509
1517
  key: `ask:${String(event.sequence)}`,
1510
1518
  kind: "read",