@youtyan/code-viewer 0.9.2 → 0.10.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 +13 -3
- package/dist/code-viewer.js +133 -37
- package/package.json +5 -4
- package/web/app.js +1276 -107
- package/web/index.html +7 -1
- package/web/style.css +328 -0
- package/web/vendor/THIRD_PARTY_NOTICES.txt +24 -0
- package/web/yaml.js +6134 -0
package/web/app.js
CHANGED
|
@@ -574,7 +574,6 @@ ${lines.join(`
|
|
|
574
574
|
var MOON_16_PATH = "M9.598 1.591a.749.749 0 0 1 .785-.175 7.001 7.001 0 1 1-8.967 8.967.75.75 0 0 1 .961-.96 5.5 5.5 0 0 0 7.046-7.046.75.75 0 0 1 .175-.786Zm1.616 1.945a7 7 0 0 1-7.678 7.678 5.499 5.499 0 1 0 7.678-7.678Z";
|
|
575
575
|
var QUESTION_16_PATH = "M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.92 6.085h.001a.749.749 0 1 1-1.342-.67c.169-.339.436-.701.849-.977C6.845 4.16 7.369 4 8 4a2.756 2.756 0 0 1 1.637.525c.503.377.863.965.863 1.725 0 .448-.115.83-.329 1.15-.205.307-.47.513-.692.662-.109.072-.22.138-.313.195l-.006.004a6.24 6.24 0 0 0-.26.16.952.952 0 0 0-.276.245.75.75 0 0 1-1.248-.832c.184-.264.42-.489.692-.661.103-.067.207-.132.313-.195l.007-.004c.1-.061.182-.11.258-.161a.969.969 0 0 0 .277-.245C8.96 6.514 9 6.427 9 6.25a.612.612 0 0 0-.262-.525A1.27 1.27 0 0 0 8 5.5c-.369 0-.595.09-.74.187a1.01 1.01 0 0 0-.34.398ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z";
|
|
576
576
|
var MARK_GITHUB_16_PATH = "M6.766 11.328c-2.063-.25-3.516-1.734-3.516-3.656 0-.781.281-1.625.75-2.188-.203-.515-.172-1.609.063-2.062.625-.078 1.468.25 1.968.703.594-.187 1.219-.281 1.985-.281.765 0 1.39.094 1.953.265.484-.437 1.344-.765 1.969-.687.218.422.25 1.515.046 2.047.5.593.766 1.39.766 2.203 0 1.922-1.453 3.375-3.547 3.64.531.344.89 1.094.89 1.954v1.625c0 .468.391.734.86.547C13.781 14.359 16 11.53 16 8.03 16 3.61 12.406 0 7.984 0 3.563 0 0 3.61 0 8.031a7.88 7.88 0 0 0 5.172 7.422c.422.156.828-.125.828-.547v-1.25c-.219.094-.5.156-.75.156-1.031 0-1.64-.562-2.078-1.609-.172-.422-.36-.672-.719-.719-.187-.015-.25-.093-.25-.187 0-.188.313-.328.625-.328.453 0 .844.281 1.25.86.313.452.64.655 1.031.655s.641-.14 1-.5c.266-.265.47-.5.657-.656";
|
|
577
|
-
var BOOK_16_PATH = "M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z";
|
|
578
577
|
var SEARCH_16_PATH = "M10.68 11.74a6 6 0 0 1-7.917-8.984 6 6 0 0 1 8.997 7.905l3.05 3.05a.75.75 0 1 1-1.06 1.06l-3.07-3.03ZM11.5 7a4.499 4.499 0 1 1-8.997 0A4.499 4.499 0 0 1 11.5 7Z";
|
|
579
578
|
var DOWNLOAD_16_PATHS = [
|
|
580
579
|
"M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14Z",
|
|
@@ -875,6 +874,19 @@ ${lines.join(`
|
|
|
875
874
|
return { url: `${target}${lineHash}`, provider: "github" };
|
|
876
875
|
}
|
|
877
876
|
|
|
877
|
+
// web-src/core/tools.ts
|
|
878
|
+
var TOOL_IDS = [
|
|
879
|
+
"markdown",
|
|
880
|
+
"mermaid",
|
|
881
|
+
"json"
|
|
882
|
+
];
|
|
883
|
+
var DEFAULT_TOOL_ID = "markdown";
|
|
884
|
+
var MIN_TOOLS_SHEET_WIDTH = 480;
|
|
885
|
+
var MAX_TOOLS_SHEET_WIDTH = 4000;
|
|
886
|
+
function isToolId(value) {
|
|
887
|
+
return TOOL_IDS.includes(value);
|
|
888
|
+
}
|
|
889
|
+
|
|
878
890
|
// web-src/core/routes.ts
|
|
879
891
|
function assertNever(value) {
|
|
880
892
|
throw new Error(`unhandled route: ${JSON.stringify(value)}`);
|
|
@@ -1145,18 +1157,28 @@ ${lines.join(`
|
|
|
1145
1157
|
const params = new URLSearchParams(search);
|
|
1146
1158
|
return params.get("doctor") === "open";
|
|
1147
1159
|
}
|
|
1148
|
-
function
|
|
1160
|
+
function withQueryParam(url, key, value) {
|
|
1149
1161
|
const queryIdx = url.indexOf("?");
|
|
1150
1162
|
const base = queryIdx >= 0 ? url.slice(0, queryIdx) : url;
|
|
1151
1163
|
const query = queryIdx >= 0 ? url.slice(queryIdx + 1) : "";
|
|
1152
1164
|
const params = new URLSearchParams(query);
|
|
1153
|
-
if (
|
|
1154
|
-
params.
|
|
1165
|
+
if (value === null)
|
|
1166
|
+
params.delete(key);
|
|
1155
1167
|
else
|
|
1156
|
-
params.
|
|
1168
|
+
params.set(key, value);
|
|
1157
1169
|
const qs = params.toString();
|
|
1158
1170
|
return qs ? `${base}?${qs}` : base;
|
|
1159
1171
|
}
|
|
1172
|
+
function withDoctorOverlay(url, open) {
|
|
1173
|
+
return withQueryParam(url, "doctor", open ? "open" : null);
|
|
1174
|
+
}
|
|
1175
|
+
function parseToolsOverlay(search) {
|
|
1176
|
+
const raw = new URLSearchParams(search).get("tools");
|
|
1177
|
+
return isToolId(raw) ? raw : null;
|
|
1178
|
+
}
|
|
1179
|
+
function withToolsOverlay(url, tool) {
|
|
1180
|
+
return withQueryParam(url, "tools", tool);
|
|
1181
|
+
}
|
|
1160
1182
|
|
|
1161
1183
|
// web-src/views/media-embed.ts
|
|
1162
1184
|
var MEDIA_RE = /\.(png|jpe?g|gif|webp|svg|avif|bmp|ico|mp4|webm|mov|mp3|wav|ogg|flac|m4a|aac|opus)(\?.*)?$/i;
|
|
@@ -7606,6 +7628,19 @@ ${lines.join(`
|
|
|
7606
7628
|
}
|
|
7607
7629
|
|
|
7608
7630
|
// web-src/core/shiki-loader.ts
|
|
7631
|
+
function highlightToInnerHtml(code2, lang, highlighter) {
|
|
7632
|
+
if (!highlighter || !code2)
|
|
7633
|
+
return "";
|
|
7634
|
+
const html = highlighter.codeToHtml(code2, {
|
|
7635
|
+
lang,
|
|
7636
|
+
themes: { light: "github-light", dark: "github-dark" },
|
|
7637
|
+
defaultColor: false
|
|
7638
|
+
});
|
|
7639
|
+
const template = document.createElement("template");
|
|
7640
|
+
template.innerHTML = html;
|
|
7641
|
+
const pre = template.content.querySelector("pre");
|
|
7642
|
+
return pre ? pre.innerHTML : "";
|
|
7643
|
+
}
|
|
7609
7644
|
var cache = new Map;
|
|
7610
7645
|
function loadShikiHighlighter(options) {
|
|
7611
7646
|
const key = JSON.stringify({
|
|
@@ -7992,7 +8027,9 @@ ${frontmatter.yaml}
|
|
|
7992
8027
|
else
|
|
7993
8028
|
break;
|
|
7994
8029
|
}
|
|
7995
|
-
|
|
8030
|
+
const container = scrollableAncestor(root);
|
|
8031
|
+
const atBottom = container ? container.scrollTop + container.clientHeight >= container.scrollHeight - 4 : window.innerHeight + scrollRoot.scrollTop >= scrollRoot.scrollHeight - 4;
|
|
8032
|
+
if (atBottom) {
|
|
7996
8033
|
active = entries[entries.length - 1];
|
|
7997
8034
|
}
|
|
7998
8035
|
entries.forEach((entry) => {
|
|
@@ -8004,8 +8041,9 @@ ${frontmatter.yaml}
|
|
|
8004
8041
|
if (!raf)
|
|
8005
8042
|
raf = requestAnimationFrame(update);
|
|
8006
8043
|
};
|
|
8007
|
-
|
|
8044
|
+
document.addEventListener("scroll", schedule, {
|
|
8008
8045
|
passive: true,
|
|
8046
|
+
capture: true,
|
|
8009
8047
|
signal: controller.signal
|
|
8010
8048
|
});
|
|
8011
8049
|
window.addEventListener("resize", schedule, { signal: controller.signal });
|
|
@@ -8028,7 +8066,21 @@ ${frontmatter.yaml}
|
|
|
8028
8066
|
function decodeHashFragment(hash) {
|
|
8029
8067
|
return decodeUriComponentSafe(hash.startsWith("#") ? hash.slice(1) : hash);
|
|
8030
8068
|
}
|
|
8069
|
+
function scrollableAncestor(element) {
|
|
8070
|
+
for (let node = element.parentElement;node; node = node.parentElement) {
|
|
8071
|
+
const style = getComputedStyle(node);
|
|
8072
|
+
if (/(auto|scroll)/.test(style.overflowY) && node.scrollHeight > node.clientHeight)
|
|
8073
|
+
return node;
|
|
8074
|
+
}
|
|
8075
|
+
return null;
|
|
8076
|
+
}
|
|
8031
8077
|
function scrollMarkdownSectionIntoView(section, behavior) {
|
|
8078
|
+
const container = scrollableAncestor(section);
|
|
8079
|
+
if (container) {
|
|
8080
|
+
const top2 = section.getBoundingClientRect().top - container.getBoundingClientRect().top + container.scrollTop - 12;
|
|
8081
|
+
container.scrollTo({ top: Math.max(0, top2), behavior });
|
|
8082
|
+
return;
|
|
8083
|
+
}
|
|
8032
8084
|
const top = section.getBoundingClientRect().top + window.scrollY - markdownAnchorOffset() - 12;
|
|
8033
8085
|
window.scrollTo({ top: Math.max(0, top), behavior });
|
|
8034
8086
|
}
|
|
@@ -13165,6 +13217,75 @@ ${frontmatter.yaml}
|
|
|
13165
13217
|
};
|
|
13166
13218
|
}
|
|
13167
13219
|
|
|
13220
|
+
// web-src/core/diagram-viewport.ts
|
|
13221
|
+
var MIN_SCALE = 0.2;
|
|
13222
|
+
var MAX_SCALE = 3;
|
|
13223
|
+
var BUTTON_STEP = 0.2;
|
|
13224
|
+
var WHEEL_STEP = 0.1;
|
|
13225
|
+
function clampScale(value) {
|
|
13226
|
+
return Math.max(MIN_SCALE, Math.min(MAX_SCALE, value));
|
|
13227
|
+
}
|
|
13228
|
+
function createDiagramViewport(options) {
|
|
13229
|
+
const container = document.createElement("div");
|
|
13230
|
+
container.className = options.containerClassName;
|
|
13231
|
+
const content = document.createElement("div");
|
|
13232
|
+
content.className = options.contentClassName;
|
|
13233
|
+
container.appendChild(content);
|
|
13234
|
+
let scale = 1;
|
|
13235
|
+
function applyZoom(next) {
|
|
13236
|
+
scale = clampScale(next);
|
|
13237
|
+
content.style.transform = `scale(${scale})`;
|
|
13238
|
+
content.style.transformOrigin = "top left";
|
|
13239
|
+
}
|
|
13240
|
+
let dragState = null;
|
|
13241
|
+
container.addEventListener("mousedown", (event) => {
|
|
13242
|
+
if (event.button !== 0)
|
|
13243
|
+
return;
|
|
13244
|
+
dragState = {
|
|
13245
|
+
x: event.clientX,
|
|
13246
|
+
y: event.clientY,
|
|
13247
|
+
sl: container.scrollLeft,
|
|
13248
|
+
st: container.scrollTop
|
|
13249
|
+
};
|
|
13250
|
+
container.style.cursor = "grabbing";
|
|
13251
|
+
event.preventDefault();
|
|
13252
|
+
});
|
|
13253
|
+
const onWindowMouseMove = (event) => {
|
|
13254
|
+
if (!dragState)
|
|
13255
|
+
return;
|
|
13256
|
+
container.scrollLeft = dragState.sl - (event.clientX - dragState.x);
|
|
13257
|
+
container.scrollTop = dragState.st - (event.clientY - dragState.y);
|
|
13258
|
+
};
|
|
13259
|
+
const onWindowMouseUp = () => {
|
|
13260
|
+
if (!dragState)
|
|
13261
|
+
return;
|
|
13262
|
+
dragState = null;
|
|
13263
|
+
container.style.cursor = "";
|
|
13264
|
+
};
|
|
13265
|
+
window.addEventListener("mousemove", onWindowMouseMove);
|
|
13266
|
+
window.addEventListener("mouseup", onWindowMouseUp);
|
|
13267
|
+
container.addEventListener("wheel", (event) => {
|
|
13268
|
+
if (!event.ctrlKey && !event.metaKey)
|
|
13269
|
+
return;
|
|
13270
|
+
event.preventDefault();
|
|
13271
|
+
applyZoom(scale + (event.deltaY > 0 ? -WHEEL_STEP : WHEEL_STEP));
|
|
13272
|
+
}, { passive: false });
|
|
13273
|
+
applyZoom(1);
|
|
13274
|
+
return {
|
|
13275
|
+
container,
|
|
13276
|
+
content,
|
|
13277
|
+
zoomIn: () => applyZoom(scale + BUTTON_STEP),
|
|
13278
|
+
zoomOut: () => applyZoom(scale - BUTTON_STEP),
|
|
13279
|
+
reset: () => applyZoom(1),
|
|
13280
|
+
dispose() {
|
|
13281
|
+
dragState = null;
|
|
13282
|
+
container.style.cursor = "";
|
|
13283
|
+
window.removeEventListener("mousemove", onWindowMouseMove);
|
|
13284
|
+
window.removeEventListener("mouseup", onWindowMouseUp);
|
|
13285
|
+
}
|
|
13286
|
+
};
|
|
13287
|
+
}
|
|
13288
|
+
|
|
13168
13289
|
// web-src/views/database/er-diagram.ts
|
|
13169
13290
|
function mermaidType(sqlType) {
|
|
13170
13291
|
const upper = sqlType.toUpperCase();
|
|
@@ -13255,30 +13376,17 @@ ${frontmatter.yaml}
|
|
|
13255
13376
|
copyBtn.textContent = text3().er.copyMermaid;
|
|
13256
13377
|
copyBtn.title = text3().er.copyMermaidTitle;
|
|
13257
13378
|
toolbar.append(zoomIn, zoomOut, zoomReset, copyBtn);
|
|
13258
|
-
const
|
|
13259
|
-
|
|
13260
|
-
|
|
13261
|
-
|
|
13262
|
-
container.
|
|
13379
|
+
const viewport = createDiagramViewport({
|
|
13380
|
+
containerClassName: "db-er-container",
|
|
13381
|
+
contentClassName: "db-er-svg-wrap"
|
|
13382
|
+
});
|
|
13383
|
+
const container = viewport.container;
|
|
13384
|
+
const svgWrap = viewport.content;
|
|
13263
13385
|
el.append(toolbar, container);
|
|
13264
|
-
let scale = 1;
|
|
13265
13386
|
let lastMarkup = "";
|
|
13266
|
-
|
|
13267
|
-
|
|
13268
|
-
|
|
13269
|
-
}
|
|
13270
|
-
zoomIn.addEventListener("click", () => {
|
|
13271
|
-
scale = Math.min(3, scale + 0.2);
|
|
13272
|
-
applyZoom();
|
|
13273
|
-
});
|
|
13274
|
-
zoomOut.addEventListener("click", () => {
|
|
13275
|
-
scale = Math.max(0.2, scale - 0.2);
|
|
13276
|
-
applyZoom();
|
|
13277
|
-
});
|
|
13278
|
-
zoomReset.addEventListener("click", () => {
|
|
13279
|
-
scale = 1;
|
|
13280
|
-
applyZoom();
|
|
13281
|
-
});
|
|
13387
|
+
zoomIn.addEventListener("click", () => viewport.zoomIn());
|
|
13388
|
+
zoomOut.addEventListener("click", () => viewport.zoomOut());
|
|
13389
|
+
zoomReset.addEventListener("click", () => viewport.reset());
|
|
13282
13390
|
copyBtn.addEventListener("click", () => {
|
|
13283
13391
|
if (lastMarkup) {
|
|
13284
13392
|
navigator.clipboard.writeText(lastMarkup).then(() => {
|
|
@@ -13291,46 +13399,10 @@ ${frontmatter.yaml}
|
|
|
13291
13399
|
});
|
|
13292
13400
|
}
|
|
13293
13401
|
});
|
|
13294
|
-
let dragState = null;
|
|
13295
|
-
container.addEventListener("mousedown", (e2) => {
|
|
13296
|
-
if (e2.button !== 0)
|
|
13297
|
-
return;
|
|
13298
|
-
dragState = {
|
|
13299
|
-
x: e2.clientX,
|
|
13300
|
-
y: e2.clientY,
|
|
13301
|
-
sl: container.scrollLeft,
|
|
13302
|
-
st: container.scrollTop
|
|
13303
|
-
};
|
|
13304
|
-
container.style.cursor = "grabbing";
|
|
13305
|
-
e2.preventDefault();
|
|
13306
|
-
});
|
|
13307
|
-
const onWindowMouseMove = (e2) => {
|
|
13308
|
-
if (!dragState)
|
|
13309
|
-
return;
|
|
13310
|
-
container.scrollLeft = dragState.sl - (e2.clientX - dragState.x);
|
|
13311
|
-
container.scrollTop = dragState.st - (e2.clientY - dragState.y);
|
|
13312
|
-
};
|
|
13313
|
-
const onWindowMouseUp = () => {
|
|
13314
|
-
if (dragState) {
|
|
13315
|
-
dragState = null;
|
|
13316
|
-
container.style.cursor = "";
|
|
13317
|
-
}
|
|
13318
|
-
};
|
|
13319
|
-
window.addEventListener("mousemove", onWindowMouseMove);
|
|
13320
|
-
window.addEventListener("mouseup", onWindowMouseUp);
|
|
13321
|
-
container.addEventListener("wheel", (e2) => {
|
|
13322
|
-
if (e2.ctrlKey || e2.metaKey) {
|
|
13323
|
-
e2.preventDefault();
|
|
13324
|
-
const delta = e2.deltaY > 0 ? -0.1 : 0.1;
|
|
13325
|
-
scale = Math.max(0.2, Math.min(3, scale + delta));
|
|
13326
|
-
applyZoom();
|
|
13327
|
-
}
|
|
13328
|
-
}, { passive: false });
|
|
13329
13402
|
async function render(schema, columnsMap) {
|
|
13330
13403
|
el.hidden = false;
|
|
13331
13404
|
svgWrap.innerHTML = "";
|
|
13332
|
-
|
|
13333
|
-
applyZoom();
|
|
13405
|
+
viewport.reset();
|
|
13334
13406
|
const tables = schema.tables.filter((t2) => t2.type === "table");
|
|
13335
13407
|
if (tables.length === 0) {
|
|
13336
13408
|
svgWrap.textContent = text3().er.noTables;
|
|
@@ -13357,13 +13429,10 @@ ${frontmatter.yaml}
|
|
|
13357
13429
|
el.hidden = true;
|
|
13358
13430
|
svgWrap.innerHTML = "";
|
|
13359
13431
|
lastMarkup = "";
|
|
13360
|
-
dragState = null;
|
|
13361
|
-
container.style.cursor = "";
|
|
13362
13432
|
}
|
|
13363
13433
|
function dispose() {
|
|
13364
13434
|
clear();
|
|
13365
|
-
|
|
13366
|
-
window.removeEventListener("mouseup", onWindowMouseUp);
|
|
13435
|
+
viewport.dispose();
|
|
13367
13436
|
}
|
|
13368
13437
|
function localize() {
|
|
13369
13438
|
const t2 = text3().er;
|
|
@@ -13651,17 +13720,7 @@ ${frontmatter.yaml}
|
|
|
13651
13720
|
|
|
13652
13721
|
// web-src/views/database/shiki-sql.ts
|
|
13653
13722
|
function highlightSqlToInnerHtml(code2, highlighter) {
|
|
13654
|
-
|
|
13655
|
-
return "";
|
|
13656
|
-
const html = highlighter.codeToHtml(code2, {
|
|
13657
|
-
lang: "sql",
|
|
13658
|
-
themes: { light: "github-light", dark: "github-dark" },
|
|
13659
|
-
defaultColor: false
|
|
13660
|
-
});
|
|
13661
|
-
const template = document.createElement("template");
|
|
13662
|
-
template.innerHTML = html;
|
|
13663
|
-
const pre = template.content.querySelector("pre");
|
|
13664
|
-
return pre ? pre.innerHTML : "";
|
|
13723
|
+
return highlightToInnerHtml(code2, "sql", highlighter);
|
|
13665
13724
|
}
|
|
13666
13725
|
|
|
13667
13726
|
// web-src/views/database/query-editor.ts
|
|
@@ -26455,6 +26514,19 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
26455
26514
|
}
|
|
26456
26515
|
]
|
|
26457
26516
|
},
|
|
26517
|
+
{
|
|
26518
|
+
title: "Scratch on pasted text",
|
|
26519
|
+
blocks: [
|
|
26520
|
+
{
|
|
26521
|
+
kind: "paragraph",
|
|
26522
|
+
text: "The Tools item in the header menu opens a drawer for text you paste, without leaving the screen you are on. It holds a Markdown preview (the same renderer as the file preview, so the table of contents, task lists, frontmatter, code highlighting and ```mermaid fences all work), a Mermaid preview with zoom and drag-pan, and a JSON / YAML tool that reads either format and re-emits it as formatted JSON or YAML — a validator and a converter in one."
|
|
26523
|
+
},
|
|
26524
|
+
{
|
|
26525
|
+
kind: "paragraph",
|
|
26526
|
+
text: "Each tool keeps its own draft in .code-viewer/tools.json, so the drawer reopens where you left it. The open tool is part of the URL (?tools=markdown), which makes it shareable and survives a reload, and the drawer width is draggable from the grip on its left edge."
|
|
26527
|
+
}
|
|
26528
|
+
]
|
|
26529
|
+
},
|
|
26458
26530
|
{
|
|
26459
26531
|
title: "Environment doctor",
|
|
26460
26532
|
blocks: [
|
|
@@ -26513,6 +26585,10 @@ code-viewer doctor --bin git=/opt/bin/git --bin docker=/opt/bin/docker --bin gh=
|
|
|
26513
26585
|
"db-ui.json",
|
|
26514
26586
|
"Datastore UI preferences — column widths per (DB, table, column) and toggle states such as Rails FK inference and the S3 tooltip."
|
|
26515
26587
|
],
|
|
26588
|
+
[
|
|
26589
|
+
"tools.json",
|
|
26590
|
+
"Tools drawer state — the pasted draft for each tool (Markdown, Mermaid, JSON / YAML), the tool you used last, and the drawer width."
|
|
26591
|
+
],
|
|
26516
26592
|
[
|
|
26517
26593
|
"annotations.json",
|
|
26518
26594
|
"AI annotation walkthroughs — sessions with their ordered steps (file, line range, title, body), used by the annotation panel and synced live to open tabs over SSE."
|
|
@@ -27150,6 +27226,19 @@ code-viewer query agent-help`
|
|
|
27150
27226
|
}
|
|
27151
27227
|
]
|
|
27152
27228
|
},
|
|
27229
|
+
{
|
|
27230
|
+
title: "貼り付けたテキストを扱う",
|
|
27231
|
+
blocks: [
|
|
27232
|
+
{
|
|
27233
|
+
kind: "paragraph",
|
|
27234
|
+
text: "ヘッダメニューの Tools は、いま見ている画面を離れずに使える貼り付け用のドロワーを開きます。Markdown プレビュー(ファイルプレビューと同じ描画なので、目次・タスクリスト・frontmatter・コードハイライト・```mermaid フェンスがそのまま効きます)、ズームとドラッグ移動ができる Mermaid プレビュー、JSON と YAML のどちらでも読み取って整形し直す JSON / YAML ツール(検証と相互変換を兼ねます)が入っています。"
|
|
27235
|
+
},
|
|
27236
|
+
{
|
|
27237
|
+
kind: "paragraph",
|
|
27238
|
+
text: "各ツールの入力は .code-viewer/tools.json に保存されるので、開き直すと続きから使えます。開いているツールは URL(?tools=markdown)に載るので共有もリロードもでき、ドロワーの幅は左端のグリップをドラッグして変えられます。"
|
|
27239
|
+
}
|
|
27240
|
+
]
|
|
27241
|
+
},
|
|
27153
27242
|
{
|
|
27154
27243
|
title: "環境ドクター",
|
|
27155
27244
|
blocks: [
|
|
@@ -27208,6 +27297,10 @@ code-viewer doctor --bin git=/opt/bin/git --bin docker=/opt/bin/docker --bin gh=
|
|
|
27208
27297
|
"db-ui.json",
|
|
27209
27298
|
"データストア UI の設定 — (DB, テーブル, カラム) ごとの列幅、Rails FK 推測や S3 ツールチップなどのトグル状態。"
|
|
27210
27299
|
],
|
|
27300
|
+
[
|
|
27301
|
+
"tools.json",
|
|
27302
|
+
"ツールドロワーの状態 — 各ツール(Markdown / Mermaid / JSON・YAML)に貼り付けた下書き、最後に使ったツール、ドロワーの幅。"
|
|
27303
|
+
],
|
|
27211
27304
|
[
|
|
27212
27305
|
"annotations.json",
|
|
27213
27306
|
"AI 注釈のウォークスルー — セッションと順序付きステップ(ファイル、行範囲、タイトル、本文)。注釈パネルが読み、SSE で開いているタブへもライブ同期されます。"
|
|
@@ -36195,6 +36288,1017 @@ code-viewer query agent-help`
|
|
|
36195
36288
|
};
|
|
36196
36289
|
}
|
|
36197
36290
|
|
|
36291
|
+
// web-src/views/tools/i18n.ts
|
|
36292
|
+
var TEXT2 = {
|
|
36293
|
+
en: {
|
|
36294
|
+
title: "Tools",
|
|
36295
|
+
open: "tools (Markdown / Mermaid / JSON)",
|
|
36296
|
+
close: "Close",
|
|
36297
|
+
resizeSheet: "Resize the tools drawer",
|
|
36298
|
+
saveFailed: "could not save this draft",
|
|
36299
|
+
loadFailed: "could not load saved drafts",
|
|
36300
|
+
tabs: {
|
|
36301
|
+
markdown: "Markdown",
|
|
36302
|
+
mermaid: "Mermaid",
|
|
36303
|
+
json: "JSON / YAML"
|
|
36304
|
+
},
|
|
36305
|
+
pane: {
|
|
36306
|
+
input: "Input",
|
|
36307
|
+
clear: "clear",
|
|
36308
|
+
clearTitle: "Clear the input",
|
|
36309
|
+
copy: "copy",
|
|
36310
|
+
copyTitle: "Copy the output",
|
|
36311
|
+
copied: "copied",
|
|
36312
|
+
copyFailed: "copy failed",
|
|
36313
|
+
resize: "Resize input and output panes",
|
|
36314
|
+
emptyInput: "Paste something on the left to see it rendered here."
|
|
36315
|
+
},
|
|
36316
|
+
markdown: {
|
|
36317
|
+
output: "Preview",
|
|
36318
|
+
placeholder: "Paste Markdown here…"
|
|
36319
|
+
},
|
|
36320
|
+
mermaid: {
|
|
36321
|
+
output: "Diagram",
|
|
36322
|
+
placeholder: `Paste Mermaid source here, for example:
|
|
36323
|
+
graph TD
|
|
36324
|
+
A --> B`,
|
|
36325
|
+
zoomIn: "Zoom in",
|
|
36326
|
+
zoomOut: "Zoom out",
|
|
36327
|
+
zoomReset: "Reset zoom",
|
|
36328
|
+
loadError: "Failed to load the Mermaid renderer.",
|
|
36329
|
+
renderError: "Could not render this diagram. Check the syntax."
|
|
36330
|
+
},
|
|
36331
|
+
json: {
|
|
36332
|
+
output: "Output",
|
|
36333
|
+
placeholder: "Paste JSON or YAML here…",
|
|
36334
|
+
outputFormat: "output format",
|
|
36335
|
+
asJson: "JSON",
|
|
36336
|
+
asYaml: "YAML",
|
|
36337
|
+
minify: "minify",
|
|
36338
|
+
minifyTitle: "Emit JSON on a single line",
|
|
36339
|
+
minifyYamlTitle: "Minify applies to JSON output only",
|
|
36340
|
+
parsedAsJson: "read as JSON",
|
|
36341
|
+
parsedAsYaml: "read as YAML",
|
|
36342
|
+
invalid: (message) => `Invalid input: ${message}`,
|
|
36343
|
+
loadError: "Failed to load the YAML parser."
|
|
36344
|
+
}
|
|
36345
|
+
},
|
|
36346
|
+
ja: {
|
|
36347
|
+
title: "ツール",
|
|
36348
|
+
open: "ツール (Markdown / Mermaid / JSON)",
|
|
36349
|
+
close: "閉じる",
|
|
36350
|
+
resizeSheet: "ツールの幅を変える",
|
|
36351
|
+
saveFailed: "この下書きを保存できません",
|
|
36352
|
+
loadFailed: "保存済みの下書きを読み出せません",
|
|
36353
|
+
tabs: {
|
|
36354
|
+
markdown: "Markdown",
|
|
36355
|
+
mermaid: "Mermaid",
|
|
36356
|
+
json: "JSON / YAML"
|
|
36357
|
+
},
|
|
36358
|
+
pane: {
|
|
36359
|
+
input: "入力",
|
|
36360
|
+
clear: "クリア",
|
|
36361
|
+
clearTitle: "入力を空にする",
|
|
36362
|
+
copy: "コピー",
|
|
36363
|
+
copyTitle: "出力をコピーする",
|
|
36364
|
+
copied: "コピーしました",
|
|
36365
|
+
copyFailed: "コピーできません",
|
|
36366
|
+
resize: "入力と出力の幅を変える",
|
|
36367
|
+
emptyInput: "左に貼り付けると、ここに結果が出ます。"
|
|
36368
|
+
},
|
|
36369
|
+
markdown: {
|
|
36370
|
+
output: "プレビュー",
|
|
36371
|
+
placeholder: "Markdown を貼り付け…"
|
|
36372
|
+
},
|
|
36373
|
+
mermaid: {
|
|
36374
|
+
output: "図",
|
|
36375
|
+
placeholder: `Mermaid の記法を貼り付け。例:
|
|
36376
|
+
graph TD
|
|
36377
|
+
A --> B`,
|
|
36378
|
+
zoomIn: "拡大",
|
|
36379
|
+
zoomOut: "縮小",
|
|
36380
|
+
zoomReset: "等倍に戻す",
|
|
36381
|
+
loadError: "Mermaid の読み込みに失敗しました。",
|
|
36382
|
+
renderError: "この図を描けませんでした。記法を確認してください。"
|
|
36383
|
+
},
|
|
36384
|
+
json: {
|
|
36385
|
+
output: "出力",
|
|
36386
|
+
placeholder: "JSON か YAML を貼り付け…",
|
|
36387
|
+
outputFormat: "出力形式",
|
|
36388
|
+
asJson: "JSON",
|
|
36389
|
+
asYaml: "YAML",
|
|
36390
|
+
minify: "1行化",
|
|
36391
|
+
minifyTitle: "JSON を1行で出力する",
|
|
36392
|
+
minifyYamlTitle: "1行化は JSON 出力のときだけ効きます",
|
|
36393
|
+
parsedAsJson: "JSON として解釈",
|
|
36394
|
+
parsedAsYaml: "YAML として解釈",
|
|
36395
|
+
invalid: (message) => `解釈できません: ${message}`,
|
|
36396
|
+
loadError: "YAML パーサの読み込みに失敗しました。"
|
|
36397
|
+
}
|
|
36398
|
+
}
|
|
36399
|
+
};
|
|
36400
|
+
function toolsText(lang) {
|
|
36401
|
+
return TEXT2[lang];
|
|
36402
|
+
}
|
|
36403
|
+
|
|
36404
|
+
// web-src/core/drag-resizer.ts
|
|
36405
|
+
var DEFAULT_KEYBOARD_STEP = 16;
|
|
36406
|
+
function attachDragResizer(options) {
|
|
36407
|
+
const { handle } = options;
|
|
36408
|
+
const step = options.keyboardStep ?? DEFAULT_KEYBOARD_STEP;
|
|
36409
|
+
let pointerId = null;
|
|
36410
|
+
let startX = 0;
|
|
36411
|
+
let startSize = 0;
|
|
36412
|
+
function setActive(active) {
|
|
36413
|
+
if (!options.activeClassTarget || !options.activeClassName)
|
|
36414
|
+
return;
|
|
36415
|
+
options.activeClassTarget.classList.toggle(options.activeClassName, active);
|
|
36416
|
+
}
|
|
36417
|
+
const onPointerDown = (event) => {
|
|
36418
|
+
if (event.button !== 0)
|
|
36419
|
+
return;
|
|
36420
|
+
pointerId = event.pointerId;
|
|
36421
|
+
startX = event.clientX;
|
|
36422
|
+
startSize = options.getSize();
|
|
36423
|
+
try {
|
|
36424
|
+
handle.setPointerCapture?.(event.pointerId);
|
|
36425
|
+
} catch {}
|
|
36426
|
+
setActive(true);
|
|
36427
|
+
event.preventDefault();
|
|
36428
|
+
};
|
|
36429
|
+
const onPointerMove = (event) => {
|
|
36430
|
+
if (pointerId !== event.pointerId)
|
|
36431
|
+
return;
|
|
36432
|
+
options.applySize(startSize + (event.clientX - startX) * options.direction);
|
|
36433
|
+
};
|
|
36434
|
+
const onPointerEnd = (event) => {
|
|
36435
|
+
if (pointerId !== event.pointerId)
|
|
36436
|
+
return;
|
|
36437
|
+
pointerId = null;
|
|
36438
|
+
setActive(false);
|
|
36439
|
+
options.onEnd?.();
|
|
36440
|
+
};
|
|
36441
|
+
const onKeyDown = (event) => {
|
|
36442
|
+
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
|
|
36443
|
+
return;
|
|
36444
|
+
const delta = (event.key === "ArrowRight" ? 1 : -1) * options.direction;
|
|
36445
|
+
options.applySize(options.getSize() + delta * step * (event.shiftKey ? 4 : 1));
|
|
36446
|
+
event.preventDefault();
|
|
36447
|
+
options.onEnd?.();
|
|
36448
|
+
};
|
|
36449
|
+
handle.addEventListener("pointerdown", onPointerDown);
|
|
36450
|
+
handle.addEventListener("pointermove", onPointerMove);
|
|
36451
|
+
handle.addEventListener("pointerup", onPointerEnd);
|
|
36452
|
+
handle.addEventListener("pointercancel", onPointerEnd);
|
|
36453
|
+
handle.addEventListener("keydown", onKeyDown);
|
|
36454
|
+
return () => {
|
|
36455
|
+
handle.removeEventListener("pointerdown", onPointerDown);
|
|
36456
|
+
handle.removeEventListener("pointermove", onPointerMove);
|
|
36457
|
+
handle.removeEventListener("pointerup", onPointerEnd);
|
|
36458
|
+
handle.removeEventListener("pointercancel", onPointerEnd);
|
|
36459
|
+
handle.removeEventListener("keydown", onKeyDown);
|
|
36460
|
+
pointerId = null;
|
|
36461
|
+
setActive(false);
|
|
36462
|
+
};
|
|
36463
|
+
}
|
|
36464
|
+
|
|
36465
|
+
// web-src/core/yaml-loader.ts
|
|
36466
|
+
var yamlPromise = null;
|
|
36467
|
+
function loadYaml() {
|
|
36468
|
+
if (!yamlPromise) {
|
|
36469
|
+
yamlPromise = import("/yaml.js").then((mod) => mod).catch(() => null);
|
|
36470
|
+
}
|
|
36471
|
+
return yamlPromise;
|
|
36472
|
+
}
|
|
36473
|
+
|
|
36474
|
+
// web-src/views/tools/scratchpad-pane.ts
|
|
36475
|
+
var RENDER_DEBOUNCE_MS = 200;
|
|
36476
|
+
var STATUS_CLEAR_MS = 1800;
|
|
36477
|
+
function createPaneAction(label, title) {
|
|
36478
|
+
const button = document.createElement("button");
|
|
36479
|
+
button.type = "button";
|
|
36480
|
+
button.className = "tools-pane-action";
|
|
36481
|
+
button.textContent = label;
|
|
36482
|
+
button.title = title;
|
|
36483
|
+
return button;
|
|
36484
|
+
}
|
|
36485
|
+
function createHead(title) {
|
|
36486
|
+
const head = document.createElement("header");
|
|
36487
|
+
head.className = "tools-pane-head";
|
|
36488
|
+
const titleEl = document.createElement("span");
|
|
36489
|
+
titleEl.className = "tools-pane-title";
|
|
36490
|
+
titleEl.textContent = title;
|
|
36491
|
+
head.appendChild(titleEl);
|
|
36492
|
+
return { head, titleEl };
|
|
36493
|
+
}
|
|
36494
|
+
function createScratchpadPane(text3, outputTitle, placeholder, options) {
|
|
36495
|
+
let currentText = text3;
|
|
36496
|
+
const el = document.createElement("div");
|
|
36497
|
+
el.className = `tools-pane ${options.toolClassName}`;
|
|
36498
|
+
const inputSide = document.createElement("section");
|
|
36499
|
+
inputSide.className = "tools-pane-side tools-pane-input";
|
|
36500
|
+
const { head: inputHead, titleEl: inputTitle } = createHead(text3.pane.input);
|
|
36501
|
+
const clearBtn = document.createElement("button");
|
|
36502
|
+
clearBtn.type = "button";
|
|
36503
|
+
clearBtn.className = "tools-pane-action";
|
|
36504
|
+
clearBtn.textContent = text3.pane.clear;
|
|
36505
|
+
clearBtn.title = text3.pane.clearTitle;
|
|
36506
|
+
inputHead.appendChild(clearBtn);
|
|
36507
|
+
const input2 = document.createElement("textarea");
|
|
36508
|
+
input2.className = "tools-textarea";
|
|
36509
|
+
input2.spellcheck = false;
|
|
36510
|
+
input2.placeholder = placeholder;
|
|
36511
|
+
input2.value = options.initialText;
|
|
36512
|
+
inputSide.append(inputHead, input2);
|
|
36513
|
+
const resizer = document.createElement("div");
|
|
36514
|
+
resizer.className = "tools-pane-resizer";
|
|
36515
|
+
resizer.role = "separator";
|
|
36516
|
+
resizer.tabIndex = 0;
|
|
36517
|
+
resizer.setAttribute("aria-orientation", "vertical");
|
|
36518
|
+
resizer.setAttribute("aria-label", text3.pane.resize);
|
|
36519
|
+
const outputSide = document.createElement("section");
|
|
36520
|
+
outputSide.className = "tools-pane-side tools-pane-output";
|
|
36521
|
+
const { head: outputHead, titleEl: outputTitleEl } = createHead(outputTitle);
|
|
36522
|
+
const actions = document.createElement("div");
|
|
36523
|
+
actions.className = "tools-pane-actions";
|
|
36524
|
+
for (const action of options.outputActions ?? [])
|
|
36525
|
+
actions.appendChild(action);
|
|
36526
|
+
const status = document.createElement("span");
|
|
36527
|
+
status.className = "tools-pane-status";
|
|
36528
|
+
status.role = "status";
|
|
36529
|
+
outputHead.append(actions, status);
|
|
36530
|
+
const output = document.createElement("div");
|
|
36531
|
+
output.className = "tools-pane-body";
|
|
36532
|
+
outputSide.append(outputHead, output);
|
|
36533
|
+
el.append(inputSide, resizer, outputSide);
|
|
36534
|
+
let statusTimer = null;
|
|
36535
|
+
let pinnedStatus = null;
|
|
36536
|
+
let normalStatus = { message: "", tone: "info" };
|
|
36537
|
+
function applyStatus(state) {
|
|
36538
|
+
status.textContent = state.message;
|
|
36539
|
+
status.classList.toggle("tools-pane-status-error", state.tone === "error");
|
|
36540
|
+
}
|
|
36541
|
+
function clearStatusTimer() {
|
|
36542
|
+
if (!statusTimer)
|
|
36543
|
+
return;
|
|
36544
|
+
clearTimeout(statusTimer);
|
|
36545
|
+
statusTimer = null;
|
|
36546
|
+
}
|
|
36547
|
+
function setStatus(message, tone = "info") {
|
|
36548
|
+
clearStatusTimer();
|
|
36549
|
+
normalStatus = { message, tone };
|
|
36550
|
+
if (pinnedStatus)
|
|
36551
|
+
return;
|
|
36552
|
+
applyStatus(normalStatus);
|
|
36553
|
+
}
|
|
36554
|
+
function flashStatus(message, tone = "info") {
|
|
36555
|
+
clearStatusTimer();
|
|
36556
|
+
if (pinnedStatus)
|
|
36557
|
+
return;
|
|
36558
|
+
applyStatus({ message, tone });
|
|
36559
|
+
statusTimer = setTimeout(() => {
|
|
36560
|
+
statusTimer = null;
|
|
36561
|
+
applyStatus(pinnedStatus ?? normalStatus);
|
|
36562
|
+
}, STATUS_CLEAR_MS);
|
|
36563
|
+
}
|
|
36564
|
+
function setPinnedStatus(message, tone = "error") {
|
|
36565
|
+
clearStatusTimer();
|
|
36566
|
+
pinnedStatus = message ? { message, tone } : null;
|
|
36567
|
+
applyStatus(pinnedStatus ?? normalStatus);
|
|
36568
|
+
}
|
|
36569
|
+
let renderController = null;
|
|
36570
|
+
let debounceTimer = null;
|
|
36571
|
+
function renderNow() {
|
|
36572
|
+
renderController?.abort();
|
|
36573
|
+
const controller = new AbortController;
|
|
36574
|
+
renderController = controller;
|
|
36575
|
+
const value = input2.value;
|
|
36576
|
+
if (!value.trim()) {
|
|
36577
|
+
const empty = document.createElement("p");
|
|
36578
|
+
empty.className = "tools-pane-empty";
|
|
36579
|
+
empty.textContent = currentText.pane.emptyInput;
|
|
36580
|
+
output.replaceChildren(empty);
|
|
36581
|
+
setStatus("");
|
|
36582
|
+
return;
|
|
36583
|
+
}
|
|
36584
|
+
(async () => {
|
|
36585
|
+
try {
|
|
36586
|
+
await options.render(value, output, controller.signal);
|
|
36587
|
+
} catch (err) {
|
|
36588
|
+
if (controller.signal.aborted)
|
|
36589
|
+
return;
|
|
36590
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
36591
|
+
setStatus(message, "error");
|
|
36592
|
+
}
|
|
36593
|
+
})();
|
|
36594
|
+
}
|
|
36595
|
+
function scheduleRender() {
|
|
36596
|
+
if (debounceTimer)
|
|
36597
|
+
clearTimeout(debounceTimer);
|
|
36598
|
+
debounceTimer = setTimeout(() => {
|
|
36599
|
+
debounceTimer = null;
|
|
36600
|
+
renderNow();
|
|
36601
|
+
}, RENDER_DEBOUNCE_MS);
|
|
36602
|
+
}
|
|
36603
|
+
input2.addEventListener("input", () => {
|
|
36604
|
+
options.onInput(input2.value);
|
|
36605
|
+
renderController?.abort();
|
|
36606
|
+
renderController = null;
|
|
36607
|
+
scheduleRender();
|
|
36608
|
+
});
|
|
36609
|
+
clearBtn.addEventListener("click", () => {
|
|
36610
|
+
if (!input2.value)
|
|
36611
|
+
return;
|
|
36612
|
+
input2.value = "";
|
|
36613
|
+
options.onInput("");
|
|
36614
|
+
input2.focus();
|
|
36615
|
+
renderNow();
|
|
36616
|
+
});
|
|
36617
|
+
const MIN_SIDE_WIDTH = 220;
|
|
36618
|
+
function applyInputWidth(width) {
|
|
36619
|
+
const total = el.getBoundingClientRect().width;
|
|
36620
|
+
const max = Math.max(MIN_SIDE_WIDTH, total - MIN_SIDE_WIDTH);
|
|
36621
|
+
const clamped = Math.max(MIN_SIDE_WIDTH, Math.min(max, width));
|
|
36622
|
+
el.style.setProperty("--tools-input-width", `${Math.round(clamped)}px`);
|
|
36623
|
+
}
|
|
36624
|
+
const detachResizer = attachDragResizer({
|
|
36625
|
+
handle: resizer,
|
|
36626
|
+
getSize: () => inputSide.getBoundingClientRect().width,
|
|
36627
|
+
applySize: applyInputWidth,
|
|
36628
|
+
direction: 1,
|
|
36629
|
+
activeClassTarget: el,
|
|
36630
|
+
activeClassName: "tools-pane-resizing"
|
|
36631
|
+
});
|
|
36632
|
+
return {
|
|
36633
|
+
el,
|
|
36634
|
+
input: input2,
|
|
36635
|
+
output,
|
|
36636
|
+
setStatus,
|
|
36637
|
+
flashStatus,
|
|
36638
|
+
setPinnedStatus,
|
|
36639
|
+
refresh: renderNow,
|
|
36640
|
+
setText(next) {
|
|
36641
|
+
input2.value = next;
|
|
36642
|
+
},
|
|
36643
|
+
getText: () => input2.value,
|
|
36644
|
+
localize(nextText, nextOutputTitle, nextPlaceholder) {
|
|
36645
|
+
currentText = nextText;
|
|
36646
|
+
inputTitle.textContent = nextText.pane.input;
|
|
36647
|
+
clearBtn.textContent = nextText.pane.clear;
|
|
36648
|
+
clearBtn.title = nextText.pane.clearTitle;
|
|
36649
|
+
resizer.setAttribute("aria-label", nextText.pane.resize);
|
|
36650
|
+
outputTitleEl.textContent = nextOutputTitle;
|
|
36651
|
+
input2.placeholder = nextPlaceholder;
|
|
36652
|
+
},
|
|
36653
|
+
focus: () => input2.focus(),
|
|
36654
|
+
dispose() {
|
|
36655
|
+
detachResizer();
|
|
36656
|
+
renderController?.abort();
|
|
36657
|
+
renderController = null;
|
|
36658
|
+
if (debounceTimer)
|
|
36659
|
+
clearTimeout(debounceTimer);
|
|
36660
|
+
debounceTimer = null;
|
|
36661
|
+
if (statusTimer)
|
|
36662
|
+
clearTimeout(statusTimer);
|
|
36663
|
+
statusTimer = null;
|
|
36664
|
+
}
|
|
36665
|
+
};
|
|
36666
|
+
}
|
|
36667
|
+
|
|
36668
|
+
// web-src/views/tools/structured-text.ts
|
|
36669
|
+
var JSON_INDENT = 2;
|
|
36670
|
+
function parseStructuredText(text3, yaml) {
|
|
36671
|
+
const trimmed = text3.trim();
|
|
36672
|
+
if (!trimmed)
|
|
36673
|
+
return { status: "error", message: "empty input" };
|
|
36674
|
+
let jsonMessage = "";
|
|
36675
|
+
try {
|
|
36676
|
+
return { status: "ok", source: "json", value: JSON.parse(trimmed) };
|
|
36677
|
+
} catch (err) {
|
|
36678
|
+
jsonMessage = err instanceof Error ? err.message : String(err);
|
|
36679
|
+
}
|
|
36680
|
+
if (!yaml)
|
|
36681
|
+
return { status: "error", message: jsonMessage };
|
|
36682
|
+
try {
|
|
36683
|
+
const doc = yaml.parseDocument(text3);
|
|
36684
|
+
if (doc.errors.length > 0)
|
|
36685
|
+
return {
|
|
36686
|
+
status: "error",
|
|
36687
|
+
message: doc.errors.map((issue) => issue.message).join(`
|
|
36688
|
+
|
|
36689
|
+
`)
|
|
36690
|
+
};
|
|
36691
|
+
const warnings = doc.warnings.map((issue) => issue.message);
|
|
36692
|
+
return {
|
|
36693
|
+
status: "ok",
|
|
36694
|
+
source: "yaml",
|
|
36695
|
+
value: doc.toJS(),
|
|
36696
|
+
...warnings.length ? { warnings } : {}
|
|
36697
|
+
};
|
|
36698
|
+
} catch (err) {
|
|
36699
|
+
return {
|
|
36700
|
+
status: "error",
|
|
36701
|
+
message: err instanceof Error ? err.message : String(err)
|
|
36702
|
+
};
|
|
36703
|
+
}
|
|
36704
|
+
}
|
|
36705
|
+
function formatStructured(value, format2, minify, yaml) {
|
|
36706
|
+
if (format2 === "yaml") {
|
|
36707
|
+
if (!yaml)
|
|
36708
|
+
return null;
|
|
36709
|
+
return yaml.stringify(value, { indent: JSON_INDENT });
|
|
36710
|
+
}
|
|
36711
|
+
return JSON.stringify(value, null, minify ? 0 : JSON_INDENT) ?? "";
|
|
36712
|
+
}
|
|
36713
|
+
|
|
36714
|
+
// web-src/views/tools/json-tool.ts
|
|
36715
|
+
async function resolveInput(value, format2, signal) {
|
|
36716
|
+
const direct = parseStructuredText(value, null);
|
|
36717
|
+
if (direct.status === "ok" && format2 === "json")
|
|
36718
|
+
return { parsed: direct, yaml: null };
|
|
36719
|
+
const yaml = await loadYaml();
|
|
36720
|
+
if (signal.aborted)
|
|
36721
|
+
return null;
|
|
36722
|
+
return {
|
|
36723
|
+
parsed: direct.status === "ok" ? direct : parseStructuredText(value, yaml),
|
|
36724
|
+
yaml
|
|
36725
|
+
};
|
|
36726
|
+
}
|
|
36727
|
+
function createJsonTool(text3, initialText, onInput) {
|
|
36728
|
+
let currentText = text3;
|
|
36729
|
+
let format2 = "json";
|
|
36730
|
+
let minify = false;
|
|
36731
|
+
let lastOutput = "";
|
|
36732
|
+
const seg = document.createElement("div");
|
|
36733
|
+
seg.className = "seg";
|
|
36734
|
+
seg.role = "group";
|
|
36735
|
+
seg.setAttribute("aria-label", text3.json.outputFormat);
|
|
36736
|
+
const jsonBtn = document.createElement("button");
|
|
36737
|
+
jsonBtn.type = "button";
|
|
36738
|
+
jsonBtn.textContent = text3.json.asJson;
|
|
36739
|
+
jsonBtn.classList.add("active");
|
|
36740
|
+
const yamlBtn = document.createElement("button");
|
|
36741
|
+
yamlBtn.type = "button";
|
|
36742
|
+
yamlBtn.textContent = text3.json.asYaml;
|
|
36743
|
+
seg.append(jsonBtn, yamlBtn);
|
|
36744
|
+
const minifyBtn = createPaneAction(text3.json.minify, text3.json.minifyTitle);
|
|
36745
|
+
const copyBtn = createPaneAction(text3.pane.copy, text3.pane.copyTitle);
|
|
36746
|
+
function syncActionState() {
|
|
36747
|
+
jsonBtn.classList.toggle("active", format2 === "json");
|
|
36748
|
+
yamlBtn.classList.toggle("active", format2 === "yaml");
|
|
36749
|
+
minifyBtn.classList.toggle("active", minify);
|
|
36750
|
+
minifyBtn.setAttribute("aria-pressed", String(minify));
|
|
36751
|
+
minifyBtn.disabled = format2 === "yaml";
|
|
36752
|
+
minifyBtn.title = format2 === "yaml" ? currentText.json.minifyYamlTitle : currentText.json.minifyTitle;
|
|
36753
|
+
}
|
|
36754
|
+
const pane = createScratchpadPane(text3, text3.json.output, text3.json.placeholder, {
|
|
36755
|
+
toolClassName: "tools-pane-json",
|
|
36756
|
+
initialText,
|
|
36757
|
+
onInput,
|
|
36758
|
+
outputActions: [seg, minifyBtn, copyBtn],
|
|
36759
|
+
render: async (value, output, signal) => {
|
|
36760
|
+
const resolved = await resolveInput(value, format2, signal);
|
|
36761
|
+
if (!resolved)
|
|
36762
|
+
return;
|
|
36763
|
+
const { parsed, yaml } = resolved;
|
|
36764
|
+
if (parsed.status === "error") {
|
|
36765
|
+
lastOutput = "";
|
|
36766
|
+
const error2 = document.createElement("p");
|
|
36767
|
+
error2.className = "tools-pane-error";
|
|
36768
|
+
error2.textContent = currentText.json.invalid(parsed.message);
|
|
36769
|
+
output.replaceChildren(error2);
|
|
36770
|
+
pane.setStatus("");
|
|
36771
|
+
return;
|
|
36772
|
+
}
|
|
36773
|
+
const formatted = formatStructured(parsed.value, format2, minify, yaml);
|
|
36774
|
+
if (formatted === null) {
|
|
36775
|
+
lastOutput = "";
|
|
36776
|
+
const error2 = document.createElement("p");
|
|
36777
|
+
error2.className = "tools-pane-error";
|
|
36778
|
+
error2.textContent = currentText.json.loadError;
|
|
36779
|
+
output.replaceChildren(error2);
|
|
36780
|
+
pane.setStatus("");
|
|
36781
|
+
return;
|
|
36782
|
+
}
|
|
36783
|
+
lastOutput = formatted;
|
|
36784
|
+
const highlighter = await loadShikiHighlighter({
|
|
36785
|
+
themes: ["github-light", "github-dark"],
|
|
36786
|
+
langs: ["json", "yaml"]
|
|
36787
|
+
});
|
|
36788
|
+
if (signal.aborted)
|
|
36789
|
+
return;
|
|
36790
|
+
const pre = document.createElement("pre");
|
|
36791
|
+
pre.className = "tools-code";
|
|
36792
|
+
const html = highlightToInnerHtml(formatted, format2, highlighter);
|
|
36793
|
+
if (html)
|
|
36794
|
+
pre.innerHTML = html;
|
|
36795
|
+
else
|
|
36796
|
+
pre.textContent = formatted;
|
|
36797
|
+
if (parsed.warnings?.length) {
|
|
36798
|
+
const warning = document.createElement("p");
|
|
36799
|
+
warning.className = "tools-pane-warning";
|
|
36800
|
+
warning.textContent = parsed.warnings.join(`
|
|
36801
|
+
|
|
36802
|
+
`);
|
|
36803
|
+
output.replaceChildren(warning, pre);
|
|
36804
|
+
} else {
|
|
36805
|
+
output.replaceChildren(pre);
|
|
36806
|
+
}
|
|
36807
|
+
pane.setStatus(parsed.source === "json" ? currentText.json.parsedAsJson : currentText.json.parsedAsYaml);
|
|
36808
|
+
}
|
|
36809
|
+
});
|
|
36810
|
+
jsonBtn.addEventListener("click", () => {
|
|
36811
|
+
if (format2 === "json")
|
|
36812
|
+
return;
|
|
36813
|
+
format2 = "json";
|
|
36814
|
+
syncActionState();
|
|
36815
|
+
pane.refresh();
|
|
36816
|
+
});
|
|
36817
|
+
yamlBtn.addEventListener("click", () => {
|
|
36818
|
+
if (format2 === "yaml")
|
|
36819
|
+
return;
|
|
36820
|
+
format2 = "yaml";
|
|
36821
|
+
syncActionState();
|
|
36822
|
+
pane.refresh();
|
|
36823
|
+
});
|
|
36824
|
+
minifyBtn.addEventListener("click", () => {
|
|
36825
|
+
minify = !minify;
|
|
36826
|
+
syncActionState();
|
|
36827
|
+
pane.refresh();
|
|
36828
|
+
});
|
|
36829
|
+
copyBtn.addEventListener("click", () => {
|
|
36830
|
+
if (!lastOutput)
|
|
36831
|
+
return;
|
|
36832
|
+
navigator.clipboard.writeText(lastOutput).then(() => pane.flashStatus(currentText.pane.copied), () => pane.flashStatus(currentText.pane.copyFailed, "error"));
|
|
36833
|
+
});
|
|
36834
|
+
syncActionState();
|
|
36835
|
+
return {
|
|
36836
|
+
...pane,
|
|
36837
|
+
localizeActions(nextText) {
|
|
36838
|
+
currentText = nextText;
|
|
36839
|
+
seg.setAttribute("aria-label", nextText.json.outputFormat);
|
|
36840
|
+
jsonBtn.textContent = nextText.json.asJson;
|
|
36841
|
+
yamlBtn.textContent = nextText.json.asYaml;
|
|
36842
|
+
minifyBtn.textContent = nextText.json.minify;
|
|
36843
|
+
copyBtn.textContent = nextText.pane.copy;
|
|
36844
|
+
copyBtn.title = nextText.pane.copyTitle;
|
|
36845
|
+
syncActionState();
|
|
36846
|
+
}
|
|
36847
|
+
};
|
|
36848
|
+
}
|
|
36849
|
+
|
|
36850
|
+
// web-src/views/tools/markdown-tool.ts
|
|
36851
|
+
var SCRATCHPAD_TARGET = { path: "scratchpad.md", ref: "worktree" };
|
|
36852
|
+
function createMarkdownTool(text3, initialText, onInput) {
|
|
36853
|
+
return createScratchpadPane(text3, text3.markdown.output, text3.markdown.placeholder, {
|
|
36854
|
+
toolClassName: "tools-pane-markdown",
|
|
36855
|
+
initialText,
|
|
36856
|
+
onInput,
|
|
36857
|
+
render: async (value, output, signal) => {
|
|
36858
|
+
const rendered = await renderMarkdownPreview(value, SCRATCHPAD_TARGET, {
|
|
36859
|
+
syntaxHighlight: true,
|
|
36860
|
+
signal
|
|
36861
|
+
});
|
|
36862
|
+
if (signal.aborted)
|
|
36863
|
+
return;
|
|
36864
|
+
output.replaceChildren(rendered);
|
|
36865
|
+
}
|
|
36866
|
+
});
|
|
36867
|
+
}
|
|
36868
|
+
|
|
36869
|
+
// web-src/views/tools/mermaid-tool.ts
|
|
36870
|
+
function createMermaidTool(text3, initialText, onInput) {
|
|
36871
|
+
let currentText = text3;
|
|
36872
|
+
const viewport = createDiagramViewport({
|
|
36873
|
+
containerClassName: "tools-mermaid-container",
|
|
36874
|
+
contentClassName: "tools-mermaid-canvas"
|
|
36875
|
+
});
|
|
36876
|
+
const zoomIn = createPaneAction("+", text3.mermaid.zoomIn);
|
|
36877
|
+
const zoomOut = createPaneAction("−", text3.mermaid.zoomOut);
|
|
36878
|
+
const zoomReset = createPaneAction("1:1", text3.mermaid.zoomReset);
|
|
36879
|
+
zoomIn.addEventListener("click", () => viewport.zoomIn());
|
|
36880
|
+
zoomOut.addEventListener("click", () => viewport.zoomOut());
|
|
36881
|
+
zoomReset.addEventListener("click", () => viewport.reset());
|
|
36882
|
+
const pane = createScratchpadPane(text3, text3.mermaid.output, text3.mermaid.placeholder, {
|
|
36883
|
+
toolClassName: "tools-pane-mermaid",
|
|
36884
|
+
initialText,
|
|
36885
|
+
onInput,
|
|
36886
|
+
outputActions: [zoomIn, zoomOut, zoomReset],
|
|
36887
|
+
render: async (value, output, signal) => {
|
|
36888
|
+
const mermaid = await loadMermaid();
|
|
36889
|
+
if (signal.aborted)
|
|
36890
|
+
return;
|
|
36891
|
+
if (!mermaid) {
|
|
36892
|
+
pane.setStatus(currentText.mermaid.loadError, "error");
|
|
36893
|
+
return;
|
|
36894
|
+
}
|
|
36895
|
+
const node = document.createElement("div");
|
|
36896
|
+
node.className = "mermaid";
|
|
36897
|
+
node.textContent = value;
|
|
36898
|
+
viewport.content.replaceChildren(node);
|
|
36899
|
+
viewport.reset();
|
|
36900
|
+
output.replaceChildren(viewport.container);
|
|
36901
|
+
try {
|
|
36902
|
+
await mermaid.run({ nodes: [node], suppressErrors: true });
|
|
36903
|
+
} catch {
|
|
36904
|
+
if (signal.aborted)
|
|
36905
|
+
return;
|
|
36906
|
+
pane.setStatus(currentText.mermaid.renderError, "error");
|
|
36907
|
+
return;
|
|
36908
|
+
}
|
|
36909
|
+
if (signal.aborted)
|
|
36910
|
+
return;
|
|
36911
|
+
if (node.querySelector("svg"))
|
|
36912
|
+
pane.setStatus("");
|
|
36913
|
+
else
|
|
36914
|
+
pane.setStatus(currentText.mermaid.renderError, "error");
|
|
36915
|
+
}
|
|
36916
|
+
});
|
|
36917
|
+
const disposePane = pane.dispose;
|
|
36918
|
+
return {
|
|
36919
|
+
...pane,
|
|
36920
|
+
localizeActions(nextText) {
|
|
36921
|
+
currentText = nextText;
|
|
36922
|
+
zoomIn.title = nextText.mermaid.zoomIn;
|
|
36923
|
+
zoomOut.title = nextText.mermaid.zoomOut;
|
|
36924
|
+
zoomReset.title = nextText.mermaid.zoomReset;
|
|
36925
|
+
},
|
|
36926
|
+
dispose() {
|
|
36927
|
+
disposePane();
|
|
36928
|
+
viewport.dispose();
|
|
36929
|
+
}
|
|
36930
|
+
};
|
|
36931
|
+
}
|
|
36932
|
+
|
|
36933
|
+
// web-src/views/tools/tools-view.ts
|
|
36934
|
+
var SAVE_DEBOUNCE_MS = 800;
|
|
36935
|
+
var SAVE_RETRY_MS = 1500;
|
|
36936
|
+
var SAVE_RETRY_LIMIT = 3;
|
|
36937
|
+
var KEEPALIVE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
36938
|
+
function createToolsView(deps) {
|
|
36939
|
+
let panes = null;
|
|
36940
|
+
let tabs = null;
|
|
36941
|
+
let titleEl = null;
|
|
36942
|
+
let closeBtn = null;
|
|
36943
|
+
let activeTool = DEFAULT_TOOL_ID;
|
|
36944
|
+
let stateLoaded = false;
|
|
36945
|
+
let savedActiveTool = null;
|
|
36946
|
+
const drafts = {};
|
|
36947
|
+
let saveTimer = null;
|
|
36948
|
+
let saveRetryTimer = null;
|
|
36949
|
+
let dirty = false;
|
|
36950
|
+
let saveInFlight = false;
|
|
36951
|
+
let saveController = null;
|
|
36952
|
+
let saveFailed = false;
|
|
36953
|
+
let saveRetries = 0;
|
|
36954
|
+
let loadFailed = false;
|
|
36955
|
+
let disposed = false;
|
|
36956
|
+
let sheetWidth = null;
|
|
36957
|
+
let toolTouched = false;
|
|
36958
|
+
let widthTouched = false;
|
|
36959
|
+
let generation = 0;
|
|
36960
|
+
function text3() {
|
|
36961
|
+
return toolsText(deps.getLanguage());
|
|
36962
|
+
}
|
|
36963
|
+
function getMount() {
|
|
36964
|
+
return deps.$("#tools-sheet");
|
|
36965
|
+
}
|
|
36966
|
+
function getOverlay() {
|
|
36967
|
+
return deps.$("#tools-sheet-overlay");
|
|
36968
|
+
}
|
|
36969
|
+
function isPermanentSaveFailure(status) {
|
|
36970
|
+
return status >= 400 && status < 500;
|
|
36971
|
+
}
|
|
36972
|
+
function syncFailureStatus() {
|
|
36973
|
+
if (!panes)
|
|
36974
|
+
return;
|
|
36975
|
+
const current = text3();
|
|
36976
|
+
const message = loadFailed ? current.loadFailed : saveFailed ? current.saveFailed : null;
|
|
36977
|
+
for (const id of TOOL_IDS)
|
|
36978
|
+
panes[id].setPinnedStatus(message);
|
|
36979
|
+
}
|
|
36980
|
+
function reportSaveFailure() {
|
|
36981
|
+
saveFailed = true;
|
|
36982
|
+
syncFailureStatus();
|
|
36983
|
+
}
|
|
36984
|
+
function clearSaveFailure() {
|
|
36985
|
+
saveRetries = 0;
|
|
36986
|
+
if (!saveFailed)
|
|
36987
|
+
return;
|
|
36988
|
+
saveFailed = false;
|
|
36989
|
+
syncFailureStatus();
|
|
36990
|
+
}
|
|
36991
|
+
function reportLoadFailure() {
|
|
36992
|
+
loadFailed = true;
|
|
36993
|
+
syncFailureStatus();
|
|
36994
|
+
}
|
|
36995
|
+
function save(options = {}) {
|
|
36996
|
+
if (saveTimer) {
|
|
36997
|
+
clearTimeout(saveTimer);
|
|
36998
|
+
saveTimer = null;
|
|
36999
|
+
}
|
|
37000
|
+
if (disposed)
|
|
37001
|
+
return;
|
|
37002
|
+
if (!options.keepalive && saveRetries > SAVE_RETRY_LIMIT)
|
|
37003
|
+
return;
|
|
37004
|
+
const pending = dirty || options.keepalive === true && saveInFlight;
|
|
37005
|
+
if (!pending)
|
|
37006
|
+
return;
|
|
37007
|
+
if (saveInFlight && !options.keepalive)
|
|
37008
|
+
return;
|
|
37009
|
+
if (options.keepalive)
|
|
37010
|
+
saveController?.abort();
|
|
37011
|
+
dirty = false;
|
|
37012
|
+
const body = JSON.stringify({
|
|
37013
|
+
activeTool,
|
|
37014
|
+
drafts,
|
|
37015
|
+
...sheetWidth === null ? {} : { width: sheetWidth }
|
|
37016
|
+
});
|
|
37017
|
+
const keepalive = options.keepalive === true && new TextEncoder().encode(body).length <= KEEPALIVE_BODY_LIMIT_BYTES;
|
|
37018
|
+
const controller = new AbortController;
|
|
37019
|
+
if (!options.keepalive) {
|
|
37020
|
+
saveInFlight = true;
|
|
37021
|
+
saveController = controller;
|
|
37022
|
+
}
|
|
37023
|
+
deps.trackLoad(fetch("/_state/tools", {
|
|
37024
|
+
method: "PATCH",
|
|
37025
|
+
headers: deps.actionHeaders(),
|
|
37026
|
+
body,
|
|
37027
|
+
keepalive,
|
|
37028
|
+
signal: controller.signal
|
|
37029
|
+
})).then((res) => {
|
|
37030
|
+
if (res.ok) {
|
|
37031
|
+
clearSaveFailure();
|
|
37032
|
+
return;
|
|
37033
|
+
}
|
|
37034
|
+
if (isPermanentSaveFailure(res.status)) {
|
|
37035
|
+
reportSaveFailure();
|
|
37036
|
+
return;
|
|
37037
|
+
}
|
|
37038
|
+
throw new Error(`HTTP ${res.status}`);
|
|
37039
|
+
}).catch(() => {
|
|
37040
|
+
if (disposed)
|
|
37041
|
+
return;
|
|
37042
|
+
dirty = true;
|
|
37043
|
+
saveRetries += 1;
|
|
37044
|
+
if (saveRetries > SAVE_RETRY_LIMIT) {
|
|
37045
|
+
reportSaveFailure();
|
|
37046
|
+
return;
|
|
37047
|
+
}
|
|
37048
|
+
if (options.keepalive || saveRetryTimer)
|
|
37049
|
+
return;
|
|
37050
|
+
saveRetryTimer = setTimeout(() => {
|
|
37051
|
+
saveRetryTimer = null;
|
|
37052
|
+
save();
|
|
37053
|
+
}, SAVE_RETRY_MS);
|
|
37054
|
+
}).finally(() => {
|
|
37055
|
+
if (options.keepalive)
|
|
37056
|
+
return;
|
|
37057
|
+
saveInFlight = false;
|
|
37058
|
+
if (saveController === controller)
|
|
37059
|
+
saveController = null;
|
|
37060
|
+
if (disposed)
|
|
37061
|
+
return;
|
|
37062
|
+
if (dirty && !saveRetryTimer)
|
|
37063
|
+
save();
|
|
37064
|
+
});
|
|
37065
|
+
}
|
|
37066
|
+
function scheduleSave() {
|
|
37067
|
+
dirty = true;
|
|
37068
|
+
saveRetries = 0;
|
|
37069
|
+
if (saveTimer)
|
|
37070
|
+
clearTimeout(saveTimer);
|
|
37071
|
+
saveTimer = setTimeout(() => {
|
|
37072
|
+
saveTimer = null;
|
|
37073
|
+
save();
|
|
37074
|
+
}, SAVE_DEBOUNCE_MS);
|
|
37075
|
+
}
|
|
37076
|
+
function onDraftInput(tool, value) {
|
|
37077
|
+
drafts[tool] = value;
|
|
37078
|
+
toolTouched = true;
|
|
37079
|
+
scheduleSave();
|
|
37080
|
+
}
|
|
37081
|
+
function createPanes(current) {
|
|
37082
|
+
return {
|
|
37083
|
+
markdown: createMarkdownTool(current, drafts.markdown ?? "", (value) => onDraftInput("markdown", value)),
|
|
37084
|
+
mermaid: createMermaidTool(current, drafts.mermaid ?? "", (value) => onDraftInput("mermaid", value)),
|
|
37085
|
+
json: createJsonTool(current, drafts.json ?? "", (value) => onDraftInput("json", value))
|
|
37086
|
+
};
|
|
37087
|
+
}
|
|
37088
|
+
function setActiveTool(tool, notify) {
|
|
37089
|
+
activeTool = tool;
|
|
37090
|
+
if (!panes || !tabs)
|
|
37091
|
+
return;
|
|
37092
|
+
for (const id of TOOL_IDS) {
|
|
37093
|
+
tabs[id].classList.toggle("active", id === tool);
|
|
37094
|
+
tabs[id].setAttribute("aria-pressed", String(id === tool));
|
|
37095
|
+
panes[id].el.hidden = id !== tool;
|
|
37096
|
+
}
|
|
37097
|
+
panes[tool].refresh();
|
|
37098
|
+
panes[tool].focus();
|
|
37099
|
+
if (notify)
|
|
37100
|
+
deps.onToolChange?.(tool);
|
|
37101
|
+
if (savedActiveTool === tool)
|
|
37102
|
+
return;
|
|
37103
|
+
savedActiveTool = tool;
|
|
37104
|
+
scheduleSave();
|
|
37105
|
+
}
|
|
37106
|
+
function applySheetWidth(host, width) {
|
|
37107
|
+
const clamped = Math.round(Math.max(MIN_TOOLS_SHEET_WIDTH, Math.min(MAX_TOOLS_SHEET_WIDTH, width)));
|
|
37108
|
+
sheetWidth = clamped;
|
|
37109
|
+
host.style.setProperty("--tools-sheet-width", `${clamped}px`);
|
|
37110
|
+
}
|
|
37111
|
+
function mount(host) {
|
|
37112
|
+
if (panes)
|
|
37113
|
+
return;
|
|
37114
|
+
const current = text3();
|
|
37115
|
+
host.replaceChildren();
|
|
37116
|
+
const sheetResizer = document.createElement("div");
|
|
37117
|
+
sheetResizer.className = "tools-sheet-resizer";
|
|
37118
|
+
sheetResizer.role = "separator";
|
|
37119
|
+
sheetResizer.tabIndex = 0;
|
|
37120
|
+
sheetResizer.setAttribute("aria-orientation", "vertical");
|
|
37121
|
+
sheetResizer.setAttribute("aria-label", current.resizeSheet);
|
|
37122
|
+
attachDragResizer({
|
|
37123
|
+
handle: sheetResizer,
|
|
37124
|
+
getSize: () => host.getBoundingClientRect().width,
|
|
37125
|
+
applySize: (width) => {
|
|
37126
|
+
widthTouched = true;
|
|
37127
|
+
applySheetWidth(host, width);
|
|
37128
|
+
},
|
|
37129
|
+
direction: -1,
|
|
37130
|
+
onEnd: scheduleSave,
|
|
37131
|
+
activeClassTarget: host,
|
|
37132
|
+
activeClassName: "tools-sheet-resizing"
|
|
37133
|
+
});
|
|
37134
|
+
const header = document.createElement("header");
|
|
37135
|
+
header.className = "tools-header";
|
|
37136
|
+
titleEl = document.createElement("h1");
|
|
37137
|
+
titleEl.textContent = current.title;
|
|
37138
|
+
const tabList = document.createElement("div");
|
|
37139
|
+
tabList.className = "seg tools-tabs";
|
|
37140
|
+
tabList.role = "group";
|
|
37141
|
+
tabList.setAttribute("aria-label", current.title);
|
|
37142
|
+
closeBtn = document.createElement("button");
|
|
37143
|
+
closeBtn.type = "button";
|
|
37144
|
+
closeBtn.className = "tools-close";
|
|
37145
|
+
closeBtn.textContent = "×";
|
|
37146
|
+
closeBtn.title = current.close;
|
|
37147
|
+
closeBtn.setAttribute("aria-label", current.close);
|
|
37148
|
+
closeBtn.addEventListener("click", (event) => {
|
|
37149
|
+
event.preventDefault();
|
|
37150
|
+
deps.onCloseRequest?.();
|
|
37151
|
+
});
|
|
37152
|
+
header.append(titleEl, tabList, closeBtn);
|
|
37153
|
+
panes = createPanes(current);
|
|
37154
|
+
tabs = {};
|
|
37155
|
+
const body = document.createElement("div");
|
|
37156
|
+
body.className = "tools-body";
|
|
37157
|
+
for (const id of TOOL_IDS) {
|
|
37158
|
+
const tab = document.createElement("button");
|
|
37159
|
+
tab.type = "button";
|
|
37160
|
+
tab.textContent = current.tabs[id];
|
|
37161
|
+
tab.addEventListener("click", () => {
|
|
37162
|
+
toolTouched = true;
|
|
37163
|
+
setActiveTool(id, true);
|
|
37164
|
+
});
|
|
37165
|
+
tabs[id] = tab;
|
|
37166
|
+
tabList.appendChild(tab);
|
|
37167
|
+
panes[id].el.hidden = id !== activeTool;
|
|
37168
|
+
tab.classList.toggle("active", id === activeTool);
|
|
37169
|
+
body.appendChild(panes[id].el);
|
|
37170
|
+
}
|
|
37171
|
+
host.append(sheetResizer, header, body);
|
|
37172
|
+
}
|
|
37173
|
+
async function loadState(myGen) {
|
|
37174
|
+
if (stateLoaded)
|
|
37175
|
+
return;
|
|
37176
|
+
try {
|
|
37177
|
+
const res = await deps.trackLoad(fetch("/_state/tools"));
|
|
37178
|
+
if (myGen !== generation)
|
|
37179
|
+
return;
|
|
37180
|
+
if (!res.ok) {
|
|
37181
|
+
reportLoadFailure();
|
|
37182
|
+
return;
|
|
37183
|
+
}
|
|
37184
|
+
const data = await res.json();
|
|
37185
|
+
if (myGen !== generation)
|
|
37186
|
+
return;
|
|
37187
|
+
stateLoaded = true;
|
|
37188
|
+
if (loadFailed) {
|
|
37189
|
+
loadFailed = false;
|
|
37190
|
+
syncFailureStatus();
|
|
37191
|
+
}
|
|
37192
|
+
if (!toolTouched)
|
|
37193
|
+
savedActiveTool = data.activeTool ?? null;
|
|
37194
|
+
const host = getMount();
|
|
37195
|
+
if (host && !widthTouched && typeof data.width === "number")
|
|
37196
|
+
applySheetWidth(host, data.width);
|
|
37197
|
+
for (const id of TOOL_IDS) {
|
|
37198
|
+
const draft = data.drafts?.[id];
|
|
37199
|
+
if (typeof draft !== "string")
|
|
37200
|
+
continue;
|
|
37201
|
+
if (id in drafts)
|
|
37202
|
+
continue;
|
|
37203
|
+
drafts[id] = draft;
|
|
37204
|
+
panes?.[id].setText(draft);
|
|
37205
|
+
}
|
|
37206
|
+
} catch {
|
|
37207
|
+
if (myGen === generation)
|
|
37208
|
+
reportLoadFailure();
|
|
37209
|
+
}
|
|
37210
|
+
}
|
|
37211
|
+
function isOpen() {
|
|
37212
|
+
const host = getMount();
|
|
37213
|
+
return host ? !host.hidden : false;
|
|
37214
|
+
}
|
|
37215
|
+
async function open(tool) {
|
|
37216
|
+
const host = getMount();
|
|
37217
|
+
if (!host)
|
|
37218
|
+
return;
|
|
37219
|
+
const myGen = ++generation;
|
|
37220
|
+
mount(host);
|
|
37221
|
+
host.hidden = false;
|
|
37222
|
+
host.setAttribute("aria-hidden", "false");
|
|
37223
|
+
host.removeAttribute("inert");
|
|
37224
|
+
const overlay = getOverlay();
|
|
37225
|
+
if (overlay) {
|
|
37226
|
+
overlay.hidden = false;
|
|
37227
|
+
overlay.setAttribute("aria-hidden", "false");
|
|
37228
|
+
}
|
|
37229
|
+
document.body.classList.add("tools-sheet-open");
|
|
37230
|
+
await loadState(myGen);
|
|
37231
|
+
if (myGen !== generation || !isOpen())
|
|
37232
|
+
return;
|
|
37233
|
+
setActiveTool(tool ?? savedActiveTool ?? DEFAULT_TOOL_ID, !tool);
|
|
37234
|
+
}
|
|
37235
|
+
function close() {
|
|
37236
|
+
const host = getMount();
|
|
37237
|
+
if (!host)
|
|
37238
|
+
return;
|
|
37239
|
+
host.hidden = true;
|
|
37240
|
+
host.setAttribute("aria-hidden", "true");
|
|
37241
|
+
host.setAttribute("inert", "");
|
|
37242
|
+
const overlay = getOverlay();
|
|
37243
|
+
if (overlay) {
|
|
37244
|
+
overlay.hidden = true;
|
|
37245
|
+
overlay.setAttribute("aria-hidden", "true");
|
|
37246
|
+
}
|
|
37247
|
+
document.body.classList.remove("tools-sheet-open");
|
|
37248
|
+
generation += 1;
|
|
37249
|
+
save();
|
|
37250
|
+
}
|
|
37251
|
+
function localize() {
|
|
37252
|
+
if (!panes || !tabs)
|
|
37253
|
+
return;
|
|
37254
|
+
const current = text3();
|
|
37255
|
+
if (titleEl)
|
|
37256
|
+
titleEl.textContent = current.title;
|
|
37257
|
+
if (closeBtn) {
|
|
37258
|
+
closeBtn.title = current.close;
|
|
37259
|
+
closeBtn.setAttribute("aria-label", current.close);
|
|
37260
|
+
}
|
|
37261
|
+
for (const id of TOOL_IDS)
|
|
37262
|
+
tabs[id].textContent = current.tabs[id];
|
|
37263
|
+
panes.markdown.localize(current, current.markdown.output, current.markdown.placeholder);
|
|
37264
|
+
panes.mermaid.localize(current, current.mermaid.output, current.mermaid.placeholder);
|
|
37265
|
+
panes.json.localize(current, current.json.output, current.json.placeholder);
|
|
37266
|
+
for (const id of TOOL_IDS)
|
|
37267
|
+
panes[id].localizeActions?.(current);
|
|
37268
|
+
panes[activeTool].refresh();
|
|
37269
|
+
syncFailureStatus();
|
|
37270
|
+
}
|
|
37271
|
+
const onBeforeUnload = () => {
|
|
37272
|
+
if (dirty || saveInFlight)
|
|
37273
|
+
save({ keepalive: true });
|
|
37274
|
+
};
|
|
37275
|
+
window.addEventListener("beforeunload", onBeforeUnload);
|
|
37276
|
+
return {
|
|
37277
|
+
open,
|
|
37278
|
+
close,
|
|
37279
|
+
isOpen,
|
|
37280
|
+
getActiveTool: () => activeTool,
|
|
37281
|
+
localize,
|
|
37282
|
+
dispose() {
|
|
37283
|
+
disposed = true;
|
|
37284
|
+
generation += 1;
|
|
37285
|
+
saveController?.abort();
|
|
37286
|
+
saveController = null;
|
|
37287
|
+
window.removeEventListener("beforeunload", onBeforeUnload);
|
|
37288
|
+
if (saveTimer)
|
|
37289
|
+
clearTimeout(saveTimer);
|
|
37290
|
+
saveTimer = null;
|
|
37291
|
+
if (saveRetryTimer)
|
|
37292
|
+
clearTimeout(saveRetryTimer);
|
|
37293
|
+
saveRetryTimer = null;
|
|
37294
|
+
if (!panes)
|
|
37295
|
+
return;
|
|
37296
|
+
for (const id of TOOL_IDS)
|
|
37297
|
+
panes[id].dispose();
|
|
37298
|
+
}
|
|
37299
|
+
};
|
|
37300
|
+
}
|
|
37301
|
+
|
|
36198
37302
|
// web-src/app.ts
|
|
36199
37303
|
window.GdpExpandLogic = GdpExpandLogic;
|
|
36200
37304
|
(() => {
|
|
@@ -37029,6 +38133,7 @@ code-viewer query agent-help`
|
|
|
37029
38133
|
history: "History",
|
|
37030
38134
|
journal: "Work Log",
|
|
37031
38135
|
database: "Datastores",
|
|
38136
|
+
tools: "Tools",
|
|
37032
38137
|
help: "Help"
|
|
37033
38138
|
},
|
|
37034
38139
|
global: {
|
|
@@ -37322,6 +38427,7 @@ code-viewer query agent-help`
|
|
|
37322
38427
|
history: "履歴",
|
|
37323
38428
|
journal: "ワークログ",
|
|
37324
38429
|
database: "データストア",
|
|
38430
|
+
tools: "ツール",
|
|
37325
38431
|
help: "ヘルプ"
|
|
37326
38432
|
},
|
|
37327
38433
|
global: {
|
|
@@ -37636,11 +38742,6 @@ code-viewer query agent-help`
|
|
|
37636
38742
|
if (route && text3.nav[route])
|
|
37637
38743
|
link2.textContent = text3.nav[route];
|
|
37638
38744
|
});
|
|
37639
|
-
const helpLink = document.querySelector(".global-icon-link[data-route='help']");
|
|
37640
|
-
if (helpLink) {
|
|
37641
|
-
helpLink.title = text3.nav.help;
|
|
37642
|
-
helpLink.setAttribute("aria-label", text3.nav.help);
|
|
37643
|
-
}
|
|
37644
38745
|
const repoWebLink = document.querySelector("#repo-web-link");
|
|
37645
38746
|
if (repoWebLink) {
|
|
37646
38747
|
repoWebLink.title = text3.global.repoWebLink;
|
|
@@ -37676,6 +38777,12 @@ code-viewer query agent-help`
|
|
|
37676
38777
|
doctorBtn.setAttribute("aria-label", doctorTitle);
|
|
37677
38778
|
}
|
|
37678
38779
|
document.querySelector("#doctor-sheet")?.setAttribute("aria-label", doctorTitle);
|
|
38780
|
+
const toolsChrome = toolsText(STATE.language);
|
|
38781
|
+
const toolsBtn = document.querySelector("#tools-btn");
|
|
38782
|
+
if (toolsBtn)
|
|
38783
|
+
toolsBtn.title = toolsChrome.open;
|
|
38784
|
+
document.querySelector("#tools-sheet")?.setAttribute("aria-label", toolsChrome.title);
|
|
38785
|
+
relocalizeTools?.();
|
|
37679
38786
|
const copyAiContext = document.querySelector("#copy-ai-context");
|
|
37680
38787
|
if (copyAiContext) {
|
|
37681
38788
|
copyAiContext.title = text3.global.copyAiContext;
|
|
@@ -37837,6 +38944,7 @@ code-viewer query agent-help`
|
|
|
37837
38944
|
}
|
|
37838
38945
|
let relocalizeHistory = null;
|
|
37839
38946
|
let relocalizeJournal = null;
|
|
38947
|
+
let relocalizeTools = null;
|
|
37840
38948
|
let relocalizeDatabase = null;
|
|
37841
38949
|
let QUICK_HELP = null;
|
|
37842
38950
|
function setViewerLanguage(language, persist = true) {
|
|
@@ -38297,8 +39405,11 @@ code-viewer query agent-help`
|
|
|
38297
39405
|
function withAnnotationSessionParam(rawUrl) {
|
|
38298
39406
|
return ANNOTATIONS_UI ? ANNOTATIONS_UI.withSessionParam(rawUrl) : rawUrl;
|
|
38299
39407
|
}
|
|
39408
|
+
function withOverlayState(url) {
|
|
39409
|
+
return withToolsOverlay(withDoctorOverlay(url, parseDoctorOverlay(window.location.pathname, window.location.search)), parseToolsOverlay(window.location.search));
|
|
39410
|
+
}
|
|
38300
39411
|
function urlForRoute(route) {
|
|
38301
|
-
return
|
|
39412
|
+
return withOverlayState(withAnnotationSessionParam(buildRoute(route)));
|
|
38302
39413
|
}
|
|
38303
39414
|
function historyStateForRoute(route) {
|
|
38304
39415
|
return route.screen === "file" ? {
|
|
@@ -38309,8 +39420,7 @@ code-viewer query agent-help`
|
|
|
38309
39420
|
} : { view: route.screen };
|
|
38310
39421
|
}
|
|
38311
39422
|
function replaceUrlWithCurrentRoute() {
|
|
38312
|
-
const
|
|
38313
|
-
const url = withDoctorOverlay(base2, parseDoctorOverlay(window.location.pathname, window.location.search));
|
|
39423
|
+
const url = urlForRoute(STATE.route);
|
|
38314
39424
|
const current = window.location.pathname + window.location.search;
|
|
38315
39425
|
if (url !== current) {
|
|
38316
39426
|
history.replaceState(historyStateForRoute(STATE.route), "", url + window.location.hash);
|
|
@@ -38458,51 +39568,57 @@ code-viewer query agent-help`
|
|
|
38458
39568
|
applyHideTests();
|
|
38459
39569
|
}
|
|
38460
39570
|
function syncHeaderMenu() {
|
|
38461
|
-
document.
|
|
39571
|
+
const toolsBtn = document.querySelector("#tools-btn");
|
|
39572
|
+
if (toolsBtn) {
|
|
39573
|
+
const open = parseToolsOverlay(window.location.search) !== null;
|
|
39574
|
+
toolsBtn.classList.toggle("active", open);
|
|
39575
|
+
toolsBtn.setAttribute("aria-pressed", String(open));
|
|
39576
|
+
}
|
|
39577
|
+
document.querySelectorAll("a.app-menu-item, a.global-icon-link").forEach((link2) => {
|
|
38462
39578
|
const fileRouteOwner = STATE.route.screen === "file" && (STATE.route.view === "blob" || STATE.route.view === "blame" || STATE.route.view === "history") ? "repo" : "diff";
|
|
38463
39579
|
const active = link2.dataset.route === STATE.route.screen || STATE.route.screen === "file" && link2.dataset.route === fileRouteOwner;
|
|
38464
39580
|
link2.classList.toggle("active", active);
|
|
38465
39581
|
link2.setAttribute("aria-current", active ? "page" : "false");
|
|
38466
39582
|
if (link2.dataset.route === "repo") {
|
|
38467
|
-
link2.href = buildRoute({
|
|
39583
|
+
link2.href = withOverlayState(buildRoute({
|
|
38468
39584
|
screen: "repo",
|
|
38469
39585
|
ref: STATE.repoRef || "worktree",
|
|
38470
39586
|
path: "",
|
|
38471
39587
|
range: currentRange()
|
|
38472
|
-
});
|
|
39588
|
+
}));
|
|
38473
39589
|
}
|
|
38474
39590
|
if (link2.dataset.route === "diff") {
|
|
38475
|
-
link2.href = buildRoute({
|
|
39591
|
+
link2.href = withOverlayState(buildRoute({
|
|
38476
39592
|
screen: "diff",
|
|
38477
39593
|
range: preHistoryRange ?? currentRange()
|
|
38478
|
-
});
|
|
39594
|
+
}));
|
|
38479
39595
|
}
|
|
38480
39596
|
if (link2.dataset.route === "history") {
|
|
38481
|
-
link2.href = buildRoute({
|
|
39597
|
+
link2.href = withOverlayState(buildRoute({
|
|
38482
39598
|
screen: "history",
|
|
38483
39599
|
ref: "HEAD",
|
|
38484
39600
|
range: currentRange()
|
|
38485
|
-
});
|
|
39601
|
+
}));
|
|
38486
39602
|
}
|
|
38487
39603
|
if (link2.dataset.route === "journal") {
|
|
38488
|
-
link2.href = buildRoute({
|
|
39604
|
+
link2.href = withOverlayState(buildRoute({
|
|
38489
39605
|
screen: "journal",
|
|
38490
39606
|
range: currentRange()
|
|
38491
|
-
});
|
|
39607
|
+
}));
|
|
38492
39608
|
}
|
|
38493
39609
|
if (link2.dataset.route === "database") {
|
|
38494
|
-
link2.href = buildRoute({
|
|
39610
|
+
link2.href = withOverlayState(buildRoute({
|
|
38495
39611
|
screen: "database",
|
|
38496
39612
|
range: currentRange()
|
|
38497
|
-
});
|
|
39613
|
+
}));
|
|
38498
39614
|
}
|
|
38499
39615
|
if (link2.dataset.route === "help") {
|
|
38500
|
-
link2.href = buildRoute({
|
|
39616
|
+
link2.href = withOverlayState(buildRoute({
|
|
38501
39617
|
screen: "help",
|
|
38502
39618
|
lang: STATE.route.screen === "help" ? helpLanguageFromRoute(STATE.route) : STATE.language,
|
|
38503
39619
|
section: helpSectionFromRoute(STATE.route),
|
|
38504
39620
|
range: currentRange()
|
|
38505
|
-
});
|
|
39621
|
+
}));
|
|
38506
39622
|
}
|
|
38507
39623
|
});
|
|
38508
39624
|
}
|
|
@@ -38712,10 +39828,6 @@ code-viewer query agent-help`
|
|
|
38712
39828
|
if (quickHelpIcon) {
|
|
38713
39829
|
quickHelpIcon.innerHTML = iconSvg("octicon-question", QUESTION_16_PATH);
|
|
38714
39830
|
}
|
|
38715
|
-
const helpLinkIcon = document.querySelector(".global-icon-link[data-route='help'] .goi-icon");
|
|
38716
|
-
if (helpLinkIcon) {
|
|
38717
|
-
helpLinkIcon.innerHTML = iconSvg("octicon-book", BOOK_16_PATH);
|
|
38718
|
-
}
|
|
38719
39831
|
const branchIcon = document.querySelector("#project-branch .goi-icon");
|
|
38720
39832
|
if (branchIcon) {
|
|
38721
39833
|
branchIcon.innerHTML = iconSvg("octicon-git-branch", GIT_BRANCH_16_PATH);
|
|
@@ -38757,6 +39869,10 @@ code-viewer query agent-help`
|
|
|
38757
39869
|
event.preventDefault();
|
|
38758
39870
|
toggleDoctorSheet();
|
|
38759
39871
|
});
|
|
39872
|
+
$("#tools-btn")?.addEventListener("click", (event) => {
|
|
39873
|
+
event.preventDefault();
|
|
39874
|
+
toggleToolsSheet();
|
|
39875
|
+
});
|
|
38760
39876
|
let copyAiContextFeedbackTimer = null;
|
|
38761
39877
|
$("#copy-ai-context")?.addEventListener("click", async (event) => {
|
|
38762
39878
|
const button = event.currentTarget;
|
|
@@ -39440,6 +40556,7 @@ code-viewer query agent-help`
|
|
|
39440
40556
|
load();
|
|
39441
40557
|
syncLineRefPill();
|
|
39442
40558
|
syncDoctorSheetFromUrl();
|
|
40559
|
+
syncToolsSheetFromUrl();
|
|
39443
40560
|
});
|
|
39444
40561
|
function syncRefInputs() {
|
|
39445
40562
|
const fi = $("#ref-from"), ti = $("#ref-to");
|
|
@@ -39615,6 +40732,57 @@ code-viewer query agent-help`
|
|
|
39615
40732
|
event.preventDefault();
|
|
39616
40733
|
closeDoctorSheet();
|
|
39617
40734
|
});
|
|
40735
|
+
const TOOLS_VIEW = createToolsView({
|
|
40736
|
+
$: (sel) => document.querySelector(sel),
|
|
40737
|
+
trackLoad,
|
|
40738
|
+
getLanguage: () => STATE.language,
|
|
40739
|
+
actionHeaders,
|
|
40740
|
+
onCloseRequest: () => closeToolsSheet(),
|
|
40741
|
+
onToolChange: (tool) => updateUrlForToolsOverlay(tool)
|
|
40742
|
+
});
|
|
40743
|
+
relocalizeTools = () => TOOLS_VIEW.localize();
|
|
40744
|
+
function openToolsOverlay() {
|
|
40745
|
+
return parseToolsOverlay(window.location.search);
|
|
40746
|
+
}
|
|
40747
|
+
function updateUrlForToolsOverlay(tool) {
|
|
40748
|
+
const current = window.location.pathname + window.location.search;
|
|
40749
|
+
const next = withToolsOverlay(current, tool);
|
|
40750
|
+
if (next !== current) {
|
|
40751
|
+
history.replaceState(history.state, "", next + window.location.hash);
|
|
40752
|
+
}
|
|
40753
|
+
syncHeaderMenu();
|
|
40754
|
+
}
|
|
40755
|
+
function openToolsSheet(tool) {
|
|
40756
|
+
updateUrlForToolsOverlay(tool ?? TOOLS_VIEW.getActiveTool());
|
|
40757
|
+
TOOLS_VIEW.open(tool);
|
|
40758
|
+
}
|
|
40759
|
+
function closeToolsSheet() {
|
|
40760
|
+
TOOLS_VIEW.close();
|
|
40761
|
+
updateUrlForToolsOverlay(null);
|
|
40762
|
+
}
|
|
40763
|
+
function toggleToolsSheet() {
|
|
40764
|
+
if (openToolsOverlay() || TOOLS_VIEW.isOpen())
|
|
40765
|
+
closeToolsSheet();
|
|
40766
|
+
else
|
|
40767
|
+
openToolsSheet();
|
|
40768
|
+
}
|
|
40769
|
+
function syncToolsSheetFromUrl() {
|
|
40770
|
+
const tool = openToolsOverlay();
|
|
40771
|
+
const open = TOOLS_VIEW.isOpen();
|
|
40772
|
+
if (tool && (!open || TOOLS_VIEW.getActiveTool() !== tool))
|
|
40773
|
+
TOOLS_VIEW.open(tool);
|
|
40774
|
+
else if (!tool && open)
|
|
40775
|
+
TOOLS_VIEW.close();
|
|
40776
|
+
}
|
|
40777
|
+
document.getElementById("tools-sheet-overlay")?.addEventListener("click", () => closeToolsSheet());
|
|
40778
|
+
document.addEventListener("keydown", (event) => {
|
|
40779
|
+
if (event.key !== "Escape")
|
|
40780
|
+
return;
|
|
40781
|
+
if (!TOOLS_VIEW.isOpen())
|
|
40782
|
+
return;
|
|
40783
|
+
event.preventDefault();
|
|
40784
|
+
closeToolsSheet();
|
|
40785
|
+
});
|
|
39618
40786
|
JOURNAL_VIEW = createJournalView({
|
|
39619
40787
|
getRoute: () => STATE.route,
|
|
39620
40788
|
setRoute,
|
|
@@ -39692,6 +40860,7 @@ code-viewer query agent-help`
|
|
|
39692
40860
|
syncHeaderMenu();
|
|
39693
40861
|
syncLineRefPill();
|
|
39694
40862
|
syncDoctorSheetFromUrl();
|
|
40863
|
+
syncToolsSheetFromUrl();
|
|
39695
40864
|
if (isSameBlobFileRoute(previousRoute, STATE.route) && routeBlobPreview(previousRoute) !== routeBlobPreview(STATE.route) && switchSourceTab(routeBlobPreview(STATE.route) ? "preview" : "code", {
|
|
39696
40865
|
updateRoute: false
|
|
39697
40866
|
})) {
|
|
@@ -39752,7 +40921,7 @@ code-viewer query agent-help`
|
|
|
39752
40921
|
}
|
|
39753
40922
|
window.addEventListener("popstate", applyRouteFromLocation);
|
|
39754
40923
|
window.addEventListener("pagehide", () => flushViewStatePatch(true));
|
|
39755
|
-
document.querySelectorAll(".app-menu-item, .global-icon-link").forEach((link2) => {
|
|
40924
|
+
document.querySelectorAll("a.app-menu-item, a.global-icon-link").forEach((link2) => {
|
|
39756
40925
|
if (link2.target === "_blank")
|
|
39757
40926
|
return;
|
|
39758
40927
|
link2.addEventListener("click", (e2) => {
|