@youtyan/code-viewer 0.1.9 → 0.1.10

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/web/app.js +327 -50
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youtyan/code-viewer",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Local browser-based code and git diff viewer",
5
5
  "type": "module",
6
6
  "bin": {
package/web/app.js CHANGED
@@ -6395,6 +6395,17 @@
6395
6395
  let highlightLoadPromise = null;
6396
6396
  let highlightConfigured = false;
6397
6397
  let PROJECT_NAME = "";
6398
+ let REPO_SIDEBAR_REF = null;
6399
+ let REPO_SIDEBAR_LOAD_REF = null;
6400
+ let REPO_SIDEBAR_LOAD = null;
6401
+ function invalidateRepoSidebar() {
6402
+ REPO_SIDEBAR_REF = null;
6403
+ REPO_SIDEBAR_LOAD_REF = null;
6404
+ REPO_SIDEBAR_LOAD = null;
6405
+ }
6406
+ function isRepoSidebarReusable(ref) {
6407
+ return REPO_SIDEBAR_REF === (ref || "worktree") && isRepositorySidebarMode();
6408
+ }
6398
6409
  const STATE = (() => {
6399
6410
  const igRaw = localStorage.getItem("gdp:ignore-ws");
6400
6411
  const fallbackRange = {
@@ -6768,6 +6779,8 @@
6768
6779
  ul.innerHTML = "";
6769
6780
  ul.classList.toggle("tree", STATE.sbView === "tree");
6770
6781
  STATE.files = files;
6782
+ if (!onFileClick)
6783
+ REPO_SIDEBAR_REF = null;
6771
6784
  if (STATE.sbView === "tree") {
6772
6785
  const root = buildTree(files);
6773
6786
  renderTreeNode(root, 0, ul, onFileClick);
@@ -7168,6 +7181,7 @@
7168
7181
  });
7169
7182
  if (!res.ok)
7170
7183
  throw new Error(await res.text());
7184
+ invalidateRepoSidebar();
7171
7185
  await loadRepo();
7172
7186
  }
7173
7187
  function createRepoUploadPanel(path) {
@@ -7282,8 +7296,8 @@
7282
7296
  removeStandaloneSource();
7283
7297
  $("#empty").classList.add("hidden");
7284
7298
  $("#diff").replaceChildren();
7285
- $("#filelist").replaceChildren();
7286
- $("#totals").textContent = "";
7299
+ if (!isRepoSidebarReusable(meta.ref))
7300
+ $("#totals").textContent = "";
7287
7301
  STATE.files = [];
7288
7302
  LOAD_QUEUE.length = 0;
7289
7303
  renderRepoBlobSidebar(meta.path || "", meta.ref);
@@ -7426,14 +7440,28 @@
7426
7440
  }
7427
7441
  function renderRepoBlobSidebar(currentPath, ref) {
7428
7442
  syncRepoTargetInput(ref);
7443
+ const normalizedRef = ref || "worktree";
7444
+ if (isRepoSidebarReusable(normalizedRef)) {
7445
+ activateRepoSidebarPath(currentPath);
7446
+ return Promise.resolve();
7447
+ }
7448
+ if (REPO_SIDEBAR_LOAD && REPO_SIDEBAR_LOAD_REF === normalizedRef) {
7449
+ return REPO_SIDEBAR_LOAD.then(() => {
7450
+ activateRepoSidebarPath(currentPath);
7451
+ });
7452
+ }
7429
7453
  const params = new URLSearchParams;
7430
- params.set("ref", ref || "worktree");
7454
+ params.set("ref", normalizedRef);
7431
7455
  params.set("recursive", "1");
7432
- return trackLoad(fetch("/_tree?" + params.toString()).then((r2) => {
7456
+ REPO_SIDEBAR_LOAD_REF = normalizedRef;
7457
+ const load2 = trackLoad(fetch("/_tree?" + params.toString()).then((r2) => {
7433
7458
  if (!r2.ok)
7434
7459
  throw new Error("failed to load repository tree");
7435
7460
  return r2.json();
7436
7461
  })).then((meta) => {
7462
+ const activeRepoRef = repoFileTargetFromRoute() || (STATE.route.screen === "repo" ? STATE.route.ref : "");
7463
+ if ((activeRepoRef || "worktree") !== normalizedRef)
7464
+ return;
7437
7465
  const files = meta.entries.map((entry, index) => ({
7438
7466
  order: index + 1,
7439
7467
  path: entry.path,
@@ -7443,19 +7471,31 @@
7443
7471
  }));
7444
7472
  renderSidebar(files, (file) => {
7445
7473
  if (file.type === "tree") {
7446
- setRoute(repoRoute(ref, file.path));
7474
+ setRoute(repoRoute(normalizedRef, file.path));
7447
7475
  loadRepo();
7448
7476
  return;
7449
7477
  }
7450
- setRoute({ screen: "file", path: file.path, ref, view: "blob", range: currentRange() });
7451
- renderStandaloneSource({ path: file.path, ref });
7478
+ setRoute({ screen: "file", path: file.path, ref: normalizedRef, view: "blob", range: currentRange() });
7479
+ renderStandaloneSource({ path: file.path, ref: normalizedRef });
7452
7480
  });
7453
- markActive(currentPath);
7454
- applyFilter();
7481
+ REPO_SIDEBAR_REF = normalizedRef;
7482
+ activateRepoSidebarPath(currentPath);
7455
7483
  }).catch(() => {
7484
+ REPO_SIDEBAR_REF = null;
7456
7485
  renderSidebar([], undefined);
7457
7486
  $("#totals").textContent = "Cannot load tree";
7487
+ }).finally(() => {
7488
+ if (REPO_SIDEBAR_LOAD === load2) {
7489
+ REPO_SIDEBAR_LOAD_REF = null;
7490
+ REPO_SIDEBAR_LOAD = null;
7491
+ }
7458
7492
  });
7493
+ REPO_SIDEBAR_LOAD = load2;
7494
+ return load2;
7495
+ }
7496
+ function activateRepoSidebarPath(currentPath) {
7497
+ markActive(currentPath);
7498
+ applyFilter();
7459
7499
  }
7460
7500
  function createPlaceholder(f2) {
7461
7501
  const card = document.createElement("div");
@@ -8135,6 +8175,269 @@
8135
8175
  function isPreviewableSource(path) {
8136
8176
  return /\.(md|markdown|mdown|mkdn|mdx)$/i.test(path);
8137
8177
  }
8178
+ const EXT_TO_LANG = {
8179
+ js: "javascript",
8180
+ mjs: "javascript",
8181
+ cjs: "javascript",
8182
+ ts: "typescript",
8183
+ tsx: "typescript",
8184
+ jsx: "javascript",
8185
+ py: "python",
8186
+ rb: "ruby",
8187
+ go: "go",
8188
+ rs: "rust",
8189
+ java: "java",
8190
+ kt: "kotlin",
8191
+ swift: "swift",
8192
+ c: "c",
8193
+ h: "c",
8194
+ cc: "cpp",
8195
+ cpp: "cpp",
8196
+ hpp: "cpp",
8197
+ cs: "csharp",
8198
+ php: "php",
8199
+ lua: "lua",
8200
+ sh: "bash",
8201
+ bash: "bash",
8202
+ zsh: "bash",
8203
+ fish: "bash",
8204
+ sql: "sql",
8205
+ json: "json",
8206
+ yaml: "yaml",
8207
+ yml: "yaml",
8208
+ toml: "toml",
8209
+ tf: "terraform",
8210
+ tfvars: "terraform",
8211
+ hcl: "terraform",
8212
+ xml: "xml",
8213
+ html: "xml",
8214
+ vue: "xml",
8215
+ css: "css",
8216
+ scss: "scss",
8217
+ md: "markdown",
8218
+ dockerfile: "dockerfile",
8219
+ proto: "protobuf",
8220
+ gradle: "gradle",
8221
+ properties: "properties",
8222
+ patch: "diff",
8223
+ diff: "diff",
8224
+ nix: "nix",
8225
+ cue: "cue",
8226
+ rego: "rego",
8227
+ bicep: "bicep",
8228
+ bazel: "starlark",
8229
+ bzl: "starlark",
8230
+ cmake: "cmake",
8231
+ groovy: "groovy",
8232
+ dart: "dart",
8233
+ scala: "scala",
8234
+ clj: "clojure",
8235
+ cljs: "clojure",
8236
+ cljc: "clojure",
8237
+ edn: "clojure",
8238
+ ex: "elixir",
8239
+ exs: "elixir",
8240
+ erl: "erlang",
8241
+ hrl: "erlang",
8242
+ hs: "haskell",
8243
+ lhs: "haskell",
8244
+ ml: "ocaml",
8245
+ mli: "ocaml",
8246
+ jl: "julia",
8247
+ r: "r",
8248
+ rmd: "r",
8249
+ pl: "perl",
8250
+ pm: "perl",
8251
+ tcl: "tcl",
8252
+ vim: "vim",
8253
+ f: "fortran",
8254
+ f90: "fortran",
8255
+ m: "objective-c",
8256
+ mm: "objective-cpp",
8257
+ tex: "tex",
8258
+ bib: "bibtex",
8259
+ rst: "rst"
8260
+ };
8261
+ const TEXT_SOURCE_EXTENSIONS = new Set([
8262
+ ...Object.keys(EXT_TO_LANG),
8263
+ "txt",
8264
+ "md",
8265
+ "markdown",
8266
+ "mdown",
8267
+ "mkdn",
8268
+ "mdx",
8269
+ "json",
8270
+ "jsonc",
8271
+ "csv",
8272
+ "tsv",
8273
+ "yaml",
8274
+ "yml",
8275
+ "toml",
8276
+ "hcl",
8277
+ "tf",
8278
+ "tfvars",
8279
+ "tfstate",
8280
+ "xml",
8281
+ "html",
8282
+ "htm",
8283
+ "css",
8284
+ "scss",
8285
+ "sass",
8286
+ "less",
8287
+ "js",
8288
+ "jsx",
8289
+ "mjs",
8290
+ "cjs",
8291
+ "ts",
8292
+ "tsx",
8293
+ "mts",
8294
+ "cts",
8295
+ "vue",
8296
+ "svelte",
8297
+ "astro",
8298
+ "rs",
8299
+ "go",
8300
+ "py",
8301
+ "rb",
8302
+ "php",
8303
+ "java",
8304
+ "kt",
8305
+ "kts",
8306
+ "c",
8307
+ "cc",
8308
+ "cpp",
8309
+ "cxx",
8310
+ "h",
8311
+ "hpp",
8312
+ "cs",
8313
+ "swift",
8314
+ "sh",
8315
+ "bash",
8316
+ "zsh",
8317
+ "fish",
8318
+ "ps1",
8319
+ "sql",
8320
+ "graphql",
8321
+ "graphqls",
8322
+ "gql",
8323
+ "ini",
8324
+ "conf",
8325
+ "env",
8326
+ "properties",
8327
+ "gitignore",
8328
+ "dockerignore",
8329
+ "editorconfig",
8330
+ "lock",
8331
+ "log",
8332
+ "patch",
8333
+ "diff",
8334
+ "sum",
8335
+ "mk",
8336
+ "proto",
8337
+ "thrift",
8338
+ "prisma",
8339
+ "gradle",
8340
+ "cmake",
8341
+ "nix",
8342
+ "cue",
8343
+ "rego",
8344
+ "bicep",
8345
+ "bazel",
8346
+ "bzl",
8347
+ "dart",
8348
+ "scala",
8349
+ "clj",
8350
+ "cljs",
8351
+ "cljc",
8352
+ "edn",
8353
+ "ex",
8354
+ "exs",
8355
+ "erl",
8356
+ "hrl",
8357
+ "hs",
8358
+ "lhs",
8359
+ "ml",
8360
+ "mli",
8361
+ "jl",
8362
+ "r",
8363
+ "rmd",
8364
+ "pl",
8365
+ "pm",
8366
+ "tcl",
8367
+ "vim",
8368
+ "groovy",
8369
+ "f",
8370
+ "f90",
8371
+ "m",
8372
+ "mm",
8373
+ "pas",
8374
+ "tex",
8375
+ "bib",
8376
+ "rst",
8377
+ "adoc",
8378
+ "org",
8379
+ "ipynb",
8380
+ "ejs",
8381
+ "hbs",
8382
+ "mustache",
8383
+ "liquid",
8384
+ "pug"
8385
+ ]);
8386
+ const TEXT_SOURCE_FILENAMES = new Set([
8387
+ "readme",
8388
+ "license",
8389
+ "copying",
8390
+ "authors",
8391
+ "contributors",
8392
+ "notice",
8393
+ "changelog",
8394
+ "todo",
8395
+ "manifest",
8396
+ "version",
8397
+ "codeowners",
8398
+ "go.mod",
8399
+ "build.bazel",
8400
+ "workspace.bazel",
8401
+ "module.bazel",
8402
+ "gemfile",
8403
+ "rakefile",
8404
+ "procfile",
8405
+ "brewfile",
8406
+ "gnumakefile",
8407
+ "bsdmakefile",
8408
+ ".gitattributes",
8409
+ ".gitmodules",
8410
+ ".npmrc",
8411
+ ".nvmrc",
8412
+ ".yarnrc",
8413
+ ".prettierrc",
8414
+ ".eslintrc",
8415
+ ".babelrc",
8416
+ ".stylelintrc"
8417
+ ]);
8418
+ const FILENAME_TO_LANG = {
8419
+ dockerfile: "dockerfile",
8420
+ makefile: "makefile",
8421
+ gnumakefile: "makefile",
8422
+ bsdmakefile: "makefile",
8423
+ "go.mod": "go",
8424
+ "build.bazel": "starlark",
8425
+ "workspace.bazel": "starlark",
8426
+ "module.bazel": "starlark"
8427
+ };
8428
+ function sourceFileName(path) {
8429
+ return (path.split("/").pop() || path).toLowerCase();
8430
+ }
8431
+ function sourceFileExtension(name) {
8432
+ const index = name.lastIndexOf(".");
8433
+ return index >= 0 ? name.slice(index + 1) : "";
8434
+ }
8435
+ function isDockerfileName(name) {
8436
+ return /^dockerfile(?:[.-].+)?$/i.test(name);
8437
+ }
8438
+ function isMakefileName(name) {
8439
+ return /^makefile(?:[.-].+)?$/i.test(name);
8440
+ }
8138
8441
  function sourceDisplayKind(path) {
8139
8442
  if (isVideo(path))
8140
8443
  return "video";
@@ -8142,9 +8445,13 @@
8142
8445
  return "image";
8143
8446
  if (/\.pdf$/i.test(path))
8144
8447
  return "pdf";
8145
- if (/\.(txt|md|markdown|mdown|mkdn|mdx|json|jsonc|csv|tsv|ya?ml|toml|xml|html?|css|scss|sass|less|js|jsx|mjs|cjs|ts|tsx|mts|cts|vue|svelte|astro|rs|go|py|rb|php|java|kt|kts|c|cc|cpp|cxx|h|hpp|cs|swift|sh|bash|zsh|fish|ps1|sql|graphql|gql|ini|conf|env|gitignore|dockerignore|editorconfig|lock|log)$/i.test(path))
8448
+ const name = sourceFileName(path);
8449
+ const ext = sourceFileExtension(name);
8450
+ if (TEXT_SOURCE_EXTENSIONS.has(ext))
8451
+ return "text";
8452
+ if (TEXT_SOURCE_FILENAMES.has(name))
8146
8453
  return "text";
8147
- if (/\/?(readme|license|notice|changelog|dockerfile|makefile)$/i.test(path))
8454
+ if (isDockerfileName(name) || isMakefileName(name))
8148
8455
  return "text";
8149
8456
  return "unsupported";
8150
8457
  }
@@ -9005,46 +9312,15 @@
9005
9312
  });
9006
9313
  }
9007
9314
  }
9008
- const EXT_TO_LANG = {
9009
- js: "javascript",
9010
- mjs: "javascript",
9011
- cjs: "javascript",
9012
- ts: "typescript",
9013
- tsx: "typescript",
9014
- jsx: "javascript",
9015
- py: "python",
9016
- rb: "ruby",
9017
- go: "go",
9018
- rs: "rust",
9019
- java: "java",
9020
- kt: "kotlin",
9021
- swift: "swift",
9022
- c: "c",
9023
- h: "c",
9024
- cc: "cpp",
9025
- cpp: "cpp",
9026
- hpp: "cpp",
9027
- cs: "csharp",
9028
- php: "php",
9029
- lua: "lua",
9030
- sh: "bash",
9031
- bash: "bash",
9032
- zsh: "bash",
9033
- fish: "bash",
9034
- sql: "sql",
9035
- json: "json",
9036
- yaml: "yaml",
9037
- yml: "yaml",
9038
- toml: "toml",
9039
- xml: "xml",
9040
- html: "xml",
9041
- vue: "xml",
9042
- css: "css",
9043
- scss: "scss",
9044
- md: "markdown",
9045
- dockerfile: "dockerfile"
9046
- };
9047
9315
  function inferLang(path) {
9316
+ const name = sourceFileName(path);
9317
+ const fileLang = FILENAME_TO_LANG[name];
9318
+ if (fileLang)
9319
+ return fileLang;
9320
+ if (isDockerfileName(name))
9321
+ return "dockerfile";
9322
+ if (isMakefileName(name))
9323
+ return "makefile";
9048
9324
  const m = path.match(/\.([^.]+)$/);
9049
9325
  if (!m)
9050
9326
  return null;
@@ -9826,6 +10102,7 @@
9826
10102
  clearTimeout(sseTimer);
9827
10103
  sseTimer = setTimeout(() => {
9828
10104
  sseTimer = null;
10105
+ invalidateRepoSidebar();
9829
10106
  const savedScroll = window.scrollY;
9830
10107
  const savedActive = STATE.activeFile;
9831
10108
  load().then(() => {