@tutti-os/workbench-surface 0.0.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.
@@ -0,0 +1,307 @@
1
+ import assert from "node:assert/strict";
2
+ import { readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import test from "node:test";
5
+
6
+ test("floating window bodies do not reserve a right-side content margin", () => {
7
+ const css = readFileSync(resolve("src/styles/workbench.css"), "utf8");
8
+
9
+ assert.doesNotMatch(
10
+ css,
11
+ /\.workbench-window\[data-display-mode="floating"\]\s+\.workbench-window__body\s*{[^}]*margin-right/s
12
+ );
13
+ });
14
+
15
+ test("left dock placement owns vertical frame and popup placement styles", () => {
16
+ const css = readFileSync(resolve("src/styles/workbench.css"), "utf8");
17
+
18
+ assert.match(
19
+ css,
20
+ /\.workbench-dock-frame\s*{[^}]*--workbench-left-dock-bottom-inset:\s*24px;[^}]*--workbench-left-dock-left-inset:\s*4px;[^}]*--workbench-left-dock-top-inset:\s*54px;/s
21
+ );
22
+ assert.match(
23
+ css,
24
+ /\.workbench-dock-frame\[data-dock-placement="left"\]\s*{[^}]*top:\s*var\(--workbench-left-dock-top-inset\);[^}]*bottom:\s*var\(--workbench-left-dock-bottom-inset\);[^}]*left:\s*var\(--workbench-left-dock-left-inset\);[^}]*align-items:\s*center;/s
25
+ );
26
+ assert.match(
27
+ css,
28
+ /\.desktop-dock\[data-dock-placement="left"\]\s+\.desktop-dock__items\s*{[^}]*align-items:\s*flex-start;[^}]*flex-direction:\s*column;/s
29
+ );
30
+ assert.match(
31
+ css,
32
+ /\.desktop-dock__hover-panel\[data-dock-placement="left"\]\s*{[^}]*left:\s*calc\(\s*var\(--desktop-dock-hover-panel-anchor-left\)\s*\+\s*var\(--desktop-dock-hover-panel-anchor-width\)\s*\+\s*var\(--desktop-dock-tooltip-gap, 32px\)\s*\);/s
33
+ );
34
+ assert.match(
35
+ css,
36
+ /\.desktop-dock-popup-root\s*{[^}]*z-index:\s*var\(--z-popover, 100200\);/s
37
+ );
38
+ assert.match(
39
+ css,
40
+ /body\[data-desktop-dock-minimized-stack-open="true"\]\s+\.workbench-dock-frame,\s*body\[data-desktop-dock-minimized-stack-open="true"\]\s+\.desktop-dock-plate,\s*body\[data-desktop-dock-minimized-stack-open="true"\]\s+\.desktop-dock\s*{[^}]*z-index:\s*0;/s
41
+ );
42
+ assert.match(
43
+ css,
44
+ /\.desktop-dock-popup-root\[data-dock-placement="bottom"\]\s*{[^}]*transform:\s*translate\(-50%, -100%\);/s
45
+ );
46
+ assert.match(
47
+ css,
48
+ /\.desktop-dock-popup\[data-popup-variant="minimized-stack"\]\s*>\s*div:nth-child\(2\)\s*{[^}]*overflow:\s*visible;/s
49
+ );
50
+ assert.match(
51
+ css,
52
+ /\.desktop-dock-popup-root\[data-dock-placement="left"\]\s*{[^}]*transform:\s*translate\(0, -50%\);/s
53
+ );
54
+ assert.match(
55
+ css,
56
+ /\.desktop-dock-popup-root\[data-dock-placement="left"\]\[data-popup-variant="minimized-stack"\]\s*{[^}]*z-index:\s*100300;[^}]*pointer-events:\s*none;[^}]*transform:\s*translate\(0, -100%\);/s
57
+ );
58
+ assert.match(
59
+ css,
60
+ /\.desktop-dock-popup-root\[data-dock-placement="left"\]\s+\.desktop-dock-popup__fan-title-tip\s*{[^}]*right:\s*auto;[^}]*left:\s*calc\(100% \+ 12px\);/s
61
+ );
62
+ assert.doesNotMatch(css, /desktop-dock-left-stack-card-fan-in/);
63
+ assert.match(
64
+ css,
65
+ /\.desktop-dock\[data-dock-placement="left"\]\s+\.desktop-dock__slot\[data-node-state="open"\]::before,[\s\S]*?\.desktop-dock\[data-dock-placement="left"\]\s+\.desktop-dock__slot\[data-node-state="minimized"\]::before\s*{[^}]*right:\s*auto;[^}]*left:\s*var\(--desktop-dock-indicator-offset\);/s
66
+ );
67
+ });
68
+
69
+ test("dock overflow keeps scroll controls viewport-bound", () => {
70
+ const css = readFileSync(resolve("src/styles/workbench.css"), "utf8");
71
+
72
+ assert.match(
73
+ css,
74
+ /\.desktop-dock-plate\s*{[^}]*--desktop-dock-safe-inline:\s*64px;/s
75
+ );
76
+ assert.match(
77
+ css,
78
+ /\.desktop-dock-plate\s*{[^}]*max-width:\s*calc\(100vw - var\(--desktop-dock-safe-inline\) \* 2\);/s
79
+ );
80
+ assert.match(
81
+ css,
82
+ /\.desktop-dock\s*{[^}]*max-width:\s*calc\(100vw - var\(--desktop-dock-safe-inline\) \* 2\);/s
83
+ );
84
+ assert.match(
85
+ css,
86
+ /\.desktop-dock-plate\s*{[^}]*--desktop-dock-viewport-size:\s*88px;/s
87
+ );
88
+ assert.match(
89
+ css,
90
+ /\.desktop-dock-plate\s*{[^}]*--desktop-dock-left-viewport-size:\s*104px;/s
91
+ );
92
+ assert.match(
93
+ css,
94
+ /\.desktop-dock-plate\s*{[^}]*--desktop-dock-icon-half-size:\s*21\.6px;[^}]*--desktop-dock-icon-center-block-end:\s*calc\(\s*var\(--desktop-dock-items-padding-block-end\)\s*\+\s*var\(--desktop-dock-icon-half-size\)\s*\);/s
95
+ );
96
+ assert.match(
97
+ css,
98
+ /\.desktop-dock-plate\s*{[^}]*--desktop-dock-separator-center-offset:\s*calc\(\s*var\(--desktop-dock-icon-half-size\)\s*-\s*var\(--desktop-dock-separator-length\)\s*\/\s*2\s*\);/s
99
+ );
100
+ assert.match(
101
+ css,
102
+ /\.desktop-dock-plate\s*{[^}]*--desktop-dock-plate-padding-block-end:\s*0px;[^}]*--desktop-dock-plate-padding-block-start:\s*21\.6px;/s
103
+ );
104
+ assert.match(
105
+ css,
106
+ /\.desktop-dock\s*{[^}]*height:\s*var\(--desktop-dock-viewport-size\);/s
107
+ );
108
+ assert.match(
109
+ css,
110
+ /\.desktop-dock\s*{[^}]*--desktop-dock-scroll-button-gap:\s*32px;/s
111
+ );
112
+ assert.match(
113
+ css,
114
+ /\.desktop-dock__hover-panel\s*{[^}]*top:\s*var\(--desktop-dock-hover-panel-anchor-top\);[^}]*pointer-events:\s*auto;/s
115
+ );
116
+ assert.match(
117
+ css,
118
+ /\.desktop-dock\[data-dock-pointer-active="true"\]:not\(\s*\[data-dock-hover-panel-open="true"\]\s*\)\s+\.desktop-dock__hover-panel\s*{[^}]*visibility:\s*hidden;[^}]*pointer-events:\s*none;/s
119
+ );
120
+ assert.match(
121
+ css,
122
+ /\.desktop-dock\s*{[^}]*--desktop-dock-scroll-button-z-index:\s*120;/s
123
+ );
124
+ assert.match(
125
+ css,
126
+ /\.desktop-dock\s*{[^}]*--desktop-dock-scroll-fade-size:\s*48px;/s
127
+ );
128
+ assert.match(
129
+ css,
130
+ /\.desktop-dock\[data-scroll-overflow="true"\]\s+\.desktop-dock__items\s*{[^}]*padding-right:\s*calc\(\s*var\(--desktop-dock-scroll-button-size\)\s*\+\s*var\(--desktop-dock-scroll-button-gap\)\s*\);[^}]*padding-left:\s*calc\(\s*var\(--desktop-dock-scroll-button-size\)\s*\+\s*var\(--desktop-dock-scroll-button-gap\)\s*\);/s
131
+ );
132
+ assert.match(
133
+ css,
134
+ /\.desktop-dock\[data-dock-placement="left"\]\s*{[^}]*width:\s*var\(--desktop-dock-left-viewport-size\);[^}]*max-height:\s*calc\(\s*100vh - var\(--workbench-left-dock-top-inset\) -\s*var\(--workbench-left-dock-bottom-inset\)\s*\);/s
135
+ );
136
+ assert.match(
137
+ css,
138
+ /\.desktop-dock__items\s*{[^}]*align-items:\s*flex-end;[^}]*overflow:\s*visible;[^}]*scrollbar-width:\s*none;/s
139
+ );
140
+ assert.match(
141
+ css,
142
+ /\.desktop-dock\[data-scroll-overflow="true"\]\s+\.desktop-dock__items\s*{[^}]*overflow-x:\s*auto;[^}]*overflow-y:\s*hidden;/s
143
+ );
144
+ assert.match(
145
+ css,
146
+ /\.desktop-dock-plate\s*{[^}]*--desktop-dock-left-indicator-gutter:\s*8px;/s
147
+ );
148
+ assert.match(
149
+ css,
150
+ /\.desktop-dock\[data-dock-placement="left"\]\s+\.desktop-dock__items\s*{[^}]*width:\s*var\(--desktop-dock-left-viewport-size\);[^}]*padding:\s*var\(--desktop-dock-left-items-padding-block\) 0\s*var\(--desktop-dock-left-items-padding-block\)\s*var\(--desktop-dock-left-indicator-gutter\);/s
151
+ );
152
+ assert.match(
153
+ css,
154
+ /\.desktop-dock\[data-dock-placement="left"\]\[data-scroll-overflow="true"\]\s+\.desktop-dock__items\s*{[^}]*overflow-x:\s*visible;[^}]*overflow-y:\s*auto;/s
155
+ );
156
+ assert.match(
157
+ css,
158
+ /\.desktop-dock\[data-dock-placement="left"\]\s+\.desktop-dock__icon-shell\s*{[^}]*transform-origin:\s*left center;/s
159
+ );
160
+ assert.match(
161
+ css,
162
+ /\.desktop-dock\[data-dock-placement="left"\]\s+\.desktop-dock__minimized-preview\s*{[^}]*transform-origin:\s*left center;/s
163
+ );
164
+ assert.match(css, /\.desktop-dock__slot\s*{[^}]*flex:\s*0 0 auto;/s);
165
+ assert.match(
166
+ css,
167
+ /\.desktop-dock__separator\s*{[^}]*flex:\s*0 0 auto;[^}]*margin:\s*0 4px var\(--desktop-dock-separator-center-offset\);[^}]*align-self:\s*flex-end;[^}]*background:\s*var\(--line-1\);/s
168
+ );
169
+ assert.match(
170
+ css,
171
+ /\.desktop-dock\[data-dock-placement="left"\]\s+\.desktop-dock__separator\s*{[^}]*align-self:\s*flex-start;[^}]*margin:\s*4px 0 4px var\(--desktop-dock-separator-center-offset\);/s
172
+ );
173
+ assert.match(
174
+ css,
175
+ /\.desktop-dock\[data-scroll-overflow="true"\]:hover\s+\.desktop-dock__scroll-button:not\(:disabled\)/s
176
+ );
177
+ assert.match(
178
+ css,
179
+ /\.desktop-dock\[data-dock-pointer-active="true"\]\s+\.desktop-dock__items\s*{[^}]*mask-image:\s*none;/s
180
+ );
181
+ assert.match(
182
+ css,
183
+ /\.desktop-dock\[data-dock-pointer-active="true"\]\s+\[data-radix-popper-content-wrapper\]\s*{[^}]*display:\s*none !important;/s
184
+ );
185
+ assert.match(
186
+ css,
187
+ /\.desktop-dock\[data-dock-pointer-active="true"\]:not\(\s*\[data-dock-hover-panel-open="true"\]\s*\)\s+\.desktop-dock__hover-panel\s*{[^}]*visibility:\s*hidden;/s
188
+ );
189
+ assert.match(
190
+ css,
191
+ /\.desktop-dock\[data-dock-hover-panel-open="true"\]\s+\.desktop-dock__items\s*{[^}]*mask-image:\s*none;/s
192
+ );
193
+ assert.match(css, /@keyframes desktop-dock-bounce-translate/s);
194
+ assert.match(
195
+ css,
196
+ /\.desktop-dock:not\(\[data-dock-pointer-active="true"\]\)\s+\.desktop-dock__btn\[data-interactive="true"\]:active\s*{[^}]*transform:\s*translateY\(-1px\) scale\(0\.99\);/s
197
+ );
198
+ assert.match(
199
+ css,
200
+ /\.desktop-dock\[data-dock-placement="left"\]:not\(\[data-dock-pointer-active="true"\]\)\s+\.desktop-dock__btn\[data-interactive="true"\]:active\s*{[^}]*transform:\s*translateX\(1px\) scale\(0\.99\);/s
201
+ );
202
+ assert.match(
203
+ css,
204
+ /\.desktop-dock\[data-dock-pointer-active="true"\]\s+\.desktop-dock__btn\s*{[^}]*transition:\s*none;/s
205
+ );
206
+ assert.match(
207
+ css,
208
+ /\.desktop-dock__slot--minimized\[data-presence="entering"\]\s*{[^}]*animation:\s*desktop-dock-minimized-slot-expand 360ms/s
209
+ );
210
+ assert.match(
211
+ css,
212
+ /\.desktop-dock\[data-dock-placement="left"\]\s+\.desktop-dock__slot--minimized\[data-presence="entering"\]\s*{[^}]*animation-name:\s*desktop-dock-minimized-slot-expand-left;/s
213
+ );
214
+ assert.match(
215
+ css,
216
+ /@keyframes desktop-dock-minimized-slot-expand\s*{[\s\S]*?width:\s*0;[\s\S]*?margin-inline:\s*calc\(var\(--desktop-dock-gap\) \/ -2\);[\s\S]*?width:\s*var\(--desktop-dock-size\);/
217
+ );
218
+ assert.match(
219
+ css,
220
+ /\.desktop-dock__slot--minimized\[data-collapsing="true"\],[\s\S]*?\.desktop-dock__slot--minimized\[data-presence="exiting"\]\s*{[^}]*animation:\s*desktop-dock-minimized-slot-collapse 360ms[^}]*pointer-events:\s*none;/s
221
+ );
222
+ assert.match(
223
+ css,
224
+ /\.desktop-dock\[data-dock-placement="left"\]\s+\.desktop-dock__slot--minimized\[data-collapsing="true"\],[\s\S]*?\.desktop-dock\[data-dock-placement="left"\]\s+\.desktop-dock__slot--minimized\[data-presence="exiting"\]\s*{[^}]*animation-name:\s*desktop-dock-minimized-slot-collapse-left;/s
225
+ );
226
+ assert.match(
227
+ css,
228
+ /@keyframes desktop-dock-minimized-slot-collapse\s*{[\s\S]*?width:\s*var\(--desktop-dock-collapse-inline-size, var\(--desktop-dock-size\)\);[\s\S]*?width:\s*0;[\s\S]*?margin-inline:\s*calc\(var\(--desktop-dock-gap\) \/ -2\);/
229
+ );
230
+ assert.match(
231
+ css,
232
+ /@keyframes desktop-dock-minimized-icon-disappear\s*{[\s\S]*?scale:\s*0\.08 0\.92;/
233
+ );
234
+ assert.match(
235
+ css,
236
+ /\.desktop-dock-popup\[data-popup-variant="minimized-stack"\]\s+\[data-desktop-dock-popup-card="true"\]\[data-launching="true"\]\s*{[^}]*opacity:\s*0;[^}]*scale:\s*0\.94;[^}]*pointer-events:\s*none;/s
237
+ );
238
+ assert.match(
239
+ css,
240
+ /\.desktop-dock__scroll-button--backward\s*{[^}]*top:\s*calc\(100% - var\(--desktop-dock-icon-center-block-end\)\);[^}]*left:\s*0;/s
241
+ );
242
+ assert.match(
243
+ css,
244
+ /\.desktop-dock__scroll-button--forward\s*{[^}]*top:\s*calc\(100% - var\(--desktop-dock-icon-center-block-end\)\);[^}]*right:\s*0;/s
245
+ );
246
+ assert.match(
247
+ css,
248
+ /\.desktop-dock__scroll-button\s*{[^}]*z-index:\s*var\(--desktop-dock-scroll-button-z-index\);/s
249
+ );
250
+ assert.match(
251
+ css,
252
+ /\.desktop-dock__scroll-button\s*{[^}]*background:\s*var\(--background-fronted\);/s
253
+ );
254
+ assert.match(
255
+ css,
256
+ /\.desktop-dock\[data-dock-placement="left"\]\s+\.desktop-dock__scroll-button\s*{[^}]*left:\s*calc\(\s*var\(--desktop-dock-left-indicator-gutter\)\s*\+\s*var\(--desktop-dock-icon-half-size\)\s*\);/s
257
+ );
258
+ assert.match(
259
+ css,
260
+ /\.desktop-dock\[data-scroll-forward="true"\]\s+\.desktop-dock__items\s*{[^}]*mask-image:\s*linear-gradient\(\s*to right,/s
261
+ );
262
+ assert.match(
263
+ css,
264
+ /\.desktop-dock\[data-scroll-backward="true"\]\[data-scroll-forward="true"\]\s+\.desktop-dock__items\s*{[^}]*transparent 0,[^}]*transparent 100%/s
265
+ );
266
+ assert.match(
267
+ css,
268
+ /\.desktop-dock\[data-dock-placement="left"\]\[data-scroll-forward="true"\]\s+\.desktop-dock__items\s*{[^}]*mask-image:\s*linear-gradient\(\s*to bottom,/s
269
+ );
270
+ });
271
+
272
+ test("launchpad top bar remains draggable without stealing controls", () => {
273
+ const css = readFileSync(resolve("src/styles/workbench.css"), "utf8");
274
+
275
+ assert.match(
276
+ css,
277
+ /\.workspace-launchpad-overlay__content\s*{[^}]*-webkit-app-region:\s*drag;/s
278
+ );
279
+ assert.match(
280
+ css,
281
+ /\.workspace-launchpad-overlay__topbar\s*{[^}]*-webkit-app-region:\s*drag;/s
282
+ );
283
+ assert.match(
284
+ css,
285
+ /\.workspace-launchpad-search\s*{[^}]*-webkit-app-region:\s*no-drag;/s
286
+ );
287
+ assert.match(
288
+ css,
289
+ /\.workspace-launchpad-grid-viewport\s*{[^}]*-webkit-app-region:\s*no-drag;/s
290
+ );
291
+ assert.match(
292
+ css,
293
+ /\.workspace-launchpad-grid-viewport\s*{[^}]*overflow:\s*visible;/s
294
+ );
295
+ assert.match(
296
+ css,
297
+ /\.workspace-launchpad-dock-hover-panel\s*{[^}]*--workspace-launchpad-hover-panel-shift:\s*0px;[^}]*left:\s*50%;[^}]*right:\s*auto;[^}]*max-width:\s*min\(240px, calc\(100vw - 32px\)\);[^}]*transform:\s*translateX\(\s*calc\(-50% \+ var\(--workspace-launchpad-hover-panel-shift\)\)\s*\)/s
298
+ );
299
+ assert.doesNotMatch(
300
+ css,
301
+ /workspace-launchpad-dock-hover-panel\[data-hover-panel-align=/
302
+ );
303
+ assert.match(
304
+ css,
305
+ /\.workspace-launchpad-pages\s*{[^}]*-webkit-app-region:\s*no-drag;/s
306
+ );
307
+ });
@@ -0,0 +1,27 @@
1
+ import { I18nRuntime, I18nDictionary } from '@tutti-os/ui-i18n-runtime';
2
+
3
+ type WorkbenchWindowChromeI18nLocale = "en" | "zh-CN";
4
+ declare const workbenchWindowChromeI18nNamespace = "workbenchWindowChrome";
5
+ declare const workbenchWindowChromeEn: {
6
+ readonly enterFullscreen: "Full Screen";
7
+ readonly exitFullscreen: "Exit Full Screen";
8
+ };
9
+ type WorkbenchWindowChromeI18nKey = keyof typeof workbenchWindowChromeEn;
10
+ type WorkbenchWindowChromeI18nRuntime = I18nRuntime<WorkbenchWindowChromeI18nKey>;
11
+ declare const workbenchWindowChromeI18nResources: Record<WorkbenchWindowChromeI18nLocale, I18nDictionary>;
12
+ declare function createWorkbenchWindowChromeI18nRuntime(runtime: I18nRuntime<string> | undefined): WorkbenchWindowChromeI18nRuntime;
13
+
14
+ declare const workbenchHostI18nNamespace = "workbenchHost";
15
+ type WorkbenchHostI18nKey = "actions.close" | "actions.minimize" | "closeWindow" | "dockLabel" | "launch" | "minimizedWindows" | "newWindow" | "scrollDockDown" | "scrollDockLeft" | "scrollDockRight" | "scrollDockUp" | "windowStatus.active" | "windowStatus.minimized" | "windowStatus.open";
16
+ type WorkbenchHostI18nRuntime = I18nRuntime<WorkbenchHostI18nKey>;
17
+ declare const workbenchHostI18nResources: {
18
+ readonly en: {
19
+ readonly workbenchHost: I18nDictionary;
20
+ };
21
+ readonly "zh-CN": {
22
+ readonly workbenchHost: I18nDictionary;
23
+ };
24
+ };
25
+ declare function createWorkbenchHostI18nRuntime(runtime: I18nRuntime<string> | undefined): WorkbenchHostI18nRuntime;
26
+
27
+ export { type WorkbenchHostI18nKey as W, type WorkbenchHostI18nRuntime as a, type WorkbenchWindowChromeI18nKey as b, type WorkbenchWindowChromeI18nRuntime as c, createWorkbenchHostI18nRuntime as d, createWorkbenchWindowChromeI18nRuntime as e, workbenchHostI18nResources as f, workbenchWindowChromeI18nNamespace as g, workbenchWindowChromeI18nResources as h, workbenchHostI18nNamespace as w };
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@tutti-os/workbench-surface",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "type": "module",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./i18n": {
13
+ "types": "./dist/i18n/index.d.ts",
14
+ "import": "./dist/i18n/index.js"
15
+ },
16
+ "./styles.css": "./dist/styles/workbench.css"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/tutti-os/tutti.git",
25
+ "directory": "packages/workbench/surface"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^24.0.1",
29
+ "@types/react": "^19.1.6",
30
+ "@types/react-dom": "^19.1.5",
31
+ "react": "^19.1.0",
32
+ "react-dom": "^19.1.0",
33
+ "typescript": "^5.8.3",
34
+ "@tutti-os/config-tsconfig": "0.0.0"
35
+ },
36
+ "dependencies": {
37
+ "@tutti-os/ui-i18n-runtime": "0.0.1",
38
+ "@tutti-os/ui-react-hooks": "0.0.1",
39
+ "@tutti-os/workbench-snapshot": "0.0.1",
40
+ "@tutti-os/ui-system": "0.0.1"
41
+ },
42
+ "peerDependencies": {
43
+ "react": "^19.1.0",
44
+ "react-dom": "^19.1.0"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "scripts": {
50
+ "build": "tsup --config tsup.config.ts && node ../../../tools/scripts/copy-package-assets.mjs src/styles dist/styles",
51
+ "test": "node --test --experimental-strip-types ./src/**/*.test.ts",
52
+ "typecheck": "tsc --noEmit -p tsconfig.json"
53
+ }
54
+ }