@young1lin/dsh-ui-gitworkbench 0.1.14 → 0.1.16

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 (43) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/CHANGELOG_EN.md +23 -0
  3. package/README.md +29 -3
  4. package/README_EN.md +1 -1
  5. package/lib/client.js +1512 -470
  6. package/lib/dir-listing.js +34 -0
  7. package/lib/fs-remove.js +5 -36
  8. package/lib/index.js +346 -117
  9. package/lib/path-lock.js +54 -0
  10. package/lib/repo-root.js +60 -0
  11. package/lib/worktree.js +83 -0
  12. package/lib/write-checked.js +1 -1
  13. package/package.json +5 -5
  14. package/src/client/ChromeGlyph.tsx +5 -0
  15. package/src/client/CodeEditor.tsx +19 -1
  16. package/src/client/DiffViews.tsx +116 -121
  17. package/src/client/FileBrowser.tsx +196 -23
  18. package/src/client/GitWorkbenchPanel.module.css +1 -0
  19. package/src/client/GitWorkbenchPanel.tsx +100 -12
  20. package/src/client/SideRails.tsx +106 -0
  21. package/src/client/diff-cells.tsx +147 -0
  22. package/src/client/diff-nav.ts +4 -1
  23. package/src/client/dir-tree.ts +31 -1
  24. package/src/client/file-rows.ts +40 -0
  25. package/src/client/h-rail.ts +70 -0
  26. package/src/client/ignored-cache.ts +193 -0
  27. package/src/client/index.ts +22 -3
  28. package/src/client/locales.ts +12 -2
  29. package/src/client/row-heights.ts +225 -0
  30. package/src/client/styles/changes.css +33 -2
  31. package/src/client/styles/controls.css +5 -0
  32. package/src/client/styles/files.css +5 -0
  33. package/src/client/styles/rails.css +72 -0
  34. package/src/client/use-row-window.ts +7 -3
  35. package/src/client/use-variable-row-window.ts +210 -0
  36. package/src/dir-listing.ts +47 -0
  37. package/src/fs-remove.ts +5 -36
  38. package/src/index.ts +372 -118
  39. package/src/path-lock.ts +56 -0
  40. package/src/repo-root.ts +66 -0
  41. package/src/types/dsh-shim.d.ts +12 -2
  42. package/src/worktree.ts +97 -0
  43. package/src/write-checked.ts +1 -1
@@ -0,0 +1,34 @@
1
+ /**
2
+ * One lazily listed directory, shaped for the wire.
3
+ *
4
+ * The Files tab browses ignored directories by reading them from the
5
+ * filesystem ONE level at a time — git cannot do this scoped: a pathspec
6
+ * under `--directory` still collapses the whole ignored directory to a
7
+ * single line, and dropping `--directory` would enumerate every file inside
8
+ * `node_modules` at once. A `readdir` is one level by construction, costs
9
+ * milliseconds, and says what a browser wants to know: what is HERE.
10
+ *
11
+ * Only the shaping is pure (ordering, capping); the read itself stays in
12
+ * `index.ts`, which vitest cannot load. Same split as `fs-remove.ts`.
13
+ *
14
+ * @module @young1lin/dsh-ui-gitworkbench/dir-listing
15
+ */
16
+ /** The most entries one expansion returns. A real directory level is a few
17
+ * hundred at most (`node_modules`'s own top level); a cap beyond that is a
18
+ * reported fuse against a pathological directory, not a working number. */
19
+ export const DIR_CHILD_CAP = 5_000;
20
+ /**
21
+ * Order and cap raw readdir results: directories before files (the shape
22
+ * every file tree has, matching `treeRows`), each run by name, and a cut
23
+ * REPORTED rather than silent.
24
+ *
25
+ * @param raw - the directory's entries with their best-known dir-ness
26
+ * (symlinks already resolved by the caller).
27
+ * @param cap - most entries to return.
28
+ */
29
+ export function shapeDirChildren(raw, cap = DIR_CHILD_CAP) {
30
+ const byName = (a, b) => a.name.localeCompare(b.name);
31
+ const ordered = [...raw.filter(entry => entry.dir).sort(byName), ...raw.filter(entry => !entry.dir).sort(byName)];
32
+ const truncated = ordered.length > cap;
33
+ return { entries: truncated ? ordered.slice(0, cap) : ordered, truncated };
34
+ }
package/lib/fs-remove.js CHANGED
@@ -6,8 +6,8 @@
6
6
  * `git clean` refuses paths it cannot index, which on Windows includes every
7
7
  * reserved device name (`nul`, `con`, `aux`, `com1`, and the same names with
8
8
  * any extension). So the removal goes through the filesystem, where git's own
9
- * refusal to leave the repository does not apply — hence the checks here
10
- * rather than a bare `rm`.
9
+ * refusal to leave the repository does not apply — hence the path lock in
10
+ * `path-lock.ts` rather than a bare `rm`.
11
11
  *
12
12
  * Lives outside `index.ts` so vitest can load it: the class there needs the
13
13
  * dsh runtime, and the property worth testing is "what does this delete, and
@@ -16,36 +16,7 @@
16
16
  * @module @young1lin/dsh-ui-gitworkbench/fs-remove
17
17
  */
18
18
  import { rm } from 'node:fs/promises';
19
- import { resolve, sep } from 'node:path';
20
- import { isSafeRelativePath } from './discard-ops.js';
21
- /**
22
- * Resolve a repo-relative path against the worktree root, refusing to leave it.
23
- *
24
- * The second lock rather than the only one: {@link isSafeRelativePath} already
25
- * rejected traversal spellings when the plan was made. This re-checks the
26
- * RESOLVED path, which is the form the filesystem acts on, so a path that
27
- * survives the first check by being spelled unusually still has to land inside
28
- * the root to be acted on.
29
- *
30
- * @param root - the worktree directory, absolute.
31
- * @param relative - repo-relative path from a plan step.
32
- * @returns the absolute path to act on.
33
- * @throws if the path is not a safe relative path, resolves outside the root,
34
- * or IS the root.
35
- */
36
- export function resolveInside(root, relative) {
37
- if (!isSafeRelativePath(relative)) {
38
- throw new Error(`unsafe path to delete: ${JSON.stringify(relative)}`);
39
- }
40
- const base = resolve(root);
41
- const target = resolve(base, relative);
42
- if (target === base)
43
- throw new Error('refusing to delete the worktree root');
44
- if (!target.startsWith(base + sep)) {
45
- throw new Error(`refusing to delete outside the worktree: ${JSON.stringify(relative)}`);
46
- }
47
- return target;
48
- }
19
+ import { resolveInside } from './path-lock.js';
49
20
  /**
50
21
  * Remove one entry from the worktree, having proven it is inside it.
51
22
  *
@@ -60,10 +31,8 @@ export function resolveInside(root, relative) {
60
31
  * `force` makes an absent entry a success: the reader asked for it to be gone,
61
32
  * and it is.
62
33
  *
63
- * A symlinked directory inside the worktree could still point outward; that is
64
- * a repository someone already has write access to, and resolving link targets
65
- * per segment on every delete would cost a stat per segment for a case git
66
- * itself does not defend against.
34
+ * A symlinked directory inside the worktree could still point outward the
35
+ * limit of a lexical resolve, stated where the lock is.
67
36
  *
68
37
  * @param root - the worktree directory, absolute.
69
38
  * @param relative - repo-relative path from a plan step.