codexui-android 0.1.89 → 0.1.91
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/dist/assets/{ReviewPane-CwRK6K44.js → ReviewPane-gbhCGpfh.js} +2 -2
- package/dist/assets/{ReviewPane-CwRK6K44.js.map → ReviewPane-gbhCGpfh.js.map} +1 -1
- package/dist/assets/{SkillsHub-BIRLBCzr.js → SkillsHub-xXcF9J80.js} +2 -2
- package/dist/assets/{SkillsHub-BIRLBCzr.js.map → SkillsHub-xXcF9J80.js.map} +1 -1
- package/dist/assets/{ThreadConversation-BKfO22g2.css → ThreadConversation-BsN7bN3q.css} +1 -1
- package/dist/assets/ThreadConversation-Dr0u8WbA.js +40 -0
- package/dist/assets/ThreadConversation-Dr0u8WbA.js.map +1 -0
- package/dist/assets/index-B81KnkV8.js +92 -0
- package/dist/assets/index-B81KnkV8.js.map +1 -0
- package/dist/index.html +1 -1
- package/dist-cli/index.js +123 -593
- package/dist-cli/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/ThreadConversation-DymdJ3Hs.js +0 -40
- package/dist/assets/ThreadConversation-DymdJ3Hs.js.map +0 -1
- package/dist/assets/index-Bn_MPBwp.js +0 -92
- package/dist/assets/index-Bn_MPBwp.js.map +0 -1
package/dist-cli/index.js
CHANGED
|
@@ -217,10 +217,9 @@ function parseApprovalPolicy(value) {
|
|
|
217
217
|
|
|
218
218
|
// src/server/httpServer.ts
|
|
219
219
|
import { fileURLToPath } from "url";
|
|
220
|
-
import {
|
|
220
|
+
import { dirname as dirname3, extname as extname3, isAbsolute as isAbsolute3, join as join7 } from "path";
|
|
221
221
|
import { existsSync as existsSync3 } from "fs";
|
|
222
222
|
import { writeFile as writeFile5, stat as stat6 } from "fs/promises";
|
|
223
|
-
import { createReadStream as createReadStream2 } from "fs";
|
|
224
223
|
import express from "express";
|
|
225
224
|
|
|
226
225
|
// src/server/codexAppServerBridge.ts
|
|
@@ -7281,92 +7280,76 @@ function createAuthSession(password) {
|
|
|
7281
7280
|
}
|
|
7282
7281
|
|
|
7283
7282
|
// src/server/localBrowseUi.ts
|
|
7284
|
-
import {
|
|
7283
|
+
import { dirname as dirname2, extname as extname2, join as join6 } from "path";
|
|
7285
7284
|
import { open, readFile as readFile4, readdir as readdir3, stat as stat5 } from "fs/promises";
|
|
7286
|
-
var
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7285
|
+
var TEXT_EDITABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
7286
|
+
".txt",
|
|
7287
|
+
".md",
|
|
7288
|
+
".json",
|
|
7289
|
+
".js",
|
|
7290
|
+
".ts",
|
|
7291
|
+
".tsx",
|
|
7292
|
+
".jsx",
|
|
7293
|
+
".css",
|
|
7294
|
+
".scss",
|
|
7295
|
+
".html",
|
|
7296
|
+
".htm",
|
|
7297
|
+
".xml",
|
|
7298
|
+
".yml",
|
|
7299
|
+
".yaml",
|
|
7300
|
+
".log",
|
|
7301
|
+
".csv",
|
|
7302
|
+
".env",
|
|
7303
|
+
".py",
|
|
7304
|
+
".sh",
|
|
7305
|
+
".toml",
|
|
7306
|
+
".ini",
|
|
7307
|
+
".conf",
|
|
7308
|
+
".sql",
|
|
7309
|
+
".bat",
|
|
7310
|
+
".cmd",
|
|
7311
|
+
".ps1"
|
|
7304
7312
|
]);
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
[".sh", { label: "shell", aceMode: "sh" }],
|
|
7345
|
-
[".bash", { label: "shell", aceMode: "sh" }],
|
|
7346
|
-
[".zsh", { label: "shell", aceMode: "sh" }],
|
|
7347
|
-
[".ini", { label: "ini", aceMode: "ini" }],
|
|
7348
|
-
[".conf", { label: "ini", aceMode: "ini" }],
|
|
7349
|
-
[".ps1", { label: "powershell", aceMode: "powershell" }],
|
|
7350
|
-
[".bat", TEXT_LANGUAGE],
|
|
7351
|
-
[".cmd", TEXT_LANGUAGE]
|
|
7352
|
-
]);
|
|
7353
|
-
var MAX_INLINE_PREVIEW_BYTES = 1024 * 1024;
|
|
7354
|
-
var DARK_MODE_STORAGE_KEY = "codex-web-local.dark-mode.v1";
|
|
7355
|
-
var ACE_CDN_BASE = "https://cdnjs.cloudflare.com/ajax/libs/ace/1.36.2";
|
|
7356
|
-
function getLanguageConfigForPath(pathValue) {
|
|
7357
|
-
const fileName = basename4(pathValue).toLowerCase();
|
|
7358
|
-
if (fileName === ".env" || fileName.startsWith(".env.")) {
|
|
7359
|
-
return { language: TEXT_LANGUAGE, recognized: true };
|
|
7360
|
-
}
|
|
7361
|
-
const exactMatch = FILE_LANGUAGE_RULES_BY_NAME.get(fileName);
|
|
7362
|
-
if (exactMatch) {
|
|
7363
|
-
return { language: exactMatch, recognized: true };
|
|
7313
|
+
function languageForPath(pathValue) {
|
|
7314
|
+
const extension = extname2(pathValue).toLowerCase();
|
|
7315
|
+
switch (extension) {
|
|
7316
|
+
case ".js":
|
|
7317
|
+
return "javascript";
|
|
7318
|
+
case ".ts":
|
|
7319
|
+
return "typescript";
|
|
7320
|
+
case ".jsx":
|
|
7321
|
+
return "javascript";
|
|
7322
|
+
case ".tsx":
|
|
7323
|
+
return "typescript";
|
|
7324
|
+
case ".py":
|
|
7325
|
+
return "python";
|
|
7326
|
+
case ".sh":
|
|
7327
|
+
return "sh";
|
|
7328
|
+
case ".css":
|
|
7329
|
+
case ".scss":
|
|
7330
|
+
return "css";
|
|
7331
|
+
case ".html":
|
|
7332
|
+
case ".htm":
|
|
7333
|
+
return "html";
|
|
7334
|
+
case ".json":
|
|
7335
|
+
return "json";
|
|
7336
|
+
case ".md":
|
|
7337
|
+
return "markdown";
|
|
7338
|
+
case ".yaml":
|
|
7339
|
+
case ".yml":
|
|
7340
|
+
return "yaml";
|
|
7341
|
+
case ".xml":
|
|
7342
|
+
return "xml";
|
|
7343
|
+
case ".sql":
|
|
7344
|
+
return "sql";
|
|
7345
|
+
case ".toml":
|
|
7346
|
+
return "ini";
|
|
7347
|
+
case ".ini":
|
|
7348
|
+
case ".conf":
|
|
7349
|
+
return "ini";
|
|
7350
|
+
default:
|
|
7351
|
+
return "plaintext";
|
|
7364
7352
|
}
|
|
7365
|
-
const extensionMatch = FILE_LANGUAGE_RULES_BY_EXTENSION.get(extname2(fileName));
|
|
7366
|
-
if (extensionMatch) {
|
|
7367
|
-
return { language: extensionMatch, recognized: true };
|
|
7368
|
-
}
|
|
7369
|
-
return { language: TEXT_LANGUAGE, recognized: false };
|
|
7370
7353
|
}
|
|
7371
7354
|
function normalizeLocalPath(rawPath) {
|
|
7372
7355
|
const trimmed = rawPath.trim();
|
|
@@ -7389,7 +7372,7 @@ function decodeBrowsePath(rawPath) {
|
|
|
7389
7372
|
}
|
|
7390
7373
|
}
|
|
7391
7374
|
function isTextEditablePath(pathValue) {
|
|
7392
|
-
return
|
|
7375
|
+
return TEXT_EDITABLE_EXTENSIONS.has(extname2(pathValue).toLowerCase());
|
|
7393
7376
|
}
|
|
7394
7377
|
function isHiddenName(value) {
|
|
7395
7378
|
return value.startsWith(".");
|
|
@@ -7423,157 +7406,25 @@ async function isTextEditableFile(localPath) {
|
|
|
7423
7406
|
return false;
|
|
7424
7407
|
}
|
|
7425
7408
|
}
|
|
7426
|
-
async function getLocalTextFileMetadata(localPath) {
|
|
7427
|
-
const { language, recognized } = getLanguageConfigForPath(localPath);
|
|
7428
|
-
try {
|
|
7429
|
-
const fileStat = await stat5(localPath);
|
|
7430
|
-
if (!fileStat.isFile()) return null;
|
|
7431
|
-
if (recognized) {
|
|
7432
|
-
return {
|
|
7433
|
-
language,
|
|
7434
|
-
sizeBytes: fileStat.size
|
|
7435
|
-
};
|
|
7436
|
-
}
|
|
7437
|
-
const isText = await probeFileIsText(localPath);
|
|
7438
|
-
if (!isText) return null;
|
|
7439
|
-
return {
|
|
7440
|
-
language,
|
|
7441
|
-
sizeBytes: fileStat.size
|
|
7442
|
-
};
|
|
7443
|
-
} catch {
|
|
7444
|
-
return null;
|
|
7445
|
-
}
|
|
7446
|
-
}
|
|
7447
7409
|
function escapeHtml(value) {
|
|
7448
7410
|
return value.replace(/&/gu, "&").replace(/</gu, "<").replace(/>/gu, ">").replace(/"/gu, """).replace(/'/gu, "'");
|
|
7449
7411
|
}
|
|
7450
7412
|
function normalizeNewProjectName(value) {
|
|
7451
7413
|
return value.trim().replace(/[\\/]+/gu, "").trim();
|
|
7452
7414
|
}
|
|
7453
|
-
function
|
|
7454
|
-
|
|
7455
|
-
}
|
|
7456
|
-
|
|
7457
|
-
const query = new URLSearchParams();
|
|
7458
|
-
const normalizedName = normalizeNewProjectName(options?.newProjectName ?? "");
|
|
7459
|
-
const line = normalizeLineTarget(options?.line);
|
|
7460
|
-
const column = line ? normalizeLineTarget(options?.column) : null;
|
|
7461
|
-
if (normalizedName) query.set("newProjectName", normalizedName);
|
|
7462
|
-
if (line) query.set("line", String(line));
|
|
7463
|
-
if (column) query.set("column", String(column));
|
|
7464
|
-
const encoded = query.toString();
|
|
7465
|
-
return encoded ? `?${encoded}` : "";
|
|
7466
|
-
}
|
|
7467
|
-
function toBrowseHref(pathValue, options) {
|
|
7468
|
-
return `/codex-local-browse${encodeURI(pathValue)}${buildLocationQuery(options)}`;
|
|
7469
|
-
}
|
|
7470
|
-
function toEditHref(pathValue, options) {
|
|
7471
|
-
return `/codex-local-edit${encodeURI(pathValue)}${buildLocationQuery(options)}`;
|
|
7415
|
+
function toBrowseHref(pathValue, newProjectName = "") {
|
|
7416
|
+
const normalizedName = normalizeNewProjectName(newProjectName);
|
|
7417
|
+
const query = normalizedName ? `?newProjectName=${encodeURIComponent(normalizedName)}` : "";
|
|
7418
|
+
return `/codex-local-browse${encodeURI(pathValue)}${query}`;
|
|
7472
7419
|
}
|
|
7473
|
-
function
|
|
7474
|
-
const
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
}
|
|
7478
|
-
return `/codex-local-file?${query.toString()}`;
|
|
7420
|
+
function toEditHref(pathValue, newProjectName = "") {
|
|
7421
|
+
const normalizedName = normalizeNewProjectName(newProjectName);
|
|
7422
|
+
const query = normalizedName ? `?newProjectName=${encodeURIComponent(normalizedName)}` : "";
|
|
7423
|
+
return `/codex-local-edit${encodeURI(pathValue)}${query}`;
|
|
7479
7424
|
}
|
|
7480
7425
|
function escapeForInlineScriptString(value) {
|
|
7481
7426
|
return JSON.stringify(value).replace(/<\//gu, "<\\/").replace(/<!--/gu, "<\\!--").replace(/\u2028/gu, "\\u2028").replace(/\u2029/gu, "\\u2029");
|
|
7482
7427
|
}
|
|
7483
|
-
function renderStandaloneThemeBootstrapScript() {
|
|
7484
|
-
return [
|
|
7485
|
-
"(function() {",
|
|
7486
|
-
` const storageKey = ${JSON.stringify(DARK_MODE_STORAGE_KEY)};`,
|
|
7487
|
-
' const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");',
|
|
7488
|
-
" function readStoredPreference() {",
|
|
7489
|
-
" try {",
|
|
7490
|
-
" const value = window.localStorage.getItem(storageKey);",
|
|
7491
|
-
' return value === "light" || value === "dark" ? value : "system";',
|
|
7492
|
-
" } catch {",
|
|
7493
|
-
' return "system";',
|
|
7494
|
-
" }",
|
|
7495
|
-
" }",
|
|
7496
|
-
" function resolveTheme(preference) {",
|
|
7497
|
-
' if (preference === "light" || preference === "dark") return preference;',
|
|
7498
|
-
' return mediaQuery.matches ? "dark" : "light";',
|
|
7499
|
-
" }",
|
|
7500
|
-
" function applyTheme() {",
|
|
7501
|
-
" const preference = readStoredPreference();",
|
|
7502
|
-
" const resolvedTheme = resolveTheme(preference);",
|
|
7503
|
-
" document.documentElement.dataset.theme = resolvedTheme;",
|
|
7504
|
-
" document.documentElement.style.colorScheme = resolvedTheme;",
|
|
7505
|
-
" window.__codexLocalBrowseTheme = { preference, resolvedTheme };",
|
|
7506
|
-
' window.dispatchEvent(new CustomEvent("codex-local-browse-themechange", { detail: window.__codexLocalBrowseTheme }));',
|
|
7507
|
-
" }",
|
|
7508
|
-
" applyTheme();",
|
|
7509
|
-
" window.__codexApplyLocalBrowseTheme = applyTheme;",
|
|
7510
|
-
' window.addEventListener("storage", function(event) {',
|
|
7511
|
-
" if (event.key !== storageKey) return;",
|
|
7512
|
-
" applyTheme();",
|
|
7513
|
-
" });",
|
|
7514
|
-
' mediaQuery.addEventListener("change", function() {',
|
|
7515
|
-
' if (readStoredPreference() !== "system") return;',
|
|
7516
|
-
" applyTheme();",
|
|
7517
|
-
" });",
|
|
7518
|
-
"})();"
|
|
7519
|
-
].join("\n");
|
|
7520
|
-
}
|
|
7521
|
-
function renderStandaloneThemeCss() {
|
|
7522
|
-
return `
|
|
7523
|
-
:root {
|
|
7524
|
-
color-scheme: light;
|
|
7525
|
-
--lb-bg: #f3f6fb;
|
|
7526
|
-
--lb-surface: #ffffff;
|
|
7527
|
-
--lb-surface-muted: #eef3fb;
|
|
7528
|
-
--lb-surface-strong: #e7edf8;
|
|
7529
|
-
--lb-toolbar-bg: rgba(243, 246, 251, 0.96);
|
|
7530
|
-
--lb-border: #cfd9ea;
|
|
7531
|
-
--lb-border-strong: #b6c4db;
|
|
7532
|
-
--lb-text: #172033;
|
|
7533
|
-
--lb-text-muted: #52607a;
|
|
7534
|
-
--lb-link: #2457b8;
|
|
7535
|
-
--lb-button-text: #172033;
|
|
7536
|
-
--lb-accent-bg: #dfeafe;
|
|
7537
|
-
--lb-accent-border: #9db8ef;
|
|
7538
|
-
--lb-primary-bg: linear-gradient(135deg, #2f67d8 0%, #4b88ff 100%);
|
|
7539
|
-
--lb-primary-border: #2f67d8;
|
|
7540
|
-
--lb-primary-text: #f8fbff;
|
|
7541
|
-
--lb-selection: rgba(77, 124, 255, 0.18);
|
|
7542
|
-
--lb-selection-strong: rgba(77, 124, 255, 0.24);
|
|
7543
|
-
--lb-target-line: rgba(77, 124, 255, 0.14);
|
|
7544
|
-
--lb-target-stripe: #4d7cff;
|
|
7545
|
-
--lb-warning-text: #8a5a00;
|
|
7546
|
-
--lb-warning-bg: rgba(194, 136, 18, 0.12);
|
|
7547
|
-
--lb-shadow: 0 16px 40px rgba(31, 49, 82, 0.12);
|
|
7548
|
-
}
|
|
7549
|
-
:root[data-theme='dark'] {
|
|
7550
|
-
color-scheme: dark;
|
|
7551
|
-
--lb-bg: #09111f;
|
|
7552
|
-
--lb-surface: #0d182b;
|
|
7553
|
-
--lb-surface-muted: #101f3a;
|
|
7554
|
-
--lb-surface-strong: #13233d;
|
|
7555
|
-
--lb-toolbar-bg: rgba(9, 17, 31, 0.96);
|
|
7556
|
-
--lb-border: #20324d;
|
|
7557
|
-
--lb-border-strong: #36557a;
|
|
7558
|
-
--lb-text: #dbe6ff;
|
|
7559
|
-
--lb-text-muted: #8fb8ec;
|
|
7560
|
-
--lb-link: #8cc2ff;
|
|
7561
|
-
--lb-button-text: #dbe6ff;
|
|
7562
|
-
--lb-accent-bg: #162643;
|
|
7563
|
-
--lb-accent-border: #36557a;
|
|
7564
|
-
--lb-primary-bg: linear-gradient(135deg, #2e6ee6 0%, #3d8cff 100%);
|
|
7565
|
-
--lb-primary-border: #4f8de0;
|
|
7566
|
-
--lb-primary-text: #eef6ff;
|
|
7567
|
-
--lb-selection: rgba(140, 194, 255, 0.16);
|
|
7568
|
-
--lb-selection-strong: rgba(140, 194, 255, 0.3);
|
|
7569
|
-
--lb-target-line: rgba(140, 194, 255, 0.16);
|
|
7570
|
-
--lb-target-stripe: #8cc2ff;
|
|
7571
|
-
--lb-warning-text: #f0cf78;
|
|
7572
|
-
--lb-warning-bg: rgba(240, 207, 120, 0.08);
|
|
7573
|
-
--lb-shadow: 0 20px 40px rgba(0, 0, 0, 0.24);
|
|
7574
|
-
}
|
|
7575
|
-
`;
|
|
7576
|
-
}
|
|
7577
7428
|
async function getDirectoryItems(localPath) {
|
|
7578
7429
|
const entries = await readdir3(localPath, { withFileTypes: true });
|
|
7579
7430
|
const withMeta = await Promise.all(entries.map(async (entry) => {
|
|
@@ -7623,64 +7474,6 @@ function actionButtonsHtml(localPath, newProjectName) {
|
|
|
7623
7474
|
const openButton = `<button class="header-open-btn open-folder-btn" type="button" aria-label="Open current folder in Codex" title="Open folder in Codex" data-path="${escapeHtml(localPath)}" data-label="" data-status="${escapeHtml(openFolderStatusText(normalizedName))}" data-error="${escapeHtml(failureStatusText(normalizedName))}">Open folder in Codex</button>`;
|
|
7624
7475
|
return `${createButton}${openButton}`;
|
|
7625
7476
|
}
|
|
7626
|
-
function renderTextPreviewToolbar(localPath, options) {
|
|
7627
|
-
const newProjectName = normalizeNewProjectName(options?.newProjectName ?? "");
|
|
7628
|
-
const line = normalizeLineTarget(options?.line);
|
|
7629
|
-
const column = line ? normalizeLineTarget(options?.column) : null;
|
|
7630
|
-
const backHref = toBrowseHref(dirname2(localPath), { newProjectName });
|
|
7631
|
-
const rawHref = toRawFileHref(localPath);
|
|
7632
|
-
const downloadHref = toRawFileHref(localPath, { download: true });
|
|
7633
|
-
const lineLocation = line ? { newProjectName, line, column } : { newProjectName };
|
|
7634
|
-
return [
|
|
7635
|
-
`<a href="${escapeHtml(backHref)}">Back</a>`,
|
|
7636
|
-
`<a href="${escapeHtml(rawHref)}" target="_blank" rel="noopener noreferrer">Raw</a>`,
|
|
7637
|
-
`<a href="${escapeHtml(downloadHref)}">Download</a>`,
|
|
7638
|
-
`<a href="${escapeHtml(toEditHref(localPath, lineLocation))}">Edit</a>`
|
|
7639
|
-
].filter(Boolean).join("");
|
|
7640
|
-
}
|
|
7641
|
-
function formatLineTargetLabel(line, column) {
|
|
7642
|
-
if (!line) return "";
|
|
7643
|
-
if (column) return `line ${String(line)}:${String(column)}`;
|
|
7644
|
-
return `line ${String(line)}`;
|
|
7645
|
-
}
|
|
7646
|
-
function renderPlainPreviewContent(content, targetLine) {
|
|
7647
|
-
if (!targetLine || targetLine < 1) return escapeHtml(content);
|
|
7648
|
-
const trailingNewline = content.endsWith("\n");
|
|
7649
|
-
const lines = content.split("\n");
|
|
7650
|
-
if (trailingNewline) lines.pop();
|
|
7651
|
-
if (targetLine > lines.length) return escapeHtml(content);
|
|
7652
|
-
return lines.map((line, index) => {
|
|
7653
|
-
const escapedLine = escapeHtml(line || " ");
|
|
7654
|
-
if (index + 1 === targetLine) {
|
|
7655
|
-
return `<span id="previewTargetLine" class="preview-target-line">${escapedLine}</span>`;
|
|
7656
|
-
}
|
|
7657
|
-
return escapedLine;
|
|
7658
|
-
}).join("\n") + (trailingNewline ? "\n" : "");
|
|
7659
|
-
}
|
|
7660
|
-
function formatPreviewSize(sizeBytes) {
|
|
7661
|
-
if (sizeBytes >= 1024 * 1024) return `${(sizeBytes / (1024 * 1024)).toFixed(1).replace(/\.0$/u, "")} MiB`;
|
|
7662
|
-
if (sizeBytes >= 1024) return `${Math.round(sizeBytes / 1024)} KiB`;
|
|
7663
|
-
return `${sizeBytes} B`;
|
|
7664
|
-
}
|
|
7665
|
-
function renderAceLineTargetScript() {
|
|
7666
|
-
return [
|
|
7667
|
-
"function selectTargetLine(editorInstance, lineNumber, columnNumber) {",
|
|
7668
|
-
" if (!lineNumber) return;",
|
|
7669
|
-
" const zeroBasedRow = lineNumber - 1;",
|
|
7670
|
-
" const zeroBasedColumn = Math.max(0, (columnNumber ?? 1) - 1);",
|
|
7671
|
-
" editorInstance.gotoLine(lineNumber, zeroBasedColumn, true);",
|
|
7672
|
-
" editorInstance.scrollToLine(lineNumber, true, true, function() {});",
|
|
7673
|
-
" editorInstance.selection.moveCursorToPosition({ row: zeroBasedRow, column: zeroBasedColumn });",
|
|
7674
|
-
" editorInstance.selection.selectLine();",
|
|
7675
|
-
"}",
|
|
7676
|
-
"function currentAceThemeName() {",
|
|
7677
|
-
' return document.documentElement.dataset.theme === "light" ? "tomorrow" : "tomorrow_night";',
|
|
7678
|
-
"}",
|
|
7679
|
-
"function applyAceTheme(editorInstance) {",
|
|
7680
|
-
' editorInstance.setTheme("ace/theme/" + currentAceThemeName());',
|
|
7681
|
-
"}"
|
|
7682
|
-
].join("\n");
|
|
7683
|
-
}
|
|
7684
7477
|
async function getLocalDirectoryListing(localPath, options = {}) {
|
|
7685
7478
|
const entries = await readdir3(localPath, { withFileTypes: true });
|
|
7686
7479
|
const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
|
|
@@ -7704,10 +7497,10 @@ async function createDirectoryListingHtml(localPath, options) {
|
|
|
7704
7497
|
const parentPath = dirname2(localPath);
|
|
7705
7498
|
const rows = items.map((item) => {
|
|
7706
7499
|
const suffix = item.isDirectory ? "/" : "";
|
|
7707
|
-
const editAction = item.editable ? ` <a class="icon-btn" aria-label="Edit ${escapeHtml(item.name)}" href="${escapeHtml(toEditHref(item.path,
|
|
7708
|
-
return `<li class="file-row"><a class="file-link" href="${escapeHtml(toBrowseHref(item.path,
|
|
7500
|
+
const editAction = item.editable ? ` <a class="icon-btn" aria-label="Edit ${escapeHtml(item.name)}" href="${escapeHtml(toEditHref(item.path, newProjectName))}" title="Edit">\u270F\uFE0F</a>` : "";
|
|
7501
|
+
return `<li class="file-row"><a class="file-link" href="${escapeHtml(toBrowseHref(item.path, newProjectName))}">${escapeHtml(item.name)}${suffix}</a><span class="row-actions">${editAction}</span></li>`;
|
|
7709
7502
|
}).join("\n");
|
|
7710
|
-
const parentLink = localPath !== parentPath ? `<a class="header-parent-link" href="${escapeHtml(toBrowseHref(parentPath,
|
|
7503
|
+
const parentLink = localPath !== parentPath ? `<a class="header-parent-link" href="${escapeHtml(toBrowseHref(parentPath, newProjectName))}">..</a>` : "";
|
|
7711
7504
|
const pickerSummary = newProjectName ? `<p class="picker-summary">Browse to the parent folder where you want to create <strong>${escapeHtml(newProjectName)}</strong>, or open the current folder directly.</p>` : "";
|
|
7712
7505
|
const actionButtons = actionButtonsHtml(localPath, newProjectName);
|
|
7713
7506
|
return `<!doctype html>
|
|
@@ -7716,37 +7509,35 @@ async function createDirectoryListingHtml(localPath, options) {
|
|
|
7716
7509
|
<meta charset="utf-8" />
|
|
7717
7510
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7718
7511
|
<title>Index of ${escapeHtml(localPath)}</title>
|
|
7719
|
-
<script>${renderStandaloneThemeBootstrapScript()}</script>
|
|
7720
7512
|
<style>
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
a { color: var(--lb-link); text-decoration: none; }
|
|
7513
|
+
body { font-family: ui-monospace, Menlo, Monaco, monospace; margin: 16px; background: #0b1020; color: #dbe6ff; }
|
|
7514
|
+
a { color: #8cc2ff; text-decoration: none; }
|
|
7724
7515
|
a:hover { text-decoration: underline; }
|
|
7725
7516
|
ul { list-style: none; padding: 0; margin: 12px 0 0; display: flex; flex-direction: column; gap: 8px; }
|
|
7726
7517
|
.file-row { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: center; gap: 10px; }
|
|
7727
|
-
.file-link { display: block; padding: 10px 12px; border: 1px solid
|
|
7518
|
+
.file-link { display: block; padding: 10px 12px; border: 1px solid #28405f; border-radius: 10px; background: #0f1b33; overflow-wrap: anywhere; }
|
|
7728
7519
|
.header-actions { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
|
|
7729
|
-
.header-parent-link { color:
|
|
7520
|
+
.header-parent-link { color: #9ec8ff; font-size: 14px; padding: 8px 10px; border: 1px solid #2a4569; border-radius: 10px; background: #101f3a; }
|
|
7730
7521
|
.header-parent-link:hover { text-decoration: none; filter: brightness(1.08); }
|
|
7731
7522
|
.header-open-btn {
|
|
7732
7523
|
height: 42px;
|
|
7733
7524
|
padding: 0 14px;
|
|
7734
|
-
border: 1px solid
|
|
7525
|
+
border: 1px solid #4f8de0;
|
|
7735
7526
|
border-radius: 10px;
|
|
7736
|
-
background:
|
|
7737
|
-
color:
|
|
7527
|
+
background: linear-gradient(135deg, #2e6ee6 0%, #3d8cff 100%);
|
|
7528
|
+
color: #eef6ff;
|
|
7738
7529
|
font-weight: 700;
|
|
7739
7530
|
letter-spacing: 0.01em;
|
|
7740
7531
|
cursor: pointer;
|
|
7741
|
-
box-shadow:
|
|
7532
|
+
box-shadow: 0 6px 18px rgba(33, 90, 199, 0.35);
|
|
7742
7533
|
}
|
|
7743
7534
|
.header-open-btn:hover { filter: brightness(1.08); }
|
|
7744
7535
|
.header-open-btn:disabled { opacity: 0.6; cursor: default; }
|
|
7745
|
-
.picker-summary { margin: 10px 0 0; color:
|
|
7536
|
+
.picker-summary { margin: 10px 0 0; color: #b8d5ff; max-width: 60rem; line-height: 1.45; }
|
|
7746
7537
|
.row-actions { display: inline-flex; align-items: center; gap: 8px; min-width: 42px; justify-content: flex-end; }
|
|
7747
|
-
.icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 42px; height: 42px; border: 1px solid
|
|
7538
|
+
.icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 42px; height: 42px; border: 1px solid #36557a; border-radius: 10px; background: #162643; color: #dbe6ff; text-decoration: none; cursor: pointer; }
|
|
7748
7539
|
.icon-btn:hover { filter: brightness(1.08); text-decoration: none; }
|
|
7749
|
-
.status { margin: 10px 0 0; color:
|
|
7540
|
+
.status { margin: 10px 0 0; color: #8cc2ff; min-height: 1.25em; }
|
|
7750
7541
|
h1 { font-size: 18px; margin: 0; word-break: break-all; }
|
|
7751
7542
|
@media (max-width: 640px) {
|
|
7752
7543
|
body { margin: 12px; }
|
|
@@ -7807,144 +7598,10 @@ async function createDirectoryListingHtml(localPath, options) {
|
|
|
7807
7598
|
</body>
|
|
7808
7599
|
</html>`;
|
|
7809
7600
|
}
|
|
7810
|
-
async function
|
|
7811
|
-
const newProjectName = normalizeNewProjectName(options?.newProjectName ?? "");
|
|
7812
|
-
const line = normalizeLineTarget(options?.line);
|
|
7813
|
-
const column = line ? normalizeLineTarget(options?.column) : null;
|
|
7814
|
-
const metadata = await getLocalTextFileMetadata(localPath);
|
|
7815
|
-
if (!metadata) {
|
|
7816
|
-
throw new Error("Only text-like files can be previewed inline.");
|
|
7817
|
-
}
|
|
7818
|
-
const toolbar = renderTextPreviewToolbar(localPath, { newProjectName, line, column });
|
|
7819
|
-
const lineTargetLabel = formatLineTargetLabel(line, column);
|
|
7820
|
-
const previewMeta = [localPath, metadata.language.label, formatPreviewSize(metadata.sizeBytes), lineTargetLabel].filter(Boolean).join(" \xB7 ");
|
|
7821
|
-
const previewUnavailable = metadata.sizeBytes > MAX_INLINE_PREVIEW_BYTES;
|
|
7822
|
-
const previewContent = previewUnavailable ? "" : await readFile4(localPath, "utf8");
|
|
7823
|
-
const previewPlainHtml = renderPlainPreviewContent(previewContent, line);
|
|
7824
|
-
const safePreviewLiteral = escapeForInlineScriptString(previewContent);
|
|
7825
|
-
return `<!doctype html>
|
|
7826
|
-
<html lang="en">
|
|
7827
|
-
<head>
|
|
7828
|
-
<meta charset="utf-8" />
|
|
7829
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7830
|
-
<title>${escapeHtml(basename4(localPath))}</title>
|
|
7831
|
-
<script>${renderStandaloneThemeBootstrapScript()}</script>
|
|
7832
|
-
<style>
|
|
7833
|
-
${renderStandaloneThemeCss()}
|
|
7834
|
-
html, body { margin: 0; width: 100%; min-height: 100%; }
|
|
7835
|
-
body { min-height: 100vh; font-family: ui-monospace, Menlo, Monaco, monospace; background: var(--lb-bg); color: var(--lb-text); display: flex; flex-direction: column; }
|
|
7836
|
-
a { color: inherit; text-decoration: none; }
|
|
7837
|
-
.toolbar { position: sticky; top: 0; z-index: 10; display: flex; flex-direction: column; gap: 10px; padding: 12px 16px; background: var(--lb-toolbar-bg); backdrop-filter: blur(8px); border-bottom: 1px solid var(--lb-border); }
|
|
7838
|
-
.toolbar-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
7839
|
-
.toolbar-row a { display: inline-flex; align-items: center; justify-content: center; min-height: 38px; padding: 0 12px; border: 1px solid var(--lb-accent-border); border-radius: 10px; background: var(--lb-accent-bg); color: var(--lb-button-text); }
|
|
7840
|
-
.toolbar-row a:hover { filter: brightness(1.08); }
|
|
7841
|
-
.meta { color: var(--lb-text-muted); font-size: 12px; overflow-wrap: anywhere; line-height: 1.5; }
|
|
7842
|
-
.preview-shell { flex: 1 1 auto; min-height: 0; display: flex; padding: 18px 16px 24px; }
|
|
7843
|
-
.preview-card { flex: 1 1 auto; min-height: 0; max-width: 100%; border: 1px solid var(--lb-border); border-radius: 14px; background: var(--lb-surface); overflow: hidden; box-shadow: var(--lb-shadow); display: flex; flex-direction: column; }
|
|
7844
|
-
.preview-notice { margin: 0; padding: 12px 16px; border-bottom: 1px solid var(--lb-border); color: var(--lb-warning-text); background: var(--lb-warning-bg); }
|
|
7845
|
-
.preview-unavailable { padding: 26px 20px; }
|
|
7846
|
-
.preview-unavailable-title { margin: 0 0 8px; font-size: 15px; font-weight: 700; }
|
|
7847
|
-
.preview-unavailable-text { margin: 0; color: var(--lb-text-muted); line-height: 1.6; }
|
|
7848
|
-
.preview-plain {
|
|
7849
|
-
flex: 1 1 auto;
|
|
7850
|
-
box-sizing: border-box;
|
|
7851
|
-
margin: 0;
|
|
7852
|
-
padding: 18px 20px 24px;
|
|
7853
|
-
min-height: 0;
|
|
7854
|
-
overflow: auto;
|
|
7855
|
-
white-space: pre-wrap;
|
|
7856
|
-
overflow-wrap: anywhere;
|
|
7857
|
-
tab-size: 2;
|
|
7858
|
-
line-height: 1.55;
|
|
7859
|
-
color: var(--lb-text);
|
|
7860
|
-
background: var(--lb-surface);
|
|
7861
|
-
}
|
|
7862
|
-
.preview-target-line {
|
|
7863
|
-
display: inline-block;
|
|
7864
|
-
min-width: 100%;
|
|
7865
|
-
margin: 0 -20px;
|
|
7866
|
-
padding: 0 20px;
|
|
7867
|
-
background: var(--lb-target-line);
|
|
7868
|
-
box-shadow: inset 3px 0 0 var(--lb-target-stripe);
|
|
7869
|
-
}
|
|
7870
|
-
#previewEditor { flex: 1 1 auto; width: 100%; min-height: 0; }
|
|
7871
|
-
.ace_editor { width: 100% !important; height: 100% !important; }
|
|
7872
|
-
.ace_marker-layer .ace_active-line { background: transparent !important; }
|
|
7873
|
-
.ace_marker-layer .ace_selection { background: var(--lb-selection) !important; }
|
|
7874
|
-
@media (max-width: 640px) {
|
|
7875
|
-
.toolbar { padding: 12px; }
|
|
7876
|
-
.preview-shell { padding: 12px; }
|
|
7877
|
-
}
|
|
7878
|
-
</style>
|
|
7879
|
-
</head>
|
|
7880
|
-
<body>
|
|
7881
|
-
<div class="toolbar">
|
|
7882
|
-
<div class="toolbar-row">${toolbar}</div>
|
|
7883
|
-
<div class="meta">${escapeHtml(previewMeta)}</div>
|
|
7884
|
-
</div>
|
|
7885
|
-
<main class="preview-shell">
|
|
7886
|
-
<section class="preview-card">
|
|
7887
|
-
${previewUnavailable ? `<div class="preview-unavailable">
|
|
7888
|
-
<p class="preview-unavailable-title">Inline preview disabled</p>
|
|
7889
|
-
<p class="preview-unavailable-text">This file is larger than ${String(Math.floor(MAX_INLINE_PREVIEW_BYTES / 1024))} KiB. Use <strong>Raw</strong> or <strong>Download</strong> instead.</p>
|
|
7890
|
-
</div>` : `<pre id="previewFallback" class="preview-plain">${previewPlainHtml}</pre>
|
|
7891
|
-
<div id="previewEditor" hidden></div>`}
|
|
7892
|
-
</section>
|
|
7893
|
-
</main>
|
|
7894
|
-
${previewUnavailable ? "" : `<script src="${ACE_CDN_BASE}/ace.min.js"></script>
|
|
7895
|
-
<script>
|
|
7896
|
-
const targetLineNumber = ${line ?? "null"};
|
|
7897
|
-
const targetColumnNumber = ${column ?? "null"};
|
|
7898
|
-
const previewFallback = document.getElementById('previewFallback');
|
|
7899
|
-
const previewTargetLine = document.getElementById('previewTargetLine');
|
|
7900
|
-
const previewEditor = document.getElementById('previewEditor');
|
|
7901
|
-
${renderAceLineTargetScript()}
|
|
7902
|
-
if (previewTargetLine) {
|
|
7903
|
-
previewTargetLine.scrollIntoView({ block: 'center' });
|
|
7904
|
-
}
|
|
7905
|
-
if (window.ace && previewEditor) {
|
|
7906
|
-
previewEditor.hidden = false;
|
|
7907
|
-
if (previewFallback) previewFallback.hidden = true;
|
|
7908
|
-
ace.config.set('basePath', '${ACE_CDN_BASE}/');
|
|
7909
|
-
const editor = ace.edit('previewEditor');
|
|
7910
|
-
applyAceTheme(editor);
|
|
7911
|
-
editor.session.setMode('ace/mode/${escapeHtml(metadata.language.aceMode)}');
|
|
7912
|
-
editor.session.setUseWorker(false);
|
|
7913
|
-
editor.setValue(${safePreviewLiteral}, -1);
|
|
7914
|
-
editor.setOptions({
|
|
7915
|
-
readOnly: true,
|
|
7916
|
-
highlightActiveLine: !!targetLineNumber,
|
|
7917
|
-
highlightGutterLine: false,
|
|
7918
|
-
showPrintMargin: false,
|
|
7919
|
-
fontSize: '13px',
|
|
7920
|
-
wrap: true,
|
|
7921
|
-
behavioursEnabled: false,
|
|
7922
|
-
displayIndentGuides: true,
|
|
7923
|
-
});
|
|
7924
|
-
editor.renderer.setShowGutter(true);
|
|
7925
|
-
editor.renderer.$cursorLayer.element.style.display = 'none';
|
|
7926
|
-
if (targetLineNumber) {
|
|
7927
|
-
selectTargetLine(editor, targetLineNumber, targetColumnNumber);
|
|
7928
|
-
}
|
|
7929
|
-
window.addEventListener('codex-local-browse-themechange', function() {
|
|
7930
|
-
applyAceTheme(editor);
|
|
7931
|
-
});
|
|
7932
|
-
editor.resize();
|
|
7933
|
-
}
|
|
7934
|
-
</script>`}
|
|
7935
|
-
</body>
|
|
7936
|
-
</html>`;
|
|
7937
|
-
}
|
|
7938
|
-
async function createTextEditorHtml(localPath, options) {
|
|
7939
|
-
const newProjectName = normalizeNewProjectName(options?.newProjectName ?? "");
|
|
7940
|
-
const line = normalizeLineTarget(options?.line);
|
|
7941
|
-
const column = line ? normalizeLineTarget(options?.column) : null;
|
|
7942
|
-
const metadata = await getLocalTextFileMetadata(localPath);
|
|
7943
|
-
if (!metadata) {
|
|
7944
|
-
throw new Error("Only text-like files are editable.");
|
|
7945
|
-
}
|
|
7601
|
+
async function createTextEditorHtml(localPath) {
|
|
7946
7602
|
const content = await readFile4(localPath, "utf8");
|
|
7947
7603
|
const parentPath = dirname2(localPath);
|
|
7604
|
+
const language = languageForPath(localPath);
|
|
7948
7605
|
const safeContentLiteral = escapeForInlineScriptString(content);
|
|
7949
7606
|
return `<!doctype html>
|
|
7950
7607
|
<html lang="en">
|
|
@@ -7952,64 +7609,49 @@ async function createTextEditorHtml(localPath, options) {
|
|
|
7952
7609
|
<meta charset="utf-8" />
|
|
7953
7610
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7954
7611
|
<title>Edit ${escapeHtml(localPath)}</title>
|
|
7955
|
-
<script>${renderStandaloneThemeBootstrapScript()}</script>
|
|
7956
7612
|
<style>
|
|
7957
|
-
${renderStandaloneThemeCss()}
|
|
7958
7613
|
html, body { width: 100%; height: 100%; margin: 0; }
|
|
7959
|
-
body { font-family: ui-monospace, Menlo, Monaco, monospace; background:
|
|
7960
|
-
.toolbar { position: sticky; top: 0; z-index: 10; display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; background:
|
|
7614
|
+
body { font-family: ui-monospace, Menlo, Monaco, monospace; background: #0b1020; color: #dbe6ff; display: flex; flex-direction: column; overflow: hidden; }
|
|
7615
|
+
.toolbar { position: sticky; top: 0; z-index: 10; display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; background: #0b1020; border-bottom: 1px solid #243a5a; }
|
|
7961
7616
|
.row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
|
7962
|
-
button, a { background:
|
|
7617
|
+
button, a { background: #1b2a4a; color: #dbe6ff; border: 1px solid #345; padding: 6px 10px; border-radius: 6px; text-decoration: none; cursor: pointer; }
|
|
7963
7618
|
button:hover, a:hover { filter: brightness(1.08); }
|
|
7964
7619
|
#editor { flex: 1 1 auto; min-height: 0; width: 100%; border: none; overflow: hidden; }
|
|
7965
|
-
#status { margin-left: 8px; color:
|
|
7966
|
-
.ace_editor { width: 100% !important; height: 100% !important; }
|
|
7967
|
-
.
|
|
7968
|
-
.ace_marker-layer .
|
|
7969
|
-
.
|
|
7620
|
+
#status { margin-left: 8px; color: #8cc2ff; }
|
|
7621
|
+
.ace_editor { background: #07101f !important; color: #dbe6ff !important; width: 100% !important; height: 100% !important; }
|
|
7622
|
+
.ace_gutter { background: #07101f !important; color: #6f8eb5 !important; }
|
|
7623
|
+
.ace_marker-layer .ace_active-line { background: #10213c !important; }
|
|
7624
|
+
.ace_marker-layer .ace_selection { background: rgba(140, 194, 255, 0.3) !important; }
|
|
7625
|
+
.meta { opacity: 0.9; font-size: 12px; overflow-wrap: anywhere; }
|
|
7970
7626
|
</style>
|
|
7971
7627
|
</head>
|
|
7972
7628
|
<body>
|
|
7973
7629
|
<div class="toolbar">
|
|
7974
7630
|
<div class="row">
|
|
7975
|
-
<a href="${escapeHtml(toBrowseHref(parentPath
|
|
7976
|
-
<a href="${escapeHtml(toBrowseHref(localPath, { newProjectName, line, column }))}">Preview</a>
|
|
7977
|
-
<a href="${escapeHtml(toRawFileHref(localPath))}" target="_blank" rel="noopener noreferrer">Raw</a>
|
|
7978
|
-
<a href="${escapeHtml(toRawFileHref(localPath, { download: true }))}">Download</a>
|
|
7631
|
+
<a href="${escapeHtml(toBrowseHref(parentPath))}">Back</a>
|
|
7979
7632
|
<button id="saveBtn" type="button">Save</button>
|
|
7980
7633
|
<span id="status"></span>
|
|
7981
7634
|
</div>
|
|
7982
|
-
<div class="meta">${escapeHtml(
|
|
7635
|
+
<div class="meta">${escapeHtml(localPath)} \xB7 ${escapeHtml(language)}</div>
|
|
7983
7636
|
</div>
|
|
7984
7637
|
<div id="editor"></div>
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
const editor = ace.edit('editor');
|
|
7994
|
-
applyAceTheme(editor);
|
|
7995
|
-
editor.session.setMode('ace/mode/${escapeHtml(metadata.language.aceMode)}');
|
|
7996
|
-
editor.session.setUseWorker(false);
|
|
7997
|
-
editor.setValue(${safeContentLiteral}, -1);
|
|
7638
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.36.2/ace.js"></script>
|
|
7639
|
+
<script>
|
|
7640
|
+
const saveBtn = document.getElementById('saveBtn');
|
|
7641
|
+
const status = document.getElementById('status');
|
|
7642
|
+
const editor = ace.edit('editor');
|
|
7643
|
+
editor.setTheme('ace/theme/tomorrow_night');
|
|
7644
|
+
editor.session.setMode('ace/mode/${escapeHtml(language)}');
|
|
7645
|
+
editor.setValue(${safeContentLiteral}, -1);
|
|
7998
7646
|
editor.setOptions({
|
|
7999
7647
|
fontSize: '13px',
|
|
8000
7648
|
wrap: true,
|
|
8001
7649
|
showPrintMargin: false,
|
|
8002
7650
|
useSoftTabs: true,
|
|
8003
7651
|
tabSize: 2,
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
selectTargetLine(editor, targetLineNumber, targetColumnNumber);
|
|
8008
|
-
}
|
|
8009
|
-
window.addEventListener('codex-local-browse-themechange', function() {
|
|
8010
|
-
applyAceTheme(editor);
|
|
8011
|
-
});
|
|
8012
|
-
editor.resize();
|
|
7652
|
+
behavioursEnabled: true,
|
|
7653
|
+
});
|
|
7654
|
+
editor.resize();
|
|
8013
7655
|
|
|
8014
7656
|
saveBtn.addEventListener('click', async () => {
|
|
8015
7657
|
status.textContent = 'Saving...';
|
|
@@ -8075,34 +7717,6 @@ function readWildcardPathParam(value) {
|
|
|
8075
7717
|
if (Array.isArray(value)) return value.join("/");
|
|
8076
7718
|
return "";
|
|
8077
7719
|
}
|
|
8078
|
-
function readBooleanQueryFlag(value) {
|
|
8079
|
-
return typeof value === "string" && ["1", "true", "yes", "on"].includes(value.toLowerCase());
|
|
8080
|
-
}
|
|
8081
|
-
function readPositiveIntegerQueryParam(value) {
|
|
8082
|
-
if (typeof value !== "string") return null;
|
|
8083
|
-
const parsed = Number(value);
|
|
8084
|
-
return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
|
|
8085
|
-
}
|
|
8086
|
-
function localFileErrorResponse(error) {
|
|
8087
|
-
const code = typeof error === "object" && error !== null && "code" in error ? String(error.code ?? "") : "";
|
|
8088
|
-
const statusCode = typeof error === "object" && error !== null && "statusCode" in error ? Number(error.statusCode) : NaN;
|
|
8089
|
-
if (code === "ENOENT" || statusCode === 404) {
|
|
8090
|
-
return { status: 404, body: { error: "File not found." } };
|
|
8091
|
-
}
|
|
8092
|
-
if (code === "EACCES" || code === "EPERM" || statusCode === 403) {
|
|
8093
|
-
return { status: 403, body: { error: "Access denied." } };
|
|
8094
|
-
}
|
|
8095
|
-
return { status: 500, body: { error: "Failed to read file." } };
|
|
8096
|
-
}
|
|
8097
|
-
function sendLocalFileJsonError(res, error) {
|
|
8098
|
-
if (res.headersSent) return;
|
|
8099
|
-
const response = localFileErrorResponse(error);
|
|
8100
|
-
res.status(response.status).json(response.body);
|
|
8101
|
-
}
|
|
8102
|
-
function attachmentContentDisposition(pathValue) {
|
|
8103
|
-
const fileName = basename5(pathValue).replace(/["\\]/gu, "_");
|
|
8104
|
-
return `attachment; filename="${fileName}"`;
|
|
8105
|
-
}
|
|
8106
7720
|
function createServer(options = {}) {
|
|
8107
7721
|
const app = express();
|
|
8108
7722
|
const bridge = createCodexBridgeMiddleware();
|
|
@@ -8130,87 +7744,19 @@ function createServer(options = {}) {
|
|
|
8130
7744
|
if (!res.headersSent) res.status(404).json({ error: "Image file not found." });
|
|
8131
7745
|
});
|
|
8132
7746
|
});
|
|
8133
|
-
app.
|
|
8134
|
-
const body = req.body && typeof req.body === "object" && !Array.isArray(req.body) ? req.body : {};
|
|
8135
|
-
const rawPaths = Array.isArray(body.paths) ? body.paths : [];
|
|
8136
|
-
const normalizedPaths = [];
|
|
8137
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8138
|
-
for (const candidate of rawPaths.slice(0, 200)) {
|
|
8139
|
-
if (typeof candidate !== "string") continue;
|
|
8140
|
-
const normalized = normalizeLocalPath(candidate);
|
|
8141
|
-
if (!normalized || seen.has(normalized)) continue;
|
|
8142
|
-
seen.add(normalized);
|
|
8143
|
-
normalizedPaths.push(normalized);
|
|
8144
|
-
}
|
|
8145
|
-
const entries = await Promise.all(normalizedPaths.map(async (pathValue) => {
|
|
8146
|
-
if (!isAbsolute3(pathValue)) {
|
|
8147
|
-
return {
|
|
8148
|
-
path: pathValue,
|
|
8149
|
-
exists: false,
|
|
8150
|
-
isDirectory: false
|
|
8151
|
-
};
|
|
8152
|
-
}
|
|
8153
|
-
try {
|
|
8154
|
-
const fileStat = await stat6(pathValue);
|
|
8155
|
-
return {
|
|
8156
|
-
path: pathValue,
|
|
8157
|
-
exists: true,
|
|
8158
|
-
isDirectory: fileStat.isDirectory()
|
|
8159
|
-
};
|
|
8160
|
-
} catch {
|
|
8161
|
-
return {
|
|
8162
|
-
path: pathValue,
|
|
8163
|
-
exists: false,
|
|
8164
|
-
isDirectory: false
|
|
8165
|
-
};
|
|
8166
|
-
}
|
|
8167
|
-
}));
|
|
8168
|
-
res.status(200).json({ data: { entries } });
|
|
8169
|
-
});
|
|
8170
|
-
app.get("/codex-local-file", async (req, res) => {
|
|
7747
|
+
app.get("/codex-local-file", (req, res) => {
|
|
8171
7748
|
const rawPath = typeof req.query.path === "string" ? req.query.path : "";
|
|
8172
7749
|
const localPath = normalizeLocalPath(rawPath);
|
|
8173
|
-
const wantsDownload = readBooleanQueryFlag(req.query.download);
|
|
8174
7750
|
if (!localPath || !isAbsolute3(localPath)) {
|
|
8175
7751
|
res.status(400).json({ error: "Expected absolute local file path." });
|
|
8176
7752
|
return;
|
|
8177
7753
|
}
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
const textMetadata = await getLocalTextFileMetadata(localPath);
|
|
8185
|
-
res.setHeader("Cache-Control", "private, no-store");
|
|
8186
|
-
if (wantsDownload) {
|
|
8187
|
-
res.setHeader("Content-Disposition", attachmentContentDisposition(localPath));
|
|
8188
|
-
res.sendFile(localPath, { dotfiles: "allow" }, (error) => {
|
|
8189
|
-
if (!error) return;
|
|
8190
|
-
sendLocalFileJsonError(res, error);
|
|
8191
|
-
});
|
|
8192
|
-
return;
|
|
8193
|
-
}
|
|
8194
|
-
if (textMetadata) {
|
|
8195
|
-
const stream = createReadStream2(localPath, { encoding: "utf8" });
|
|
8196
|
-
stream.on("open", () => {
|
|
8197
|
-
res.status(200).type("text/plain; charset=utf-8");
|
|
8198
|
-
});
|
|
8199
|
-
stream.on("error", (error) => {
|
|
8200
|
-
stream.destroy();
|
|
8201
|
-
sendLocalFileJsonError(res, error);
|
|
8202
|
-
});
|
|
8203
|
-
stream.pipe(res);
|
|
8204
|
-
return;
|
|
8205
|
-
}
|
|
8206
|
-
res.setHeader("Content-Disposition", "inline");
|
|
8207
|
-
res.sendFile(localPath, { dotfiles: "allow" }, (error) => {
|
|
8208
|
-
if (!error) return;
|
|
8209
|
-
sendLocalFileJsonError(res, error);
|
|
8210
|
-
});
|
|
8211
|
-
} catch (error) {
|
|
8212
|
-
sendLocalFileJsonError(res, error);
|
|
8213
|
-
}
|
|
7754
|
+
res.setHeader("Cache-Control", "private, no-store");
|
|
7755
|
+
res.setHeader("Content-Disposition", "inline");
|
|
7756
|
+
res.sendFile(localPath, { dotfiles: "allow" }, (error) => {
|
|
7757
|
+
if (!error) return;
|
|
7758
|
+
if (!res.headersSent) res.status(404).json({ error: "File not found." });
|
|
7759
|
+
});
|
|
8214
7760
|
});
|
|
8215
7761
|
app.get("/codex-local-directories", async (req, res) => {
|
|
8216
7762
|
const rawPath = typeof req.query.path === "string" ? req.query.path : "";
|
|
@@ -8236,8 +7782,6 @@ function createServer(options = {}) {
|
|
|
8236
7782
|
const rawPath = readWildcardPathParam(req.params.path);
|
|
8237
7783
|
const localPath = decodeBrowsePath(`/${rawPath}`);
|
|
8238
7784
|
const newProjectName = typeof req.query.newProjectName === "string" ? req.query.newProjectName : "";
|
|
8239
|
-
const line = readPositiveIntegerQueryParam(req.query.line);
|
|
8240
|
-
const column = line ? readPositiveIntegerQueryParam(req.query.column) : null;
|
|
8241
7785
|
if (!localPath || !isAbsolute3(localPath)) {
|
|
8242
7786
|
res.status(400).json({ error: "Expected absolute local file path." });
|
|
8243
7787
|
return;
|
|
@@ -8250,13 +7794,6 @@ function createServer(options = {}) {
|
|
|
8250
7794
|
res.status(200).type("text/html; charset=utf-8").send(html);
|
|
8251
7795
|
return;
|
|
8252
7796
|
}
|
|
8253
|
-
const textMetadata = await getLocalTextFileMetadata(localPath);
|
|
8254
|
-
if (textMetadata) {
|
|
8255
|
-
const html = await createTextPreviewHtml(localPath, { newProjectName, line, column });
|
|
8256
|
-
res.status(200).type("text/html; charset=utf-8").send(html);
|
|
8257
|
-
return;
|
|
8258
|
-
}
|
|
8259
|
-
res.setHeader("Content-Disposition", "attachment");
|
|
8260
7797
|
res.sendFile(localPath, { dotfiles: "allow" }, (error) => {
|
|
8261
7798
|
if (!error) return;
|
|
8262
7799
|
if (!res.headersSent) res.status(404).json({ error: "File not found." });
|
|
@@ -8268,9 +7805,6 @@ function createServer(options = {}) {
|
|
|
8268
7805
|
app.get("/codex-local-edit/*path", async (req, res) => {
|
|
8269
7806
|
const rawPath = readWildcardPathParam(req.params.path);
|
|
8270
7807
|
const localPath = decodeBrowsePath(`/${rawPath}`);
|
|
8271
|
-
const newProjectName = typeof req.query.newProjectName === "string" ? req.query.newProjectName : "";
|
|
8272
|
-
const line = readPositiveIntegerQueryParam(req.query.line);
|
|
8273
|
-
const column = line ? readPositiveIntegerQueryParam(req.query.column) : null;
|
|
8274
7808
|
if (!localPath || !isAbsolute3(localPath)) {
|
|
8275
7809
|
res.status(400).json({ error: "Expected absolute local file path." });
|
|
8276
7810
|
return;
|
|
@@ -8281,11 +7815,7 @@ function createServer(options = {}) {
|
|
|
8281
7815
|
res.status(400).json({ error: "Expected file path." });
|
|
8282
7816
|
return;
|
|
8283
7817
|
}
|
|
8284
|
-
|
|
8285
|
-
res.status(415).json({ error: "Only text-like files are editable." });
|
|
8286
|
-
return;
|
|
8287
|
-
}
|
|
8288
|
-
const html = await createTextEditorHtml(localPath, { newProjectName, line, column });
|
|
7818
|
+
const html = await createTextEditorHtml(localPath);
|
|
8289
7819
|
res.status(200).type("text/html; charset=utf-8").send(html);
|
|
8290
7820
|
} catch {
|
|
8291
7821
|
res.status(404).json({ error: "File not found." });
|