gentle-shell-bridge 0.3.0 → 0.3.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.
Files changed (75) hide show
  1. package/LICENSE +202 -0
  2. package/docs/contract.md +1 -1
  3. package/package.json +13 -1
  4. package/src/api.ts +1 -1
  5. package/.codegraph/.gitignore +0 -5
  6. package/PRD.md +0 -144
  7. package/examples/bash-card.ts +0 -1694
  8. package/gentle-shell-bridge-0.3.0.tgz +0 -0
  9. package/odd/tasks/palette-sections.md +0 -30
  10. package/odd/tasks/plugin-keymap.md +0 -32
  11. package/odd/tasks/plugin-surface-contract.md +0 -55
  12. package/test/api.test.ts +0 -395
  13. package/test/apply-on-move.test.ts +0 -477
  14. package/test/arm.test.ts +0 -220
  15. package/test/bridge-ready.test.ts +0 -309
  16. package/test/bus.test.ts +0 -165
  17. package/test/button-rows.test.ts +0 -409
  18. package/test/capture.test.ts +0 -356
  19. package/test/card-style-rows.test.ts +0 -510
  20. package/test/card-style-transform.test.ts +0 -379
  21. package/test/card-style-wiring.test.ts +0 -258
  22. package/test/card-styles-e2e.test.ts +0 -491
  23. package/test/card-styles.test.ts +0 -324
  24. package/test/card-transcript-path.test.ts +0 -178
  25. package/test/config.test.ts +0 -93
  26. package/test/e2e.test.ts +0 -443
  27. package/test/example-bash-identity.test.ts +0 -372
  28. package/test/example-bash-matrix.test.ts +0 -856
  29. package/test/example-designs.test.ts +0 -734
  30. package/test/example-neon-transform.test.ts +0 -283
  31. package/test/example-plain-style.test.ts +0 -1268
  32. package/test/extension.test.ts +0 -160
  33. package/test/fake-events.ts +0 -58
  34. package/test/fake-host.ts +0 -211
  35. package/test/fake-sidebar.ts +0 -516
  36. package/test/fake-transcript.ts +0 -486
  37. package/test/frame-telemetry.test.ts +0 -417
  38. package/test/hook.test.ts +0 -196
  39. package/test/manifest.test.ts +0 -105
  40. package/test/new-category.test.ts +0 -979
  41. package/test/notices.test.ts +0 -328
  42. package/test/plugin-integration-lines.test.ts +0 -979
  43. package/test/plugin-surface.test.ts +0 -152
  44. package/test/presence.test.ts +0 -459
  45. package/test/rail-surfaces.test.ts +0 -457
  46. package/test/rail-width.test.ts +0 -423
  47. package/test/regression.test.ts +0 -483
  48. package/test/repaint-api.test.ts +0 -200
  49. package/test/reserved-categories.test.ts +0 -361
  50. package/test/row-labels.test.ts +0 -284
  51. package/test/row-names.test.ts +0 -473
  52. package/test/row-origin.test.ts +0 -395
  53. package/test/section-interaction.test.ts +0 -364
  54. package/test/section-panel.test.ts +0 -403
  55. package/test/sections-rows.test.ts +0 -291
  56. package/test/sidebar-backend.test.ts +0 -471
  57. package/test/sidebar-e2e.test.ts +0 -331
  58. package/test/sidebar-hover.test.ts +0 -276
  59. package/test/sidebar-independent.test.ts +0 -349
  60. package/test/sidebar-render.test.ts +0 -281
  61. package/test/sidebar-sections.test.ts +0 -245
  62. package/test/status-card-styles.test.ts +0 -201
  63. package/test/status-section-frame.test.ts +0 -181
  64. package/test/status-sections.test.ts +0 -243
  65. package/test/status.test.ts +0 -209
  66. package/test/store.test.ts +0 -192
  67. package/test/surface-repaint-cost.test.ts +0 -520
  68. package/test/tool-design-chain.test.ts +0 -483
  69. package/test/tool-design-cost.test.ts +0 -473
  70. package/test/tool-design-rows.test.ts +0 -359
  71. package/test/tool-designs.test.ts +0 -745
  72. package/test/transform-pipeline.test.ts +0 -465
  73. package/test/transforms-bus.test.ts +0 -233
  74. package/test/verifier-width-regressions.test.ts +0 -164
  75. package/tsconfig.json +0 -18
@@ -1,164 +0,0 @@
1
- /**
2
- * The verifier's width findings, pinned (W2-T2–W2-T4, A-T5, C-T2/C-T3 adjacent).
3
- *
4
- * Each expectation below is the value pi-tui's own `visibleWidth` produces for that string
5
- * (`@earendil-works/pi-tui/dist/utils.js:126-268`, measured with the installed host copy), so this
6
- * file is a differential fixture with pinned numbers instead of a second implementation:
7
- *
8
- * - a cluster is charged the way `graphemeWidth` charges it: zero for a cluster of marks, two only
9
- * when `couldBeEmoji` AND `\p{RGI_Emoji}` both hold (so `©` is ONE column, `©️` is two), the base
10
- * code point's East Asian width otherwise, plus every trailing halfwidth/fullwidth form and the
11
- * Thai/Lao AM vowels;
12
- * - a row that already fits is byte-identical, escapes included, even at width zero;
13
- * - a cut never splits a grapheme cluster, even when an SGR sequence sits inside it.
14
- *
15
- * The README assertion at the end pins the exact footer string the host prints for a bridge row
16
- * (A5).
17
- */
18
-
19
- import assert from "node:assert/strict";
20
- import { readFileSync } from "node:fs";
21
- import test from "node:test";
22
-
23
- import { registerTransform, resetTransformsForTests } from "../src/bus.ts";
24
- import { clipToWidth, hostColumns } from "../src/card-style-transform.ts";
25
- import { applyTransforms, resetTransformPipelineForTests } from "../src/transform-pipeline.ts";
26
-
27
- test.beforeEach(() => {
28
- resetTransformsForTests();
29
- resetTransformPipelineForTests();
30
- });
31
-
32
- // ---------------------------------------------------------------------------------------------
33
- // The host's measure, cluster by cluster
34
- // ---------------------------------------------------------------------------------------------
35
-
36
- test("a symbol that is not an RGI emoji is one column; a qualified or keycap emoji is two", () => {
37
- for (const symbol of ["©", "®", "™", "☺"]) {
38
- assert.equal(hostColumns(symbol), 1, `${symbol} is not an RGI emoji: the host charges one column`);
39
- }
40
- assert.equal(hostColumns("©️"), 2, "the emoji presentation selector makes it an RGI emoji");
41
- assert.equal(hostColumns("1️⃣"), 2, "a keycap sequence is one two-column cluster");
42
- });
43
-
44
- test("a trailing halfwidth/fullwidth form is charged its own column", () => {
45
- assert.equal(hostColumns("ガ"), 2, "halfwidth KA plus the voiced sound mark");
46
- assert.equal(hostColumns("ガギ"), 4, "two voiced halfwidth kana are four columns");
47
- assert.equal(hostColumns("ア"), 1, "a bare halfwidth kana is one column");
48
- });
49
-
50
- test("a cluster of marks charges nothing, and a leading mark rides its base", () => {
51
- assert.equal(hostColumns("्"), 0, "a lone Devanagari virama occupies no cell");
52
- assert.equal(hostColumns("़क"), 1, "the leading nukta is stripped before measuring क");
53
- assert.equal(hostColumns("क्"), 1);
54
- assert.equal(hostColumns("e\u0301"), 1, "a combining acute rides its base");
55
- });
56
-
57
- test("a Devanagari spacing mark occupies its own cell, as the host allocates one", () => {
58
- assert.equal(hostColumns("ि"), 1, "U+093F is a Spacing_Mark: the host charges it one column");
59
- assert.equal(hostColumns("ा"), 1, "U+093E is a Spacing_Mark too");
60
- assert.equal(hostColumns("नमस्ते"), 4, "the host's own number for नमस्ते (pi-tui 1.5.x)");
61
- assert.equal(hostColumns("हिन्दी"), 5, "the host's own number for हिन्दी");
62
- });
63
-
64
- // ---------------------------------------------------------------------------------------------
65
- // W2-3: a consonant that follows a mark is charged, so it cannot overflow a narrower frame
66
- // ---------------------------------------------------------------------------------------------
67
-
68
- test("an Indic conjunct that follows a mark is two columns, and is never cut into one column", () => {
69
- // pi-tui 1.5.x charges the base (क) plus the visible consonant after the virama (ष): 2 columns.
70
- // Charging the cluster as its base alone made a "one column" frame hold two.
71
- assert.equal(hostColumns("क्‍ष"), 2, "the host charges the conjunct two columns");
72
- assert.equal(hostColumns("क्ष"), 2, "with or without the joiner");
73
- assert.equal(hostColumns("क्‍षा"), 3, "the trailing spacing mark adds its own cell");
74
-
75
- assert.equal(
76
- clipToWidth("क्‍षx", 1),
77
- "",
78
- "a two-column cluster cannot be emitted in a one-column frame",
79
- );
80
- assert.equal(clipToWidth("क्‍षx", 2), "क्‍ष", "at two columns the whole cluster fits");
81
- assert.equal(hostColumns(clipToWidth("क्‍षx", 2)), 2, "and the result measures exactly two");
82
- for (const width of [0, 1, 2, 3]) {
83
- assert.ok(
84
- hostColumns(clipToWidth("क्‍षx", width)) <= width,
85
- `the cut at ${width} never exceeds the frame`,
86
- );
87
- }
88
- });
89
-
90
- test("the pipeline clips an overflowing Indic conjunct instead of emitting it", () => {
91
- registerTransform({ id: "indic", transform: () => ["क्‍षx"] });
92
-
93
- assert.deepEqual(applyTransforms(["host"], 1), [""], "nothing wider than the frame reaches the host");
94
- assert.deepEqual(applyTransforms(["host"], 2), ["क्‍ष"]);
95
- });
96
-
97
- test("a Thai/Lao AM vowel adds a column, and an ignorable symbol adds none", () => {
98
- assert.equal(hostColumns("กำ"), 2, "the trailing AM vowel is charged one extra column");
99
- assert.equal(hostColumns("ເກຳ"), 3, "the same rule for Lao");
100
- assert.equal(hostColumns("\u00ad"), 0, "a soft hyphen is default-ignorable: no columns");
101
- assert.equal(hostColumns("👨‍👩‍👧‍👦"), 2, "a ZWJ family is one two-column cluster");
102
- assert.equal(hostColumns("👍🏽"), 2, "a skin-tone modifier rides its base");
103
- });
104
-
105
- // ---------------------------------------------------------------------------------------------
106
- // Fitting rows are byte-identical, at every width — including zero
107
- // ---------------------------------------------------------------------------------------------
108
-
109
- test("a row that fits at width zero keeps its bytes, escapes included", () => {
110
- const sgr = "\u001b[31m";
111
- const link = "\u001b]8;;http://example.com\u0007\u001b]8;;\u0007";
112
- const background = "\u001b[48;5;236m\u001b[0m";
113
-
114
- assert.equal(clipToWidth(sgr, 0), sgr, "an SGR-only row has no columns and must survive");
115
- assert.equal(clipToWidth(link, 0), link, "an empty hyperlink is the same empty hyperlink");
116
- assert.equal(clipToWidth(background, 0), background);
117
- assert.equal(clipToWidth("\u001b[31m\u001b[39m", 0), "\u001b[31m\u001b[39m");
118
- });
119
-
120
- test("a row with no columns is passed through, and a row with columns is emptied, at width zero", () => {
121
- assert.equal(clipToWidth("", 0), "");
122
- assert.equal(clipToWidth("A", 0), "", "a visible column cannot be drawn in zero columns");
123
- assert.equal(clipToWidth("\u001b[31mA", 0), "", "the paint alone is not a row fit to draw");
124
- });
125
-
126
- test("the pipeline returns an ANSI-only row untouched at width zero", () => {
127
- registerTransform({ id: "ansi-only", transform: () => ["\u001b[31m", "A"] });
128
-
129
- assert.deepEqual(applyTransforms(["host"], 0), ["\u001b[31m", ""], "only the visible column is cut");
130
- });
131
-
132
- // ---------------------------------------------------------------------------------------------
133
- // The cut never splits a cluster, even with SGR interleaved
134
- // ---------------------------------------------------------------------------------------------
135
-
136
- test("an SGR inside a grapheme cluster does not split it, and the kept paint is reset", () => {
137
- const row = "👨\u001b[31m‍👩‍👧‍👦AB";
138
- assert.equal(hostColumns(row), 4, "the host measures the family as one cluster plus A and B");
139
-
140
- const clipped = clipToWidth(row, 2);
141
-
142
- assert.equal(clipped, "👨\u001b[31m‍👩‍👧‍👦\u001b[0m", "the whole family is kept, then the paint is reset");
143
- assert.equal(clipped.split("\u200d").length - 1, 3, "every joiner of the family survived the cut");
144
- assert.equal(hostColumns(clipped), 2, "and it measures the host's width exactly");
145
- });
146
-
147
- test("the pipeline applies that cut to what a transform returns", () => {
148
- registerTransform({ id: "family", transform: () => ["👨\u001b[31m‍👩‍👧‍👦AB"] });
149
-
150
- assert.deepEqual(applyTransforms(["host"], 2), ["👨\u001b[31m‍👩‍👧‍👦\u001b[0m"]);
151
- });
152
-
153
- // ---------------------------------------------------------------------------------------------
154
- // A5 — the README states the host's exact footer
155
- // ---------------------------------------------------------------------------------------------
156
-
157
- test("the README states the host's exact footer for a focused bridge row (A5)", () => {
158
- const readme = readFileSync(new URL("../README.md", import.meta.url), "utf8");
159
-
160
- assert.ok(
161
- readme.includes("←/→ choose · Enter apply"),
162
- "the README must name the footer verbatim, as the host prints it",
163
- );
164
- });
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2024",
4
- "lib": ["ES2024"],
5
- "module": "nodenext",
6
- "moduleResolution": "nodenext",
7
- "types": ["node"],
8
- "strict": true,
9
- "noEmit": true,
10
- "allowImportingTsExtensions": true,
11
- "erasableSyntaxOnly": true,
12
- "verbatimModuleSyntax": true,
13
- "isolatedModules": true,
14
- "forceConsistentCasingInFileNames": true,
15
- "skipLibCheck": true
16
- },
17
- "include": ["src/**/*.ts", "test/**/*.ts"]
18
- }