@youtyan/code-viewer 0.9.1 → 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 +1200 -718
- package/package.json +5 -4
- package/web/app.js +1302 -113
- package/web/index.html +8 -2
- 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
|
@@ -247,6 +247,22 @@ ${lines.join(`
|
|
|
247
247
|
};
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
// web-src/core/changed-paths.ts
|
|
251
|
+
function changedPathCoversPath(changed, path) {
|
|
252
|
+
return path === changed || path.startsWith(`${changed}/`);
|
|
253
|
+
}
|
|
254
|
+
function changedPathsCoverPath(changedPaths, path) {
|
|
255
|
+
if (!changedPaths)
|
|
256
|
+
return true;
|
|
257
|
+
if (changedPaths.has(path))
|
|
258
|
+
return true;
|
|
259
|
+
for (const changed of changedPaths) {
|
|
260
|
+
if (changedPathCoversPath(changed, path))
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
|
|
250
266
|
// web-src/core/expand-logic.ts
|
|
251
267
|
function initExpandState(prevHunkEndNew, hunkNewStart) {
|
|
252
268
|
return {
|
|
@@ -558,7 +574,6 @@ ${lines.join(`
|
|
|
558
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";
|
|
559
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";
|
|
560
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";
|
|
561
|
-
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";
|
|
562
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";
|
|
563
578
|
var DOWNLOAD_16_PATHS = [
|
|
564
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",
|
|
@@ -859,6 +874,19 @@ ${lines.join(`
|
|
|
859
874
|
return { url: `${target}${lineHash}`, provider: "github" };
|
|
860
875
|
}
|
|
861
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
|
+
|
|
862
890
|
// web-src/core/routes.ts
|
|
863
891
|
function assertNever(value) {
|
|
864
892
|
throw new Error(`unhandled route: ${JSON.stringify(value)}`);
|
|
@@ -1129,18 +1157,28 @@ ${lines.join(`
|
|
|
1129
1157
|
const params = new URLSearchParams(search);
|
|
1130
1158
|
return params.get("doctor") === "open";
|
|
1131
1159
|
}
|
|
1132
|
-
function
|
|
1160
|
+
function withQueryParam(url, key, value) {
|
|
1133
1161
|
const queryIdx = url.indexOf("?");
|
|
1134
1162
|
const base = queryIdx >= 0 ? url.slice(0, queryIdx) : url;
|
|
1135
1163
|
const query = queryIdx >= 0 ? url.slice(queryIdx + 1) : "";
|
|
1136
1164
|
const params = new URLSearchParams(query);
|
|
1137
|
-
if (
|
|
1138
|
-
params.
|
|
1165
|
+
if (value === null)
|
|
1166
|
+
params.delete(key);
|
|
1139
1167
|
else
|
|
1140
|
-
params.
|
|
1168
|
+
params.set(key, value);
|
|
1141
1169
|
const qs = params.toString();
|
|
1142
1170
|
return qs ? `${base}?${qs}` : base;
|
|
1143
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
|
+
}
|
|
1144
1182
|
|
|
1145
1183
|
// web-src/views/media-embed.ts
|
|
1146
1184
|
var MEDIA_RE = /\.(png|jpe?g|gif|webp|svg|avif|bmp|ico|mp4|webm|mov|mp3|wav|ogg|flac|m4a|aac|opus)(\?.*)?$/i;
|
|
@@ -7590,6 +7628,19 @@ ${lines.join(`
|
|
|
7590
7628
|
}
|
|
7591
7629
|
|
|
7592
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
|
+
}
|
|
7593
7644
|
var cache = new Map;
|
|
7594
7645
|
function loadShikiHighlighter(options) {
|
|
7595
7646
|
const key = JSON.stringify({
|
|
@@ -7976,7 +8027,9 @@ ${frontmatter.yaml}
|
|
|
7976
8027
|
else
|
|
7977
8028
|
break;
|
|
7978
8029
|
}
|
|
7979
|
-
|
|
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) {
|
|
7980
8033
|
active = entries[entries.length - 1];
|
|
7981
8034
|
}
|
|
7982
8035
|
entries.forEach((entry) => {
|
|
@@ -7988,8 +8041,9 @@ ${frontmatter.yaml}
|
|
|
7988
8041
|
if (!raf)
|
|
7989
8042
|
raf = requestAnimationFrame(update);
|
|
7990
8043
|
};
|
|
7991
|
-
|
|
8044
|
+
document.addEventListener("scroll", schedule, {
|
|
7992
8045
|
passive: true,
|
|
8046
|
+
capture: true,
|
|
7993
8047
|
signal: controller.signal
|
|
7994
8048
|
});
|
|
7995
8049
|
window.addEventListener("resize", schedule, { signal: controller.signal });
|
|
@@ -8012,7 +8066,21 @@ ${frontmatter.yaml}
|
|
|
8012
8066
|
function decodeHashFragment(hash) {
|
|
8013
8067
|
return decodeUriComponentSafe(hash.startsWith("#") ? hash.slice(1) : hash);
|
|
8014
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
|
+
}
|
|
8015
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
|
+
}
|
|
8016
8084
|
const top = section.getBoundingClientRect().top + window.scrollY - markdownAnchorOffset() - 12;
|
|
8017
8085
|
window.scrollTo({ top: Math.max(0, top), behavior });
|
|
8018
8086
|
}
|
|
@@ -13149,6 +13217,75 @@ ${frontmatter.yaml}
|
|
|
13149
13217
|
};
|
|
13150
13218
|
}
|
|
13151
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
|
+
|
|
13152
13289
|
// web-src/views/database/er-diagram.ts
|
|
13153
13290
|
function mermaidType(sqlType) {
|
|
13154
13291
|
const upper = sqlType.toUpperCase();
|
|
@@ -13239,30 +13376,17 @@ ${frontmatter.yaml}
|
|
|
13239
13376
|
copyBtn.textContent = text3().er.copyMermaid;
|
|
13240
13377
|
copyBtn.title = text3().er.copyMermaidTitle;
|
|
13241
13378
|
toolbar.append(zoomIn, zoomOut, zoomReset, copyBtn);
|
|
13242
|
-
const
|
|
13243
|
-
|
|
13244
|
-
|
|
13245
|
-
|
|
13246
|
-
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;
|
|
13247
13385
|
el.append(toolbar, container);
|
|
13248
|
-
let scale = 1;
|
|
13249
13386
|
let lastMarkup = "";
|
|
13250
|
-
|
|
13251
|
-
|
|
13252
|
-
|
|
13253
|
-
}
|
|
13254
|
-
zoomIn.addEventListener("click", () => {
|
|
13255
|
-
scale = Math.min(3, scale + 0.2);
|
|
13256
|
-
applyZoom();
|
|
13257
|
-
});
|
|
13258
|
-
zoomOut.addEventListener("click", () => {
|
|
13259
|
-
scale = Math.max(0.2, scale - 0.2);
|
|
13260
|
-
applyZoom();
|
|
13261
|
-
});
|
|
13262
|
-
zoomReset.addEventListener("click", () => {
|
|
13263
|
-
scale = 1;
|
|
13264
|
-
applyZoom();
|
|
13265
|
-
});
|
|
13387
|
+
zoomIn.addEventListener("click", () => viewport.zoomIn());
|
|
13388
|
+
zoomOut.addEventListener("click", () => viewport.zoomOut());
|
|
13389
|
+
zoomReset.addEventListener("click", () => viewport.reset());
|
|
13266
13390
|
copyBtn.addEventListener("click", () => {
|
|
13267
13391
|
if (lastMarkup) {
|
|
13268
13392
|
navigator.clipboard.writeText(lastMarkup).then(() => {
|
|
@@ -13275,46 +13399,10 @@ ${frontmatter.yaml}
|
|
|
13275
13399
|
});
|
|
13276
13400
|
}
|
|
13277
13401
|
});
|
|
13278
|
-
let dragState = null;
|
|
13279
|
-
container.addEventListener("mousedown", (e2) => {
|
|
13280
|
-
if (e2.button !== 0)
|
|
13281
|
-
return;
|
|
13282
|
-
dragState = {
|
|
13283
|
-
x: e2.clientX,
|
|
13284
|
-
y: e2.clientY,
|
|
13285
|
-
sl: container.scrollLeft,
|
|
13286
|
-
st: container.scrollTop
|
|
13287
|
-
};
|
|
13288
|
-
container.style.cursor = "grabbing";
|
|
13289
|
-
e2.preventDefault();
|
|
13290
|
-
});
|
|
13291
|
-
const onWindowMouseMove = (e2) => {
|
|
13292
|
-
if (!dragState)
|
|
13293
|
-
return;
|
|
13294
|
-
container.scrollLeft = dragState.sl - (e2.clientX - dragState.x);
|
|
13295
|
-
container.scrollTop = dragState.st - (e2.clientY - dragState.y);
|
|
13296
|
-
};
|
|
13297
|
-
const onWindowMouseUp = () => {
|
|
13298
|
-
if (dragState) {
|
|
13299
|
-
dragState = null;
|
|
13300
|
-
container.style.cursor = "";
|
|
13301
|
-
}
|
|
13302
|
-
};
|
|
13303
|
-
window.addEventListener("mousemove", onWindowMouseMove);
|
|
13304
|
-
window.addEventListener("mouseup", onWindowMouseUp);
|
|
13305
|
-
container.addEventListener("wheel", (e2) => {
|
|
13306
|
-
if (e2.ctrlKey || e2.metaKey) {
|
|
13307
|
-
e2.preventDefault();
|
|
13308
|
-
const delta = e2.deltaY > 0 ? -0.1 : 0.1;
|
|
13309
|
-
scale = Math.max(0.2, Math.min(3, scale + delta));
|
|
13310
|
-
applyZoom();
|
|
13311
|
-
}
|
|
13312
|
-
}, { passive: false });
|
|
13313
13402
|
async function render(schema, columnsMap) {
|
|
13314
13403
|
el.hidden = false;
|
|
13315
13404
|
svgWrap.innerHTML = "";
|
|
13316
|
-
|
|
13317
|
-
applyZoom();
|
|
13405
|
+
viewport.reset();
|
|
13318
13406
|
const tables = schema.tables.filter((t2) => t2.type === "table");
|
|
13319
13407
|
if (tables.length === 0) {
|
|
13320
13408
|
svgWrap.textContent = text3().er.noTables;
|
|
@@ -13341,13 +13429,10 @@ ${frontmatter.yaml}
|
|
|
13341
13429
|
el.hidden = true;
|
|
13342
13430
|
svgWrap.innerHTML = "";
|
|
13343
13431
|
lastMarkup = "";
|
|
13344
|
-
dragState = null;
|
|
13345
|
-
container.style.cursor = "";
|
|
13346
13432
|
}
|
|
13347
13433
|
function dispose() {
|
|
13348
13434
|
clear();
|
|
13349
|
-
|
|
13350
|
-
window.removeEventListener("mouseup", onWindowMouseUp);
|
|
13435
|
+
viewport.dispose();
|
|
13351
13436
|
}
|
|
13352
13437
|
function localize() {
|
|
13353
13438
|
const t2 = text3().er;
|
|
@@ -13635,17 +13720,7 @@ ${frontmatter.yaml}
|
|
|
13635
13720
|
|
|
13636
13721
|
// web-src/views/database/shiki-sql.ts
|
|
13637
13722
|
function highlightSqlToInnerHtml(code2, highlighter) {
|
|
13638
|
-
|
|
13639
|
-
return "";
|
|
13640
|
-
const html = highlighter.codeToHtml(code2, {
|
|
13641
|
-
lang: "sql",
|
|
13642
|
-
themes: { light: "github-light", dark: "github-dark" },
|
|
13643
|
-
defaultColor: false
|
|
13644
|
-
});
|
|
13645
|
-
const template = document.createElement("template");
|
|
13646
|
-
template.innerHTML = html;
|
|
13647
|
-
const pre = template.content.querySelector("pre");
|
|
13648
|
-
return pre ? pre.innerHTML : "";
|
|
13723
|
+
return highlightToInnerHtml(code2, "sql", highlighter);
|
|
13649
13724
|
}
|
|
13650
13725
|
|
|
13651
13726
|
// web-src/views/database/query-editor.ts
|
|
@@ -23697,7 +23772,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
23697
23772
|
function invalidateLoadedCard(card, file, changedPaths, measuredHeight) {
|
|
23698
23773
|
if (!card.classList.contains("loaded") && !card.classList.contains("loading"))
|
|
23699
23774
|
return false;
|
|
23700
|
-
if (changedPaths
|
|
23775
|
+
if (!changedPathsCoverPath(changedPaths, file.path))
|
|
23701
23776
|
return false;
|
|
23702
23777
|
card.dataset.reqId = String(++CLIENT_REQ_SEQ);
|
|
23703
23778
|
card.style.minHeight = `${measuredHeight ?? card.offsetHeight}px`;
|
|
@@ -23787,13 +23862,12 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
23787
23862
|
const pathsUnknown = !changedPaths;
|
|
23788
23863
|
const contentMayNeedReload = !listSame || pathsUnknown || (changedPaths ? newFiles.some((file) => {
|
|
23789
23864
|
const key = fileKey(file);
|
|
23790
|
-
return prevCardSignatures.get(key) !== newCardSigs.get(key) || changedPaths
|
|
23865
|
+
return prevCardSignatures.get(key) !== newCardSigs.get(key) || changedPathsCoverPath(changedPaths, file.path);
|
|
23791
23866
|
}) : false);
|
|
23792
23867
|
if (contentMayNeedReload)
|
|
23793
23868
|
resetLoadScheduling();
|
|
23794
23869
|
let invalidatedCards = 0;
|
|
23795
23870
|
if (listSame && domIntact) {
|
|
23796
|
-
const pathsUnknown2 = !changedPaths;
|
|
23797
23871
|
const cardsByKey = collectDiffCardsByKey(target);
|
|
23798
23872
|
const measuredHeights = new Map;
|
|
23799
23873
|
for (const f2 of newFiles) {
|
|
@@ -23808,7 +23882,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
23808
23882
|
const oldSig = prevCardSignatures.get(key);
|
|
23809
23883
|
const newSig = newCardSigs.get(key) ?? computeCardSignature(f2);
|
|
23810
23884
|
const sigChanged = oldSig !== newSig;
|
|
23811
|
-
const pathHint =
|
|
23885
|
+
const pathHint = changedPathsCoverPath(changedPaths, f2.path);
|
|
23812
23886
|
if (!sigChanged && !pathHint) {
|
|
23813
23887
|
continue;
|
|
23814
23888
|
}
|
|
@@ -26440,6 +26514,19 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
26440
26514
|
}
|
|
26441
26515
|
]
|
|
26442
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
|
+
},
|
|
26443
26530
|
{
|
|
26444
26531
|
title: "Environment doctor",
|
|
26445
26532
|
blocks: [
|
|
@@ -26498,6 +26585,10 @@ code-viewer doctor --bin git=/opt/bin/git --bin docker=/opt/bin/docker --bin gh=
|
|
|
26498
26585
|
"db-ui.json",
|
|
26499
26586
|
"Datastore UI preferences — column widths per (DB, table, column) and toggle states such as Rails FK inference and the S3 tooltip."
|
|
26500
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
|
+
],
|
|
26501
26592
|
[
|
|
26502
26593
|
"annotations.json",
|
|
26503
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."
|
|
@@ -27135,6 +27226,19 @@ code-viewer query agent-help`
|
|
|
27135
27226
|
}
|
|
27136
27227
|
]
|
|
27137
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
|
+
},
|
|
27138
27242
|
{
|
|
27139
27243
|
title: "環境ドクター",
|
|
27140
27244
|
blocks: [
|
|
@@ -27193,6 +27297,10 @@ code-viewer doctor --bin git=/opt/bin/git --bin docker=/opt/bin/docker --bin gh=
|
|
|
27193
27297
|
"db-ui.json",
|
|
27194
27298
|
"データストア UI の設定 — (DB, テーブル, カラム) ごとの列幅、Rails FK 推測や S3 ツールチップなどのトグル状態。"
|
|
27195
27299
|
],
|
|
27300
|
+
[
|
|
27301
|
+
"tools.json",
|
|
27302
|
+
"ツールドロワーの状態 — 各ツール(Markdown / Mermaid / JSON・YAML)に貼り付けた下書き、最後に使ったツール、ドロワーの幅。"
|
|
27303
|
+
],
|
|
27196
27304
|
[
|
|
27197
27305
|
"annotations.json",
|
|
27198
27306
|
"AI 注釈のウォークスルー — セッションと順序付きステップ(ファイル、行範囲、タイトル、本文)。注釈パネルが読み、SSE で開いているタブへもライブ同期されます。"
|
|
@@ -36180,6 +36288,1017 @@ code-viewer query agent-help`
|
|
|
36180
36288
|
};
|
|
36181
36289
|
}
|
|
36182
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
|
+
|
|
36183
37302
|
// web-src/app.ts
|
|
36184
37303
|
window.GdpExpandLogic = GdpExpandLogic;
|
|
36185
37304
|
(() => {
|
|
@@ -36593,6 +37712,11 @@ code-viewer query agent-help`
|
|
|
36593
37712
|
SERVER_SCOPE_WATCH_LIMIT_MIN = settings.scope.watch_limit_min;
|
|
36594
37713
|
if (typeof settings.scope.watch_limit_max === "number")
|
|
36595
37714
|
SERVER_SCOPE_WATCH_LIMIT_MAX = settings.scope.watch_limit_max;
|
|
37715
|
+
if (typeof settings.scope.watch_recursive === "boolean") {
|
|
37716
|
+
const watchSection = document.querySelector("#watch-settings-section");
|
|
37717
|
+
if (watchSection)
|
|
37718
|
+
watchSection.hidden = settings.scope.watch_recursive;
|
|
37719
|
+
}
|
|
36596
37720
|
return settings;
|
|
36597
37721
|
} catch {
|
|
36598
37722
|
return null;
|
|
@@ -37009,6 +38133,7 @@ code-viewer query agent-help`
|
|
|
37009
38133
|
history: "History",
|
|
37010
38134
|
journal: "Work Log",
|
|
37011
38135
|
database: "Datastores",
|
|
38136
|
+
tools: "Tools",
|
|
37012
38137
|
help: "Help"
|
|
37013
38138
|
},
|
|
37014
38139
|
global: {
|
|
@@ -37302,6 +38427,7 @@ code-viewer query agent-help`
|
|
|
37302
38427
|
history: "履歴",
|
|
37303
38428
|
journal: "ワークログ",
|
|
37304
38429
|
database: "データストア",
|
|
38430
|
+
tools: "ツール",
|
|
37305
38431
|
help: "ヘルプ"
|
|
37306
38432
|
},
|
|
37307
38433
|
global: {
|
|
@@ -37616,11 +38742,6 @@ code-viewer query agent-help`
|
|
|
37616
38742
|
if (route && text3.nav[route])
|
|
37617
38743
|
link2.textContent = text3.nav[route];
|
|
37618
38744
|
});
|
|
37619
|
-
const helpLink = document.querySelector(".global-icon-link[data-route='help']");
|
|
37620
|
-
if (helpLink) {
|
|
37621
|
-
helpLink.title = text3.nav.help;
|
|
37622
|
-
helpLink.setAttribute("aria-label", text3.nav.help);
|
|
37623
|
-
}
|
|
37624
38745
|
const repoWebLink = document.querySelector("#repo-web-link");
|
|
37625
38746
|
if (repoWebLink) {
|
|
37626
38747
|
repoWebLink.title = text3.global.repoWebLink;
|
|
@@ -37656,6 +38777,12 @@ code-viewer query agent-help`
|
|
|
37656
38777
|
doctorBtn.setAttribute("aria-label", doctorTitle);
|
|
37657
38778
|
}
|
|
37658
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?.();
|
|
37659
38786
|
const copyAiContext = document.querySelector("#copy-ai-context");
|
|
37660
38787
|
if (copyAiContext) {
|
|
37661
38788
|
copyAiContext.title = text3.global.copyAiContext;
|
|
@@ -37817,6 +38944,7 @@ code-viewer query agent-help`
|
|
|
37817
38944
|
}
|
|
37818
38945
|
let relocalizeHistory = null;
|
|
37819
38946
|
let relocalizeJournal = null;
|
|
38947
|
+
let relocalizeTools = null;
|
|
37820
38948
|
let relocalizeDatabase = null;
|
|
37821
38949
|
let QUICK_HELP = null;
|
|
37822
38950
|
function setViewerLanguage(language, persist = true) {
|
|
@@ -38277,8 +39405,11 @@ code-viewer query agent-help`
|
|
|
38277
39405
|
function withAnnotationSessionParam(rawUrl) {
|
|
38278
39406
|
return ANNOTATIONS_UI ? ANNOTATIONS_UI.withSessionParam(rawUrl) : rawUrl;
|
|
38279
39407
|
}
|
|
39408
|
+
function withOverlayState(url) {
|
|
39409
|
+
return withToolsOverlay(withDoctorOverlay(url, parseDoctorOverlay(window.location.pathname, window.location.search)), parseToolsOverlay(window.location.search));
|
|
39410
|
+
}
|
|
38280
39411
|
function urlForRoute(route) {
|
|
38281
|
-
return
|
|
39412
|
+
return withOverlayState(withAnnotationSessionParam(buildRoute(route)));
|
|
38282
39413
|
}
|
|
38283
39414
|
function historyStateForRoute(route) {
|
|
38284
39415
|
return route.screen === "file" ? {
|
|
@@ -38289,8 +39420,7 @@ code-viewer query agent-help`
|
|
|
38289
39420
|
} : { view: route.screen };
|
|
38290
39421
|
}
|
|
38291
39422
|
function replaceUrlWithCurrentRoute() {
|
|
38292
|
-
const
|
|
38293
|
-
const url = withDoctorOverlay(base2, parseDoctorOverlay(window.location.pathname, window.location.search));
|
|
39423
|
+
const url = urlForRoute(STATE.route);
|
|
38294
39424
|
const current = window.location.pathname + window.location.search;
|
|
38295
39425
|
if (url !== current) {
|
|
38296
39426
|
history.replaceState(historyStateForRoute(STATE.route), "", url + window.location.hash);
|
|
@@ -38438,51 +39568,57 @@ code-viewer query agent-help`
|
|
|
38438
39568
|
applyHideTests();
|
|
38439
39569
|
}
|
|
38440
39570
|
function syncHeaderMenu() {
|
|
38441
|
-
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) => {
|
|
38442
39578
|
const fileRouteOwner = STATE.route.screen === "file" && (STATE.route.view === "blob" || STATE.route.view === "blame" || STATE.route.view === "history") ? "repo" : "diff";
|
|
38443
39579
|
const active = link2.dataset.route === STATE.route.screen || STATE.route.screen === "file" && link2.dataset.route === fileRouteOwner;
|
|
38444
39580
|
link2.classList.toggle("active", active);
|
|
38445
39581
|
link2.setAttribute("aria-current", active ? "page" : "false");
|
|
38446
39582
|
if (link2.dataset.route === "repo") {
|
|
38447
|
-
link2.href = buildRoute({
|
|
39583
|
+
link2.href = withOverlayState(buildRoute({
|
|
38448
39584
|
screen: "repo",
|
|
38449
39585
|
ref: STATE.repoRef || "worktree",
|
|
38450
39586
|
path: "",
|
|
38451
39587
|
range: currentRange()
|
|
38452
|
-
});
|
|
39588
|
+
}));
|
|
38453
39589
|
}
|
|
38454
39590
|
if (link2.dataset.route === "diff") {
|
|
38455
|
-
link2.href = buildRoute({
|
|
39591
|
+
link2.href = withOverlayState(buildRoute({
|
|
38456
39592
|
screen: "diff",
|
|
38457
39593
|
range: preHistoryRange ?? currentRange()
|
|
38458
|
-
});
|
|
39594
|
+
}));
|
|
38459
39595
|
}
|
|
38460
39596
|
if (link2.dataset.route === "history") {
|
|
38461
|
-
link2.href = buildRoute({
|
|
39597
|
+
link2.href = withOverlayState(buildRoute({
|
|
38462
39598
|
screen: "history",
|
|
38463
39599
|
ref: "HEAD",
|
|
38464
39600
|
range: currentRange()
|
|
38465
|
-
});
|
|
39601
|
+
}));
|
|
38466
39602
|
}
|
|
38467
39603
|
if (link2.dataset.route === "journal") {
|
|
38468
|
-
link2.href = buildRoute({
|
|
39604
|
+
link2.href = withOverlayState(buildRoute({
|
|
38469
39605
|
screen: "journal",
|
|
38470
39606
|
range: currentRange()
|
|
38471
|
-
});
|
|
39607
|
+
}));
|
|
38472
39608
|
}
|
|
38473
39609
|
if (link2.dataset.route === "database") {
|
|
38474
|
-
link2.href = buildRoute({
|
|
39610
|
+
link2.href = withOverlayState(buildRoute({
|
|
38475
39611
|
screen: "database",
|
|
38476
39612
|
range: currentRange()
|
|
38477
|
-
});
|
|
39613
|
+
}));
|
|
38478
39614
|
}
|
|
38479
39615
|
if (link2.dataset.route === "help") {
|
|
38480
|
-
link2.href = buildRoute({
|
|
39616
|
+
link2.href = withOverlayState(buildRoute({
|
|
38481
39617
|
screen: "help",
|
|
38482
39618
|
lang: STATE.route.screen === "help" ? helpLanguageFromRoute(STATE.route) : STATE.language,
|
|
38483
39619
|
section: helpSectionFromRoute(STATE.route),
|
|
38484
39620
|
range: currentRange()
|
|
38485
|
-
});
|
|
39621
|
+
}));
|
|
38486
39622
|
}
|
|
38487
39623
|
});
|
|
38488
39624
|
}
|
|
@@ -38692,10 +39828,6 @@ code-viewer query agent-help`
|
|
|
38692
39828
|
if (quickHelpIcon) {
|
|
38693
39829
|
quickHelpIcon.innerHTML = iconSvg("octicon-question", QUESTION_16_PATH);
|
|
38694
39830
|
}
|
|
38695
|
-
const helpLinkIcon = document.querySelector(".global-icon-link[data-route='help'] .goi-icon");
|
|
38696
|
-
if (helpLinkIcon) {
|
|
38697
|
-
helpLinkIcon.innerHTML = iconSvg("octicon-book", BOOK_16_PATH);
|
|
38698
|
-
}
|
|
38699
39831
|
const branchIcon = document.querySelector("#project-branch .goi-icon");
|
|
38700
39832
|
if (branchIcon) {
|
|
38701
39833
|
branchIcon.innerHTML = iconSvg("octicon-git-branch", GIT_BRANCH_16_PATH);
|
|
@@ -38737,6 +39869,10 @@ code-viewer query agent-help`
|
|
|
38737
39869
|
event.preventDefault();
|
|
38738
39870
|
toggleDoctorSheet();
|
|
38739
39871
|
});
|
|
39872
|
+
$("#tools-btn")?.addEventListener("click", (event) => {
|
|
39873
|
+
event.preventDefault();
|
|
39874
|
+
toggleToolsSheet();
|
|
39875
|
+
});
|
|
38740
39876
|
let copyAiContextFeedbackTimer = null;
|
|
38741
39877
|
$("#copy-ai-context")?.addEventListener("click", async (event) => {
|
|
38742
39878
|
const button = event.currentTarget;
|
|
@@ -39420,6 +40556,7 @@ code-viewer query agent-help`
|
|
|
39420
40556
|
load();
|
|
39421
40557
|
syncLineRefPill();
|
|
39422
40558
|
syncDoctorSheetFromUrl();
|
|
40559
|
+
syncToolsSheetFromUrl();
|
|
39423
40560
|
});
|
|
39424
40561
|
function syncRefInputs() {
|
|
39425
40562
|
const fi = $("#ref-from"), ti = $("#ref-to");
|
|
@@ -39595,6 +40732,57 @@ code-viewer query agent-help`
|
|
|
39595
40732
|
event.preventDefault();
|
|
39596
40733
|
closeDoctorSheet();
|
|
39597
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
|
+
});
|
|
39598
40786
|
JOURNAL_VIEW = createJournalView({
|
|
39599
40787
|
getRoute: () => STATE.route,
|
|
39600
40788
|
setRoute,
|
|
@@ -39672,6 +40860,7 @@ code-viewer query agent-help`
|
|
|
39672
40860
|
syncHeaderMenu();
|
|
39673
40861
|
syncLineRefPill();
|
|
39674
40862
|
syncDoctorSheetFromUrl();
|
|
40863
|
+
syncToolsSheetFromUrl();
|
|
39675
40864
|
if (isSameBlobFileRoute(previousRoute, STATE.route) && routeBlobPreview(previousRoute) !== routeBlobPreview(STATE.route) && switchSourceTab(routeBlobPreview(STATE.route) ? "preview" : "code", {
|
|
39676
40865
|
updateRoute: false
|
|
39677
40866
|
})) {
|
|
@@ -39732,7 +40921,7 @@ code-viewer query agent-help`
|
|
|
39732
40921
|
}
|
|
39733
40922
|
window.addEventListener("popstate", applyRouteFromLocation);
|
|
39734
40923
|
window.addEventListener("pagehide", () => flushViewStatePatch(true));
|
|
39735
|
-
document.querySelectorAll(".app-menu-item, .global-icon-link").forEach((link2) => {
|
|
40924
|
+
document.querySelectorAll("a.app-menu-item, a.global-icon-link").forEach((link2) => {
|
|
39736
40925
|
if (link2.target === "_blank")
|
|
39737
40926
|
return;
|
|
39738
40927
|
link2.addEventListener("click", (e2) => {
|
|
@@ -40079,7 +41268,7 @@ code-viewer query agent-help`
|
|
|
40079
41268
|
return;
|
|
40080
41269
|
if (isBlobOrBlameFileRoute(route)) {
|
|
40081
41270
|
const viewingPath = route.path;
|
|
40082
|
-
if (
|
|
41271
|
+
if (viewingPath && !changedPathsCoverPath(paths, viewingPath))
|
|
40083
41272
|
return;
|
|
40084
41273
|
dispatchFileRoute(route, { refresh: true });
|
|
40085
41274
|
return;
|
|
@@ -40127,7 +41316,7 @@ code-viewer query agent-help`
|
|
|
40127
41316
|
const route = STATE.route;
|
|
40128
41317
|
if (isBlobOrBlameFileRoute(route)) {
|
|
40129
41318
|
const viewingPath = route.path;
|
|
40130
|
-
if (
|
|
41319
|
+
if (viewingPath && !changedPathsCoverPath(paths, viewingPath))
|
|
40131
41320
|
return;
|
|
40132
41321
|
}
|
|
40133
41322
|
if (STATE.autoUpdate) {
|