@youtyan/code-viewer 0.11.1 → 0.12.0
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/README.md +14 -1
- package/dist/code-viewer.js +2290 -1116
- package/package.json +1 -1
- package/web/app.js +1420 -553
- package/web/style.css +172 -8
package/web/app.js
CHANGED
|
@@ -5,6 +5,311 @@
|
|
|
5
5
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
// web-src/core/terminal-paste.ts
|
|
9
|
+
var PASTE_IMAGE_TYPES = {
|
|
10
|
+
"image/png": "png",
|
|
11
|
+
"image/jpeg": "jpg",
|
|
12
|
+
"image/gif": "gif",
|
|
13
|
+
"image/webp": "webp"
|
|
14
|
+
};
|
|
15
|
+
var MAX_PASTE_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
16
|
+
var MAX_PASTE_BODY_BYTES = Math.ceil(MAX_PASTE_IMAGE_BYTES * 1.4);
|
|
17
|
+
var SHIFT_ENTER_SEQUENCE = `${String.fromCharCode(27)}[200~${String.fromCharCode(10)}${String.fromCharCode(27)}[201~`;
|
|
18
|
+
function isShiftEnter(event) {
|
|
19
|
+
return event.key === "Enter" && event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// web-src/core/terminal-images.ts
|
|
23
|
+
var TERMINAL_IMAGE_EXTENSIONS = [
|
|
24
|
+
...new Set(Object.values(PASTE_IMAGE_TYPES)),
|
|
25
|
+
"jpeg"
|
|
26
|
+
];
|
|
27
|
+
var MAX_TERMINAL_IMAGE_PATHS = 64;
|
|
28
|
+
var MAX_TERMINAL_IMAGE_QUERY = 16;
|
|
29
|
+
var MAX_IMAGE_PATH_LENGTH = 1024;
|
|
30
|
+
var ESC = String.fromCharCode(27);
|
|
31
|
+
var BEL = String.fromCharCode(7);
|
|
32
|
+
var ANSI_RE = new RegExp(
|
|
33
|
+
`${ESC}\\[[0-9;?]*[ -/]*[@-~]|${ESC}\\][^${BEL}${ESC}]*(?:${BEL}|${ESC}\\\\)|${ESC}[@-Z\\\\-_]`,
|
|
34
|
+
"g"
|
|
35
|
+
);
|
|
36
|
+
function stripAnsi(text3) {
|
|
37
|
+
return text3.replace(ANSI_RE, "");
|
|
38
|
+
}
|
|
39
|
+
var PATH_CHAR = "[\\p{L}\\p{N}._~+@%/-]";
|
|
40
|
+
var NAME_CHAR = "[\\p{L}\\p{N}_~+@%-]";
|
|
41
|
+
var IMAGE_PATH_RE = new RegExp(
|
|
42
|
+
`${PATH_CHAR}*${NAME_CHAR}\\.(?:${TERMINAL_IMAGE_EXTENSIONS.join("|")})(?![\\p{L}\\p{N}])`,
|
|
43
|
+
"giu"
|
|
44
|
+
);
|
|
45
|
+
function looksLikeUrl(value) {
|
|
46
|
+
return value.startsWith("//") || value.includes("://");
|
|
47
|
+
}
|
|
48
|
+
function findImagePaths(text3, limit = MAX_TERMINAL_IMAGE_PATHS) {
|
|
49
|
+
const found = [];
|
|
50
|
+
const seen = /* @__PURE__ */ new Set();
|
|
51
|
+
for (const match2 of text3.matchAll(IMAGE_PATH_RE)) {
|
|
52
|
+
const path = match2[0];
|
|
53
|
+
if (path.length > MAX_IMAGE_PATH_LENGTH) continue;
|
|
54
|
+
if (looksLikeUrl(path)) continue;
|
|
55
|
+
if (seen.has(path)) continue;
|
|
56
|
+
seen.add(path);
|
|
57
|
+
found.push(path);
|
|
58
|
+
if (found.length >= limit) break;
|
|
59
|
+
}
|
|
60
|
+
return found;
|
|
61
|
+
}
|
|
62
|
+
function joinWrappedLines(text3, width) {
|
|
63
|
+
if (!Number.isFinite(width) || width <= 0) return text3;
|
|
64
|
+
const lines = text3.split("\n");
|
|
65
|
+
const joined = [];
|
|
66
|
+
let current = null;
|
|
67
|
+
let previousLength = 0;
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
if (current === null) {
|
|
70
|
+
current = line;
|
|
71
|
+
} else if (previousLength >= width) {
|
|
72
|
+
current += line;
|
|
73
|
+
} else {
|
|
74
|
+
joined.push(current);
|
|
75
|
+
current = line;
|
|
76
|
+
}
|
|
77
|
+
previousLength = line.length;
|
|
78
|
+
}
|
|
79
|
+
if (current !== null) joined.push(current);
|
|
80
|
+
return joined.join("\n");
|
|
81
|
+
}
|
|
82
|
+
function joinBrokenPathLines(text3) {
|
|
83
|
+
const lines = text3.split("\n");
|
|
84
|
+
const candidates = [];
|
|
85
|
+
for (let i2 = 0; i2 + 1 < lines.length; i2 += 1) {
|
|
86
|
+
const head = lastPathFragment(lines[i2] ?? "");
|
|
87
|
+
const tail = firstWord(lines[i2 + 1] ?? "");
|
|
88
|
+
if (!head || !tail) continue;
|
|
89
|
+
candidates.push(head.text + tail);
|
|
90
|
+
}
|
|
91
|
+
return candidates.join("\n");
|
|
92
|
+
}
|
|
93
|
+
function lastPathFragment(line) {
|
|
94
|
+
let found = null;
|
|
95
|
+
const words = /\S+/g;
|
|
96
|
+
for (const match2 of line.matchAll(words)) {
|
|
97
|
+
if (match2[0].includes("/")) {
|
|
98
|
+
found = { text: match2[0], index: match2.index ?? 0 };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return found;
|
|
102
|
+
}
|
|
103
|
+
function firstWord(line) {
|
|
104
|
+
return /\S+/.exec(line)?.[0] ?? "";
|
|
105
|
+
}
|
|
106
|
+
function findImagePathsInText(plain, width = 0, limit = MAX_TERMINAL_IMAGE_PATHS) {
|
|
107
|
+
const merged = [];
|
|
108
|
+
const seen = /* @__PURE__ */ new Set();
|
|
109
|
+
const sources = [
|
|
110
|
+
plain,
|
|
111
|
+
width > 0 ? joinWrappedLines(plain, width) : "",
|
|
112
|
+
joinBrokenPathLines(plain)
|
|
113
|
+
];
|
|
114
|
+
for (const source of sources) {
|
|
115
|
+
if (!source) continue;
|
|
116
|
+
for (const path of findImagePaths(source, limit)) {
|
|
117
|
+
if (seen.has(path)) continue;
|
|
118
|
+
seen.add(path);
|
|
119
|
+
merged.push(path);
|
|
120
|
+
if (merged.length >= limit) return merged;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return merged;
|
|
124
|
+
}
|
|
125
|
+
function findPathAnchors(lines, candidates) {
|
|
126
|
+
const anchors = [];
|
|
127
|
+
for (const candidate of candidates) {
|
|
128
|
+
if (!candidate) continue;
|
|
129
|
+
const anchor = firstAnchor(lines, candidate);
|
|
130
|
+
if (anchor) anchors.push(anchor);
|
|
131
|
+
}
|
|
132
|
+
return anchors;
|
|
133
|
+
}
|
|
134
|
+
function firstAnchor(lines, candidate) {
|
|
135
|
+
for (let row = 0; row < lines.length; row += 1) {
|
|
136
|
+
const line = lines[row] ?? "";
|
|
137
|
+
const direct = line.indexOf(candidate);
|
|
138
|
+
if (direct >= 0) return { candidate, row, col: direct, span: 1 };
|
|
139
|
+
const head = lastPathFragment(line);
|
|
140
|
+
if (!head) continue;
|
|
141
|
+
const tail = firstWord(lines[row + 1] ?? "");
|
|
142
|
+
if (tail && head.text + tail === candidate) {
|
|
143
|
+
return { candidate, row, col: head.index, span: 2 };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// web-src/core/agent-screen.ts
|
|
150
|
+
var AGENT_SCREEN_RULE_SET_VERSION = 1;
|
|
151
|
+
var ESC2 = String.fromCharCode(27);
|
|
152
|
+
var BEL2 = String.fromCharCode(7);
|
|
153
|
+
var OSC_TITLE_RE = new RegExp(
|
|
154
|
+
`${ESC2}\\](?:0|2);([^${BEL2}${ESC2}]*)(?:${BEL2}|${ESC2}\\\\)`,
|
|
155
|
+
"g"
|
|
156
|
+
);
|
|
157
|
+
var BLOCKING_HINTS = [
|
|
158
|
+
{ contains: ["enter to confirm"] },
|
|
159
|
+
{ contains: ["enter to select"] },
|
|
160
|
+
{ contains: ["enter to submit"] },
|
|
161
|
+
{ contains: ["allow command?"] },
|
|
162
|
+
{ contains: ["[y/n]"] },
|
|
163
|
+
{ contains: ["yes (y)"] },
|
|
164
|
+
{ contains: ["do you want to proceed?"] }
|
|
165
|
+
];
|
|
166
|
+
var DEFAULT_AGENT_SCREEN_RULES = {
|
|
167
|
+
version: AGENT_SCREEN_RULE_SET_VERSION,
|
|
168
|
+
rules: [
|
|
169
|
+
{
|
|
170
|
+
id: "title_requires_input",
|
|
171
|
+
state: "waiting",
|
|
172
|
+
priority: 1100,
|
|
173
|
+
region: "osc_title",
|
|
174
|
+
contains: ["action required"]
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: "title_spinner",
|
|
178
|
+
state: "working",
|
|
179
|
+
priority: 1050,
|
|
180
|
+
region: "osc_title",
|
|
181
|
+
regex: ["^[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]"]
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
id: "transcript_view",
|
|
185
|
+
state: "skip",
|
|
186
|
+
priority: 1e3,
|
|
187
|
+
region: "bottom_non_empty",
|
|
188
|
+
lines: 8,
|
|
189
|
+
any: [
|
|
190
|
+
{ contains: ["showing detailed transcript"] },
|
|
191
|
+
{ contains: ["pgup/pgdn", "home/end to jump", "q to quit"] }
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: "interactive_form",
|
|
196
|
+
state: "waiting",
|
|
197
|
+
priority: 980,
|
|
198
|
+
region: "bottom_non_empty",
|
|
199
|
+
lines: 14,
|
|
200
|
+
contains: ["esc to cancel"],
|
|
201
|
+
any: [
|
|
202
|
+
{ contains: ["enter to confirm"] },
|
|
203
|
+
{ contains: ["enter to select"] },
|
|
204
|
+
{ contains: ["enter to submit"] }
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
id: "live_reasoning",
|
|
209
|
+
state: "working",
|
|
210
|
+
priority: 970,
|
|
211
|
+
region: "bottom_non_empty",
|
|
212
|
+
lines: 8,
|
|
213
|
+
contains: ["tokens", "thinking"],
|
|
214
|
+
any: [{ lineRegex: ["^\\s*[✢✻✽✶✳]"] }, { lineRegex: ["(?i)^\\s*·"] }]
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
id: "prompt_box",
|
|
218
|
+
state: "idle",
|
|
219
|
+
priority: 950,
|
|
220
|
+
region: "bottom_non_empty",
|
|
221
|
+
lines: 8,
|
|
222
|
+
lineRegex: ["^\\s*❯"],
|
|
223
|
+
not: [
|
|
224
|
+
...BLOCKING_HINTS,
|
|
225
|
+
{ contains: ["esc to cancel"] },
|
|
226
|
+
{ contains: ["arrow keys"] },
|
|
227
|
+
{ contains: ["↑/↓ to navigate"] }
|
|
228
|
+
]
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
id: "strong_input_request",
|
|
232
|
+
state: "waiting",
|
|
233
|
+
priority: 900,
|
|
234
|
+
region: "bottom_non_empty",
|
|
235
|
+
lines: 12,
|
|
236
|
+
any: [
|
|
237
|
+
{ contains: ["press enter to confirm or esc to cancel"] },
|
|
238
|
+
{ contains: ["enter to submit answer"] },
|
|
239
|
+
{ contains: ["enter to submit all"] },
|
|
240
|
+
{ contains: ["allow command?"] }
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: "permission_request",
|
|
245
|
+
state: "waiting",
|
|
246
|
+
priority: 850,
|
|
247
|
+
region: "bottom_non_empty",
|
|
248
|
+
lines: 14,
|
|
249
|
+
contains: ["do you want to proceed?"],
|
|
250
|
+
any: [
|
|
251
|
+
{ lineRegex: ["(?i)^\\P{L}*yes\\b"] },
|
|
252
|
+
{ lineRegex: ["(?i)^\\P{L}*no\\b"] }
|
|
253
|
+
]
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
id: "weak_input_request",
|
|
257
|
+
state: "waiting",
|
|
258
|
+
priority: 600,
|
|
259
|
+
region: "bottom_non_empty",
|
|
260
|
+
lines: 8,
|
|
261
|
+
any: [
|
|
262
|
+
{ contains: ["[y/n]"] },
|
|
263
|
+
{ contains: ["yes (y)"] },
|
|
264
|
+
{
|
|
265
|
+
any: [
|
|
266
|
+
{ contains: ["do you want to"] },
|
|
267
|
+
{ contains: ["would you like to"] }
|
|
268
|
+
],
|
|
269
|
+
all: [{ any: [{ contains: ["yes"] }, { contains: ["❯"] }] }]
|
|
270
|
+
}
|
|
271
|
+
]
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
id: "live_working_status",
|
|
275
|
+
state: "working",
|
|
276
|
+
priority: 500,
|
|
277
|
+
region: "bottom_non_empty",
|
|
278
|
+
lines: 3,
|
|
279
|
+
lineRegex: ["^[•◦]\\s+Working"],
|
|
280
|
+
not: [{ contains: ["conversation interrupted"] }]
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
id: "last_prompt",
|
|
284
|
+
state: "idle",
|
|
285
|
+
priority: 400,
|
|
286
|
+
region: "last_non_empty",
|
|
287
|
+
lineRegex: ["^\\s*[›❯]"],
|
|
288
|
+
not: BLOCKING_HINTS
|
|
289
|
+
}
|
|
290
|
+
]
|
|
291
|
+
};
|
|
292
|
+
var MATCHER_KEYS = /* @__PURE__ */ new Set([
|
|
293
|
+
"contains",
|
|
294
|
+
"regex",
|
|
295
|
+
"lineRegex",
|
|
296
|
+
"all",
|
|
297
|
+
"any",
|
|
298
|
+
"not"
|
|
299
|
+
]);
|
|
300
|
+
var RULE_KEYS = /* @__PURE__ */ new Set([
|
|
301
|
+
...MATCHER_KEYS,
|
|
302
|
+
"id",
|
|
303
|
+
"state",
|
|
304
|
+
"priority",
|
|
305
|
+
"region",
|
|
306
|
+
"lines"
|
|
307
|
+
]);
|
|
308
|
+
function formatAgentScreenRuleSet(rules) {
|
|
309
|
+
return `${JSON.stringify(rules, null, 2)}
|
|
310
|
+
`;
|
|
311
|
+
}
|
|
312
|
+
|
|
8
313
|
// web-src/core/diff-file-kinds.ts
|
|
9
314
|
var HEAVY_SIZE_CLASSES = /* @__PURE__ */ new Set(["medium", "large", "huge"]);
|
|
10
315
|
function classifyDiffFileKind(file) {
|
|
@@ -277,6 +582,191 @@ ${lines.join("\n")}
|
|
|
277
582
|
};
|
|
278
583
|
}
|
|
279
584
|
|
|
585
|
+
// web-src/core/error-detail.ts
|
|
586
|
+
function errorWithCause(message, cause) {
|
|
587
|
+
return Object.assign(new Error(message), { cause });
|
|
588
|
+
}
|
|
589
|
+
function errorWithCauses(message, errors2) {
|
|
590
|
+
return Object.assign(new Error(message), { errors: [...errors2] });
|
|
591
|
+
}
|
|
592
|
+
var OMIT_VALUE = /* @__PURE__ */ Symbol("omit-sensitive-error-field");
|
|
593
|
+
function isSensitiveFieldName(key) {
|
|
594
|
+
const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
595
|
+
return normalized === "auth" || normalized.endsWith("auth") || normalized.startsWith("auth") && !normalized.startsWith("author") || normalized.includes("authorization") || normalized.includes("cookie") || normalized.includes("token") || normalized.includes("password") || normalized.includes("passwd") || normalized.includes("secret") || normalized.includes("credential") || normalized.includes("apikey") || normalized.includes("privatekey");
|
|
596
|
+
}
|
|
597
|
+
function errorName(error2) {
|
|
598
|
+
try {
|
|
599
|
+
return typeof error2.name === "string" ? error2.name : "Error";
|
|
600
|
+
} catch {
|
|
601
|
+
return "Error";
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function errorMessage(error2) {
|
|
605
|
+
try {
|
|
606
|
+
return typeof error2.message === "string" ? error2.message : "unable to read error message";
|
|
607
|
+
} catch {
|
|
608
|
+
return "unable to read error message";
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
function sanitizeObjectFields(value, ancestors, excludedKeys = /* @__PURE__ */ new Set()) {
|
|
612
|
+
let keys;
|
|
613
|
+
try {
|
|
614
|
+
keys = Object.getOwnPropertyNames(value);
|
|
615
|
+
} catch {
|
|
616
|
+
return { value: "[Unserializable object]", removedSensitive: false };
|
|
617
|
+
}
|
|
618
|
+
const output = /* @__PURE__ */ Object.create(null);
|
|
619
|
+
let removedSensitive = false;
|
|
620
|
+
for (const key of keys) {
|
|
621
|
+
if (excludedKeys.has(key)) continue;
|
|
622
|
+
if (isSensitiveFieldName(key)) {
|
|
623
|
+
removedSensitive = true;
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
let descriptor;
|
|
627
|
+
try {
|
|
628
|
+
descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
629
|
+
} catch {
|
|
630
|
+
output[key] = "[Unserializable field]";
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
if (!descriptor) continue;
|
|
634
|
+
if (!("value" in descriptor)) {
|
|
635
|
+
output[key] = "[Accessor]";
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
const sanitized = sanitizeValue(descriptor.value, ancestors);
|
|
639
|
+
if (sanitized === OMIT_VALUE) {
|
|
640
|
+
removedSensitive = true;
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
output[key] = sanitized.value;
|
|
644
|
+
removedSensitive ||= sanitized.removedSensitive;
|
|
645
|
+
}
|
|
646
|
+
if (Object.keys(output).length === 0 && removedSensitive) return OMIT_VALUE;
|
|
647
|
+
return { value: output, removedSensitive };
|
|
648
|
+
}
|
|
649
|
+
function sanitizeError(error2, ancestors) {
|
|
650
|
+
const output = /* @__PURE__ */ Object.create(null);
|
|
651
|
+
output.name = errorName(error2);
|
|
652
|
+
output.message = errorMessage(error2);
|
|
653
|
+
const fields = sanitizeObjectFields(
|
|
654
|
+
error2,
|
|
655
|
+
ancestors,
|
|
656
|
+
/* @__PURE__ */ new Set(["name", "message", "stack"])
|
|
657
|
+
);
|
|
658
|
+
if (fields !== OMIT_VALUE) Object.assign(output, fields.value);
|
|
659
|
+
return {
|
|
660
|
+
value: output,
|
|
661
|
+
removedSensitive: fields === OMIT_VALUE ? true : fields.removedSensitive
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function sanitizeValue(value, ancestors) {
|
|
665
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
666
|
+
return { value, removedSensitive: false };
|
|
667
|
+
if (typeof value === "number") {
|
|
668
|
+
return {
|
|
669
|
+
value: Number.isFinite(value) ? value : String(value),
|
|
670
|
+
removedSensitive: false
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
if (typeof value === "bigint") {
|
|
674
|
+
return { value: `${value}n`, removedSensitive: false };
|
|
675
|
+
}
|
|
676
|
+
if (typeof value === "undefined") {
|
|
677
|
+
return { value: "[undefined]", removedSensitive: false };
|
|
678
|
+
}
|
|
679
|
+
if (typeof value === "symbol") {
|
|
680
|
+
return { value: "[symbol]", removedSensitive: false };
|
|
681
|
+
}
|
|
682
|
+
if (typeof value === "function") {
|
|
683
|
+
return { value: "[function]", removedSensitive: false };
|
|
684
|
+
}
|
|
685
|
+
const objectValue = value;
|
|
686
|
+
if (ancestors.has(objectValue)) {
|
|
687
|
+
return { value: "[Circular]", removedSensitive: false };
|
|
688
|
+
}
|
|
689
|
+
ancestors.add(objectValue);
|
|
690
|
+
try {
|
|
691
|
+
if (Array.isArray(objectValue)) {
|
|
692
|
+
const output = [];
|
|
693
|
+
let removedSensitive = false;
|
|
694
|
+
for (const item of objectValue) {
|
|
695
|
+
const sanitized = sanitizeValue(item, ancestors);
|
|
696
|
+
if (sanitized === OMIT_VALUE) {
|
|
697
|
+
removedSensitive = true;
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
output.push(sanitized.value);
|
|
701
|
+
removedSensitive ||= sanitized.removedSensitive;
|
|
702
|
+
}
|
|
703
|
+
if (output.length === 0 && removedSensitive) return OMIT_VALUE;
|
|
704
|
+
return { value: output, removedSensitive };
|
|
705
|
+
}
|
|
706
|
+
if (objectValue instanceof Error)
|
|
707
|
+
return sanitizeError(objectValue, ancestors);
|
|
708
|
+
return sanitizeObjectFields(objectValue, ancestors);
|
|
709
|
+
} catch {
|
|
710
|
+
return { value: "[Unserializable object]", removedSensitive: false };
|
|
711
|
+
} finally {
|
|
712
|
+
ancestors.delete(objectValue);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function formatNonError(value) {
|
|
716
|
+
if (typeof value === "string") return value;
|
|
717
|
+
try {
|
|
718
|
+
const sanitized = sanitizeValue(value, /* @__PURE__ */ new Set());
|
|
719
|
+
const serializable = sanitized === OMIT_VALUE ? {} : sanitized.value;
|
|
720
|
+
return JSON.stringify(serializable);
|
|
721
|
+
} catch {
|
|
722
|
+
return "[Unserializable value]";
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
function formatErrorFields(error2) {
|
|
726
|
+
const fields = sanitizeObjectFields(
|
|
727
|
+
error2,
|
|
728
|
+
/* @__PURE__ */ new Set([error2]),
|
|
729
|
+
/* @__PURE__ */ new Set(["name", "message", "stack", "cause"])
|
|
730
|
+
);
|
|
731
|
+
if (fields === OMIT_VALUE) return "";
|
|
732
|
+
const output = fields.value;
|
|
733
|
+
return Object.keys(output).length > 0 ? `
|
|
734
|
+
Details: ${JSON.stringify(output)}` : "";
|
|
735
|
+
}
|
|
736
|
+
function formatErrorDetail(error2) {
|
|
737
|
+
const parts = [];
|
|
738
|
+
const seen = /* @__PURE__ */ new Set();
|
|
739
|
+
let current = error2;
|
|
740
|
+
while (current instanceof Error && !seen.has(current)) {
|
|
741
|
+
seen.add(current);
|
|
742
|
+
parts.push(
|
|
743
|
+
`${errorName(current)}: ${errorMessage(current)}${formatErrorFields(current)}`
|
|
744
|
+
);
|
|
745
|
+
try {
|
|
746
|
+
current = current.cause;
|
|
747
|
+
} catch {
|
|
748
|
+
current = "[Unserializable error cause]";
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
if (current !== void 0) {
|
|
752
|
+
parts.push(
|
|
753
|
+
seen.has(current) ? "Error cause cycle detected" : formatNonError(current)
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
return parts.join("\nCaused by: ") || formatNonError(error2);
|
|
757
|
+
}
|
|
758
|
+
async function responseErrorMessage(response, operation) {
|
|
759
|
+
const statusText = response.statusText ? ` ${response.statusText}` : "";
|
|
760
|
+
const prefix = `${operation} (HTTP ${response.status}${statusText})`;
|
|
761
|
+
let body;
|
|
762
|
+
try {
|
|
763
|
+
body = await response.text();
|
|
764
|
+
} catch (error2) {
|
|
765
|
+
throw errorWithCause(`${prefix}: failed to read response body`, error2);
|
|
766
|
+
}
|
|
767
|
+
return body ? `${prefix}: ${body}` : prefix;
|
|
768
|
+
}
|
|
769
|
+
|
|
280
770
|
// web-src/core/expand-logic.ts
|
|
281
771
|
function initExpandState(prevHunkEndNew, hunkNewStart) {
|
|
282
772
|
return {
|
|
@@ -8381,6 +8871,22 @@ ${lines.join("\n")}
|
|
|
8381
8871
|
md.core.ruler.after("inline", "footnote_tail", footnote_tail);
|
|
8382
8872
|
}
|
|
8383
8873
|
|
|
8874
|
+
// web-src/core/lazy-bundle.ts
|
|
8875
|
+
function createBundleLoader(bundleFile, init) {
|
|
8876
|
+
let pending = null;
|
|
8877
|
+
return () => {
|
|
8878
|
+
if (!pending) {
|
|
8879
|
+
pending = import(
|
|
8880
|
+
/* @vite-ignore */
|
|
8881
|
+
`/${bundleFile}`
|
|
8882
|
+
).then(
|
|
8883
|
+
(mod) => init ? init(mod) : mod
|
|
8884
|
+
);
|
|
8885
|
+
}
|
|
8886
|
+
return pending;
|
|
8887
|
+
};
|
|
8888
|
+
}
|
|
8889
|
+
|
|
8384
8890
|
// web-src/core/mermaid-loader.ts
|
|
8385
8891
|
var DEFAULT_CONFIG = {
|
|
8386
8892
|
startOnLoad: false,
|
|
@@ -8388,18 +8894,19 @@ ${lines.join("\n")}
|
|
|
8388
8894
|
theme: "default",
|
|
8389
8895
|
er: { useMaxWidth: false }
|
|
8390
8896
|
};
|
|
8897
|
+
var loadMermaidModule = createBundleLoader("mermaid.js");
|
|
8391
8898
|
var mermaidPromise = null;
|
|
8392
8899
|
var initialized = false;
|
|
8393
8900
|
function loadMermaid() {
|
|
8394
8901
|
if (!mermaidPromise) {
|
|
8395
|
-
mermaidPromise =
|
|
8902
|
+
mermaidPromise = loadMermaidModule().then((mod) => {
|
|
8396
8903
|
const mermaid = mod.default;
|
|
8397
8904
|
if (!initialized) {
|
|
8398
8905
|
mermaid.initialize(DEFAULT_CONFIG);
|
|
8399
8906
|
initialized = true;
|
|
8400
8907
|
}
|
|
8401
8908
|
return mermaid;
|
|
8402
|
-
})
|
|
8909
|
+
});
|
|
8403
8910
|
}
|
|
8404
8911
|
return mermaidPromise;
|
|
8405
8912
|
}
|
|
@@ -8417,20 +8924,23 @@ ${lines.join("\n")}
|
|
|
8417
8924
|
const pre = template.content.querySelector("pre");
|
|
8418
8925
|
return pre ? pre.innerHTML : "";
|
|
8419
8926
|
}
|
|
8927
|
+
var loadShikiModule = createBundleLoader("shiki.js");
|
|
8420
8928
|
var cache = /* @__PURE__ */ new Map();
|
|
8421
8929
|
function loadShikiHighlighter(options) {
|
|
8422
8930
|
const key = JSON.stringify({
|
|
8423
8931
|
themes: [...options.themes].sort(),
|
|
8424
|
-
langs: [...options.langs].sort()
|
|
8932
|
+
langs: [...options.langs].sort(),
|
|
8933
|
+
failureMode: options.failureMode ?? "fallback"
|
|
8425
8934
|
});
|
|
8426
8935
|
const cached = cache.get(key);
|
|
8427
8936
|
if (cached) return cached;
|
|
8428
|
-
const
|
|
8937
|
+
const load = loadShikiModule().then(
|
|
8429
8938
|
(mod) => mod.createHighlighter({
|
|
8430
8939
|
themes: options.themes,
|
|
8431
8940
|
langs: options.langs
|
|
8432
8941
|
})
|
|
8433
|
-
)
|
|
8942
|
+
);
|
|
8943
|
+
const promise = options.failureMode === "throw" ? load : load.catch(() => null);
|
|
8434
8944
|
cache.set(key, promise);
|
|
8435
8945
|
return promise;
|
|
8436
8946
|
}
|
|
@@ -11305,188 +11815,6 @@ ${frontmatter.yaml}
|
|
|
11305
11815
|
return inferred;
|
|
11306
11816
|
}
|
|
11307
11817
|
|
|
11308
|
-
// web-src/core/error-detail.ts
|
|
11309
|
-
function errorWithCause(message, cause) {
|
|
11310
|
-
return Object.assign(new Error(message), { cause });
|
|
11311
|
-
}
|
|
11312
|
-
var OMIT_VALUE = /* @__PURE__ */ Symbol("omit-sensitive-error-field");
|
|
11313
|
-
function isSensitiveFieldName(key) {
|
|
11314
|
-
const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
11315
|
-
return normalized === "auth" || normalized.endsWith("auth") || normalized.startsWith("auth") && !normalized.startsWith("author") || normalized.includes("authorization") || normalized.includes("cookie") || normalized.includes("token") || normalized.includes("password") || normalized.includes("passwd") || normalized.includes("secret") || normalized.includes("credential") || normalized.includes("apikey") || normalized.includes("privatekey");
|
|
11316
|
-
}
|
|
11317
|
-
function errorName(error2) {
|
|
11318
|
-
try {
|
|
11319
|
-
return typeof error2.name === "string" ? error2.name : "Error";
|
|
11320
|
-
} catch {
|
|
11321
|
-
return "Error";
|
|
11322
|
-
}
|
|
11323
|
-
}
|
|
11324
|
-
function errorMessage(error2) {
|
|
11325
|
-
try {
|
|
11326
|
-
return typeof error2.message === "string" ? error2.message : "unable to read error message";
|
|
11327
|
-
} catch {
|
|
11328
|
-
return "unable to read error message";
|
|
11329
|
-
}
|
|
11330
|
-
}
|
|
11331
|
-
function sanitizeObjectFields(value, ancestors, excludedKeys = /* @__PURE__ */ new Set()) {
|
|
11332
|
-
let keys;
|
|
11333
|
-
try {
|
|
11334
|
-
keys = Object.getOwnPropertyNames(value);
|
|
11335
|
-
} catch {
|
|
11336
|
-
return { value: "[Unserializable object]", removedSensitive: false };
|
|
11337
|
-
}
|
|
11338
|
-
const output = /* @__PURE__ */ Object.create(null);
|
|
11339
|
-
let removedSensitive = false;
|
|
11340
|
-
for (const key of keys) {
|
|
11341
|
-
if (excludedKeys.has(key)) continue;
|
|
11342
|
-
if (isSensitiveFieldName(key)) {
|
|
11343
|
-
removedSensitive = true;
|
|
11344
|
-
continue;
|
|
11345
|
-
}
|
|
11346
|
-
let descriptor;
|
|
11347
|
-
try {
|
|
11348
|
-
descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
11349
|
-
} catch {
|
|
11350
|
-
output[key] = "[Unserializable field]";
|
|
11351
|
-
continue;
|
|
11352
|
-
}
|
|
11353
|
-
if (!descriptor) continue;
|
|
11354
|
-
if (!("value" in descriptor)) {
|
|
11355
|
-
output[key] = "[Accessor]";
|
|
11356
|
-
continue;
|
|
11357
|
-
}
|
|
11358
|
-
const sanitized = sanitizeValue(descriptor.value, ancestors);
|
|
11359
|
-
if (sanitized === OMIT_VALUE) {
|
|
11360
|
-
removedSensitive = true;
|
|
11361
|
-
continue;
|
|
11362
|
-
}
|
|
11363
|
-
output[key] = sanitized.value;
|
|
11364
|
-
removedSensitive ||= sanitized.removedSensitive;
|
|
11365
|
-
}
|
|
11366
|
-
if (Object.keys(output).length === 0 && removedSensitive) return OMIT_VALUE;
|
|
11367
|
-
return { value: output, removedSensitive };
|
|
11368
|
-
}
|
|
11369
|
-
function sanitizeError(error2, ancestors) {
|
|
11370
|
-
const output = /* @__PURE__ */ Object.create(null);
|
|
11371
|
-
output.name = errorName(error2);
|
|
11372
|
-
output.message = errorMessage(error2);
|
|
11373
|
-
const fields = sanitizeObjectFields(
|
|
11374
|
-
error2,
|
|
11375
|
-
ancestors,
|
|
11376
|
-
/* @__PURE__ */ new Set(["name", "message", "stack"])
|
|
11377
|
-
);
|
|
11378
|
-
if (fields !== OMIT_VALUE) Object.assign(output, fields.value);
|
|
11379
|
-
return {
|
|
11380
|
-
value: output,
|
|
11381
|
-
removedSensitive: fields === OMIT_VALUE ? true : fields.removedSensitive
|
|
11382
|
-
};
|
|
11383
|
-
}
|
|
11384
|
-
function sanitizeValue(value, ancestors) {
|
|
11385
|
-
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
11386
|
-
return { value, removedSensitive: false };
|
|
11387
|
-
if (typeof value === "number") {
|
|
11388
|
-
return {
|
|
11389
|
-
value: Number.isFinite(value) ? value : String(value),
|
|
11390
|
-
removedSensitive: false
|
|
11391
|
-
};
|
|
11392
|
-
}
|
|
11393
|
-
if (typeof value === "bigint") {
|
|
11394
|
-
return { value: `${value}n`, removedSensitive: false };
|
|
11395
|
-
}
|
|
11396
|
-
if (typeof value === "undefined") {
|
|
11397
|
-
return { value: "[undefined]", removedSensitive: false };
|
|
11398
|
-
}
|
|
11399
|
-
if (typeof value === "symbol") {
|
|
11400
|
-
return { value: "[symbol]", removedSensitive: false };
|
|
11401
|
-
}
|
|
11402
|
-
if (typeof value === "function") {
|
|
11403
|
-
return { value: "[function]", removedSensitive: false };
|
|
11404
|
-
}
|
|
11405
|
-
const objectValue = value;
|
|
11406
|
-
if (ancestors.has(objectValue)) {
|
|
11407
|
-
return { value: "[Circular]", removedSensitive: false };
|
|
11408
|
-
}
|
|
11409
|
-
ancestors.add(objectValue);
|
|
11410
|
-
try {
|
|
11411
|
-
if (Array.isArray(objectValue)) {
|
|
11412
|
-
const output = [];
|
|
11413
|
-
let removedSensitive = false;
|
|
11414
|
-
for (const item of objectValue) {
|
|
11415
|
-
const sanitized = sanitizeValue(item, ancestors);
|
|
11416
|
-
if (sanitized === OMIT_VALUE) {
|
|
11417
|
-
removedSensitive = true;
|
|
11418
|
-
continue;
|
|
11419
|
-
}
|
|
11420
|
-
output.push(sanitized.value);
|
|
11421
|
-
removedSensitive ||= sanitized.removedSensitive;
|
|
11422
|
-
}
|
|
11423
|
-
if (output.length === 0 && removedSensitive) return OMIT_VALUE;
|
|
11424
|
-
return { value: output, removedSensitive };
|
|
11425
|
-
}
|
|
11426
|
-
if (objectValue instanceof Error)
|
|
11427
|
-
return sanitizeError(objectValue, ancestors);
|
|
11428
|
-
return sanitizeObjectFields(objectValue, ancestors);
|
|
11429
|
-
} catch {
|
|
11430
|
-
return { value: "[Unserializable object]", removedSensitive: false };
|
|
11431
|
-
} finally {
|
|
11432
|
-
ancestors.delete(objectValue);
|
|
11433
|
-
}
|
|
11434
|
-
}
|
|
11435
|
-
function formatNonError(value) {
|
|
11436
|
-
if (typeof value === "string") return value;
|
|
11437
|
-
try {
|
|
11438
|
-
const sanitized = sanitizeValue(value, /* @__PURE__ */ new Set());
|
|
11439
|
-
const serializable = sanitized === OMIT_VALUE ? {} : sanitized.value;
|
|
11440
|
-
return JSON.stringify(serializable);
|
|
11441
|
-
} catch {
|
|
11442
|
-
return "[Unserializable value]";
|
|
11443
|
-
}
|
|
11444
|
-
}
|
|
11445
|
-
function formatErrorFields(error2) {
|
|
11446
|
-
const fields = sanitizeObjectFields(
|
|
11447
|
-
error2,
|
|
11448
|
-
/* @__PURE__ */ new Set([error2]),
|
|
11449
|
-
/* @__PURE__ */ new Set(["name", "message", "stack", "cause"])
|
|
11450
|
-
);
|
|
11451
|
-
if (fields === OMIT_VALUE) return "";
|
|
11452
|
-
const output = fields.value;
|
|
11453
|
-
return Object.keys(output).length > 0 ? `
|
|
11454
|
-
Details: ${JSON.stringify(output)}` : "";
|
|
11455
|
-
}
|
|
11456
|
-
function formatErrorDetail(error2) {
|
|
11457
|
-
const parts = [];
|
|
11458
|
-
const seen = /* @__PURE__ */ new Set();
|
|
11459
|
-
let current = error2;
|
|
11460
|
-
while (current instanceof Error && !seen.has(current)) {
|
|
11461
|
-
seen.add(current);
|
|
11462
|
-
parts.push(
|
|
11463
|
-
`${errorName(current)}: ${errorMessage(current)}${formatErrorFields(current)}`
|
|
11464
|
-
);
|
|
11465
|
-
try {
|
|
11466
|
-
current = current.cause;
|
|
11467
|
-
} catch {
|
|
11468
|
-
current = "[Unserializable error cause]";
|
|
11469
|
-
}
|
|
11470
|
-
}
|
|
11471
|
-
if (current !== void 0) {
|
|
11472
|
-
parts.push(
|
|
11473
|
-
seen.has(current) ? "Error cause cycle detected" : formatNonError(current)
|
|
11474
|
-
);
|
|
11475
|
-
}
|
|
11476
|
-
return parts.join("\nCaused by: ") || formatNonError(error2);
|
|
11477
|
-
}
|
|
11478
|
-
async function responseErrorMessage(response, operation) {
|
|
11479
|
-
const statusText = response.statusText ? ` ${response.statusText}` : "";
|
|
11480
|
-
const prefix = `${operation} (HTTP ${response.status}${statusText})`;
|
|
11481
|
-
let body;
|
|
11482
|
-
try {
|
|
11483
|
-
body = await response.text();
|
|
11484
|
-
} catch (error2) {
|
|
11485
|
-
throw errorWithCause(`${prefix}: failed to read response body`, error2);
|
|
11486
|
-
}
|
|
11487
|
-
return body ? `${prefix}: ${body}` : prefix;
|
|
11488
|
-
}
|
|
11489
|
-
|
|
11490
11818
|
// web-src/core/id.ts
|
|
11491
11819
|
function bytesToHex(bytes) {
|
|
11492
11820
|
return Array.from(bytes, (b2) => b2.toString(16).padStart(2, "0")).join("");
|
|
@@ -23929,11 +24257,31 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
23929
24257
|
}
|
|
23930
24258
|
async function ensureDbUiState() {
|
|
23931
24259
|
if (dbUiLoadPromise) return dbUiLoadPromise;
|
|
23932
|
-
|
|
23933
|
-
|
|
23934
|
-
|
|
23935
|
-
|
|
23936
|
-
|
|
24260
|
+
const operation = (async () => {
|
|
24261
|
+
const response = await deps.trackLoad(fetch("/_db/ui"));
|
|
24262
|
+
if (!response.ok) {
|
|
24263
|
+
throw new Error(
|
|
24264
|
+
await responseErrorMessage(response, "load database UI settings")
|
|
24265
|
+
);
|
|
24266
|
+
}
|
|
24267
|
+
let state;
|
|
24268
|
+
try {
|
|
24269
|
+
state = await response.json();
|
|
24270
|
+
} catch (error2) {
|
|
24271
|
+
throw errorWithCause(
|
|
24272
|
+
"load database UI settings: response is not valid JSON",
|
|
24273
|
+
error2
|
|
24274
|
+
);
|
|
24275
|
+
}
|
|
24276
|
+
applyDbUiState(state);
|
|
24277
|
+
})();
|
|
24278
|
+
dbUiLoadPromise = operation;
|
|
24279
|
+
try {
|
|
24280
|
+
await operation;
|
|
24281
|
+
} catch (error2) {
|
|
24282
|
+
if (dbUiLoadPromise === operation) dbUiLoadPromise = null;
|
|
24283
|
+
throw error2;
|
|
24284
|
+
}
|
|
23937
24285
|
}
|
|
23938
24286
|
function getColumnWidths(dbId, table2) {
|
|
23939
24287
|
return { ...dbUiState.columnWidths[dbId]?.[table2] || {} };
|
|
@@ -24001,14 +24349,29 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
24001
24349
|
const v = dbUiState.prefs?.[key];
|
|
24002
24350
|
return v === void 0 ? fallback : v;
|
|
24003
24351
|
}
|
|
24004
|
-
function
|
|
24005
|
-
const
|
|
24006
|
-
|
|
24007
|
-
|
|
24008
|
-
|
|
24009
|
-
|
|
24010
|
-
|
|
24011
|
-
|
|
24352
|
+
async function saveDbUiPrefs(patch) {
|
|
24353
|
+
const response = await deps.trackLoad(
|
|
24354
|
+
fetch("/_db/ui", {
|
|
24355
|
+
method: "PATCH",
|
|
24356
|
+
headers: actionHeaders(),
|
|
24357
|
+
body: JSON.stringify({ prefs: patch })
|
|
24358
|
+
})
|
|
24359
|
+
);
|
|
24360
|
+
if (!response.ok) {
|
|
24361
|
+
throw new Error(
|
|
24362
|
+
await responseErrorMessage(response, "save database UI settings")
|
|
24363
|
+
);
|
|
24364
|
+
}
|
|
24365
|
+
let saved;
|
|
24366
|
+
try {
|
|
24367
|
+
saved = await response.json();
|
|
24368
|
+
} catch (error2) {
|
|
24369
|
+
throw errorWithCause(
|
|
24370
|
+
"save database UI settings: response is not valid JSON",
|
|
24371
|
+
error2
|
|
24372
|
+
);
|
|
24373
|
+
}
|
|
24374
|
+
applyDbUiState({ ...dbUiState, prefs: saved.prefs });
|
|
24012
24375
|
}
|
|
24013
24376
|
function onDbUiPrefChange(listener) {
|
|
24014
24377
|
dbUiPrefListeners.add(listener);
|
|
@@ -24437,7 +24800,6 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
24437
24800
|
getSnapshotSelectedTables,
|
|
24438
24801
|
setSnapshotSelectedTables,
|
|
24439
24802
|
getDbUiPref,
|
|
24440
|
-
setDbUiPref,
|
|
24441
24803
|
onDbUiPrefChange,
|
|
24442
24804
|
loadSqlHistory,
|
|
24443
24805
|
refreshDatastores,
|
|
@@ -24854,7 +25216,8 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
24854
25216
|
localize,
|
|
24855
25217
|
refresh: refreshDatastores,
|
|
24856
25218
|
getDbUiPref,
|
|
24857
|
-
|
|
25219
|
+
loadDbUiPrefs: ensureDbUiState,
|
|
25220
|
+
saveDbUiPrefs,
|
|
24858
25221
|
onDbUiPrefChange
|
|
24859
25222
|
};
|
|
24860
25223
|
}
|
|
@@ -28403,7 +28766,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
28403
28766
|
settings: {
|
|
28404
28767
|
nav: "Settings",
|
|
28405
28768
|
title: "Settings",
|
|
28406
|
-
intro: "Viewer preferences for this project and this browser.
|
|
28769
|
+
intro: "Viewer preferences for this project and this browser. Edits remain a draft until you select Save changes.",
|
|
28407
28770
|
groups: []
|
|
28408
28771
|
},
|
|
28409
28772
|
overview: {
|
|
@@ -28524,6 +28887,10 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
28524
28887
|
kind: "paragraph",
|
|
28525
28888
|
text: "The left side starts with a Your turn section for terminals that are waiting for input or finished but unread. Below it, you can scope the session list to this repository or all tmux sessions, filter by state, and search by task, place, or id. Its two-tier tree puts shells opened by this panel at the top: a shell running tmux carries a terminal icon and its session name, and that session's windows and panes hang underneath it. The bottom tier is the tmux sessions no shell has opened yet. Each pane is labelled with the title tmux shows for it — a coding agent running in a pane usually puts what it is doing there, so the tree alone tells you which pane is busy."
|
|
28526
28889
|
},
|
|
28890
|
+
{
|
|
28891
|
+
kind: "paragraph",
|
|
28892
|
+
text: "Status first uses lifecycle reports when they are available. Otherwise it evaluates every enabled screen rule against the live terminal title and recent visible lines, then uses the highest-priority match. A terminal is tracked only after a report or a visible rule identifies it; screen motion then provides the working/idle fallback. Working matches expire when the title and screen stop changing, so stale status text does not stay active. Settings & Help → Settings contains the full JSON rule set, including regions, priorities, contains checks, regular expressions, and nested all/any/not conditions. Regular expressions use a bounded safe subset: groups, alternation, and backreferences are rejected, and AND/OR belongs in all/any. Saving validates the whole set and shows every error without replacing the active rules. Restoring the built-in rules removes the saved override so updated defaults can arrive with later releases."
|
|
28893
|
+
},
|
|
28527
28894
|
{
|
|
28528
28895
|
kind: "paragraph",
|
|
28529
28896
|
text: "Click a pane and the panel takes you to it. If a shell already has that session open, it switches to that shell and makes the pane current; otherwise a shell is opened and attached for you. A session moves up to the top tier the moment a shell opens it and drops back down when that shell closes, so you end up with one shell per tmux session rather than one per pane. Powerline separators and file icons render when a Nerd Font is installed on the machine running the browser; no font ships with the package. The tree needs tmux on PATH — without it the panel says so, and shells still work. Opening shells needs the optional @lydell/node-pty package."
|
|
@@ -28578,6 +28945,10 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
28578
28945
|
"settings.json",
|
|
28579
28946
|
"Settings — diff layout, theme, language, sidebar/history widths, font sizes, syntax highlight, ignore-whitespace, hide-tests, scope overrides (omitted dirs / excluded names), upload toggle, annotation panel open/width/follow/mute/rate, and the last viewed diff range."
|
|
28580
28947
|
],
|
|
28948
|
+
[
|
|
28949
|
+
"agent-screen-rules.json",
|
|
28950
|
+
"Terminal status screen rules saved from Settings. Removing the override restores the built-in rules."
|
|
28951
|
+
],
|
|
28581
28952
|
[
|
|
28582
28953
|
"view-state.json",
|
|
28583
28954
|
"Sidebar tree state — collapsed directories, lazy-expanded directories (folders opened on demand in large repos), and the viewed-file list used to dim already-read entries."
|
|
@@ -29137,7 +29508,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
29137
29508
|
settings: {
|
|
29138
29509
|
nav: "設定",
|
|
29139
29510
|
title: "設定",
|
|
29140
|
-
intro: "
|
|
29511
|
+
intro: "このプロジェクトとこのブラウザのビューア設定です。「変更を保存」を押すまで編集内容は下書きのままです。",
|
|
29141
29512
|
groups: []
|
|
29142
29513
|
},
|
|
29143
29514
|
overview: {
|
|
@@ -29258,6 +29629,10 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
29258
29629
|
kind: "paragraph",
|
|
29259
29630
|
text: "左側の先頭には、入力待ちと、終わったのにまだ見ていないターミナルを集める「あなたの番」があります。その下の一覧は、このリポジトリだけ、またはすべての tmux セッションに範囲を切り替え、状態で絞り込み、作業内容・場所・宛先で検索できます。2 段のツリーの上段はこのパネルが開いたシェルで、中で tmux が動いていれば端末の印とセッション名が付き、そのセッションのウィンドウとペインがその下にぶら下がります。下段は、まだどのシェルも開いていない tmux セッションです。ペインには tmux 側のタイトルが付きます。ペインで動いているコーディングエージェントは作業内容をタイトルに出すので、ツリーを見るだけでどのペインが動いているか分かります。"
|
|
29260
29631
|
},
|
|
29632
|
+
{
|
|
29633
|
+
kind: "paragraph",
|
|
29634
|
+
text: "状態変更の申告がある場合はそれを先に使います。申告が無い場合は、現在のターミナルタイトルと画面下端の表示に対して全ルールを評価し、優先度が最大の一致から「作業中」「入力待ち」「待機中」「直前の状態を維持」を決めます。申告か見えているルールで対象を識別した後だけ、画面の変化量を作業中・待機中の補助判定に使います。作業中ルールの文字が残っていても、タイトルと画面が変化しなくなれば待機中へ移ります。設定・ヘルプ → 設定では、見る範囲、優先度、contains、正規表現、入れ子の all/any/not を含むJSONルール集を編集できます。正規表現は処理時間を抑えた範囲だけを許可し、グループ・選択・後方参照は使えません。AND/OR は all/any で表します。保存時は全ルールを検証し、エラーはすべて表示して適用中のルールを置き換えません。組み込みルールへ戻すと保存済みの上書きを削除するため、以後の更新で新しい既定ルールを受け取れます。"
|
|
29635
|
+
},
|
|
29261
29636
|
{
|
|
29262
29637
|
kind: "paragraph",
|
|
29263
29638
|
text: "ペインを押すと、そこまで連れて行きます。そのセッションを既に開いているシェルがあれば、そのシェルに切り替えてペインをカレントにします。無ければ、こちらでシェルを開いて attach します。シェルで開いた瞬間にセッションは下段から上段へ移り、そのシェルを閉じると下段へ戻ります。つまりペインごとではなく、tmux のセッション 1 つにつきシェル 1 本になります。powerline のセパレータやファイルアイコンは、ブラウザを動かしている環境に Nerd Font が入っていれば表示されます(フォントはパッケージに同梱していません)。ツリーには tmux が PATH にあることが必要で、無い場合はその旨を表示します(シェルはそのまま使えます)。シェルを開くには任意依存の @lydell/node-pty が必要です。"
|
|
@@ -29312,6 +29687,10 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
29312
29687
|
"settings.json",
|
|
29313
29688
|
"設定 — diff レイアウト、テーマ、言語、サイドバー/履歴幅、フォントサイズ、シンタックスハイライト、whitespace 無視、テスト非表示、scope 上書き(除外ディレクトリ / 除外名)、アップロード許可、注釈パネルの開閉/幅/follow/ミュート/再生速度、最後に表示した diff 範囲を保存します。"
|
|
29314
29689
|
],
|
|
29690
|
+
[
|
|
29691
|
+
"agent-screen-rules.json",
|
|
29692
|
+
"設定画面で保存したターミナル状態の画面判定ルール。上書きを削除すると組み込みルールへ戻ります。"
|
|
29693
|
+
],
|
|
29315
29694
|
[
|
|
29316
29695
|
"view-state.json",
|
|
29317
29696
|
"サイドバーツリーの状態 — 折りたたみ済みディレクトリ、遅延展開済みディレクトリ(大規模リポジトリで必要に応じて開かれたフォルダ)、既読扱いするための表示済みファイル一覧。"
|
|
@@ -39302,7 +39681,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
39302
39681
|
stateWaiting: "waiting",
|
|
39303
39682
|
stateDone: "unread",
|
|
39304
39683
|
stateIdle: "idle",
|
|
39305
|
-
guessed: "
|
|
39684
|
+
guessed: "detected from visible terminal UI or screen activity",
|
|
39306
39685
|
filterPlaceholder: "task, place, or id",
|
|
39307
39686
|
filterAll: "all",
|
|
39308
39687
|
noMatches: "Nothing matches this filter.",
|
|
@@ -39373,11 +39752,11 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
39373
39752
|
yourTurnHint: "入力待ちと、終わったのにまだ見ていないもの",
|
|
39374
39753
|
yourTurnEmpty: "いま手を入れるものはありません。",
|
|
39375
39754
|
sessions: "ターミナル",
|
|
39376
|
-
stateWorking: "
|
|
39377
|
-
stateWaiting: "
|
|
39755
|
+
stateWorking: "作業中",
|
|
39756
|
+
stateWaiting: "入力待ち",
|
|
39378
39757
|
stateDone: "未読",
|
|
39379
|
-
stateIdle: "
|
|
39380
|
-
guessed: "
|
|
39758
|
+
stateIdle: "待機中",
|
|
39759
|
+
guessed: "画面表示または画面の動きからの判定",
|
|
39381
39760
|
filterPlaceholder: "作業内容・場所・宛先",
|
|
39382
39761
|
filterAll: "すべて",
|
|
39383
39762
|
noMatches: "この条件に合うものはありません。",
|
|
@@ -39642,6 +40021,10 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
39642
40021
|
const attentionEmpty = document.createElement("p");
|
|
39643
40022
|
attentionEmpty.className = "terminal-empty";
|
|
39644
40023
|
attentionSection.append(attentionHead, attentionCards, attentionEmpty);
|
|
40024
|
+
const observationErrors = document.createElement("pre");
|
|
40025
|
+
observationErrors.className = "terminal-observation-errors";
|
|
40026
|
+
observationErrors.setAttribute("role", "alert");
|
|
40027
|
+
observationErrors.hidden = true;
|
|
39645
40028
|
const filters = document.createElement("div");
|
|
39646
40029
|
filters.className = "terminal-filters";
|
|
39647
40030
|
const stateSelect = document.createElement("select");
|
|
@@ -39660,14 +40043,15 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
39660
40043
|
tree.role = "tree";
|
|
39661
40044
|
const treeEmpty = document.createElement("p");
|
|
39662
40045
|
treeEmpty.className = "terminal-empty";
|
|
39663
|
-
el.append(scopeBar, attentionSection, filters, tree);
|
|
40046
|
+
el.append(scopeBar, observationErrors, attentionSection, filters, tree);
|
|
39664
40047
|
let data = {
|
|
39665
40048
|
panes: null,
|
|
39666
40049
|
shells: [],
|
|
39667
40050
|
clients: [],
|
|
39668
40051
|
shellAvailable: true,
|
|
39669
40052
|
shellUnavailableReason: "",
|
|
39670
|
-
states: []
|
|
40053
|
+
states: [],
|
|
40054
|
+
stateErrors: []
|
|
39671
40055
|
};
|
|
39672
40056
|
let selected = null;
|
|
39673
40057
|
let stateFilter = null;
|
|
@@ -39807,7 +40191,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
39807
40191
|
button.title = [
|
|
39808
40192
|
row.task,
|
|
39809
40193
|
row.kind === "tmux" ? `${row.locator} · ${row.place}` : row.place,
|
|
39810
|
-
row.source
|
|
40194
|
+
row.source && row.source !== "hook" ? text3.guessed : "",
|
|
39811
40195
|
row.lastPrompt ? `${text3.lastPrompt}: ${row.lastPrompt}` : ""
|
|
39812
40196
|
].filter(Boolean).join("\n");
|
|
39813
40197
|
button.addEventListener("click", () => activate(row));
|
|
@@ -40010,6 +40394,13 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
40010
40394
|
data.clients
|
|
40011
40395
|
);
|
|
40012
40396
|
const scoped = filterByScope(all, scope);
|
|
40397
|
+
observationErrors.textContent = data.stateErrors.map((error2) => {
|
|
40398
|
+
const target = error2.target ? ` ${error2.target}` : "";
|
|
40399
|
+
return `[${error2.operation}${target}]
|
|
40400
|
+
${error2.detail}${error2.stack ? `
|
|
40401
|
+
${error2.stack}` : ""}`;
|
|
40402
|
+
}).join("\n\n");
|
|
40403
|
+
observationErrors.hidden = data.stateErrors.length === 0;
|
|
40013
40404
|
renderScope(all, scoped);
|
|
40014
40405
|
const attention = attentionRows(scoped);
|
|
40015
40406
|
attentionTitle.textContent = text3.yourTurn;
|
|
@@ -40039,158 +40430,6 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
40039
40430
|
};
|
|
40040
40431
|
}
|
|
40041
40432
|
|
|
40042
|
-
// web-src/core/terminal-paste.ts
|
|
40043
|
-
var PASTE_IMAGE_TYPES = {
|
|
40044
|
-
"image/png": "png",
|
|
40045
|
-
"image/jpeg": "jpg",
|
|
40046
|
-
"image/gif": "gif",
|
|
40047
|
-
"image/webp": "webp"
|
|
40048
|
-
};
|
|
40049
|
-
var MAX_PASTE_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
40050
|
-
var MAX_PASTE_BODY_BYTES = Math.ceil(MAX_PASTE_IMAGE_BYTES * 1.4);
|
|
40051
|
-
var SHIFT_ENTER_SEQUENCE = `${String.fromCharCode(27)}[200~${String.fromCharCode(10)}${String.fromCharCode(27)}[201~`;
|
|
40052
|
-
function isShiftEnter(event) {
|
|
40053
|
-
return event.key === "Enter" && event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
40054
|
-
}
|
|
40055
|
-
|
|
40056
|
-
// web-src/core/terminal-images.ts
|
|
40057
|
-
var TERMINAL_IMAGE_EXTENSIONS = [
|
|
40058
|
-
...new Set(Object.values(PASTE_IMAGE_TYPES)),
|
|
40059
|
-
"jpeg"
|
|
40060
|
-
];
|
|
40061
|
-
var MAX_TERMINAL_IMAGE_PATHS = 64;
|
|
40062
|
-
var MAX_TERMINAL_IMAGE_QUERY = 16;
|
|
40063
|
-
var MAX_IMAGE_PATH_LENGTH = 1024;
|
|
40064
|
-
var ESC = String.fromCharCode(27);
|
|
40065
|
-
var BEL = String.fromCharCode(7);
|
|
40066
|
-
var ANSI_RE = new RegExp(
|
|
40067
|
-
`${ESC}\\[[0-9;?]*[ -/]*[@-~]|${ESC}\\][^${BEL}${ESC}]*(?:${BEL}|${ESC}\\\\)|${ESC}[@-Z\\\\-_]`,
|
|
40068
|
-
"g"
|
|
40069
|
-
);
|
|
40070
|
-
function stripAnsi(text3) {
|
|
40071
|
-
return text3.replace(ANSI_RE, "");
|
|
40072
|
-
}
|
|
40073
|
-
var PATH_CHAR = "[\\p{L}\\p{N}._~+@%/-]";
|
|
40074
|
-
var NAME_CHAR = "[\\p{L}\\p{N}_~+@%-]";
|
|
40075
|
-
var IMAGE_PATH_RE = new RegExp(
|
|
40076
|
-
`${PATH_CHAR}*${NAME_CHAR}\\.(?:${TERMINAL_IMAGE_EXTENSIONS.join("|")})(?![\\p{L}\\p{N}])`,
|
|
40077
|
-
"giu"
|
|
40078
|
-
);
|
|
40079
|
-
function looksLikeUrl(value) {
|
|
40080
|
-
return value.startsWith("//") || value.includes("://");
|
|
40081
|
-
}
|
|
40082
|
-
function findImagePaths(text3, limit = MAX_TERMINAL_IMAGE_PATHS) {
|
|
40083
|
-
const found = [];
|
|
40084
|
-
const seen = /* @__PURE__ */ new Set();
|
|
40085
|
-
for (const match2 of text3.matchAll(IMAGE_PATH_RE)) {
|
|
40086
|
-
const path = match2[0];
|
|
40087
|
-
if (path.length > MAX_IMAGE_PATH_LENGTH) continue;
|
|
40088
|
-
if (looksLikeUrl(path)) continue;
|
|
40089
|
-
if (seen.has(path)) continue;
|
|
40090
|
-
seen.add(path);
|
|
40091
|
-
found.push(path);
|
|
40092
|
-
if (found.length >= limit) break;
|
|
40093
|
-
}
|
|
40094
|
-
return found;
|
|
40095
|
-
}
|
|
40096
|
-
function joinWrappedLines(text3, width) {
|
|
40097
|
-
if (!Number.isFinite(width) || width <= 0) return text3;
|
|
40098
|
-
const lines = text3.split("\n");
|
|
40099
|
-
const joined = [];
|
|
40100
|
-
let current = null;
|
|
40101
|
-
let previousLength = 0;
|
|
40102
|
-
for (const line of lines) {
|
|
40103
|
-
if (current === null) {
|
|
40104
|
-
current = line;
|
|
40105
|
-
} else if (previousLength >= width) {
|
|
40106
|
-
current += line;
|
|
40107
|
-
} else {
|
|
40108
|
-
joined.push(current);
|
|
40109
|
-
current = line;
|
|
40110
|
-
}
|
|
40111
|
-
previousLength = line.length;
|
|
40112
|
-
}
|
|
40113
|
-
if (current !== null) joined.push(current);
|
|
40114
|
-
return joined.join("\n");
|
|
40115
|
-
}
|
|
40116
|
-
function joinBrokenPathLines(text3) {
|
|
40117
|
-
const lines = text3.split("\n");
|
|
40118
|
-
const candidates = [];
|
|
40119
|
-
for (let i2 = 0; i2 + 1 < lines.length; i2 += 1) {
|
|
40120
|
-
const head = lastPathFragment(lines[i2] ?? "");
|
|
40121
|
-
const tail = firstWord(lines[i2 + 1] ?? "");
|
|
40122
|
-
if (!head || !tail) continue;
|
|
40123
|
-
candidates.push(head.text + tail);
|
|
40124
|
-
}
|
|
40125
|
-
return candidates.join("\n");
|
|
40126
|
-
}
|
|
40127
|
-
function lastPathFragment(line) {
|
|
40128
|
-
let found = null;
|
|
40129
|
-
const words = /\S+/g;
|
|
40130
|
-
for (const match2 of line.matchAll(words)) {
|
|
40131
|
-
if (match2[0].includes("/")) {
|
|
40132
|
-
found = { text: match2[0], index: match2.index ?? 0 };
|
|
40133
|
-
}
|
|
40134
|
-
}
|
|
40135
|
-
return found;
|
|
40136
|
-
}
|
|
40137
|
-
function firstWord(line) {
|
|
40138
|
-
return /\S+/.exec(line)?.[0] ?? "";
|
|
40139
|
-
}
|
|
40140
|
-
function findImagePathsInText(plain, width = 0, limit = MAX_TERMINAL_IMAGE_PATHS) {
|
|
40141
|
-
const merged = [];
|
|
40142
|
-
const seen = /* @__PURE__ */ new Set();
|
|
40143
|
-
const sources = [
|
|
40144
|
-
plain,
|
|
40145
|
-
width > 0 ? joinWrappedLines(plain, width) : "",
|
|
40146
|
-
joinBrokenPathLines(plain)
|
|
40147
|
-
];
|
|
40148
|
-
for (const source of sources) {
|
|
40149
|
-
if (!source) continue;
|
|
40150
|
-
for (const path of findImagePaths(source, limit)) {
|
|
40151
|
-
if (seen.has(path)) continue;
|
|
40152
|
-
seen.add(path);
|
|
40153
|
-
merged.push(path);
|
|
40154
|
-
if (merged.length >= limit) return merged;
|
|
40155
|
-
}
|
|
40156
|
-
}
|
|
40157
|
-
return merged;
|
|
40158
|
-
}
|
|
40159
|
-
function findPathAnchors(lines, candidates) {
|
|
40160
|
-
const anchors = [];
|
|
40161
|
-
for (const candidate of candidates) {
|
|
40162
|
-
if (!candidate) continue;
|
|
40163
|
-
const anchor = firstAnchor(lines, candidate);
|
|
40164
|
-
if (anchor) anchors.push(anchor);
|
|
40165
|
-
}
|
|
40166
|
-
return anchors;
|
|
40167
|
-
}
|
|
40168
|
-
function firstAnchor(lines, candidate) {
|
|
40169
|
-
for (let row = 0; row < lines.length; row += 1) {
|
|
40170
|
-
const line = lines[row] ?? "";
|
|
40171
|
-
const direct = line.indexOf(candidate);
|
|
40172
|
-
if (direct >= 0) return { candidate, row, col: direct, span: 1 };
|
|
40173
|
-
const head = lastPathFragment(line);
|
|
40174
|
-
if (!head) continue;
|
|
40175
|
-
const tail = firstWord(lines[row + 1] ?? "");
|
|
40176
|
-
if (tail && head.text + tail === candidate) {
|
|
40177
|
-
return { candidate, row, col: head.index, span: 2 };
|
|
40178
|
-
}
|
|
40179
|
-
}
|
|
40180
|
-
return null;
|
|
40181
|
-
}
|
|
40182
|
-
|
|
40183
|
-
// web-src/core/lazy-bundle.ts
|
|
40184
|
-
function createBundleLoader(bundleFile, init) {
|
|
40185
|
-
let pending = null;
|
|
40186
|
-
return () => {
|
|
40187
|
-
if (!pending) {
|
|
40188
|
-
pending = import(`/${bundleFile}`).then((mod) => init ? init(mod) : mod).catch(() => null);
|
|
40189
|
-
}
|
|
40190
|
-
return pending;
|
|
40191
|
-
};
|
|
40192
|
-
}
|
|
40193
|
-
|
|
40194
40433
|
// web-src/core/xterm-loader.ts
|
|
40195
40434
|
var loadXterm = createBundleLoader("xterm.js");
|
|
40196
40435
|
|
|
@@ -40887,6 +41126,7 @@ ${formatErrorDetail(error2)}`
|
|
|
40887
41126
|
let board = null;
|
|
40888
41127
|
let lastTargetId = null;
|
|
40889
41128
|
let states = [];
|
|
41129
|
+
let stateErrors = [];
|
|
40890
41130
|
let screen = null;
|
|
40891
41131
|
let reloadBtn = null;
|
|
40892
41132
|
let fontSmaller = null;
|
|
@@ -40996,7 +41236,8 @@ ${formatErrorDetail(error2)}`);
|
|
|
40996
41236
|
clients: clients?.clients ?? [],
|
|
40997
41237
|
shellAvailable: shells?.available ?? true,
|
|
40998
41238
|
shellUnavailableReason: shells?.reason ?? "",
|
|
40999
|
-
states
|
|
41239
|
+
states,
|
|
41240
|
+
stateErrors
|
|
41000
41241
|
});
|
|
41001
41242
|
board?.setSelected(attached?.id ?? null);
|
|
41002
41243
|
}
|
|
@@ -41029,12 +41270,13 @@ ${formatErrorDetail(error2)}`);
|
|
|
41029
41270
|
}
|
|
41030
41271
|
const nextPanes = await paneRes.json();
|
|
41031
41272
|
const nextShells = await shellRes.json();
|
|
41032
|
-
const
|
|
41273
|
+
const nextStateResponse = await stateRes.json();
|
|
41033
41274
|
const nextClients = await clientRes.json();
|
|
41034
41275
|
if (stale()) return;
|
|
41035
41276
|
panes = nextPanes;
|
|
41036
41277
|
shells = nextShells;
|
|
41037
|
-
states =
|
|
41278
|
+
states = nextStateResponse.states ?? [];
|
|
41279
|
+
stateErrors = nextStateResponse.errors ?? [];
|
|
41038
41280
|
clients = nextClients;
|
|
41039
41281
|
renderLists();
|
|
41040
41282
|
if (attached && !findShell(attached.id)) {
|
|
@@ -42268,6 +42510,17 @@ ${formatErrorDetail(error2)}`);
|
|
|
42268
42510
|
{ value: "en", label: "English" },
|
|
42269
42511
|
{ value: "ja", label: "日本語" }
|
|
42270
42512
|
];
|
|
42513
|
+
var GENERAL_SETTING_FIELDS = [
|
|
42514
|
+
"language",
|
|
42515
|
+
"sidebarFontSize",
|
|
42516
|
+
"codeFontSize",
|
|
42517
|
+
"omitDirs",
|
|
42518
|
+
"excludeNames",
|
|
42519
|
+
"watchLimit",
|
|
42520
|
+
"uploadEnabled",
|
|
42521
|
+
"inferFkRails",
|
|
42522
|
+
"s3TooltipEnabled"
|
|
42523
|
+
];
|
|
42271
42524
|
function section() {
|
|
42272
42525
|
const div = document.createElement("div");
|
|
42273
42526
|
div.className = "scope-settings-section";
|
|
@@ -42309,8 +42562,8 @@ ${formatErrorDetail(error2)}`);
|
|
|
42309
42562
|
wrap.append(input2, text3);
|
|
42310
42563
|
return { wrap, input: input2, text: text3 };
|
|
42311
42564
|
}
|
|
42312
|
-
function setFieldValue(field2, value) {
|
|
42313
|
-
if (field2 === document.activeElement) return;
|
|
42565
|
+
function setFieldValue(field2, value, force = false) {
|
|
42566
|
+
if (!force && field2 === document.activeElement) return;
|
|
42314
42567
|
if (field2.value !== value) field2.value = value;
|
|
42315
42568
|
}
|
|
42316
42569
|
function createViewerSettings(deps) {
|
|
@@ -42334,22 +42587,66 @@ ${formatErrorDetail(error2)}`);
|
|
|
42334
42587
|
const watchLimitNumber = document.createElement("input");
|
|
42335
42588
|
const watchLimitRange = document.createElement("input");
|
|
42336
42589
|
const watchLimitHelp = helpText("scope-watch-limit-help");
|
|
42337
|
-
const
|
|
42590
|
+
const agentRules = document.createElement("textarea");
|
|
42591
|
+
const agentRulesHelp = helpText("agent-screen-rules-help");
|
|
42592
|
+
const agentRulesSource = helpText("agent-screen-rules-source");
|
|
42593
|
+
const agentRulesError = helpText("agent-screen-rules-error");
|
|
42594
|
+
agentRulesError.classList.add("scope-settings-refresh-error");
|
|
42595
|
+
agentRulesError.hidden = true;
|
|
42596
|
+
const agentRulesHighlightError = helpText(
|
|
42597
|
+
"agent-screen-rules-highlight-error"
|
|
42598
|
+
);
|
|
42599
|
+
agentRulesHighlightError.classList.add("scope-settings-refresh-error");
|
|
42600
|
+
agentRulesHighlightError.hidden = true;
|
|
42601
|
+
const agentRulesHighlight = document.createElement("pre");
|
|
42602
|
+
agentRulesHighlight.className = "agent-screen-rules-highlight";
|
|
42603
|
+
agentRulesHighlight.setAttribute("aria-hidden", "true");
|
|
42604
|
+
const agentRulesGuide = document.createElement("details");
|
|
42605
|
+
agentRulesGuide.id = "agent-screen-rules-guide";
|
|
42606
|
+
agentRulesGuide.className = "agent-screen-rules-guide";
|
|
42607
|
+
agentRulesGuide.open = true;
|
|
42608
|
+
const agentRulesGuideTitle = document.createElement("summary");
|
|
42609
|
+
const agentRulesGuideIntro = helpText();
|
|
42610
|
+
const agentRulesGuideFields = helpText();
|
|
42611
|
+
const agentRulesGuideMatchers = helpText();
|
|
42612
|
+
const agentRulesGuideRegions = helpText();
|
|
42613
|
+
const agentRulesGuideExample = document.createElement("pre");
|
|
42614
|
+
agentRulesGuideExample.className = "agent-screen-rules-guide-example";
|
|
42615
|
+
const agentRulesSave = document.createElement("button");
|
|
42616
|
+
const agentRulesReset = document.createElement("button");
|
|
42617
|
+
const saveNote = helpText("scope-settings-save-note");
|
|
42618
|
+
const saveStatus = helpText("scope-settings-save-status");
|
|
42619
|
+
saveStatus.setAttribute("aria-live", "polite");
|
|
42620
|
+
const saveError = helpText("scope-settings-save-error");
|
|
42621
|
+
saveError.classList.add("scope-settings-refresh-error");
|
|
42622
|
+
saveError.hidden = true;
|
|
42338
42623
|
const refreshError = helpText("scope-settings-refresh-error");
|
|
42339
42624
|
refreshError.classList.add("scope-settings-refresh-error");
|
|
42340
42625
|
refreshError.hidden = true;
|
|
42341
42626
|
const resetButton = document.createElement("button");
|
|
42627
|
+
const saveButton = document.createElement("button");
|
|
42342
42628
|
const displayTitle = sectionTitle();
|
|
42343
42629
|
const uploadsTitle = sectionTitle();
|
|
42344
42630
|
const excludedTitle = sectionTitle();
|
|
42345
42631
|
const datastoreTitle = sectionTitle();
|
|
42346
42632
|
const watchTitle = sectionTitle();
|
|
42633
|
+
const agentRulesTitle = sectionTitle();
|
|
42347
42634
|
const languageLabel = fieldLabel("viewer-language");
|
|
42348
42635
|
const sidebarFontSizeLabel = fieldLabel("sidebar-font-size");
|
|
42349
42636
|
const codeFontSizeLabel = fieldLabel("code-font-size");
|
|
42350
42637
|
const omitDirsLabel = fieldLabel("scope-omit-dirs");
|
|
42351
42638
|
const excludeNamesLabel = fieldLabel("scope-exclude-names");
|
|
42352
42639
|
const watchLimitLabel = fieldLabel("scope-watch-limit");
|
|
42640
|
+
const agentRulesLabel = fieldLabel("agent-screen-rules");
|
|
42641
|
+
let generalDirty = false;
|
|
42642
|
+
let generalSavePending = false;
|
|
42643
|
+
let restoreDefaults = false;
|
|
42644
|
+
const changedGeneralFields = /* @__PURE__ */ new Set();
|
|
42645
|
+
let generalStatus = "idle";
|
|
42646
|
+
let agentRulesPending = false;
|
|
42647
|
+
let agentRulesDirty = false;
|
|
42648
|
+
let jsonHighlighter = null;
|
|
42649
|
+
let refreshGeneration = 0;
|
|
42353
42650
|
function build() {
|
|
42354
42651
|
const wrap = document.createElement("div");
|
|
42355
42652
|
wrap.className = "scope-settings";
|
|
@@ -42417,63 +42714,318 @@ ${formatErrorDetail(error2)}`);
|
|
|
42417
42714
|
const watch = section();
|
|
42418
42715
|
watch.id = "watch-settings-section";
|
|
42419
42716
|
watch.append(watchTitle, watchLimitLabel, watchRow, watchLimitHelp);
|
|
42717
|
+
agentRulesTitle.id = "agent-screen-rules-title";
|
|
42718
|
+
agentRules.id = "agent-screen-rules";
|
|
42719
|
+
agentRules.rows = 24;
|
|
42720
|
+
agentRules.spellcheck = false;
|
|
42721
|
+
agentRules.setAttribute(
|
|
42722
|
+
"aria-describedby",
|
|
42723
|
+
[
|
|
42724
|
+
"agent-screen-rules-help",
|
|
42725
|
+
"agent-screen-rules-source",
|
|
42726
|
+
"agent-screen-rules-error",
|
|
42727
|
+
"agent-screen-rules-highlight-error"
|
|
42728
|
+
].join(" ")
|
|
42729
|
+
);
|
|
42730
|
+
const agentRulesEditor = document.createElement("div");
|
|
42731
|
+
agentRulesEditor.className = "agent-screen-rules-editor";
|
|
42732
|
+
agentRulesEditor.append(agentRulesHighlight, agentRules);
|
|
42733
|
+
agentRulesGuide.append(
|
|
42734
|
+
agentRulesGuideTitle,
|
|
42735
|
+
agentRulesGuideIntro,
|
|
42736
|
+
agentRulesGuideFields,
|
|
42737
|
+
agentRulesGuideMatchers,
|
|
42738
|
+
agentRulesGuideRegions,
|
|
42739
|
+
agentRulesGuideExample
|
|
42740
|
+
);
|
|
42741
|
+
agentRulesSave.type = "button";
|
|
42742
|
+
agentRulesSave.id = "agent-screen-rules-save";
|
|
42743
|
+
agentRulesReset.type = "button";
|
|
42744
|
+
agentRulesReset.id = "agent-screen-rules-reset";
|
|
42745
|
+
const agentRuleActions = document.createElement("div");
|
|
42746
|
+
agentRuleActions.className = "scope-settings-actions";
|
|
42747
|
+
agentRuleActions.append(agentRulesReset, agentRulesSave);
|
|
42748
|
+
const ruleSettings = section();
|
|
42749
|
+
ruleSettings.classList.add("agent-screen-rules-section");
|
|
42750
|
+
ruleSettings.append(
|
|
42751
|
+
agentRulesTitle,
|
|
42752
|
+
agentRulesLabel,
|
|
42753
|
+
agentRulesHelp,
|
|
42754
|
+
agentRulesGuide,
|
|
42755
|
+
agentRulesEditor,
|
|
42756
|
+
agentRulesSource,
|
|
42757
|
+
agentRulesError,
|
|
42758
|
+
agentRulesHighlightError,
|
|
42759
|
+
agentRuleActions
|
|
42760
|
+
);
|
|
42420
42761
|
resetButton.id = "scope-omit-reset";
|
|
42421
42762
|
resetButton.type = "button";
|
|
42763
|
+
saveButton.id = "scope-settings-save";
|
|
42764
|
+
saveButton.type = "button";
|
|
42765
|
+
saveButton.className = "scope-settings-primary-action";
|
|
42766
|
+
const generalActions = document.createElement("div");
|
|
42767
|
+
generalActions.className = "scope-settings-actions";
|
|
42768
|
+
generalActions.append(resetButton, saveButton);
|
|
42422
42769
|
const footer = document.createElement("div");
|
|
42423
42770
|
footer.className = "scope-settings-footer";
|
|
42424
|
-
footer.append(
|
|
42425
|
-
|
|
42771
|
+
footer.append(
|
|
42772
|
+
saveNote,
|
|
42773
|
+
saveStatus,
|
|
42774
|
+
saveError,
|
|
42775
|
+
refreshError,
|
|
42776
|
+
generalActions
|
|
42777
|
+
);
|
|
42778
|
+
wrap.append(
|
|
42779
|
+
display,
|
|
42780
|
+
uploads,
|
|
42781
|
+
excluded,
|
|
42782
|
+
datastores,
|
|
42783
|
+
watch,
|
|
42784
|
+
ruleSettings,
|
|
42785
|
+
footer
|
|
42786
|
+
);
|
|
42426
42787
|
wire();
|
|
42788
|
+
void initializeJsonHighlighting();
|
|
42427
42789
|
return wrap;
|
|
42428
42790
|
}
|
|
42429
42791
|
function wire() {
|
|
42430
|
-
language.addEventListener(
|
|
42431
|
-
"change",
|
|
42432
|
-
() => deps.onLanguageChange(language.value)
|
|
42433
|
-
);
|
|
42792
|
+
language.addEventListener("change", () => markGeneralDirty("language"));
|
|
42434
42793
|
sidebarFontSize.addEventListener(
|
|
42435
42794
|
"change",
|
|
42436
|
-
() =>
|
|
42795
|
+
() => markGeneralDirty("sidebarFontSize")
|
|
42437
42796
|
);
|
|
42438
42797
|
codeFontSize.addEventListener(
|
|
42439
42798
|
"change",
|
|
42440
|
-
() =>
|
|
42799
|
+
() => markGeneralDirty("codeFontSize")
|
|
42441
42800
|
);
|
|
42442
42801
|
upload.input.addEventListener(
|
|
42443
42802
|
"change",
|
|
42444
|
-
() =>
|
|
42445
|
-
);
|
|
42446
|
-
omitDirs.addEventListener(
|
|
42447
|
-
"change",
|
|
42448
|
-
() => deps.onOmitDirsChange(omitDirs.value)
|
|
42449
|
-
);
|
|
42450
|
-
excludeNames.addEventListener(
|
|
42451
|
-
"change",
|
|
42452
|
-
() => deps.onExcludeNamesChange(excludeNames.value)
|
|
42803
|
+
() => markGeneralDirty("uploadEnabled")
|
|
42453
42804
|
);
|
|
42454
42805
|
inferFk.input.addEventListener(
|
|
42455
42806
|
"change",
|
|
42456
|
-
() =>
|
|
42807
|
+
() => markGeneralDirty("inferFkRails")
|
|
42457
42808
|
);
|
|
42458
42809
|
s3Tooltip.input.addEventListener(
|
|
42459
42810
|
"change",
|
|
42460
|
-
() =>
|
|
42811
|
+
() => markGeneralDirty("s3TooltipEnabled")
|
|
42461
42812
|
);
|
|
42813
|
+
omitDirs.addEventListener("input", () => markGeneralDirty("omitDirs"));
|
|
42814
|
+
excludeNames.addEventListener(
|
|
42815
|
+
"input",
|
|
42816
|
+
() => markGeneralDirty("excludeNames")
|
|
42817
|
+
);
|
|
42818
|
+
watchLimitNumber.addEventListener("input", () => {
|
|
42819
|
+
watchLimitRange.value = watchLimitNumber.value;
|
|
42820
|
+
markGeneralDirty("watchLimit");
|
|
42821
|
+
});
|
|
42462
42822
|
watchLimitNumber.addEventListener("change", () => {
|
|
42463
42823
|
watchLimitRange.value = watchLimitNumber.value;
|
|
42464
|
-
|
|
42824
|
+
markGeneralDirty("watchLimit");
|
|
42465
42825
|
});
|
|
42466
42826
|
watchLimitRange.addEventListener("input", () => {
|
|
42467
42827
|
watchLimitNumber.value = watchLimitRange.value;
|
|
42828
|
+
markGeneralDirty("watchLimit");
|
|
42468
42829
|
});
|
|
42469
42830
|
watchLimitRange.addEventListener(
|
|
42470
42831
|
"change",
|
|
42471
|
-
() =>
|
|
42832
|
+
() => markGeneralDirty("watchLimit")
|
|
42472
42833
|
);
|
|
42473
|
-
|
|
42474
|
-
|
|
42475
|
-
|
|
42834
|
+
agentRules.addEventListener("input", () => {
|
|
42835
|
+
agentRulesDirty = true;
|
|
42836
|
+
syncAgentRulesHighlight();
|
|
42837
|
+
});
|
|
42838
|
+
agentRules.addEventListener("scroll", syncAgentRulesScroll);
|
|
42839
|
+
agentRulesSave.addEventListener("click", () => {
|
|
42840
|
+
void updateAgentRules(() => deps.onAgentRulesSave(agentRules.value));
|
|
42476
42841
|
});
|
|
42842
|
+
agentRulesReset.addEventListener("click", () => {
|
|
42843
|
+
void updateAgentRules(() => deps.onAgentRulesReset());
|
|
42844
|
+
});
|
|
42845
|
+
resetButton.addEventListener("click", () => {
|
|
42846
|
+
applyGeneralFields(deps.getDefaultValues(), true);
|
|
42847
|
+
restoreDefaults = true;
|
|
42848
|
+
changedGeneralFields.clear();
|
|
42849
|
+
for (const field2 of GENERAL_SETTING_FIELDS) {
|
|
42850
|
+
changedGeneralFields.add(field2);
|
|
42851
|
+
}
|
|
42852
|
+
generalDirty = true;
|
|
42853
|
+
generalStatus = "dirty";
|
|
42854
|
+
clearGeneralSaveError();
|
|
42855
|
+
renderGeneralSaveState();
|
|
42856
|
+
});
|
|
42857
|
+
saveButton.addEventListener("click", () => {
|
|
42858
|
+
void saveGeneralSettings();
|
|
42859
|
+
});
|
|
42860
|
+
}
|
|
42861
|
+
function markGeneralDirty(field2) {
|
|
42862
|
+
if (generalSavePending) return;
|
|
42863
|
+
changedGeneralFields.add(field2);
|
|
42864
|
+
generalDirty = true;
|
|
42865
|
+
restoreDefaults = false;
|
|
42866
|
+
generalStatus = "dirty";
|
|
42867
|
+
watchLimitNumber.setCustomValidity("");
|
|
42868
|
+
clearGeneralSaveError();
|
|
42869
|
+
renderGeneralSaveState();
|
|
42870
|
+
}
|
|
42871
|
+
function clearGeneralSaveError() {
|
|
42872
|
+
saveError.textContent = "";
|
|
42873
|
+
saveError.hidden = true;
|
|
42874
|
+
}
|
|
42875
|
+
function renderGeneralSaveState() {
|
|
42876
|
+
const text3 = deps.getText();
|
|
42877
|
+
saveButton.textContent = generalStatus === "saving" ? text3.saving : text3.save;
|
|
42878
|
+
saveButton.disabled = generalSavePending || !generalDirty;
|
|
42879
|
+
resetButton.disabled = generalSavePending;
|
|
42880
|
+
saveStatus.textContent = generalStatus === "saving" ? text3.saving : generalStatus === "saved" ? text3.saved : generalDirty ? text3.unsaved : "";
|
|
42881
|
+
}
|
|
42882
|
+
function setGeneralControlsDisabled(disabled) {
|
|
42883
|
+
for (const field2 of [
|
|
42884
|
+
language,
|
|
42885
|
+
sidebarFontSize,
|
|
42886
|
+
codeFontSize,
|
|
42887
|
+
upload.input,
|
|
42888
|
+
omitDirs,
|
|
42889
|
+
excludeNames,
|
|
42890
|
+
inferFk.input,
|
|
42891
|
+
s3Tooltip.input,
|
|
42892
|
+
watchLimitNumber,
|
|
42893
|
+
watchLimitRange
|
|
42894
|
+
]) {
|
|
42895
|
+
field2.disabled = disabled;
|
|
42896
|
+
}
|
|
42897
|
+
}
|
|
42898
|
+
function applyGeneralFields(values, force = false) {
|
|
42899
|
+
setFieldValue(language, values.language, force);
|
|
42900
|
+
setFieldValue(sidebarFontSize, values.sidebarFontSize, force);
|
|
42901
|
+
setFieldValue(codeFontSize, values.codeFontSize, force);
|
|
42902
|
+
setFieldValue(omitDirs, values.omitDirs, force);
|
|
42903
|
+
setFieldValue(excludeNames, values.excludeNames, force);
|
|
42904
|
+
setFieldValue(watchLimitNumber, String(values.watchLimit), force);
|
|
42905
|
+
setFieldValue(watchLimitRange, String(values.watchLimit), force);
|
|
42906
|
+
upload.input.checked = values.uploadEnabled;
|
|
42907
|
+
inferFk.input.checked = values.inferFkRails;
|
|
42908
|
+
s3Tooltip.input.checked = values.s3TooltipEnabled;
|
|
42909
|
+
}
|
|
42910
|
+
function readGeneralDraft() {
|
|
42911
|
+
const values = deps.getValues();
|
|
42912
|
+
const watchLimit = Number(watchLimitNumber.value);
|
|
42913
|
+
if (!Number.isInteger(watchLimit) || watchLimit < values.watchLimitMin || watchLimit > values.watchLimitMax) {
|
|
42914
|
+
const message = deps.getText().watchLimitInvalid(values.watchLimitMin, values.watchLimitMax);
|
|
42915
|
+
watchLimitNumber.setCustomValidity(message);
|
|
42916
|
+
saveError.textContent = message;
|
|
42917
|
+
saveError.hidden = false;
|
|
42918
|
+
generalStatus = "dirty";
|
|
42919
|
+
renderGeneralSaveState();
|
|
42920
|
+
return null;
|
|
42921
|
+
}
|
|
42922
|
+
watchLimitNumber.setCustomValidity("");
|
|
42923
|
+
return {
|
|
42924
|
+
language: language.value,
|
|
42925
|
+
sidebarFontSize: sidebarFontSize.value,
|
|
42926
|
+
codeFontSize: codeFontSize.value,
|
|
42927
|
+
omitDirs: omitDirs.value,
|
|
42928
|
+
excludeNames: excludeNames.value,
|
|
42929
|
+
watchLimit,
|
|
42930
|
+
uploadEnabled: upload.input.checked,
|
|
42931
|
+
inferFkRails: inferFk.input.checked,
|
|
42932
|
+
s3TooltipEnabled: s3Tooltip.input.checked
|
|
42933
|
+
};
|
|
42934
|
+
}
|
|
42935
|
+
async function saveGeneralSettings() {
|
|
42936
|
+
if (generalSavePending || !generalDirty) return;
|
|
42937
|
+
const draft = readGeneralDraft();
|
|
42938
|
+
if (!draft) return;
|
|
42939
|
+
refreshGeneration += 1;
|
|
42940
|
+
generalSavePending = true;
|
|
42941
|
+
generalStatus = "saving";
|
|
42942
|
+
clearGeneralSaveError();
|
|
42943
|
+
setGeneralControlsDisabled(true);
|
|
42944
|
+
renderGeneralSaveState();
|
|
42945
|
+
let completed = false;
|
|
42946
|
+
try {
|
|
42947
|
+
await deps.onSave(draft, {
|
|
42948
|
+
restoreDefaults,
|
|
42949
|
+
changedFields: [...changedGeneralFields]
|
|
42950
|
+
});
|
|
42951
|
+
generalDirty = false;
|
|
42952
|
+
restoreDefaults = false;
|
|
42953
|
+
changedGeneralFields.clear();
|
|
42954
|
+
generalStatus = "saved";
|
|
42955
|
+
completed = true;
|
|
42956
|
+
} catch (error2) {
|
|
42957
|
+
console.error("[code-viewer] viewer settings save failed", error2);
|
|
42958
|
+
saveError.textContent = formatErrorDetail(error2);
|
|
42959
|
+
saveError.hidden = false;
|
|
42960
|
+
generalStatus = "dirty";
|
|
42961
|
+
} finally {
|
|
42962
|
+
generalSavePending = false;
|
|
42963
|
+
setGeneralControlsDisabled(false);
|
|
42964
|
+
if (completed) sync();
|
|
42965
|
+
else renderGeneralSaveState();
|
|
42966
|
+
}
|
|
42967
|
+
}
|
|
42968
|
+
async function initializeJsonHighlighting() {
|
|
42969
|
+
try {
|
|
42970
|
+
jsonHighlighter = await loadShikiHighlighter({
|
|
42971
|
+
themes: ["github-light", "github-dark"],
|
|
42972
|
+
langs: ["json"],
|
|
42973
|
+
failureMode: "throw"
|
|
42974
|
+
});
|
|
42975
|
+
if (!jsonHighlighter) {
|
|
42976
|
+
throw new Error("JSON syntax highlighting could not be loaded");
|
|
42977
|
+
}
|
|
42978
|
+
syncAgentRulesHighlight();
|
|
42979
|
+
} catch (error2) {
|
|
42980
|
+
console.error("[code-viewer] JSON syntax highlighting failed", error2);
|
|
42981
|
+
agentRulesHighlightError.textContent = formatErrorDetail(error2);
|
|
42982
|
+
agentRulesHighlightError.hidden = false;
|
|
42983
|
+
}
|
|
42984
|
+
}
|
|
42985
|
+
function syncAgentRulesHighlight() {
|
|
42986
|
+
const highlighted = highlightToInnerHtml(
|
|
42987
|
+
agentRules.value,
|
|
42988
|
+
"json",
|
|
42989
|
+
jsonHighlighter
|
|
42990
|
+
);
|
|
42991
|
+
if (highlighted) agentRulesHighlight.innerHTML = highlighted;
|
|
42992
|
+
else agentRulesHighlight.textContent = agentRules.value;
|
|
42993
|
+
syncAgentRulesScroll();
|
|
42994
|
+
}
|
|
42995
|
+
function syncAgentRulesScroll() {
|
|
42996
|
+
agentRulesHighlight.scrollTop = agentRules.scrollTop;
|
|
42997
|
+
agentRulesHighlight.scrollLeft = agentRules.scrollLeft;
|
|
42998
|
+
}
|
|
42999
|
+
async function updateAgentRules(operation) {
|
|
43000
|
+
if (agentRulesPending) return;
|
|
43001
|
+
refreshGeneration += 1;
|
|
43002
|
+
agentRulesPending = true;
|
|
43003
|
+
agentRules.disabled = true;
|
|
43004
|
+
agentRulesSave.disabled = true;
|
|
43005
|
+
agentRulesReset.disabled = true;
|
|
43006
|
+
agentRulesSource.textContent = deps.getText().agentRulesSaving;
|
|
43007
|
+
agentRulesError.hidden = true;
|
|
43008
|
+
agentRulesError.textContent = "";
|
|
43009
|
+
let completed = false;
|
|
43010
|
+
try {
|
|
43011
|
+
await operation();
|
|
43012
|
+
agentRulesDirty = false;
|
|
43013
|
+
completed = true;
|
|
43014
|
+
} catch (error2) {
|
|
43015
|
+
console.error("[code-viewer] terminal rule update failed", error2);
|
|
43016
|
+
agentRulesError.textContent = formatErrorDetail(error2);
|
|
43017
|
+
agentRulesError.hidden = false;
|
|
43018
|
+
} finally {
|
|
43019
|
+
agentRulesPending = false;
|
|
43020
|
+
agentRules.disabled = false;
|
|
43021
|
+
agentRulesSave.disabled = false;
|
|
43022
|
+
agentRulesReset.disabled = false;
|
|
43023
|
+
if (completed) {
|
|
43024
|
+
sync();
|
|
43025
|
+
} else {
|
|
43026
|
+
agentRulesSource.textContent = deps.getValues().agentRulesSource === "saved" ? deps.getText().agentRulesSourceSaved : deps.getText().agentRulesSourceDefault;
|
|
43027
|
+
}
|
|
43028
|
+
}
|
|
42477
43029
|
}
|
|
42478
43030
|
function applyText() {
|
|
42479
43031
|
const text3 = deps.getText();
|
|
@@ -42483,12 +43035,14 @@ ${formatErrorDetail(error2)}`);
|
|
|
42483
43035
|
excludedTitle.textContent = text3.excludedDirectories;
|
|
42484
43036
|
datastoreTitle.textContent = text3.datastoreTitle;
|
|
42485
43037
|
watchTitle.textContent = text3.watchTitle;
|
|
43038
|
+
agentRulesTitle.textContent = text3.agentRulesTitle;
|
|
42486
43039
|
languageLabel.textContent = text3.language;
|
|
42487
43040
|
sidebarFontSizeLabel.textContent = text3.fileListFontSize;
|
|
42488
43041
|
codeFontSizeLabel.textContent = text3.codeFontSize;
|
|
42489
43042
|
omitDirsLabel.textContent = text3.omitDirs;
|
|
42490
43043
|
excludeNamesLabel.textContent = text3.excludeNames;
|
|
42491
43044
|
watchLimitLabel.textContent = text3.watchLimit;
|
|
43045
|
+
agentRulesLabel.textContent = text3.agentRulesLabel;
|
|
42492
43046
|
uiFontSizeHelp.textContent = text3.fileListFontSizeHelp;
|
|
42493
43047
|
displaySource.textContent = text3.displaySource;
|
|
42494
43048
|
upload.text.textContent = text3.uploadEnabledLabel;
|
|
@@ -42500,37 +43054,49 @@ ${formatErrorDetail(error2)}`);
|
|
|
42500
43054
|
s3Tooltip.text.textContent = text3.datastoreS3TooltipLabel;
|
|
42501
43055
|
s3TooltipHelp.textContent = text3.datastoreS3TooltipHelp;
|
|
42502
43056
|
watchLimitHelp.textContent = text3.watchLimitHelp(values.watchLimitDefault);
|
|
42503
|
-
|
|
43057
|
+
agentRulesHelp.textContent = text3.agentRulesHelp;
|
|
43058
|
+
agentRulesGuideTitle.textContent = text3.agentRulesGuideTitle;
|
|
43059
|
+
agentRulesGuideIntro.textContent = text3.agentRulesGuideIntro;
|
|
43060
|
+
agentRulesGuideFields.textContent = text3.agentRulesGuideFields;
|
|
43061
|
+
agentRulesGuideMatchers.textContent = text3.agentRulesGuideMatchers;
|
|
43062
|
+
agentRulesGuideRegions.textContent = text3.agentRulesGuideRegions;
|
|
43063
|
+
agentRulesGuideExample.textContent = text3.agentRulesGuideExample;
|
|
43064
|
+
agentRulesSave.textContent = text3.agentRulesSave;
|
|
43065
|
+
agentRulesReset.textContent = text3.agentRulesReset;
|
|
43066
|
+
saveNote.textContent = text3.saveNote;
|
|
42504
43067
|
resetButton.textContent = text3.reset;
|
|
43068
|
+
renderGeneralSaveState();
|
|
42505
43069
|
const sizeLabels = {
|
|
42506
43070
|
compact: text3.sizeSmall,
|
|
42507
43071
|
regular: text3.sizeRegular,
|
|
42508
43072
|
large: text3.sizeLarge,
|
|
42509
43073
|
xlarge: text3.sizeExtraLarge
|
|
42510
43074
|
};
|
|
42511
|
-
for (const select of [sidebarFontSize, codeFontSize])
|
|
42512
|
-
for (const option of Array.from(select.options))
|
|
43075
|
+
for (const select of [sidebarFontSize, codeFontSize]) {
|
|
43076
|
+
for (const option of Array.from(select.options)) {
|
|
42513
43077
|
option.textContent = sizeLabels[option.value] || option.value;
|
|
43078
|
+
}
|
|
43079
|
+
}
|
|
42514
43080
|
}
|
|
42515
43081
|
function sync() {
|
|
42516
43082
|
if (!root) return;
|
|
42517
43083
|
applyText();
|
|
42518
43084
|
const values = deps.getValues();
|
|
42519
|
-
setFieldValue(language, values.language);
|
|
42520
|
-
setFieldValue(sidebarFontSize, values.sidebarFontSize);
|
|
42521
|
-
setFieldValue(codeFontSize, values.codeFontSize);
|
|
42522
|
-
setFieldValue(omitDirs, values.omitDirs);
|
|
42523
|
-
setFieldValue(excludeNames, values.excludeNames);
|
|
42524
43085
|
watchLimitNumber.min = String(values.watchLimitMin);
|
|
42525
43086
|
watchLimitNumber.max = String(values.watchLimitMax);
|
|
42526
43087
|
watchLimitRange.min = String(values.watchLimitMin);
|
|
42527
43088
|
watchLimitRange.max = String(values.watchLimitMax);
|
|
42528
|
-
|
|
42529
|
-
setFieldValue(watchLimitRange, String(values.watchLimit));
|
|
42530
|
-
upload.input.checked = values.uploadEnabled;
|
|
42531
|
-
inferFk.input.checked = values.inferFkRails;
|
|
42532
|
-
s3Tooltip.input.checked = values.s3TooltipEnabled;
|
|
43089
|
+
if (!generalDirty && !generalSavePending) applyGeneralFields(values);
|
|
42533
43090
|
scopeSource.textContent = values.scopeSource;
|
|
43091
|
+
if (!agentRulesDirty && !agentRulesPending) {
|
|
43092
|
+
setFieldValue(agentRules, values.agentRulesJson);
|
|
43093
|
+
syncAgentRulesHighlight();
|
|
43094
|
+
}
|
|
43095
|
+
if (!agentRulesPending) {
|
|
43096
|
+
agentRulesSource.textContent = values.agentRulesSource === "saved" ? deps.getText().agentRulesSourceSaved : deps.getText().agentRulesSourceDefault;
|
|
43097
|
+
agentRulesError.textContent = values.agentRulesErrors;
|
|
43098
|
+
agentRulesError.hidden = !values.agentRulesErrors;
|
|
43099
|
+
}
|
|
42534
43100
|
}
|
|
42535
43101
|
function mount(host) {
|
|
42536
43102
|
if (!root) root = build();
|
|
@@ -42550,11 +43116,19 @@ ${formatErrorDetail(error2)}`);
|
|
|
42550
43116
|
sync();
|
|
42551
43117
|
refreshError.hidden = true;
|
|
42552
43118
|
refreshError.textContent = "";
|
|
42553
|
-
|
|
42554
|
-
|
|
42555
|
-
|
|
42556
|
-
|
|
42557
|
-
|
|
43119
|
+
const generation = ++refreshGeneration;
|
|
43120
|
+
void deps.refresh().then(
|
|
43121
|
+
() => {
|
|
43122
|
+
if (generation !== refreshGeneration) return;
|
|
43123
|
+
sync();
|
|
43124
|
+
},
|
|
43125
|
+
(error2) => {
|
|
43126
|
+
if (generation !== refreshGeneration) return;
|
|
43127
|
+
console.error("[code-viewer] viewer settings refresh failed", error2);
|
|
43128
|
+
refreshError.textContent = formatErrorDetail(error2);
|
|
43129
|
+
refreshError.hidden = false;
|
|
43130
|
+
}
|
|
43131
|
+
);
|
|
42558
43132
|
}
|
|
42559
43133
|
function localize() {
|
|
42560
43134
|
if (!root) return;
|
|
@@ -42584,6 +43158,11 @@ ${formatErrorDetail(error2)}`);
|
|
|
42584
43158
|
let PROJECT_BRANCH = "";
|
|
42585
43159
|
let REPO_WEB_URL = null;
|
|
42586
43160
|
let APP_SETTINGS = { version: 1 };
|
|
43161
|
+
let AGENT_SCREEN_RULES = formatAgentScreenRuleSet(DEFAULT_AGENT_SCREEN_RULES);
|
|
43162
|
+
let AGENT_SCREEN_RULES_SOURCE = "default";
|
|
43163
|
+
let AGENT_SCREEN_RULE_ERRORS = [];
|
|
43164
|
+
let AGENT_SCREEN_RULES_GENERATION = 0;
|
|
43165
|
+
let AGENT_SCREEN_RULE_REQUEST_GENERATION = 0;
|
|
42587
43166
|
let VIEW_STATE = {
|
|
42588
43167
|
version: 1,
|
|
42589
43168
|
collapsedDirs: [],
|
|
@@ -42752,6 +43331,13 @@ ${formatErrorDetail(error2)}`);
|
|
|
42752
43331
|
"X-Code-Viewer-Action": "1"
|
|
42753
43332
|
};
|
|
42754
43333
|
}
|
|
43334
|
+
function reportPersistenceError(operation, error2) {
|
|
43335
|
+
const failure = errorWithCause(`${operation} failed`, error2);
|
|
43336
|
+
console.error(failure);
|
|
43337
|
+
setStatus("error");
|
|
43338
|
+
const statusEl = document.querySelector("#status");
|
|
43339
|
+
if (statusEl) statusEl.title = formatErrorDetail(failure);
|
|
43340
|
+
}
|
|
42755
43341
|
let cachedKeymapOverrides;
|
|
42756
43342
|
let cachedKeyBindings = DEFAULT_KEY_BINDINGS;
|
|
42757
43343
|
function activeKeyBindings() {
|
|
@@ -42761,29 +43347,95 @@ ${formatErrorDetail(error2)}`);
|
|
|
42761
43347
|
}
|
|
42762
43348
|
return cachedKeyBindings;
|
|
42763
43349
|
}
|
|
42764
|
-
|
|
42765
|
-
|
|
42766
|
-
|
|
42767
|
-
|
|
42768
|
-
|
|
42769
|
-
|
|
42770
|
-
|
|
42771
|
-
|
|
42772
|
-
}
|
|
43350
|
+
let pendingSettingsPatch = null;
|
|
43351
|
+
let pendingSettingsKeepalive = false;
|
|
43352
|
+
let settingsPatchInFlight = null;
|
|
43353
|
+
async function flushSettingsPatch() {
|
|
43354
|
+
if (settingsPatchInFlight) {
|
|
43355
|
+
await settingsPatchInFlight;
|
|
43356
|
+
if (pendingSettingsPatch) await flushSettingsPatch();
|
|
43357
|
+
return;
|
|
43358
|
+
}
|
|
43359
|
+
if (!pendingSettingsPatch) return;
|
|
43360
|
+
const patch = pendingSettingsPatch;
|
|
43361
|
+
const keepalive = pendingSettingsKeepalive;
|
|
43362
|
+
pendingSettingsPatch = null;
|
|
43363
|
+
pendingSettingsKeepalive = false;
|
|
43364
|
+
const operation = sendSettingsPatch(patch, keepalive).then(() => void 0);
|
|
43365
|
+
settingsPatchInFlight = operation;
|
|
43366
|
+
try {
|
|
43367
|
+
await operation;
|
|
43368
|
+
} catch (error2) {
|
|
43369
|
+
pendingSettingsPatch = {
|
|
43370
|
+
...patch,
|
|
43371
|
+
...pendingSettingsPatch || {}
|
|
43372
|
+
};
|
|
43373
|
+
pendingSettingsKeepalive ||= keepalive;
|
|
43374
|
+
throw error2;
|
|
43375
|
+
} finally {
|
|
43376
|
+
if (settingsPatchInFlight === operation) settingsPatchInFlight = null;
|
|
43377
|
+
}
|
|
43378
|
+
if (pendingSettingsPatch) await flushSettingsPatch();
|
|
42773
43379
|
}
|
|
42774
|
-
async function
|
|
43380
|
+
async function sendSettingsPatch(patch, keepalive = false) {
|
|
42775
43381
|
const response = await trackLoad(
|
|
42776
43382
|
fetch("/_state/settings", {
|
|
42777
43383
|
method: "PATCH",
|
|
42778
43384
|
headers: actionHeaders(),
|
|
42779
|
-
body: JSON.stringify(patch)
|
|
43385
|
+
body: JSON.stringify(patch),
|
|
43386
|
+
keepalive
|
|
42780
43387
|
})
|
|
42781
43388
|
);
|
|
42782
|
-
if (!response.ok)
|
|
42783
|
-
|
|
43389
|
+
if (!response.ok) {
|
|
43390
|
+
throw new Error(
|
|
43391
|
+
await responseErrorMessage(response, "save viewer settings")
|
|
43392
|
+
);
|
|
43393
|
+
}
|
|
43394
|
+
try {
|
|
43395
|
+
return await response.json();
|
|
43396
|
+
} catch (error2) {
|
|
43397
|
+
throw errorWithCause(
|
|
43398
|
+
"save viewer settings: response is not valid JSON",
|
|
43399
|
+
error2
|
|
43400
|
+
);
|
|
43401
|
+
}
|
|
43402
|
+
}
|
|
43403
|
+
function patchSettings(patch, options = {}) {
|
|
43404
|
+
mergeLocalSettings(patch);
|
|
43405
|
+
pendingSettingsPatch = {
|
|
43406
|
+
...pendingSettingsPatch || {},
|
|
43407
|
+
...patch
|
|
43408
|
+
};
|
|
43409
|
+
pendingSettingsKeepalive ||= options.keepalive === true;
|
|
43410
|
+
if (!settingsPatchInFlight) {
|
|
43411
|
+
void flushSettingsPatch().catch((error2) => {
|
|
43412
|
+
reportPersistenceError("save viewer settings", error2);
|
|
43413
|
+
});
|
|
43414
|
+
}
|
|
43415
|
+
}
|
|
43416
|
+
async function persistSettingsPatch(patch) {
|
|
43417
|
+
await flushSettingsPatch();
|
|
43418
|
+
while (settingsPatchInFlight) await settingsPatchInFlight;
|
|
43419
|
+
const operation = sendSettingsPatch(patch).then((state) => {
|
|
43420
|
+
APP_SETTINGS = state;
|
|
43421
|
+
if (pendingSettingsPatch) mergeLocalSettings(pendingSettingsPatch);
|
|
43422
|
+
});
|
|
43423
|
+
settingsPatchInFlight = operation;
|
|
43424
|
+
try {
|
|
43425
|
+
await operation;
|
|
43426
|
+
} finally {
|
|
43427
|
+
if (settingsPatchInFlight === operation) settingsPatchInFlight = null;
|
|
43428
|
+
if (pendingSettingsPatch) {
|
|
43429
|
+
void flushSettingsPatch().catch((error2) => {
|
|
43430
|
+
reportPersistenceError("save viewer settings", error2);
|
|
43431
|
+
});
|
|
43432
|
+
}
|
|
43433
|
+
}
|
|
42784
43434
|
}
|
|
42785
43435
|
let pendingViewPatch = null;
|
|
42786
43436
|
let pendingViewTimer = null;
|
|
43437
|
+
let pendingViewKeepalive = false;
|
|
43438
|
+
let viewPatchInFlight = false;
|
|
42787
43439
|
function mergePathDelta(next, base2, patch, addKey, removeKey) {
|
|
42788
43440
|
const added = new Set(base2?.[addKey] || []);
|
|
42789
43441
|
const removed = new Set(base2?.[removeKey] || []);
|
|
@@ -42845,19 +43497,44 @@ ${formatErrorDetail(error2)}`);
|
|
|
42845
43497
|
viewedFiles: [...viewedFiles]
|
|
42846
43498
|
};
|
|
42847
43499
|
}
|
|
43500
|
+
async function sendPendingViewPatch() {
|
|
43501
|
+
if (viewPatchInFlight || !pendingViewPatch) return;
|
|
43502
|
+
const patch = pendingViewPatch;
|
|
43503
|
+
const keepalive = pendingViewKeepalive;
|
|
43504
|
+
pendingViewPatch = null;
|
|
43505
|
+
pendingViewKeepalive = false;
|
|
43506
|
+
viewPatchInFlight = true;
|
|
43507
|
+
let saved = false;
|
|
43508
|
+
try {
|
|
43509
|
+
const response = await trackLoad(
|
|
43510
|
+
fetch("/_state/view", {
|
|
43511
|
+
method: "PATCH",
|
|
43512
|
+
headers: actionHeaders(),
|
|
43513
|
+
body: JSON.stringify(patch),
|
|
43514
|
+
keepalive
|
|
43515
|
+
})
|
|
43516
|
+
);
|
|
43517
|
+
if (!response.ok) {
|
|
43518
|
+
throw new Error(
|
|
43519
|
+
await responseErrorMessage(response, "save viewer state")
|
|
43520
|
+
);
|
|
43521
|
+
}
|
|
43522
|
+
saved = true;
|
|
43523
|
+
} catch (error2) {
|
|
43524
|
+
pendingViewPatch = mergeViewPatch(patch, pendingViewPatch || {});
|
|
43525
|
+
reportPersistenceError("save viewer state", error2);
|
|
43526
|
+
} finally {
|
|
43527
|
+
viewPatchInFlight = false;
|
|
43528
|
+
}
|
|
43529
|
+
if (saved && pendingViewPatch) void sendPendingViewPatch();
|
|
43530
|
+
}
|
|
42848
43531
|
function patchViewState(patch, options = {}) {
|
|
42849
43532
|
VIEW_STATE = mergeLocalViewState(VIEW_STATE, patch);
|
|
42850
43533
|
pendingViewPatch = mergeViewPatch(pendingViewPatch, patch);
|
|
42851
43534
|
const send = (keepalive = false) => {
|
|
42852
43535
|
if (!pendingViewPatch) return;
|
|
42853
|
-
|
|
42854
|
-
|
|
42855
|
-
void fetch("/_state/view", {
|
|
42856
|
-
method: "PATCH",
|
|
42857
|
-
headers: actionHeaders(),
|
|
42858
|
-
body,
|
|
42859
|
-
keepalive
|
|
42860
|
-
}).catch(() => void 0);
|
|
43536
|
+
pendingViewKeepalive ||= keepalive;
|
|
43537
|
+
void sendPendingViewPatch();
|
|
42861
43538
|
};
|
|
42862
43539
|
if (options.keepalive) {
|
|
42863
43540
|
if (pendingViewTimer !== null) clearTimeout(pendingViewTimer);
|
|
@@ -42881,14 +43558,8 @@ ${formatErrorDetail(error2)}`);
|
|
|
42881
43558
|
if (!pendingViewPatch) return;
|
|
42882
43559
|
if (pendingViewTimer !== null) clearTimeout(pendingViewTimer);
|
|
42883
43560
|
pendingViewTimer = null;
|
|
42884
|
-
|
|
42885
|
-
|
|
42886
|
-
void fetch("/_state/view", {
|
|
42887
|
-
method: "PATCH",
|
|
42888
|
-
headers: actionHeaders(),
|
|
42889
|
-
body,
|
|
42890
|
-
keepalive
|
|
42891
|
-
}).catch(() => void 0);
|
|
43561
|
+
pendingViewKeepalive ||= keepalive;
|
|
43562
|
+
void sendPendingViewPatch();
|
|
42892
43563
|
}
|
|
42893
43564
|
function savedScopeOmitDirs() {
|
|
42894
43565
|
return APP_SETTINGS.scopeOmitDirs ? normalizeScopeOmitDirs(APP_SETTINGS.scopeOmitDirs) : null;
|
|
@@ -42968,50 +43639,152 @@ ${formatErrorDetail(error2)}`);
|
|
|
42968
43639
|
return `${ref}\0${omit ? omit.join("\0") : "server"}\0${exclude ? exclude.join("\0") : "server"}`;
|
|
42969
43640
|
}
|
|
42970
43641
|
async function loadSettings() {
|
|
43642
|
+
const res = await trackLoad(fetch("/_settings"));
|
|
43643
|
+
if (!res.ok) {
|
|
43644
|
+
throw new Error(
|
|
43645
|
+
await responseErrorMessage(res, "settings request failed")
|
|
43646
|
+
);
|
|
43647
|
+
}
|
|
43648
|
+
let settings;
|
|
42971
43649
|
try {
|
|
42972
|
-
|
|
42973
|
-
|
|
42974
|
-
|
|
42975
|
-
|
|
42976
|
-
|
|
42977
|
-
|
|
42978
|
-
|
|
42979
|
-
|
|
42980
|
-
|
|
42981
|
-
|
|
42982
|
-
|
|
42983
|
-
|
|
42984
|
-
|
|
43650
|
+
settings = await res.json();
|
|
43651
|
+
} catch (error2) {
|
|
43652
|
+
throw errorWithCause("settings response is not valid JSON", error2);
|
|
43653
|
+
}
|
|
43654
|
+
setProjectName(settings.project || "");
|
|
43655
|
+
setProjectBranch(settings.branch || "");
|
|
43656
|
+
REPO_WEB_URL = settings.repo_web_url;
|
|
43657
|
+
const repoLink = document.querySelector("#repo-web-link");
|
|
43658
|
+
if (repoLink) {
|
|
43659
|
+
repoLink.href = settings.repo_web_url || "#";
|
|
43660
|
+
repoLink.hidden = !settings.repo_web_url;
|
|
43661
|
+
}
|
|
43662
|
+
SERVER_SCOPE_OMIT_DIRS_DEFAULT = normalizeScopeOmitDirs(
|
|
43663
|
+
settings.scope.omit_dirs_effective
|
|
43664
|
+
);
|
|
43665
|
+
SERVER_SCOPE_EXCLUDE_NAMES_DEFAULT = normalizeScopeExcludeNames(
|
|
43666
|
+
settings.scope.exclude_names_effective
|
|
43667
|
+
);
|
|
43668
|
+
if (typeof settings.scope.watch_limit_default === "number")
|
|
43669
|
+
SERVER_SCOPE_WATCH_LIMIT_DEFAULT = settings.scope.watch_limit_default;
|
|
43670
|
+
if (typeof settings.scope.watch_limit_min === "number")
|
|
43671
|
+
SERVER_SCOPE_WATCH_LIMIT_MIN = settings.scope.watch_limit_min;
|
|
43672
|
+
if (typeof settings.scope.watch_limit_max === "number")
|
|
43673
|
+
SERVER_SCOPE_WATCH_LIMIT_MAX = settings.scope.watch_limit_max;
|
|
43674
|
+
if (typeof settings.scope.watch_recursive === "boolean") {
|
|
43675
|
+
const watchSection = document.querySelector(
|
|
43676
|
+
"#watch-settings-section"
|
|
43677
|
+
);
|
|
43678
|
+
if (watchSection) watchSection.hidden = settings.scope.watch_recursive;
|
|
43679
|
+
}
|
|
43680
|
+
return settings;
|
|
43681
|
+
}
|
|
43682
|
+
function agentScreenRuleErrorsText(errors2) {
|
|
43683
|
+
return errors2.map(
|
|
43684
|
+
(error2) => `${error2.path} [${error2.code}] ${error2.message}${error2.stack ? `
|
|
43685
|
+
${error2.stack}` : ""}`
|
|
43686
|
+
).join("\n\n");
|
|
43687
|
+
}
|
|
43688
|
+
async function agentScreenRuleResponse(response) {
|
|
43689
|
+
let body;
|
|
43690
|
+
try {
|
|
43691
|
+
body = await response.json();
|
|
43692
|
+
} catch (error2) {
|
|
43693
|
+
throw errorWithCause(
|
|
43694
|
+
`terminal rule request returned ${response.status} with invalid JSON`,
|
|
43695
|
+
error2
|
|
42985
43696
|
);
|
|
42986
|
-
|
|
42987
|
-
|
|
43697
|
+
}
|
|
43698
|
+
if (!response.ok) {
|
|
43699
|
+
throw Object.assign(
|
|
43700
|
+
new Error(
|
|
43701
|
+
`terminal rule request failed with status ${response.status}`
|
|
43702
|
+
),
|
|
43703
|
+
{ status: response.status, response: body }
|
|
42988
43704
|
);
|
|
42989
|
-
|
|
42990
|
-
|
|
42991
|
-
|
|
42992
|
-
|
|
42993
|
-
|
|
42994
|
-
|
|
42995
|
-
|
|
42996
|
-
|
|
42997
|
-
|
|
42998
|
-
|
|
42999
|
-
|
|
43000
|
-
|
|
43001
|
-
|
|
43002
|
-
|
|
43003
|
-
|
|
43705
|
+
}
|
|
43706
|
+
if (!body || typeof body !== "object" || !("rules" in body)) {
|
|
43707
|
+
throw Object.assign(new Error("terminal rule response is incomplete"), {
|
|
43708
|
+
response: body
|
|
43709
|
+
});
|
|
43710
|
+
}
|
|
43711
|
+
if (!("generation" in body) || typeof body.generation !== "number") {
|
|
43712
|
+
throw Object.assign(
|
|
43713
|
+
new Error("terminal rule response has no generation"),
|
|
43714
|
+
{ response: body }
|
|
43715
|
+
);
|
|
43716
|
+
}
|
|
43717
|
+
return body;
|
|
43718
|
+
}
|
|
43719
|
+
function applyAgentScreenRuleResponse(response) {
|
|
43720
|
+
if (response.generation < AGENT_SCREEN_RULES_GENERATION) return;
|
|
43721
|
+
AGENT_SCREEN_RULES = formatAgentScreenRuleSet(response.rules);
|
|
43722
|
+
AGENT_SCREEN_RULES_SOURCE = response.source;
|
|
43723
|
+
AGENT_SCREEN_RULE_ERRORS = response.errors;
|
|
43724
|
+
AGENT_SCREEN_RULES_GENERATION = response.generation;
|
|
43725
|
+
}
|
|
43726
|
+
async function loadAgentScreenRules() {
|
|
43727
|
+
const generation = ++AGENT_SCREEN_RULE_REQUEST_GENERATION;
|
|
43728
|
+
const response = await agentScreenRuleResponse(
|
|
43729
|
+
await trackLoad(fetch("/_agent/rules"))
|
|
43730
|
+
);
|
|
43731
|
+
if (generation !== AGENT_SCREEN_RULE_REQUEST_GENERATION) return;
|
|
43732
|
+
applyAgentScreenRuleResponse(response);
|
|
43733
|
+
}
|
|
43734
|
+
async function saveAgentScreenRules(value) {
|
|
43735
|
+
let rules;
|
|
43736
|
+
try {
|
|
43737
|
+
rules = JSON.parse(value);
|
|
43738
|
+
} catch (error2) {
|
|
43739
|
+
throw errorWithCause("terminal rules are not valid JSON", error2);
|
|
43740
|
+
}
|
|
43741
|
+
const generation = ++AGENT_SCREEN_RULE_REQUEST_GENERATION;
|
|
43742
|
+
const response = await agentScreenRuleResponse(
|
|
43743
|
+
await trackLoad(
|
|
43744
|
+
fetch("/_agent/rules", {
|
|
43745
|
+
method: "PUT",
|
|
43746
|
+
headers: actionHeaders(),
|
|
43747
|
+
body: JSON.stringify(rules)
|
|
43748
|
+
})
|
|
43749
|
+
)
|
|
43750
|
+
);
|
|
43751
|
+
if (generation !== AGENT_SCREEN_RULE_REQUEST_GENERATION) return;
|
|
43752
|
+
applyAgentScreenRuleResponse(response);
|
|
43753
|
+
}
|
|
43754
|
+
async function resetAgentScreenRuleSettings() {
|
|
43755
|
+
const generation = ++AGENT_SCREEN_RULE_REQUEST_GENERATION;
|
|
43756
|
+
const response = await agentScreenRuleResponse(
|
|
43757
|
+
await trackLoad(
|
|
43758
|
+
fetch("/_agent/rules", {
|
|
43759
|
+
method: "DELETE",
|
|
43760
|
+
headers: actionHeaders()
|
|
43761
|
+
})
|
|
43762
|
+
)
|
|
43763
|
+
);
|
|
43764
|
+
if (generation !== AGENT_SCREEN_RULE_REQUEST_GENERATION) return;
|
|
43765
|
+
applyAgentScreenRuleResponse(response);
|
|
43766
|
+
}
|
|
43767
|
+
async function loadStateResponse(url, operation) {
|
|
43768
|
+
const response = await trackLoad(fetch(url));
|
|
43769
|
+
if (!response.ok) {
|
|
43770
|
+
throw new Error(await responseErrorMessage(response, operation));
|
|
43771
|
+
}
|
|
43772
|
+
try {
|
|
43773
|
+
return await response.json();
|
|
43774
|
+
} catch (error2) {
|
|
43775
|
+
throw errorWithCause(`${operation}: response is not valid JSON`, error2);
|
|
43004
43776
|
}
|
|
43005
43777
|
}
|
|
43006
43778
|
async function loadPersistedState() {
|
|
43007
43779
|
const [settings, view] = await Promise.all([
|
|
43008
|
-
|
|
43009
|
-
|
|
43010
|
-
|
|
43011
|
-
|
|
43780
|
+
loadStateResponse(
|
|
43781
|
+
"/_state/settings",
|
|
43782
|
+
"settings state request failed"
|
|
43783
|
+
),
|
|
43784
|
+
loadStateResponse("/_state/view", "view state request failed")
|
|
43012
43785
|
]);
|
|
43013
|
-
|
|
43014
|
-
|
|
43786
|
+
APP_SETTINGS = settings;
|
|
43787
|
+
VIEW_STATE = view;
|
|
43015
43788
|
}
|
|
43016
43789
|
function routeFromLocation() {
|
|
43017
43790
|
const savedLanguage = viewerLanguageFromSearch(window.location.search) || savedViewerLanguage();
|
|
@@ -43421,7 +44194,7 @@ ${formatErrorDetail(error2)}`);
|
|
|
43421
44194
|
getGrepGroupByFile: () => APP_SETTINGS.grepGroupByFile === true,
|
|
43422
44195
|
getGrepPaletteWidth: () => APP_SETTINGS.grepPaletteWidth,
|
|
43423
44196
|
getGrepPaletteHeight: () => APP_SETTINGS.grepPaletteHeight,
|
|
43424
|
-
persistGrepSettings,
|
|
44197
|
+
persistGrepSettings: persistSettingsPatch,
|
|
43425
44198
|
applyGrepHideTests: (hidden) => {
|
|
43426
44199
|
STATE.hideTests = hidden;
|
|
43427
44200
|
applyHideTests();
|
|
@@ -43707,7 +44480,12 @@ ${formatErrorDetail(error2)}`);
|
|
|
43707
44480
|
excludeNames: "Hide these file or directory names completely",
|
|
43708
44481
|
excludeNamesHelp: "Removes matching files or directories from the sidebar, search, and grep results entirely. Unlike Skip, the names themselves disappear from the UI. Supports gitignore-style wildcards (*, ?, [abc], [!abc]).",
|
|
43709
44482
|
reset: "Restore defaults",
|
|
43710
|
-
|
|
44483
|
+
save: "Save changes",
|
|
44484
|
+
saving: "Saving…",
|
|
44485
|
+
saved: "Saved.",
|
|
44486
|
+
unsaved: "Unsaved changes.",
|
|
44487
|
+
saveNote: "Edits are not applied until you select Save changes.",
|
|
44488
|
+
watchLimitInvalid: (min, max) => `Enter a whole number from ${min} to ${max}.`,
|
|
43711
44489
|
scopeSource: (project, source) => `Saved for project "${project}" in this browser. Source: ${source}. Used by the sidebar, Ctrl+K, Ctrl+G, Datastores, and the file change watcher. Restore defaults removes the browser override.`,
|
|
43712
44490
|
browserOverride: "Browser override",
|
|
43713
44491
|
serverDefault: "Server default",
|
|
@@ -43721,7 +44499,34 @@ ${formatErrorDetail(error2)}`);
|
|
|
43721
44499
|
datastoreS3TooltipHelp: "Hovering an S3 object row shows the full key path and a content preview.",
|
|
43722
44500
|
watchTitle: "File change watcher",
|
|
43723
44501
|
watchLimit: "Maximum directories to watch",
|
|
43724
|
-
watchLimitHelp: (defaultLimit) => `Higher values reduce missed updates in deep trees at the cost of file handles. Combine with the Skip list above to keep heavy folders (node_modules, .git, dist...) out of the watch budget. Default: ${defaultLimit}
|
|
44502
|
+
watchLimitHelp: (defaultLimit) => `Higher values reduce missed updates in deep trees at the cost of file handles. Combine with the Skip list above to keep heavy folders (node_modules, .git, dist...) out of the watch budget. Default: ${defaultLimit}.`,
|
|
44503
|
+
agentRulesTitle: "Terminal status detection",
|
|
44504
|
+
agentRulesLabel: "Screen matching rules (JSON)",
|
|
44505
|
+
agentRulesHelp: "Rules can report working, waiting, idle, or skip. Configure priority, region, contains, regex, lineRegex, and nested all/any/not conditions. contains ignores letter case; regex accepts a leading (?i) for case-insensitive matching. To keep matching responsive, regex allows at most one variable-length repetition and rejects groups, alternation, and backreferences; express AND/OR with all/any. The highest-priority match wins; equal priorities keep the earlier rule. Save validates every rule before replacing the active set.",
|
|
44506
|
+
agentRulesGuideTitle: "JSON format and example",
|
|
44507
|
+
agentRulesGuideIntro: "Enter one object with version 1 and a rules array. Each rule needs the required fields listed below plus at least one matcher.",
|
|
44508
|
+
agentRulesGuideFields: "Required fields: id (unique name), state (working, waiting, idle, or skip), priority (higher wins), and region. lines is also required when region is bottom_non_empty.",
|
|
44509
|
+
agentRulesGuideMatchers: "Matchers: contains and regex test the selected region; lineRegex tests each line. Combine matcher objects with all, any, and not.",
|
|
44510
|
+
agentRulesGuideRegions: "Regions: osc_title checks the terminal title, whole_recent checks the recent screen, bottom_non_empty checks the last non-empty lines, and last_non_empty checks only the final non-empty line.",
|
|
44511
|
+
agentRulesGuideExample: `{
|
|
44512
|
+
"version": 1,
|
|
44513
|
+
"rules": [
|
|
44514
|
+
{
|
|
44515
|
+
"id": "waiting_for_confirmation",
|
|
44516
|
+
"state": "waiting",
|
|
44517
|
+
"priority": 900,
|
|
44518
|
+
"region": "bottom_non_empty",
|
|
44519
|
+
"lines": 12,
|
|
44520
|
+
"contains": ["enter to confirm"],
|
|
44521
|
+
"not": [{ "contains": ["finished"] }]
|
|
44522
|
+
}
|
|
44523
|
+
]
|
|
44524
|
+
}`,
|
|
44525
|
+
agentRulesSave: "Validate and save",
|
|
44526
|
+
agentRulesReset: "Use built-in rules",
|
|
44527
|
+
agentRulesSaving: "Validating and saving…",
|
|
44528
|
+
agentRulesSourceDefault: "Source: built-in rules",
|
|
44529
|
+
agentRulesSourceSaved: "Source: saved rules (active immediately)"
|
|
43725
44530
|
},
|
|
43726
44531
|
annotations: {
|
|
43727
44532
|
title: "Code annotations",
|
|
@@ -44010,7 +44815,12 @@ ${formatErrorDetail(error2)}`);
|
|
|
44010
44815
|
excludeNames: "完全に非表示にするファイル名またはディレクトリ名",
|
|
44011
44816
|
excludeNamesHelp: "リスト中の名前に一致するファイル/ディレクトリを、サイドバー・検索結果・grep 結果から完全に消します。Skip と違い、名前自体が UI に出なくなります。gitignore方式のワイルドカード(*, ?, [abc], [!abc])に対応しています。",
|
|
44012
44817
|
reset: "デフォルトに戻す",
|
|
44013
|
-
|
|
44818
|
+
save: "変更を保存",
|
|
44819
|
+
saving: "保存しています…",
|
|
44820
|
+
saved: "保存しました。",
|
|
44821
|
+
unsaved: "未保存の変更があります。",
|
|
44822
|
+
saveNote: "「変更を保存」を押すまで、編集内容は適用されません。",
|
|
44823
|
+
watchLimitInvalid: (min, max) => `${min}〜${max}の整数を入力してください。`,
|
|
44014
44824
|
scopeSource: (project, source) => `このブラウザのプロジェクト "${project}" に保存されます。ソース: ${source}。サイドバー、Ctrl+K、Ctrl+G、Datastores、File change watcher で使われます。「デフォルトに戻す」でブラウザ側の上書きを削除します。`,
|
|
44015
44825
|
browserOverride: "ブラウザ側の上書き",
|
|
44016
44826
|
serverDefault: "サーバ既定値",
|
|
@@ -44024,7 +44834,34 @@ ${formatErrorDetail(error2)}`);
|
|
|
44024
44834
|
datastoreS3TooltipHelp: "S3 オブジェクト行にホバーすると、完全な key とコンテンツプレビューを表示します。",
|
|
44025
44835
|
watchTitle: "ファイル変更の監視",
|
|
44026
44836
|
watchLimit: "監視するディレクトリ数の上限",
|
|
44027
|
-
watchLimitHelp: (defaultLimit) => `値を大きくすると深いツリーの変更を取りこぼしにくくなりますが、ファイルハンドル数を消費します。上の Skip リストと併用すると、重いフォルダ(node_modules, .git, dist など)を監視枠から外せます。既定値: ${defaultLimit}
|
|
44837
|
+
watchLimitHelp: (defaultLimit) => `値を大きくすると深いツリーの変更を取りこぼしにくくなりますが、ファイルハンドル数を消費します。上の Skip リストと併用すると、重いフォルダ(node_modules, .git, dist など)を監視枠から外せます。既定値: ${defaultLimit}。`,
|
|
44838
|
+
agentRulesTitle: "ターミナルのAI状態判定",
|
|
44839
|
+
agentRulesLabel: "画面の一致ルール(JSON)",
|
|
44840
|
+
agentRulesHelp: "各ルールで working(作業中)・waiting(入力待ち)・idle(待機中)・skip(状態を維持)を指定できます。priority、region、contains、regex、lineRegex、入れ子の all/any/not を編集できます。contains は大文字小文字を区別せず、regex は先頭の (?i) による大小無視に対応します。判定処理を止めないため、regex の可変長の繰返しは1個までで、グループ・選択・後方参照は使えません。AND/OR は all/any で表します。優先度が最大の一致が採用され、同点は上にあるルールが優先されます。保存前に全ルールを検証します。",
|
|
44841
|
+
agentRulesGuideTitle: "JSONの書式と入力例",
|
|
44842
|
+
agentRulesGuideIntro: "version が 1、rules が配列のJSONオブジェクトを入力します。各ルールには下記の必須項目と、1個以上の一致条件が必要です。",
|
|
44843
|
+
agentRulesGuideFields: "必須項目: id(一意の名前)、state(working / waiting / idle / skip)、priority(大きい値を優先)、region。region が bottom_non_empty の場合は lines も必要です。",
|
|
44844
|
+
agentRulesGuideMatchers: "一致条件: contains と regex は選択した領域全体、lineRegex は各行を調べます。一致条件のオブジェクトは all / any / not で組み合わせられます。",
|
|
44845
|
+
agentRulesGuideRegions: "region: osc_title はターミナルタイトル、whole_recent は直近の画面全体、bottom_non_empty は末尾の非空行、last_non_empty は最後の非空行だけを調べます。",
|
|
44846
|
+
agentRulesGuideExample: `{
|
|
44847
|
+
"version": 1,
|
|
44848
|
+
"rules": [
|
|
44849
|
+
{
|
|
44850
|
+
"id": "waiting_for_confirmation",
|
|
44851
|
+
"state": "waiting",
|
|
44852
|
+
"priority": 900,
|
|
44853
|
+
"region": "bottom_non_empty",
|
|
44854
|
+
"lines": 12,
|
|
44855
|
+
"contains": ["enter to confirm"],
|
|
44856
|
+
"not": [{ "contains": ["finished"] }]
|
|
44857
|
+
}
|
|
44858
|
+
]
|
|
44859
|
+
}`,
|
|
44860
|
+
agentRulesSave: "検証して保存",
|
|
44861
|
+
agentRulesReset: "組み込みルールに戻す",
|
|
44862
|
+
agentRulesSaving: "検証して保存しています…",
|
|
44863
|
+
agentRulesSourceDefault: "適用中: 組み込みルール",
|
|
44864
|
+
agentRulesSourceSaved: "適用中: 保存したルール(即時反映)"
|
|
44028
44865
|
},
|
|
44029
44866
|
annotations: {
|
|
44030
44867
|
title: "コード注釈",
|
|
@@ -44403,12 +45240,6 @@ ${formatErrorDetail(error2)}`);
|
|
|
44403
45240
|
const target = sourceTargetFromRoute();
|
|
44404
45241
|
if (target) renderRepoBlobSidebar(target.path, target.ref || "worktree");
|
|
44405
45242
|
}
|
|
44406
|
-
function saveSidebarFontSize(value) {
|
|
44407
|
-
const next = normalizeViewerFontSize(value);
|
|
44408
|
-
mergeLocalSettings({ sidebarFontSize: next });
|
|
44409
|
-
applySidebarFontSize();
|
|
44410
|
-
patchSettings({ sidebarFontSize: next });
|
|
44411
|
-
}
|
|
44412
45243
|
const CODE_FONT_STEPS = [
|
|
44413
45244
|
"compact",
|
|
44414
45245
|
"regular",
|
|
@@ -44437,22 +45268,6 @@ ${formatErrorDetail(error2)}`);
|
|
|
44437
45268
|
applyCodeFontSize();
|
|
44438
45269
|
patchSettings({ codeFontSize: next });
|
|
44439
45270
|
}
|
|
44440
|
-
function saveUploadEnabled(checked) {
|
|
44441
|
-
mergeLocalSettings({ uploadEnabled: checked });
|
|
44442
|
-
patchSettings({ uploadEnabled: checked });
|
|
44443
|
-
}
|
|
44444
|
-
function saveScopeOmitDirsField(value) {
|
|
44445
|
-
const next = normalizeScopeOmitDirs(value);
|
|
44446
|
-
mergeLocalSettings({ scopeOmitDirs: next });
|
|
44447
|
-
patchSettings({ scopeOmitDirs: next });
|
|
44448
|
-
refreshRepositoryTreeAfterSettings();
|
|
44449
|
-
}
|
|
44450
|
-
function saveScopeExcludeNamesField(value) {
|
|
44451
|
-
const next = normalizeScopeExcludeNames(value);
|
|
44452
|
-
mergeLocalSettings({ scopeExcludeNames: next });
|
|
44453
|
-
patchSettings({ scopeExcludeNames: next });
|
|
44454
|
-
refreshRepositoryTreeAfterSettings();
|
|
44455
|
-
}
|
|
44456
45271
|
function normalizeScopeWatchLimit(value) {
|
|
44457
45272
|
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
|
|
44458
45273
|
if (!Number.isFinite(parsed)) return null;
|
|
@@ -44467,34 +45282,94 @@ ${formatErrorDetail(error2)}`);
|
|
|
44467
45282
|
const saved = normalizeScopeWatchLimit(APP_SETTINGS.scopeWatchLimit);
|
|
44468
45283
|
return saved ?? SERVER_SCOPE_WATCH_LIMIT_DEFAULT;
|
|
44469
45284
|
}
|
|
44470
|
-
function
|
|
44471
|
-
|
|
44472
|
-
|
|
44473
|
-
|
|
44474
|
-
|
|
44475
|
-
|
|
44476
|
-
|
|
44477
|
-
|
|
44478
|
-
|
|
44479
|
-
|
|
44480
|
-
|
|
44481
|
-
|
|
44482
|
-
|
|
44483
|
-
|
|
44484
|
-
|
|
44485
|
-
|
|
44486
|
-
|
|
44487
|
-
|
|
44488
|
-
|
|
44489
|
-
|
|
44490
|
-
|
|
44491
|
-
|
|
44492
|
-
|
|
44493
|
-
|
|
44494
|
-
|
|
44495
|
-
|
|
44496
|
-
|
|
44497
|
-
|
|
45285
|
+
function defaultViewerSettingsDraft() {
|
|
45286
|
+
return {
|
|
45287
|
+
language: "en",
|
|
45288
|
+
sidebarFontSize: "regular",
|
|
45289
|
+
codeFontSize: "regular",
|
|
45290
|
+
omitDirs: serverScopeOmitDirsDefault().join("\n"),
|
|
45291
|
+
excludeNames: serverScopeExcludeNamesDefault().join("\n"),
|
|
45292
|
+
watchLimit: SERVER_SCOPE_WATCH_LIMIT_DEFAULT,
|
|
45293
|
+
uploadEnabled: true,
|
|
45294
|
+
inferFkRails: false,
|
|
45295
|
+
s3TooltipEnabled: true
|
|
45296
|
+
};
|
|
45297
|
+
}
|
|
45298
|
+
async function saveViewerSettings(draft, options) {
|
|
45299
|
+
const normalizedLanguage = normalizeViewerLanguage(draft.language);
|
|
45300
|
+
const normalizedSidebarFontSize = normalizeViewerFontSize(
|
|
45301
|
+
draft.sidebarFontSize
|
|
45302
|
+
);
|
|
45303
|
+
const normalizedCodeFontSize = normalizeViewerFontSize(draft.codeFontSize);
|
|
45304
|
+
const normalizedOmitDirs = normalizeScopeOmitDirs(draft.omitDirs);
|
|
45305
|
+
const normalizedExcludeNames = normalizeScopeExcludeNames(
|
|
45306
|
+
draft.excludeNames
|
|
45307
|
+
);
|
|
45308
|
+
const normalized = {
|
|
45309
|
+
language: normalizedLanguage,
|
|
45310
|
+
sidebarFontSize: normalizedSidebarFontSize,
|
|
45311
|
+
codeFontSize: normalizedCodeFontSize,
|
|
45312
|
+
omitDirs: normalizedOmitDirs.join("\n"),
|
|
45313
|
+
excludeNames: normalizedExcludeNames.join("\n"),
|
|
45314
|
+
watchLimit: normalizeScopeWatchLimit(draft.watchLimit) ?? SERVER_SCOPE_WATCH_LIMIT_DEFAULT,
|
|
45315
|
+
uploadEnabled: draft.uploadEnabled,
|
|
45316
|
+
inferFkRails: draft.inferFkRails,
|
|
45317
|
+
s3TooltipEnabled: draft.s3TooltipEnabled
|
|
45318
|
+
};
|
|
45319
|
+
const changed = new Set(options.changedFields);
|
|
45320
|
+
const appPatch = {};
|
|
45321
|
+
const dbPrefsPatch = {};
|
|
45322
|
+
if (options.restoreDefaults) {
|
|
45323
|
+
Object.assign(appPatch, {
|
|
45324
|
+
language: "en",
|
|
45325
|
+
sidebarFontSize: null,
|
|
45326
|
+
codeFontSize: null,
|
|
45327
|
+
scopeOmitDirs: null,
|
|
45328
|
+
scopeExcludeNames: null,
|
|
45329
|
+
scopeWatchLimit: null,
|
|
45330
|
+
uploadEnabled: null
|
|
45331
|
+
});
|
|
45332
|
+
dbPrefsPatch.inferFkRails = null;
|
|
45333
|
+
dbPrefsPatch.s3TooltipEnabled = null;
|
|
45334
|
+
} else {
|
|
45335
|
+
if (changed.has("language")) appPatch.language = normalizedLanguage;
|
|
45336
|
+
if (changed.has("sidebarFontSize"))
|
|
45337
|
+
appPatch.sidebarFontSize = normalizedSidebarFontSize;
|
|
45338
|
+
if (changed.has("codeFontSize"))
|
|
45339
|
+
appPatch.codeFontSize = normalizedCodeFontSize;
|
|
45340
|
+
if (changed.has("omitDirs")) appPatch.scopeOmitDirs = normalizedOmitDirs;
|
|
45341
|
+
if (changed.has("excludeNames"))
|
|
45342
|
+
appPatch.scopeExcludeNames = normalizedExcludeNames;
|
|
45343
|
+
if (changed.has("watchLimit"))
|
|
45344
|
+
appPatch.scopeWatchLimit = normalized.watchLimit;
|
|
45345
|
+
if (changed.has("uploadEnabled"))
|
|
45346
|
+
appPatch.uploadEnabled = normalized.uploadEnabled;
|
|
45347
|
+
if (changed.has("inferFkRails"))
|
|
45348
|
+
dbPrefsPatch.inferFkRails = normalized.inferFkRails;
|
|
45349
|
+
if (changed.has("s3TooltipEnabled"))
|
|
45350
|
+
dbPrefsPatch.s3TooltipEnabled = normalized.s3TooltipEnabled;
|
|
45351
|
+
}
|
|
45352
|
+
const operations = [];
|
|
45353
|
+
if (Object.keys(appPatch).length > 0)
|
|
45354
|
+
operations.push(persistSettingsPatch(appPatch));
|
|
45355
|
+
if (Object.keys(dbPrefsPatch).length > 0)
|
|
45356
|
+
operations.push(DATABASE_VIEW.saveDbUiPrefs(dbPrefsPatch));
|
|
45357
|
+
const results = await Promise.allSettled(operations);
|
|
45358
|
+
const errors2 = results.flatMap(
|
|
45359
|
+
(result) => result.status === "rejected" ? [result.reason] : []
|
|
45360
|
+
);
|
|
45361
|
+
if (errors2.length > 0) {
|
|
45362
|
+
throw errorWithCauses("save viewer settings failed", errors2);
|
|
45363
|
+
}
|
|
45364
|
+
if (options.restoreDefaults || changed.has("language"))
|
|
45365
|
+
setViewerLanguage(normalizedLanguage, false);
|
|
45366
|
+
if (options.restoreDefaults || changed.has("sidebarFontSize"))
|
|
45367
|
+
applySidebarFontSize();
|
|
45368
|
+
if (options.restoreDefaults || changed.has("codeFontSize"))
|
|
45369
|
+
applyCodeFontSize();
|
|
45370
|
+
if (options.restoreDefaults || changed.has("omitDirs") || changed.has("excludeNames")) {
|
|
45371
|
+
refreshRepositoryTreeAfterSettings();
|
|
45372
|
+
}
|
|
44498
45373
|
}
|
|
44499
45374
|
function createRefSelectorInput(options) {
|
|
44500
45375
|
const wrap = document.createElement("div");
|
|
@@ -45006,30 +45881,22 @@ ${formatErrorDetail(error2)}`);
|
|
|
45006
45881
|
scopeSource: uiText().settings.scopeSource(
|
|
45007
45882
|
PROJECT_NAME || "default",
|
|
45008
45883
|
scopeOmitSourceLabel()
|
|
45009
|
-
)
|
|
45884
|
+
),
|
|
45885
|
+
agentRulesJson: AGENT_SCREEN_RULES,
|
|
45886
|
+
agentRulesSource: AGENT_SCREEN_RULES_SOURCE,
|
|
45887
|
+
agentRulesErrors: agentScreenRuleErrorsText(AGENT_SCREEN_RULE_ERRORS)
|
|
45010
45888
|
}),
|
|
45889
|
+
getDefaultValues: defaultViewerSettingsDraft,
|
|
45011
45890
|
refresh: async () => {
|
|
45012
|
-
await
|
|
45013
|
-
|
|
45014
|
-
|
|
45015
|
-
|
|
45016
|
-
|
|
45017
|
-
},
|
|
45018
|
-
onSidebarFontSizeChange: saveSidebarFontSize,
|
|
45019
|
-
onCodeFontSizeChange: saveCodeFontSize,
|
|
45020
|
-
onUploadEnabledChange: saveUploadEnabled,
|
|
45021
|
-
onOmitDirsChange: (value) => {
|
|
45022
|
-
saveScopeOmitDirsField(value);
|
|
45023
|
-
VIEWER_SETTINGS.sync();
|
|
45024
|
-
},
|
|
45025
|
-
onExcludeNamesChange: (value) => {
|
|
45026
|
-
saveScopeExcludeNamesField(value);
|
|
45027
|
-
VIEWER_SETTINGS.sync();
|
|
45891
|
+
await Promise.all([
|
|
45892
|
+
loadSettings(),
|
|
45893
|
+
loadAgentScreenRules(),
|
|
45894
|
+
DATABASE_VIEW.loadDbUiPrefs()
|
|
45895
|
+
]);
|
|
45028
45896
|
},
|
|
45029
|
-
|
|
45030
|
-
|
|
45031
|
-
|
|
45032
|
-
onReset: resetScopeSettings
|
|
45897
|
+
onSave: saveViewerSettings,
|
|
45898
|
+
onAgentRulesSave: saveAgentScreenRules,
|
|
45899
|
+
onAgentRulesReset: resetAgentScreenRuleSettings
|
|
45033
45900
|
});
|
|
45034
45901
|
relocalizeViewerSettings = () => VIEWER_SETTINGS.localize();
|
|
45035
45902
|
const KEYBINDING_EDITOR = createHelpKeybindingEditor({
|