@swifttui/web 0.0.13 → 0.0.15
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/README.md +24 -10
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/manifest.d.ts +2 -0
- package/dist/manifest.js +2 -0
- package/dist/src/AccessibilityTree.js +156 -0
- package/dist/src/AccessibilityTree.js.map +1 -0
- package/dist/src/BoxDrawingRenderer.js +1106 -0
- package/dist/src/BoxDrawingRenderer.js.map +1 -0
- package/dist/src/WebHostApp.d.ts +41 -0
- package/dist/src/WebHostApp.js +135 -0
- package/dist/src/WebHostApp.js.map +1 -0
- package/dist/src/WebHostSceneManifest.d.ts +18 -0
- package/dist/src/WebHostSceneManifest.js +70 -0
- package/dist/src/WebHostSceneManifest.js.map +1 -0
- package/dist/src/WebHostSceneRuntime.d.ts +112 -0
- package/dist/src/WebHostSceneRuntime.js +651 -0
- package/dist/src/WebHostSceneRuntime.js.map +1 -0
- package/dist/src/WebHostSurfaceTransport.d.ts +166 -0
- package/dist/src/WebHostSurfaceTransport.js +252 -0
- package/dist/src/WebHostSurfaceTransport.js.map +1 -0
- package/dist/src/WebHostTerminalStyle.d.ts +92 -0
- package/dist/src/WebHostTerminalStyle.js +277 -0
- package/dist/src/WebHostTerminalStyle.js.map +1 -0
- package/dist/src/WebHostTestFixtures.d.ts +5 -0
- package/dist/src/WebHostTestFixtures.js +9 -0
- package/dist/src/WebHostTestFixtures.js.map +1 -0
- package/dist/src/WebSocketSceneBridge.d.ts +53 -0
- package/dist/src/WebSocketSceneBridge.js +124 -0
- package/dist/src/WebSocketSceneBridge.js.map +1 -0
- package/dist/src/wasi/BrowserWASIBridge.d.ts +33 -0
- package/dist/src/wasi/BrowserWASIBridge.js +97 -0
- package/dist/src/wasi/BrowserWASIBridge.js.map +1 -0
- package/dist/src/wasi/SharedInputQueue.d.ts +31 -0
- package/dist/src/wasi/SharedInputQueue.js +102 -0
- package/dist/src/wasi/SharedInputQueue.js.map +1 -0
- package/dist/src/wasi/StdIOPipe.d.ts +15 -0
- package/dist/src/wasi/StdIOPipe.js +56 -0
- package/dist/src/wasi/StdIOPipe.js.map +1 -0
- package/dist/src/wasi/WasiPollScheduler.js +114 -0
- package/dist/src/wasi/WasiPollScheduler.js.map +1 -0
- package/dist/src/wasi/WasmSceneRuntime.d.ts +23 -0
- package/dist/src/wasi/WasmSceneRuntime.js +119 -0
- package/dist/src/wasi/WasmSceneRuntime.js.map +1 -0
- package/dist/src/wasi/WasmSceneWorker.d.ts +27 -0
- package/dist/src/wasi/WasmSceneWorker.js +109 -0
- package/dist/src/wasi/WasmSceneWorker.js.map +1 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +2 -0
- package/dist/wasi-worker.d.ts +2 -0
- package/dist/wasi-worker.js +2 -0
- package/dist/wasi.d.ts +6 -0
- package/dist/wasi.js +6 -0
- package/dist/websocket.d.ts +2 -0
- package/dist/websocket.js +2 -0
- package/package.json +49 -18
- package/AGENTS.md +0 -52
- package/cli.ts +0 -168
- package/index.html +0 -50
- package/index.ts +0 -8
- package/manifest.ts +0 -1
- package/src/AccessibilityTree.ts +0 -262
- package/src/BoxDrawingRenderer.ts +0 -585
- package/src/PublicEntrypointBoundary.test.ts +0 -20
- package/src/WebHostApp.test.ts +0 -222
- package/src/WebHostApp.ts +0 -269
- package/src/WebHostSceneManifest.test.ts +0 -38
- package/src/WebHostSceneManifest.ts +0 -156
- package/src/WebHostSceneRuntime.test.ts +0 -1982
- package/src/WebHostSceneRuntime.ts +0 -1142
- package/src/WebHostSurfaceTransport.test.ts +0 -362
- package/src/WebHostSurfaceTransport.ts +0 -691
- package/src/WebHostTerminalStyle.test.ts +0 -123
- package/src/WebHostTerminalStyle.ts +0 -471
- package/src/WebHostTestFixtures.ts +0 -10
- package/src/WebSocketSceneBridge.test.ts +0 -198
- package/src/WebSocketSceneBridge.ts +0 -233
- package/src/browser.ts +0 -59
- package/src/wasi/BrowserWASIBridge.test.ts +0 -168
- package/src/wasi/BrowserWASIBridge.ts +0 -167
- package/src/wasi/SharedInputQueue.test.ts +0 -146
- package/src/wasi/SharedInputQueue.ts +0 -199
- package/src/wasi/StdIOPipe.ts +0 -72
- package/src/wasi/WasiPollScheduler.test.ts +0 -176
- package/src/wasi/WasiPollScheduler.ts +0 -305
- package/src/wasi/WasmSceneRuntime.ts +0 -205
- package/src/wasi/WasmSceneWorker.ts +0 -182
- package/testing.ts +0 -1
- package/tsconfig.json +0 -29
- package/wasi-worker.ts +0 -1
- package/wasi.ts +0 -4
- package/websocket.ts +0 -1
|
@@ -1,585 +0,0 @@
|
|
|
1
|
-
type LineWeight = 0 | 1 | 2 | 3;
|
|
2
|
-
type Direction = "north" | "east" | "south" | "west";
|
|
3
|
-
type Corner = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
|
|
4
|
-
type Spec = [north: LineWeight, east: LineWeight, south: LineWeight, west: LineWeight];
|
|
5
|
-
|
|
6
|
-
interface Rect {
|
|
7
|
-
x: number;
|
|
8
|
-
y: number;
|
|
9
|
-
width: number;
|
|
10
|
-
height: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface StrokeMetrics {
|
|
14
|
-
light: number;
|
|
15
|
-
heavy: number;
|
|
16
|
-
doubleGap: number;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface BoxDrawingCanvasContext {
|
|
20
|
-
fillRect(x: number, y: number, width: number, height: number): void;
|
|
21
|
-
beginPath(): void;
|
|
22
|
-
moveTo(x: number, y: number): void;
|
|
23
|
-
lineTo(x: number, y: number): void;
|
|
24
|
-
bezierCurveTo(
|
|
25
|
-
control1X: number,
|
|
26
|
-
control1Y: number,
|
|
27
|
-
control2X: number,
|
|
28
|
-
control2Y: number,
|
|
29
|
-
x: number,
|
|
30
|
-
y: number
|
|
31
|
-
): void;
|
|
32
|
-
stroke(): void;
|
|
33
|
-
setLineDash(lineDash: number[]): void;
|
|
34
|
-
lineWidth: number;
|
|
35
|
-
lineCap: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const none = 0;
|
|
39
|
-
const light = 1;
|
|
40
|
-
const heavy = 2;
|
|
41
|
-
const double = 3;
|
|
42
|
-
|
|
43
|
-
const lineSpecs: Record<number, Spec> = {
|
|
44
|
-
0x2500: [none, light, none, light],
|
|
45
|
-
0x2501: [none, heavy, none, heavy],
|
|
46
|
-
0x2502: [light, none, light, none],
|
|
47
|
-
0x2503: [heavy, none, heavy, none],
|
|
48
|
-
0x250c: [none, light, light, none],
|
|
49
|
-
0x250d: [none, heavy, light, none],
|
|
50
|
-
0x250e: [none, light, heavy, none],
|
|
51
|
-
0x250f: [none, heavy, heavy, none],
|
|
52
|
-
0x2510: [none, none, light, light],
|
|
53
|
-
0x2511: [none, none, light, heavy],
|
|
54
|
-
0x2512: [none, none, heavy, light],
|
|
55
|
-
0x2513: [none, none, heavy, heavy],
|
|
56
|
-
0x2514: [light, light, none, none],
|
|
57
|
-
0x2515: [light, heavy, none, none],
|
|
58
|
-
0x2516: [heavy, light, none, none],
|
|
59
|
-
0x2517: [heavy, heavy, none, none],
|
|
60
|
-
0x2518: [light, none, none, light],
|
|
61
|
-
0x2519: [light, none, none, heavy],
|
|
62
|
-
0x251a: [heavy, none, none, light],
|
|
63
|
-
0x251b: [heavy, none, none, heavy],
|
|
64
|
-
0x251c: [light, light, light, none],
|
|
65
|
-
0x251d: [light, heavy, light, none],
|
|
66
|
-
0x251e: [heavy, light, light, none],
|
|
67
|
-
0x251f: [light, light, heavy, none],
|
|
68
|
-
0x2520: [heavy, light, heavy, none],
|
|
69
|
-
0x2521: [heavy, heavy, light, none],
|
|
70
|
-
0x2522: [light, heavy, heavy, none],
|
|
71
|
-
0x2523: [heavy, heavy, heavy, none],
|
|
72
|
-
0x2524: [light, none, light, light],
|
|
73
|
-
0x2525: [light, none, light, heavy],
|
|
74
|
-
0x2526: [heavy, none, light, light],
|
|
75
|
-
0x2527: [light, none, heavy, light],
|
|
76
|
-
0x2528: [heavy, none, heavy, light],
|
|
77
|
-
0x2529: [heavy, none, light, heavy],
|
|
78
|
-
0x252a: [light, none, heavy, heavy],
|
|
79
|
-
0x252b: [heavy, none, heavy, heavy],
|
|
80
|
-
0x252c: [none, light, light, light],
|
|
81
|
-
0x252d: [none, light, light, heavy],
|
|
82
|
-
0x252e: [none, heavy, light, light],
|
|
83
|
-
0x252f: [none, heavy, light, heavy],
|
|
84
|
-
0x2530: [none, light, heavy, light],
|
|
85
|
-
0x2531: [none, light, heavy, heavy],
|
|
86
|
-
0x2532: [none, heavy, heavy, light],
|
|
87
|
-
0x2533: [none, heavy, heavy, heavy],
|
|
88
|
-
0x2534: [light, light, none, light],
|
|
89
|
-
0x2535: [light, light, none, heavy],
|
|
90
|
-
0x2536: [light, heavy, none, light],
|
|
91
|
-
0x2537: [light, heavy, none, heavy],
|
|
92
|
-
0x2538: [heavy, light, none, light],
|
|
93
|
-
0x2539: [heavy, light, none, heavy],
|
|
94
|
-
0x253a: [heavy, heavy, none, light],
|
|
95
|
-
0x253b: [heavy, heavy, none, heavy],
|
|
96
|
-
0x253c: [light, light, light, light],
|
|
97
|
-
0x253d: [light, light, light, heavy],
|
|
98
|
-
0x253e: [light, heavy, light, light],
|
|
99
|
-
0x253f: [light, heavy, light, heavy],
|
|
100
|
-
0x2540: [heavy, light, light, light],
|
|
101
|
-
0x2541: [light, light, heavy, light],
|
|
102
|
-
0x2542: [heavy, light, heavy, light],
|
|
103
|
-
0x2543: [heavy, light, light, heavy],
|
|
104
|
-
0x2544: [heavy, heavy, light, light],
|
|
105
|
-
0x2545: [light, light, heavy, heavy],
|
|
106
|
-
0x2546: [light, heavy, heavy, light],
|
|
107
|
-
0x2547: [heavy, heavy, light, heavy],
|
|
108
|
-
0x2548: [light, heavy, heavy, heavy],
|
|
109
|
-
0x2549: [heavy, light, heavy, heavy],
|
|
110
|
-
0x254a: [heavy, heavy, heavy, light],
|
|
111
|
-
0x254b: [heavy, heavy, heavy, heavy],
|
|
112
|
-
0x2550: [none, double, none, double],
|
|
113
|
-
0x2551: [double, none, double, none],
|
|
114
|
-
0x2552: [none, double, light, none],
|
|
115
|
-
0x2553: [none, light, double, none],
|
|
116
|
-
0x2554: [none, double, double, none],
|
|
117
|
-
0x2555: [none, none, light, double],
|
|
118
|
-
0x2556: [none, none, double, light],
|
|
119
|
-
0x2557: [none, none, double, double],
|
|
120
|
-
0x2558: [light, double, none, none],
|
|
121
|
-
0x2559: [double, light, none, none],
|
|
122
|
-
0x255a: [double, double, none, none],
|
|
123
|
-
0x255b: [light, none, none, double],
|
|
124
|
-
0x255c: [double, none, none, light],
|
|
125
|
-
0x255d: [double, none, none, double],
|
|
126
|
-
0x255e: [light, double, light, none],
|
|
127
|
-
0x255f: [double, light, double, none],
|
|
128
|
-
0x2560: [double, double, double, none],
|
|
129
|
-
0x2561: [light, none, light, double],
|
|
130
|
-
0x2562: [double, none, double, light],
|
|
131
|
-
0x2563: [double, none, double, double],
|
|
132
|
-
0x2564: [none, double, light, double],
|
|
133
|
-
0x2565: [none, light, double, light],
|
|
134
|
-
0x2566: [none, double, double, double],
|
|
135
|
-
0x2567: [light, double, none, double],
|
|
136
|
-
0x2568: [double, light, none, light],
|
|
137
|
-
0x2569: [double, double, none, double],
|
|
138
|
-
0x256a: [light, double, light, double],
|
|
139
|
-
0x256b: [double, light, double, light],
|
|
140
|
-
0x256c: [double, double, double, double],
|
|
141
|
-
0x2574: [none, none, none, light],
|
|
142
|
-
0x2575: [light, none, none, none],
|
|
143
|
-
0x2576: [none, light, none, none],
|
|
144
|
-
0x2577: [none, none, light, none],
|
|
145
|
-
0x2578: [none, none, none, heavy],
|
|
146
|
-
0x2579: [heavy, none, none, none],
|
|
147
|
-
0x257a: [none, heavy, none, none],
|
|
148
|
-
0x257b: [none, none, heavy, none],
|
|
149
|
-
0x257c: [none, heavy, none, light],
|
|
150
|
-
0x257d: [light, none, heavy, none],
|
|
151
|
-
0x257e: [none, light, none, heavy],
|
|
152
|
-
0x257f: [heavy, none, light, none],
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
export function canRenderBoxDrawing(
|
|
156
|
-
text: string
|
|
157
|
-
): boolean {
|
|
158
|
-
const codePoint = singleCodePoint(text);
|
|
159
|
-
if (codePoint === undefined) {
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
return (
|
|
163
|
-
(codePoint >= 0x2500 && codePoint <= 0x259f) ||
|
|
164
|
-
(codePoint >= 0x2800 && codePoint <= 0x28ff)
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function drawBoxDrawing(
|
|
169
|
-
context: BoxDrawingCanvasContext,
|
|
170
|
-
text: string,
|
|
171
|
-
rect: Rect
|
|
172
|
-
): boolean {
|
|
173
|
-
const codePoint = singleCodePoint(text);
|
|
174
|
-
if (codePoint === undefined) {
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (codePoint >= 0x2500 && codePoint <= 0x257f) {
|
|
179
|
-
return drawBoxDrawingCodePoint(context, codePoint, rect);
|
|
180
|
-
}
|
|
181
|
-
if (codePoint >= 0x2580 && codePoint <= 0x259f) {
|
|
182
|
-
return drawBlockElement(context, codePoint, rect);
|
|
183
|
-
}
|
|
184
|
-
if (codePoint >= 0x2800 && codePoint <= 0x28ff) {
|
|
185
|
-
return drawBraille(context, codePoint, rect);
|
|
186
|
-
}
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function singleCodePoint(
|
|
191
|
-
text: string
|
|
192
|
-
): number | undefined {
|
|
193
|
-
const characters = Array.from(text);
|
|
194
|
-
if (characters.length !== 1) {
|
|
195
|
-
return undefined;
|
|
196
|
-
}
|
|
197
|
-
return characters[0]?.codePointAt(0);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function drawBoxDrawingCodePoint(
|
|
201
|
-
context: BoxDrawingCanvasContext,
|
|
202
|
-
codePoint: number,
|
|
203
|
-
rect: Rect
|
|
204
|
-
): boolean {
|
|
205
|
-
const spec = lineSpecs[codePoint];
|
|
206
|
-
if (spec) {
|
|
207
|
-
drawCellLines(context, spec, rect);
|
|
208
|
-
return true;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
switch (codePoint) {
|
|
212
|
-
case 0x2504: drawDashedHorizontal(context, rect, light, 3); return true;
|
|
213
|
-
case 0x2505: drawDashedHorizontal(context, rect, heavy, 3); return true;
|
|
214
|
-
case 0x2506: drawDashedVertical(context, rect, light, 3); return true;
|
|
215
|
-
case 0x2507: drawDashedVertical(context, rect, heavy, 3); return true;
|
|
216
|
-
case 0x2508: drawDashedHorizontal(context, rect, light, 4); return true;
|
|
217
|
-
case 0x2509: drawDashedHorizontal(context, rect, heavy, 4); return true;
|
|
218
|
-
case 0x250a: drawDashedVertical(context, rect, light, 4); return true;
|
|
219
|
-
case 0x250b: drawDashedVertical(context, rect, heavy, 4); return true;
|
|
220
|
-
case 0x254c: drawDashedHorizontal(context, rect, light, 2); return true;
|
|
221
|
-
case 0x254d: drawDashedHorizontal(context, rect, heavy, 2); return true;
|
|
222
|
-
case 0x254e: drawDashedVertical(context, rect, light, 2); return true;
|
|
223
|
-
case 0x254f: drawDashedVertical(context, rect, heavy, 2); return true;
|
|
224
|
-
case 0x2571: drawDiagonal(context, rect, false); return true;
|
|
225
|
-
case 0x2572: drawDiagonal(context, rect, true); return true;
|
|
226
|
-
case 0x2573:
|
|
227
|
-
drawDiagonal(context, rect, false);
|
|
228
|
-
drawDiagonal(context, rect, true);
|
|
229
|
-
return true;
|
|
230
|
-
case 0x256d: drawArc(context, rect, "topLeft"); return true;
|
|
231
|
-
case 0x256e: drawArc(context, rect, "topRight"); return true;
|
|
232
|
-
case 0x256f: drawArc(context, rect, "bottomRight"); return true;
|
|
233
|
-
case 0x2570: drawArc(context, rect, "bottomLeft"); return true;
|
|
234
|
-
default:
|
|
235
|
-
return false;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function strokeMetrics(
|
|
240
|
-
rect: Rect
|
|
241
|
-
): StrokeMetrics {
|
|
242
|
-
const unit = Math.max(1, Math.round(Math.min(rect.width, rect.height) / 16));
|
|
243
|
-
return {
|
|
244
|
-
light: unit,
|
|
245
|
-
heavy: unit * 2,
|
|
246
|
-
doubleGap: unit,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function drawCellLines(
|
|
251
|
-
context: BoxDrawingCanvasContext,
|
|
252
|
-
spec: Spec,
|
|
253
|
-
rect: Rect
|
|
254
|
-
): void {
|
|
255
|
-
const metrics = strokeMetrics(rect);
|
|
256
|
-
const edges: Array<[LineWeight, Direction]> = [
|
|
257
|
-
[spec[0], "north"],
|
|
258
|
-
[spec[1], "east"],
|
|
259
|
-
[spec[2], "south"],
|
|
260
|
-
[spec[3], "west"],
|
|
261
|
-
];
|
|
262
|
-
for (const [weight, direction] of edges.sort((lhs, rhs) => lhs[0] - rhs[0])) {
|
|
263
|
-
drawHalfStroke(context, weight, direction, rect, metrics);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function drawHalfStroke(
|
|
268
|
-
context: BoxDrawingCanvasContext,
|
|
269
|
-
weight: LineWeight,
|
|
270
|
-
direction: Direction,
|
|
271
|
-
rect: Rect,
|
|
272
|
-
metrics: StrokeMetrics
|
|
273
|
-
): void {
|
|
274
|
-
if (weight === none) {
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const cx = rect.x + rect.width / 2;
|
|
279
|
-
const cy = rect.y + rect.height / 2;
|
|
280
|
-
const maxX = rect.x + rect.width;
|
|
281
|
-
const maxY = rect.y + rect.height;
|
|
282
|
-
|
|
283
|
-
const segment = (thickness: number, offset: number) => {
|
|
284
|
-
switch (direction) {
|
|
285
|
-
case "north":
|
|
286
|
-
context.fillRect(cx - thickness / 2 + offset, rect.y, thickness, cy - rect.y + thickness / 2);
|
|
287
|
-
break;
|
|
288
|
-
case "south":
|
|
289
|
-
context.fillRect(
|
|
290
|
-
cx - thickness / 2 + offset,
|
|
291
|
-
cy - thickness / 2,
|
|
292
|
-
thickness,
|
|
293
|
-
maxY - cy + thickness / 2
|
|
294
|
-
);
|
|
295
|
-
break;
|
|
296
|
-
case "west":
|
|
297
|
-
context.fillRect(rect.x, cy - thickness / 2 + offset, cx - rect.x + thickness / 2, thickness);
|
|
298
|
-
break;
|
|
299
|
-
case "east":
|
|
300
|
-
context.fillRect(
|
|
301
|
-
cx - thickness / 2,
|
|
302
|
-
cy - thickness / 2 + offset,
|
|
303
|
-
maxX - cx + thickness / 2,
|
|
304
|
-
thickness
|
|
305
|
-
);
|
|
306
|
-
break;
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
switch (weight) {
|
|
311
|
-
case light:
|
|
312
|
-
segment(metrics.light, 0);
|
|
313
|
-
break;
|
|
314
|
-
case heavy:
|
|
315
|
-
segment(metrics.heavy, 0);
|
|
316
|
-
break;
|
|
317
|
-
case double: {
|
|
318
|
-
const thickness = metrics.light;
|
|
319
|
-
const offset = (thickness + metrics.doubleGap) / 2;
|
|
320
|
-
segment(thickness, -offset);
|
|
321
|
-
segment(thickness, offset);
|
|
322
|
-
break;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function drawDashedHorizontal(
|
|
328
|
-
context: BoxDrawingCanvasContext,
|
|
329
|
-
rect: Rect,
|
|
330
|
-
weight: LineWeight,
|
|
331
|
-
segments: number
|
|
332
|
-
): void {
|
|
333
|
-
const metrics = strokeMetrics(rect);
|
|
334
|
-
const thickness = weight === heavy ? metrics.heavy : metrics.light;
|
|
335
|
-
const segmentWidth = rect.width / segments;
|
|
336
|
-
const dashWidth = segmentWidth * 0.55;
|
|
337
|
-
const gapWidth = segmentWidth - dashWidth;
|
|
338
|
-
const cy = rect.y + rect.height / 2;
|
|
339
|
-
for (let index = 0; index < segments; index += 1) {
|
|
340
|
-
const x = rect.x + index * segmentWidth + gapWidth / 2;
|
|
341
|
-
context.fillRect(x, cy - thickness / 2, dashWidth, thickness);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function drawDashedVertical(
|
|
346
|
-
context: BoxDrawingCanvasContext,
|
|
347
|
-
rect: Rect,
|
|
348
|
-
weight: LineWeight,
|
|
349
|
-
segments: number
|
|
350
|
-
): void {
|
|
351
|
-
const metrics = strokeMetrics(rect);
|
|
352
|
-
const thickness = weight === heavy ? metrics.heavy : metrics.light;
|
|
353
|
-
const segmentHeight = rect.height / segments;
|
|
354
|
-
const dashHeight = segmentHeight * 0.55;
|
|
355
|
-
const gapHeight = segmentHeight - dashHeight;
|
|
356
|
-
const cx = rect.x + rect.width / 2;
|
|
357
|
-
for (let index = 0; index < segments; index += 1) {
|
|
358
|
-
const y = rect.y + index * segmentHeight + gapHeight / 2;
|
|
359
|
-
context.fillRect(cx - thickness / 2, y, thickness, dashHeight);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function drawDiagonal(
|
|
364
|
-
context: BoxDrawingCanvasContext,
|
|
365
|
-
rect: Rect,
|
|
366
|
-
descending: boolean
|
|
367
|
-
): void {
|
|
368
|
-
const metrics = strokeMetrics(rect);
|
|
369
|
-
context.lineWidth = metrics.light;
|
|
370
|
-
context.lineCap = "square";
|
|
371
|
-
context.setLineDash([]);
|
|
372
|
-
context.beginPath();
|
|
373
|
-
if (descending) {
|
|
374
|
-
context.moveTo(rect.x, rect.y);
|
|
375
|
-
context.lineTo(rect.x + rect.width, rect.y + rect.height);
|
|
376
|
-
} else {
|
|
377
|
-
context.moveTo(rect.x + rect.width, rect.y);
|
|
378
|
-
context.lineTo(rect.x, rect.y + rect.height);
|
|
379
|
-
}
|
|
380
|
-
context.stroke();
|
|
381
|
-
context.lineCap = "butt";
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
function drawArc(
|
|
385
|
-
context: BoxDrawingCanvasContext,
|
|
386
|
-
rect: Rect,
|
|
387
|
-
corner: Corner
|
|
388
|
-
): void {
|
|
389
|
-
const metrics = strokeMetrics(rect);
|
|
390
|
-
const cx = rect.x + rect.width / 2;
|
|
391
|
-
const cy = rect.y + rect.height / 2;
|
|
392
|
-
const maxX = rect.x + rect.width;
|
|
393
|
-
const maxY = rect.y + rect.height;
|
|
394
|
-
const radius = Math.min(rect.width, rect.height) * 0.4;
|
|
395
|
-
const kappa = radius * 0.5523;
|
|
396
|
-
|
|
397
|
-
context.lineWidth = metrics.light;
|
|
398
|
-
context.lineCap = "butt";
|
|
399
|
-
context.setLineDash([]);
|
|
400
|
-
context.beginPath();
|
|
401
|
-
|
|
402
|
-
switch (corner) {
|
|
403
|
-
case "topLeft":
|
|
404
|
-
context.moveTo(cx, cy + radius);
|
|
405
|
-
context.lineTo(cx, maxY);
|
|
406
|
-
context.moveTo(cx + radius, cy);
|
|
407
|
-
context.lineTo(maxX, cy);
|
|
408
|
-
context.moveTo(cx, cy + radius);
|
|
409
|
-
context.bezierCurveTo(cx, cy + radius - kappa, cx + radius - kappa, cy, cx + radius, cy);
|
|
410
|
-
break;
|
|
411
|
-
case "topRight":
|
|
412
|
-
context.moveTo(cx, cy + radius);
|
|
413
|
-
context.lineTo(cx, maxY);
|
|
414
|
-
context.moveTo(cx - radius, cy);
|
|
415
|
-
context.lineTo(rect.x, cy);
|
|
416
|
-
context.moveTo(cx - radius, cy);
|
|
417
|
-
context.bezierCurveTo(cx - radius + kappa, cy, cx, cy + radius - kappa, cx, cy + radius);
|
|
418
|
-
break;
|
|
419
|
-
case "bottomRight":
|
|
420
|
-
context.moveTo(cx, cy - radius);
|
|
421
|
-
context.lineTo(cx, rect.y);
|
|
422
|
-
context.moveTo(cx - radius, cy);
|
|
423
|
-
context.lineTo(rect.x, cy);
|
|
424
|
-
context.moveTo(cx, cy - radius);
|
|
425
|
-
context.bezierCurveTo(cx, cy - radius + kappa, cx - radius + kappa, cy, cx - radius, cy);
|
|
426
|
-
break;
|
|
427
|
-
case "bottomLeft":
|
|
428
|
-
context.moveTo(cx, cy - radius);
|
|
429
|
-
context.lineTo(cx, rect.y);
|
|
430
|
-
context.moveTo(cx + radius, cy);
|
|
431
|
-
context.lineTo(maxX, cy);
|
|
432
|
-
context.moveTo(cx + radius, cy);
|
|
433
|
-
context.bezierCurveTo(cx + radius - kappa, cy, cx, cy - radius + kappa, cx, cy - radius);
|
|
434
|
-
break;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
context.stroke();
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function drawBlockElement(
|
|
441
|
-
context: BoxDrawingCanvasContext,
|
|
442
|
-
codePoint: number,
|
|
443
|
-
rect: Rect
|
|
444
|
-
): boolean {
|
|
445
|
-
const maxX = rect.x + rect.width;
|
|
446
|
-
const maxY = rect.y + rect.height;
|
|
447
|
-
|
|
448
|
-
const lowerEighths = (count: number) => {
|
|
449
|
-
const height = rect.height * count / 8;
|
|
450
|
-
context.fillRect(rect.x, maxY - height, rect.width, height);
|
|
451
|
-
};
|
|
452
|
-
const leftEighths = (count: number) => {
|
|
453
|
-
const width = rect.width * count / 8;
|
|
454
|
-
context.fillRect(rect.x, rect.y, width, rect.height);
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
switch (codePoint) {
|
|
458
|
-
case 0x2580: context.fillRect(rect.x, rect.y, rect.width, rect.height / 2); return true;
|
|
459
|
-
case 0x2581: lowerEighths(1); return true;
|
|
460
|
-
case 0x2582: lowerEighths(2); return true;
|
|
461
|
-
case 0x2583: lowerEighths(3); return true;
|
|
462
|
-
case 0x2584: lowerEighths(4); return true;
|
|
463
|
-
case 0x2585: lowerEighths(5); return true;
|
|
464
|
-
case 0x2586: lowerEighths(6); return true;
|
|
465
|
-
case 0x2587: lowerEighths(7); return true;
|
|
466
|
-
case 0x2588: context.fillRect(rect.x, rect.y, rect.width, rect.height); return true;
|
|
467
|
-
case 0x2589: leftEighths(7); return true;
|
|
468
|
-
case 0x258a: leftEighths(6); return true;
|
|
469
|
-
case 0x258b: leftEighths(5); return true;
|
|
470
|
-
case 0x258c: leftEighths(4); return true;
|
|
471
|
-
case 0x258d: leftEighths(3); return true;
|
|
472
|
-
case 0x258e: leftEighths(2); return true;
|
|
473
|
-
case 0x258f: leftEighths(1); return true;
|
|
474
|
-
case 0x2590:
|
|
475
|
-
context.fillRect(rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height);
|
|
476
|
-
return true;
|
|
477
|
-
case 0x2591: drawShade(context, rect, "light"); return true;
|
|
478
|
-
case 0x2592: drawShade(context, rect, "medium"); return true;
|
|
479
|
-
case 0x2593: drawShade(context, rect, "dark"); return true;
|
|
480
|
-
case 0x2594: context.fillRect(rect.x, rect.y, rect.width, rect.height / 8); return true;
|
|
481
|
-
case 0x2595:
|
|
482
|
-
context.fillRect(maxX - rect.width / 8, rect.y, rect.width / 8, rect.height);
|
|
483
|
-
return true;
|
|
484
|
-
case 0x2596: fillQuadrants(context, rect, ["bottomLeft"]); return true;
|
|
485
|
-
case 0x2597: fillQuadrants(context, rect, ["bottomRight"]); return true;
|
|
486
|
-
case 0x2598: fillQuadrants(context, rect, ["topLeft"]); return true;
|
|
487
|
-
case 0x2599: fillQuadrants(context, rect, ["topLeft", "bottomLeft", "bottomRight"]); return true;
|
|
488
|
-
case 0x259a: fillQuadrants(context, rect, ["topLeft", "bottomRight"]); return true;
|
|
489
|
-
case 0x259b: fillQuadrants(context, rect, ["topLeft", "topRight", "bottomLeft"]); return true;
|
|
490
|
-
case 0x259c: fillQuadrants(context, rect, ["topLeft", "topRight", "bottomRight"]); return true;
|
|
491
|
-
case 0x259d: fillQuadrants(context, rect, ["topRight"]); return true;
|
|
492
|
-
case 0x259e: fillQuadrants(context, rect, ["topRight", "bottomLeft"]); return true;
|
|
493
|
-
case 0x259f: fillQuadrants(context, rect, ["topRight", "bottomLeft", "bottomRight"]); return true;
|
|
494
|
-
default:
|
|
495
|
-
return false;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
function fillQuadrants(
|
|
500
|
-
context: BoxDrawingCanvasContext,
|
|
501
|
-
rect: Rect,
|
|
502
|
-
quadrants: Corner[]
|
|
503
|
-
): void {
|
|
504
|
-
const halfWidth = rect.width / 2;
|
|
505
|
-
const halfHeight = rect.height / 2;
|
|
506
|
-
for (const quadrant of quadrants) {
|
|
507
|
-
switch (quadrant) {
|
|
508
|
-
case "topLeft":
|
|
509
|
-
context.fillRect(rect.x, rect.y, halfWidth, halfHeight);
|
|
510
|
-
break;
|
|
511
|
-
case "topRight":
|
|
512
|
-
context.fillRect(rect.x + halfWidth, rect.y, halfWidth, halfHeight);
|
|
513
|
-
break;
|
|
514
|
-
case "bottomLeft":
|
|
515
|
-
context.fillRect(rect.x, rect.y + halfHeight, halfWidth, halfHeight);
|
|
516
|
-
break;
|
|
517
|
-
case "bottomRight":
|
|
518
|
-
context.fillRect(rect.x + halfWidth, rect.y + halfHeight, halfWidth, halfHeight);
|
|
519
|
-
break;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
function drawShade(
|
|
525
|
-
context: BoxDrawingCanvasContext,
|
|
526
|
-
rect: Rect,
|
|
527
|
-
density: "light" | "medium" | "dark"
|
|
528
|
-
): void {
|
|
529
|
-
const pixels = density === "light"
|
|
530
|
-
? [[0, 0]]
|
|
531
|
-
: density === "medium"
|
|
532
|
-
? [[0, 0], [1, 1]]
|
|
533
|
-
: [[0, 0], [1, 0], [0, 1]];
|
|
534
|
-
|
|
535
|
-
for (let y = rect.y; y < rect.y + rect.height; y += 2) {
|
|
536
|
-
for (let x = rect.x; x < rect.x + rect.width; x += 2) {
|
|
537
|
-
for (const [px, py] of pixels) {
|
|
538
|
-
const dotX = x + (px ?? 0);
|
|
539
|
-
const dotY = y + (py ?? 0);
|
|
540
|
-
if (dotX < rect.x + rect.width && dotY < rect.y + rect.height) {
|
|
541
|
-
context.fillRect(dotX, dotY, 1, 1);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// Bit -> (column, row) layout for the 2x4 braille mosaic. Mirrors
|
|
549
|
-
// `BrailleCell.bit(x:y:)` in `Sources/Core/BrailleCanvas.swift` so that
|
|
550
|
-
// every glyph emitted by `BrailleCanvas` round-trips visually. Each set
|
|
551
|
-
// bit fills its sub-cell rectangle solid (rather than drawing a font-style
|
|
552
|
-
// dot) so that adjacent set bits — both within and across cells — connect
|
|
553
|
-
// without visible mid-fill spacing, and a fully-set mask renders identical
|
|
554
|
-
// to U+2588 FULL BLOCK.
|
|
555
|
-
const brailleSubpixels: ReadonlyArray<readonly [bit: number, col: number, row: number]> = [
|
|
556
|
-
[0x01, 0, 0], [0x08, 1, 0],
|
|
557
|
-
[0x02, 0, 1], [0x10, 1, 1],
|
|
558
|
-
[0x04, 0, 2], [0x20, 1, 2],
|
|
559
|
-
[0x40, 0, 3], [0x80, 1, 3],
|
|
560
|
-
];
|
|
561
|
-
|
|
562
|
-
function drawBraille(
|
|
563
|
-
context: BoxDrawingCanvasContext,
|
|
564
|
-
codePoint: number,
|
|
565
|
-
rect: Rect
|
|
566
|
-
): boolean {
|
|
567
|
-
const mask = codePoint - 0x2800;
|
|
568
|
-
if (mask === 0) {
|
|
569
|
-
// U+2800 (BRAILLE PATTERN BLANK) renders as whitespace.
|
|
570
|
-
return true;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
const cellWidth = rect.width / 2;
|
|
574
|
-
const rowHeight = rect.height / 4;
|
|
575
|
-
|
|
576
|
-
for (const [bit, col, row] of brailleSubpixels) {
|
|
577
|
-
if ((mask & bit) === 0) {
|
|
578
|
-
continue;
|
|
579
|
-
}
|
|
580
|
-
const x = rect.x + col * cellWidth;
|
|
581
|
-
const y = rect.y + row * rowHeight;
|
|
582
|
-
context.fillRect(x, y, cellWidth, rowHeight);
|
|
583
|
-
}
|
|
584
|
-
return true;
|
|
585
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "bun:test";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
|
-
|
|
5
|
-
const runtimeEntrypoints = [
|
|
6
|
-
"index.ts",
|
|
7
|
-
"manifest.ts",
|
|
8
|
-
"testing.ts",
|
|
9
|
-
"wasi.ts",
|
|
10
|
-
"wasi-worker.ts",
|
|
11
|
-
"websocket.ts",
|
|
12
|
-
] as const;
|
|
13
|
-
|
|
14
|
-
test("public browser runtime entrypoints do not import build tooling", async () => {
|
|
15
|
-
for (const entrypoint of runtimeEntrypoints) {
|
|
16
|
-
const source = await readFile(resolve(import.meta.dir, "..", entrypoint), "utf8");
|
|
17
|
-
expect(source).not.toContain("@swifttui/build");
|
|
18
|
-
expect(source).not.toContain("node:");
|
|
19
|
-
}
|
|
20
|
-
});
|