@supanadit/morse-web 0.7.0 → 0.8.1

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/server.mjs CHANGED
@@ -75,7 +75,7 @@ function hasKnownType(value, allowed) {
75
75
  }
76
76
 
77
77
  // ../protocol/dist/version.js
78
- var PROTOCOL_VERSION = 18;
78
+ var PROTOCOL_VERSION = 19;
79
79
  var FRONTEND_MANIFEST_FILE = "webview.manifest.json";
80
80
  var DEFAULT_WS_PATH = "/ws";
81
81
 
@@ -3851,10 +3851,143 @@ function toPosix(path) {
3851
3851
  return path.split("\\").join("/");
3852
3852
  }
3853
3853
 
3854
- // ../server/src/internal/workspace/workspace-index.ts
3854
+ // ../server/src/internal/workspace/git-log.ts
3855
3855
  import { execFile } from "node:child_process";
3856
+ import { relative as relative3 } from "node:path";
3857
+ var DEFAULT_MAX_COMMITS = 250;
3858
+ var HARD_MAX_COMMITS = 500;
3859
+ var FIELD = "";
3860
+ var RECORD = "";
3861
+ var PRETTY_FORMAT = ["%H", "%h", "%P", "%D", "%an", "%aI", "%s"].join(FIELD) + RECORD;
3862
+ async function readGitLog(cwd, requestedMax, requestedSkip) {
3863
+ const inside = await gitText(["rev-parse", "--is-inside-work-tree"], cwd);
3864
+ if (inside?.trim() !== "true") {
3865
+ return { isRepo: false, commits: [] };
3866
+ }
3867
+ const max = clampMax(requestedMax);
3868
+ const skip = clampSkip(requestedSkip);
3869
+ const [root, branch, raw] = await Promise.all([
3870
+ gitText(["rev-parse", "--show-toplevel"], cwd),
3871
+ gitText(["rev-parse", "--abbrev-ref", "HEAD"], cwd),
3872
+ gitText(
3873
+ [
3874
+ "log",
3875
+ "--all",
3876
+ "--date-order",
3877
+ `--skip=${skip}`,
3878
+ `--max-count=${max}`,
3879
+ `--pretty=format:${PRETTY_FORMAT}`
3880
+ ],
3881
+ cwd
3882
+ )
3883
+ ]);
3884
+ return {
3885
+ isRepo: true,
3886
+ root: root?.trim() || cwd,
3887
+ branch: branch?.trim() || void 0,
3888
+ commits: parseCommits(raw ?? "")
3889
+ };
3890
+ }
3891
+ function clampMax(requested) {
3892
+ if (requested === void 0 || !Number.isFinite(requested)) {
3893
+ return DEFAULT_MAX_COMMITS;
3894
+ }
3895
+ return Math.max(1, Math.min(HARD_MAX_COMMITS, Math.floor(requested)));
3896
+ }
3897
+ async function readGitStatus(cwd) {
3898
+ const inside = await gitText(["rev-parse", "--is-inside-work-tree"], cwd);
3899
+ if (inside?.trim() !== "true") {
3900
+ return { isRepo: false, files: [] };
3901
+ }
3902
+ const [prefix, raw] = await Promise.all([
3903
+ gitText(["rev-parse", "--show-prefix"], cwd),
3904
+ gitText(["status", "--porcelain=v1", "-z", "--untracked-files=all"], cwd)
3905
+ ]);
3906
+ return { isRepo: true, files: parseStatus(raw ?? "", prefix?.trim() ?? "") };
3907
+ }
3908
+ function parseStatus(raw, root) {
3909
+ const records = raw.split("\0");
3910
+ const files = [];
3911
+ for (let index = 0; index < records.length; index += 1) {
3912
+ const record = records[index];
3913
+ if (!record || record.length < 4) {
3914
+ continue;
3915
+ }
3916
+ const status = record.slice(0, 2);
3917
+ if (status[0] === "R" || status[0] === "C") {
3918
+ index += 1;
3919
+ }
3920
+ const full = record.slice(3);
3921
+ if (root.length > 0 && !full.startsWith(root)) {
3922
+ continue;
3923
+ }
3924
+ const path = full.slice(root.length);
3925
+ if (path.length > 0) {
3926
+ files.push({ path: toPosix2(path), status });
3927
+ }
3928
+ }
3929
+ return files;
3930
+ }
3931
+ async function readGitDiff(cwd, requested) {
3932
+ const rel = toPosix2(relative3(cwd, resolveWithin(cwd, requested)));
3933
+ const diff = await gitText(
3934
+ ["diff", "--no-color", "--no-ext-diff", "-U3", "HEAD", "--", rel],
3935
+ cwd
3936
+ );
3937
+ return { path: rel, diff: diff ?? "" };
3938
+ }
3939
+ function toPosix2(path) {
3940
+ return path.split("\\").join("/");
3941
+ }
3942
+ function clampSkip(requested) {
3943
+ if (requested === void 0 || !Number.isFinite(requested)) {
3944
+ return 0;
3945
+ }
3946
+ return Math.max(0, Math.floor(requested));
3947
+ }
3948
+ function parseCommits(raw) {
3949
+ const commits = [];
3950
+ for (const record of raw.split(RECORD)) {
3951
+ const line = record.replace(/^\n+/, "");
3952
+ if (line.trim().length === 0) {
3953
+ continue;
3954
+ }
3955
+ const fields = line.split(FIELD);
3956
+ const [hash, shortHash, parents, refs, author, date, subject] = fields;
3957
+ if (!hash || !shortHash || !date) {
3958
+ continue;
3959
+ }
3960
+ commits.push({
3961
+ hash,
3962
+ shortHash,
3963
+ parents: (parents ?? "").split(" ").filter((parent) => parent.length > 0),
3964
+ refs: parseRefs(refs ?? ""),
3965
+ author: author ?? "",
3966
+ date,
3967
+ subject: subject ?? ""
3968
+ });
3969
+ }
3970
+ return commits;
3971
+ }
3972
+ function parseRefs(raw) {
3973
+ const refs = raw.split(",").map((ref) => ref.trim()).filter((ref) => ref.length > 0);
3974
+ return refs;
3975
+ }
3976
+ function gitText(args, cwd) {
3977
+ return new Promise((resolve6) => {
3978
+ execFile(
3979
+ "git",
3980
+ args,
3981
+ { cwd, timeout: 5e3, maxBuffer: 16 * 1024 * 1024, windowsHide: true },
3982
+ (error, stdout) => resolve6(error ? void 0 : stdout)
3983
+ );
3984
+ });
3985
+ }
3986
+
3987
+ // ../server/src/internal/workspace/workspace-index.ts
3988
+ import { execFile as execFile2 } from "node:child_process";
3856
3989
  import { readdir as readdir4 } from "node:fs/promises";
3857
- import { join as join7, relative as relative3 } from "node:path";
3990
+ import { join as join7, relative as relative4 } from "node:path";
3858
3991
  var SKIPPED_DIRECTORIES2 = /* @__PURE__ */ new Set([
3859
3992
  "node_modules",
3860
3993
  "dist",
@@ -3875,15 +4008,15 @@ var MAX_INDEXED_FILES = 5e3;
3875
4008
  var INDEX_TTL_MS = 6e4;
3876
4009
  var indexes = /* @__PURE__ */ new Map();
3877
4010
  var inFlight = /* @__PURE__ */ new Map();
3878
- async function workspaceFiles(cwd, logger) {
3879
- const { files, source } = await cachedIndex(cwd);
4011
+ async function workspaceFiles(cwd, logger, options = {}) {
4012
+ const { files, source } = await cachedIndex(cwd, options.fresh === true);
3880
4013
  logger.info(`File picker: ${files.length} entries via ${source} in ${cwd}`);
3881
4014
  return files;
3882
4015
  }
3883
- async function cachedIndex(cwd) {
4016
+ async function cachedIndex(cwd, fresh) {
3884
4017
  const now = Date.now();
3885
4018
  const cached = indexes.get(cwd);
3886
- if (cached && now - cached.at < INDEX_TTL_MS) {
4019
+ if (!fresh && cached && now - cached.at < INDEX_TTL_MS) {
3887
4020
  return cached;
3888
4021
  }
3889
4022
  const pending = inFlight.get(cwd);
@@ -3939,7 +4072,7 @@ async function gitFileList(cwd) {
3939
4072
  }
3940
4073
  function runGit(args, cwd) {
3941
4074
  return new Promise((resolve6) => {
3942
- execFile(
4075
+ execFile2(
3943
4076
  "git",
3944
4077
  args,
3945
4078
  { cwd, timeout: 5e3, maxBuffer: 8 * 1024 * 1024, windowsHide: true },
@@ -3974,11 +4107,11 @@ async function walk(root, directory, files) {
3974
4107
  continue;
3975
4108
  }
3976
4109
  if (entry.isFile() || entry.isSymbolicLink()) {
3977
- files.push(toPosix2(relative3(root, full)));
4110
+ files.push(toPosix3(relative4(root, full)));
3978
4111
  }
3979
4112
  }
3980
4113
  }
3981
- function toPosix2(path) {
4114
+ function toPosix3(path) {
3982
4115
  return path.split("\\").join("/");
3983
4116
  }
3984
4117
 
@@ -4008,6 +4141,9 @@ var MorseSessionFactory = class {
4008
4141
  // A browser has no editor of its own, so this host reads files for the
4009
4142
  // frontend's Explorer and preview tabs. VS Code keeps its native ones.
4010
4143
  filePreview: true,
4144
+ // The same reasoning for history: VS Code has Source Control, so the
4145
+ // browser host is the one that answers `gitLog` for its own git panel.
4146
+ gitPanel: true,
4011
4147
  // A browser cannot hand a dragged file's path to pi, so the host takes the
4012
4148
  // bytes and writes them next to the session; the frontend then `@mentions`
4013
4149
  // the path it gets back.
@@ -4071,7 +4207,7 @@ var MorseSessionFactory = class {
4071
4207
  }
4072
4208
  case "listFiles": {
4073
4209
  const cwd = this.requireWritableCwd(context);
4074
- const files = await workspaceFiles(cwd, this.logger);
4210
+ const files = await workspaceFiles(cwd, this.logger, { fresh: args?.fresh === true });
4075
4211
  return { files };
4076
4212
  }
4077
4213
  case "readFile": {
@@ -4088,6 +4224,24 @@ var MorseSessionFactory = class {
4088
4224
  this.logger.info(`Upload stored: ${saved.path} (${saved.bytes} bytes)`);
4089
4225
  return saved;
4090
4226
  }
4227
+ case "gitLog": {
4228
+ const cwd = this.requireWritableCwd(context);
4229
+ const max = typeof args?.max === "number" ? args.max : void 0;
4230
+ const skip = typeof args?.skip === "number" ? args.skip : void 0;
4231
+ return readGitLog(cwd, max, skip);
4232
+ }
4233
+ case "gitStatus": {
4234
+ const cwd = this.requireWritableCwd(context);
4235
+ return readGitStatus(cwd);
4236
+ }
4237
+ case "gitDiff": {
4238
+ const cwd = this.requireWritableCwd(context);
4239
+ const path = typeof args?.path === "string" ? args.path : "";
4240
+ if (path.length === 0) {
4241
+ throw new UnsupportedByHostError('gitDiff needs a "path" argument.');
4242
+ }
4243
+ return readGitDiff(cwd, path);
4244
+ }
4091
4245
  default:
4092
4246
  throw new UnsupportedByHostError(`This host does not support "${command}".`);
4093
4247
  }