@jxsuite/studio 0.23.2 → 0.25.0

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 (106) hide show
  1. package/dist/studio.js +2311 -718
  2. package/dist/studio.js.map +121 -104
  3. package/package.json +8 -7
  4. package/src/browse/{browse-modal.js → browse-modal.ts} +9 -10
  5. package/src/browse/{browse.js → browse.ts} +109 -84
  6. package/src/canvas/{canvas-diff.js → canvas-diff.ts} +42 -35
  7. package/src/canvas/{canvas-helpers.js → canvas-helpers.ts} +25 -19
  8. package/src/canvas/{canvas-live-render.js → canvas-live-render.ts} +53 -63
  9. package/src/canvas/{canvas-render.js → canvas-render.ts} +110 -121
  10. package/src/canvas/{canvas-utils.js → canvas-utils.ts} +48 -44
  11. package/src/canvas/{nested-site-style.js → nested-site-style.ts} +3 -3
  12. package/src/editor/{component-inline-edit.js → component-inline-edit.ts} +45 -33
  13. package/src/editor/{content-inline-edit.js → content-inline-edit.ts} +27 -45
  14. package/src/editor/{context-menu.js → context-menu.ts} +38 -36
  15. package/src/editor/{convert-targets.js → convert-targets.ts} +15 -12
  16. package/src/editor/{convert-to-component.js → convert-to-component.ts} +25 -26
  17. package/src/editor/{convert-to-repeater.js → convert-to-repeater.ts} +60 -40
  18. package/src/editor/{inline-edit.js → inline-edit.ts} +91 -55
  19. package/src/editor/{inline-format.js → inline-format.ts} +46 -51
  20. package/src/editor/{insertion-helper.js → insertion-helper.ts} +47 -63
  21. package/src/editor/{shortcuts.js → shortcuts.ts} +45 -31
  22. package/src/editor/{slash-menu.js → slash-menu.ts} +34 -35
  23. package/src/files/{components.js → components.ts} +12 -15
  24. package/src/files/{file-ops.js → file-ops.ts} +36 -29
  25. package/src/files/{files.js → files.ts} +102 -84
  26. package/src/github/{github-auth.js → github-auth.ts} +3 -3
  27. package/src/github/{github-publish.js → github-publish.ts} +20 -18
  28. package/src/markdown/{md-allowlist.js → md-allowlist.ts} +5 -2
  29. package/src/markdown/{md-convert.js → md-convert.ts} +146 -169
  30. package/src/new-project/{new-project-modal.js → new-project-modal.ts} +16 -18
  31. package/src/panels/{activity-bar.js → activity-bar.ts} +26 -34
  32. package/src/panels/{ai-panel.js → ai-panel.ts} +30 -30
  33. package/src/panels/{block-action-bar.js → block-action-bar.ts} +64 -73
  34. package/src/panels/{canvas-dnd.js → canvas-dnd.ts} +44 -49
  35. package/src/panels/{data-explorer.js → data-explorer.ts} +21 -15
  36. package/src/panels/dnd.ts +417 -0
  37. package/src/panels/{editors.js → editors.ts} +43 -50
  38. package/src/panels/{elements-panel.js → elements-panel.ts} +30 -27
  39. package/src/panels/{events-panel.js → events-panel.ts} +33 -25
  40. package/src/panels/{git-panel.js → git-panel.ts} +97 -83
  41. package/src/panels/{head-panel.js → head-panel.ts} +117 -139
  42. package/src/panels/{imports-panel.js → imports-panel.ts} +69 -89
  43. package/src/panels/{layers-panel.js → layers-panel.ts} +61 -57
  44. package/src/panels/{left-panel.js → left-panel.ts} +56 -63
  45. package/src/panels/{overlays.js → overlays.ts} +28 -36
  46. package/src/panels/{panel-events.js → panel-events.ts} +53 -51
  47. package/src/panels/{preview-render.js → preview-render.ts} +20 -15
  48. package/src/panels/{properties-panel.js → properties-panel.ts} +212 -246
  49. package/src/panels/{pseudo-preview.js → pseudo-preview.ts} +9 -8
  50. package/src/panels/{quick-search.js → quick-search.ts} +16 -17
  51. package/src/panels/{right-panel.js → right-panel.ts} +41 -59
  52. package/src/panels/{shared.js → shared.ts} +8 -6
  53. package/src/panels/{signals-panel.js → signals-panel.ts} +322 -362
  54. package/src/panels/{statusbar.js → statusbar.ts} +14 -14
  55. package/src/panels/{style-inputs.js → style-inputs.ts} +66 -48
  56. package/src/panels/{style-panel.js → style-panel.ts} +235 -290
  57. package/src/panels/{style-utils.js → style-utils.ts} +32 -31
  58. package/src/panels/{stylebook-layers-panel.js → stylebook-layers-panel.ts} +22 -14
  59. package/src/panels/{stylebook-panel.js → stylebook-panel.ts} +127 -140
  60. package/src/panels/{tab-strip.js → tab-strip.ts} +12 -14
  61. package/src/panels/{toolbar.js → toolbar.ts} +68 -70
  62. package/src/panels/{welcome-screen.js → welcome-screen.ts} +14 -16
  63. package/src/{platform.js → platform.ts} +5 -3
  64. package/src/platforms/{devserver.js → devserver.ts} +53 -44
  65. package/src/{recent-projects.js → recent-projects.ts} +17 -12
  66. package/src/{resize-edges.js → resize-edges.ts} +18 -13
  67. package/src/services/{cem-export.js → cem-export.ts} +14 -7
  68. package/src/services/{code-services.js → code-services.ts} +29 -20
  69. package/src/services/{monaco-setup.js → monaco-setup.ts} +2 -3
  70. package/src/settings/{content-types-editor.js → content-types-editor.ts} +88 -67
  71. package/src/settings/{css-vars-editor.js → css-vars-editor.ts} +78 -51
  72. package/src/settings/{defs-editor.js → defs-editor.ts} +70 -56
  73. package/src/settings/{general-settings.js → general-settings.ts} +29 -28
  74. package/src/settings/{head-editor.js → head-editor.ts} +37 -35
  75. package/src/settings/{schema-field-ui.js → schema-field-ui.ts} +86 -76
  76. package/src/settings/{settings-modal.js → settings-modal.ts} +14 -16
  77. package/src/{site-context.js → site-context.ts} +35 -32
  78. package/src/{state.js → state.ts} +95 -78
  79. package/src/{store.js → store.ts} +60 -63
  80. package/src/{studio.js → studio.ts} +117 -119
  81. package/src/tabs/tab.ts +196 -0
  82. package/src/tabs/{transact.js → transact.ts} +107 -60
  83. package/src/types.ts +185 -0
  84. package/src/ui/{button-group.js → button-group.ts} +21 -21
  85. package/src/ui/{color-selector.js → color-selector.ts} +44 -37
  86. package/src/ui/{expression-editor.js → expression-editor.ts} +65 -40
  87. package/src/ui/{field-row.js → field-row.ts} +19 -2
  88. package/src/ui/{icons.js → icons.ts} +2 -2
  89. package/src/ui/{layers.js → layers.ts} +33 -24
  90. package/src/ui/{media-picker.js → media-picker.ts} +13 -9
  91. package/src/ui/{panel-resize.js → panel-resize.ts} +9 -4
  92. package/src/ui/{spectrum.js → spectrum.ts} +4 -4
  93. package/src/ui/{unit-selector.js → unit-selector.ts} +16 -19
  94. package/src/ui/{value-selector.js → value-selector.ts} +28 -28
  95. package/src/ui/{widgets.js → widgets.ts} +62 -15
  96. package/src/utils/{canvas-media.js → canvas-media.ts} +29 -14
  97. package/src/utils/edit-display.ts +298 -0
  98. package/src/utils/{google-fonts.js → google-fonts.ts} +14 -13
  99. package/src/utils/{inherited-style.js → inherited-style.ts} +15 -10
  100. package/src/utils/{studio-utils.js → studio-utils.ts} +35 -23
  101. package/src/{view.js → view.ts} +53 -55
  102. package/src/workspace/{workspace.js → workspace.ts} +44 -33
  103. package/src/panels/dnd.js +0 -426
  104. package/src/tabs/tab.js +0 -192
  105. package/src/utils/edit-display.js +0 -203
  106. /package/src/{reactivity.js → reactivity.ts} +0 -0
@@ -1,54 +1,51 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Toolbar panel — extracted from studio.js renderToolbar(). Owns rendering of breadcrumbs, file
3
4
  * ops, feature toggles, and mode switcher.
4
5
  */
5
6
 
6
7
  import { html, render as litRender, nothing } from "lit-html";
7
- import { updateSession, updateUi } from "../store.js";
8
- import { undo as tabUndo, redo as tabRedo } from "../tabs/transact.js";
9
- import { effect, effectScope } from "../reactivity.js";
10
- import { activeTab } from "../workspace/workspace.js";
11
- import { getEffectiveMedia } from "../site-context.js";
12
- import { mediaDisplayName } from "./shared.js";
13
- import { view, applyPanelCollapse } from "../view.js";
14
- import { getRecentProjects } from "../recent-projects.js";
15
- import { openQuickSearch } from "./quick-search.js";
16
- import { getPlatform } from "../platform.js";
17
- import { refreshGitStatus } from "./git-panel.js";
18
- import { openBrowseModal } from "../browse/browse-modal.js";
19
- import { openNewProjectModal } from "../new-project/new-project-modal.js";
8
+ import { updateSession, updateUi } from "../store";
9
+ import { undo as tabUndo, redo as tabRedo } from "../tabs/transact";
10
+ import { effect, effectScope } from "../reactivity";
11
+ import { activeTab } from "../workspace/workspace";
12
+ import type { DocumentStackEntry } from "../types";
13
+ import { getEffectiveMedia } from "../site-context";
14
+ import { mediaDisplayName } from "./shared";
15
+ import { view, applyPanelCollapse } from "../view";
16
+ import { getRecentProjects } from "../recent-projects";
17
+ import { openQuickSearch } from "./quick-search";
18
+ import { getPlatform } from "../platform";
19
+ import { refreshGitStatus } from "./git-panel";
20
+ import { openBrowseModal } from "../browse/browse-modal";
21
+ import { openNewProjectModal } from "../new-project/new-project-modal";
20
22
 
21
- /** @type {HTMLElement | null} */
22
- let _rootEl = null;
23
+ interface ToolbarCtx {
24
+ navigateBack: () => void;
25
+ navigateToLevel: (level: number) => void;
26
+ openProject: () => void;
27
+ openFile?: (path: string) => void;
28
+ saveFile: () => void;
29
+ parseMediaEntries: (media: Record<string, string> | null | undefined) => {
30
+ sizeBreakpoints: { name: string; query: string; width: number; type: string }[];
31
+ featureQueries: { name: string; query: string }[];
32
+ baseWidth: number;
33
+ };
34
+ getCanvasMode: () => string;
35
+ setCanvasMode: (mode: string) => void;
36
+ renderCanvas: () => void;
37
+ safeRenderRightPanel: () => void;
38
+ openRecentProject: (root: string) => Promise<void>;
39
+ closeFunctionEditor: () => void;
40
+ }
23
41
 
24
- /**
25
- * @typedef {{
26
- * navigateBack: () => void;
27
- * navigateToLevel: (level: number) => void;
28
- * openProject: () => void;
29
- * openFile?: (path: string) => void;
30
- * saveFile: () => void;
31
- * parseMediaEntries: (media: Record<string, string> | null | undefined) => {
32
- * sizeBreakpoints: { name: string; query: string; width: number; type: string }[];
33
- * featureQueries: { name: string; query: string }[];
34
- * baseWidth: number;
35
- * };
36
- * getCanvasMode: () => string;
37
- * setCanvasMode: (mode: string) => void;
38
- * renderCanvas: () => void;
39
- * safeRenderRightPanel: () => void;
40
- * openRecentProject: (root: string) => Promise<void>;
41
- * closeFunctionEditor: () => void;
42
- * }} ToolbarCtx
43
- */
42
+ let _rootEl: HTMLElement | null = null;
44
43
 
45
- /** @type {ToolbarCtx | null} */
46
- let _ctx = null;
44
+ let _ctx: ToolbarCtx | null = null;
47
45
 
48
- /** @type {import("@vue/reactivity").EffectScope | null} */
49
- let _scope = null;
46
+ let _scope: import("@vue/reactivity").EffectScope | null = null;
50
47
 
51
- const toolbarIconMap = /** @type {Record<string, import("lit-html").TemplateResult>} */ ({
48
+ const toolbarIconMap = {
52
49
  "sp-icon-folder-open": html`<sp-icon-folder-open slot="icon"></sp-icon-folder-open>`,
53
50
  "sp-icon-save-floppy": html`<sp-icon-save-floppy slot="icon"></sp-icon-save-floppy>`,
54
51
  "sp-icon-back": html`<sp-icon-back slot="icon"></sp-icon-back>`,
@@ -64,14 +61,14 @@ const toolbarIconMap = /** @type {Record<string, import("lit-html").TemplateResu
64
61
  "sp-icon-view-list": html`<sp-icon-view-list slot="icon"></sp-icon-view-list>`,
65
62
  "sp-icon-gears": html`<sp-icon-gears slot="icon"></sp-icon-gears>`,
66
63
  "sp-icon-document": html`<sp-icon-document slot="icon"></sp-icon-document>`,
67
- });
64
+ } as Record<string, import("lit-html").TemplateResult>;
68
65
 
69
66
  /**
70
67
  * @param {string} label
71
- * @param {Function} onClick
68
+ * @param {() => void} onClick
72
69
  * @param {string} [iconTag]
73
70
  */
74
- function tbBtnTpl(label, onClick, iconTag) {
71
+ function tbBtnTpl(label: string, onClick: () => void, iconTag?: string) {
75
72
  return html`
76
73
  <sp-action-button size="s" @click=${onClick}>
77
74
  ${iconTag ? toolbarIconMap[iconTag] : nothing} ${label}
@@ -86,13 +83,12 @@ function tbBtnTpl(label, onClick, iconTag) {
86
83
  * @param {ToolbarCtx} ctx — { navigateBack, closeFunctionEditor, openProject, openFile, saveFile,
87
84
  * parseMediaEntries, getCanvasMode, setCanvasMode, renderCanvas, safeRenderRightPanel }
88
85
  */
89
- export function mount(rootEl, ctx) {
86
+ export function mount(rootEl: HTMLElement, ctx: ToolbarCtx) {
90
87
  _rootEl = rootEl;
91
88
  _ctx = ctx;
92
89
  if (
93
- /** @type {{ __jxPlatform?: { windowControls?: unknown } }} */ (
94
- /** @type {unknown} */ (globalThis)
95
- ).__jxPlatform?.windowControls
90
+ (globalThis as unknown as { __jxPlatform?: { windowControls?: unknown } }).__jxPlatform
91
+ ?.windowControls
96
92
  ) {
97
93
  rootEl.classList.add("electrobun-webkit-app-region-drag");
98
94
  }
@@ -143,7 +139,7 @@ async function handleNewProject() {
143
139
  }
144
140
 
145
141
  /** @param {ToolbarCtx} ctx */
146
- function minimalToolbarTemplate(ctx) {
142
+ function minimalToolbarTemplate(ctx: ToolbarCtx) {
147
143
  const recentProjects = getRecentProjects();
148
144
  const recentProjectsTpl = html`
149
145
  <overlay-trigger placement="bottom-start" triggered-by="click">
@@ -152,8 +148,8 @@ function minimalToolbarTemplate(ctx) {
152
148
  </sp-action-button>
153
149
  <sp-popover slot="click-content" tip>
154
150
  <sp-menu
155
- @change=${(/** @type {Event} */ e) => {
156
- const val = /** @type {HTMLInputElement} */ (/** @type {unknown} */ (e.target)).value;
151
+ @change=${(e: Event) => {
152
+ const val = (e.target as unknown as HTMLInputElement).value;
157
153
  if (val === "__new__") {
158
154
  handleNewProject();
159
155
  } else {
@@ -172,13 +168,13 @@ function minimalToolbarTemplate(ctx) {
172
168
  </overlay-trigger>
173
169
  `;
174
170
 
175
- const windowControls = /**
176
- * @type {{
177
- * __jxPlatform?: {
178
- * windowControls?: { minimize: () => void; maximize: () => void; close: () => void };
179
- * };
180
- * }}
181
- */ (/** @type {unknown} */ (globalThis)).__jxPlatform?.windowControls;
171
+ const windowControls = (
172
+ globalThis as unknown as {
173
+ __jxPlatform?: {
174
+ windowControls?: { minimize: () => void; maximize: () => void; close: () => void };
175
+ };
176
+ }
177
+ ).__jxPlatform?.windowControls;
182
178
  const csdTpl = windowControls
183
179
  ? html`
184
180
  <sp-action-group class="window-controls" size="s">
@@ -303,7 +299,7 @@ function toolbarTemplate() {
303
299
  ${toolbarIconMap["sp-icon-back"]}Back
304
300
  </sp-action-button>
305
301
  ${S.documentStack.map(
306
- (/** @type {DocumentStackEntry} */ frame, /** @type {number} */ i) => html`
302
+ (frame: DocumentStackEntry, i: number) => html`
307
303
  <span class="breadcrumb-item clickable" @click=${() => ctx.navigateToLevel(i)}
308
304
  >${frame.documentPath?.split("/").pop() || "untitled"}</span
309
305
  >
@@ -366,7 +362,9 @@ function toolbarTemplate() {
366
362
  view.panX = 0;
367
363
  view.panY = 0;
368
364
  /** @type {{ editingFunction: null; rightTab?: string }} */
369
- const uiPatch = { editingFunction: null };
365
+ const uiPatch: { editingFunction: null; rightTab?: string } = {
366
+ editingFunction: null,
367
+ };
370
368
  if (m.key === "stylebook") uiPatch.rightTab = "style";
371
369
  updateSession({ ui: uiPatch });
372
370
  ctx.renderCanvas();
@@ -380,13 +378,13 @@ function toolbarTemplate() {
380
378
  </sp-action-group>
381
379
  `;
382
380
 
383
- const windowControls = /**
384
- * @type {{
385
- * __jxPlatform?: {
386
- * windowControls?: { minimize: () => void; maximize: () => void; close: () => void };
387
- * };
388
- * }}
389
- */ (/** @type {unknown} */ (globalThis)).__jxPlatform?.windowControls;
381
+ const windowControls = (
382
+ globalThis as unknown as {
383
+ __jxPlatform?: {
384
+ windowControls?: { minimize: () => void; maximize: () => void; close: () => void };
385
+ };
386
+ }
387
+ ).__jxPlatform?.windowControls;
390
388
  const isMac = navigator.platform.startsWith("Mac");
391
389
  const csdTpl = windowControls
392
390
  ? isMac
@@ -462,8 +460,8 @@ function toolbarTemplate() {
462
460
  </sp-action-button>
463
461
  <sp-popover slot="click-content" tip>
464
462
  <sp-menu
465
- @change=${(/** @type {Event} */ e) => {
466
- const val = /** @type {HTMLInputElement} */ (/** @type {unknown} */ (e.target)).value;
463
+ @change=${(e: Event) => {
464
+ const val = (e.target as unknown as HTMLInputElement).value;
467
465
  if (val === "__new__") {
468
466
  handleNewProject();
469
467
  } else {
@@ -495,10 +493,10 @@ function toolbarTemplate() {
495
493
  ${toolbarIconMap["sp-icon-save-floppy"]} Save
496
494
  </sp-action-button>
497
495
  <sp-action-group compact size="s">
498
- <sp-action-button size="s" ?disabled=${!canUndo} @click=${() => tabUndo(activeTab.value)}>
496
+ <sp-action-button size="s" ?disabled=${!canUndo} @click=${() => tabUndo(activeTab.value!)}>
499
497
  ${toolbarIconMap["sp-icon-undo"]} Undo
500
498
  </sp-action-button>
501
- <sp-action-button size="s" ?disabled=${!canRedo} @click=${() => tabRedo(activeTab.value)}>
499
+ <sp-action-button size="s" ?disabled=${!canRedo} @click=${() => tabRedo(activeTab.value!)}>
502
500
  ${toolbarIconMap["sp-icon-redo"]} Redo
503
501
  </sp-action-button>
504
502
  </sp-action-group>
@@ -1,32 +1,30 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Welcome screen — shown when no project is loaded and no document tabs are open. Mimics VS Code's
3
4
  * welcome tab with start actions and recent projects.
4
5
  */
5
6
 
6
7
  import { html, render as litRender, nothing } from "lit-html";
7
- import { getRecentProjects } from "../recent-projects.js";
8
- import { platformSupportsClone } from "./git-panel.js";
8
+ import { getRecentProjects } from "../recent-projects";
9
+ import { platformSupportsClone } from "./git-panel";
9
10
 
10
- /**
11
- * @typedef {{
12
- * openProject: () => void;
13
- * openRecentProject: (root: string) => void;
14
- * openNewProject: () => void;
15
- * cloneRepository: () => void;
16
- * }} WelcomeCtx
17
- */
11
+ interface WelcomeCtx {
12
+ openProject: () => void;
13
+ openRecentProject: (root: string) => void;
14
+ openNewProject: () => void;
15
+ cloneRepository: () => void;
16
+ }
18
17
 
19
- /** @type {WelcomeCtx | null} */
20
- let _ctx = null;
18
+ let _ctx: WelcomeCtx | null = null;
21
19
 
22
20
  /** @param {WelcomeCtx} ctx */
23
- export function initWelcome(ctx) {
21
+ export function initWelcome(ctx: WelcomeCtx) {
24
22
  _ctx = ctx;
25
23
  }
26
24
 
27
25
  /** @param {HTMLElement} host */
28
- export function renderWelcome(host) {
29
- const ctx = /** @type {WelcomeCtx} */ (_ctx);
26
+ export function renderWelcome(host: HTMLElement) {
27
+ const ctx = _ctx as WelcomeCtx;
30
28
  const recent = getRecentProjects();
31
29
  const showClone = platformSupportsClone();
32
30
 
@@ -112,7 +110,7 @@ export function renderWelcome(host) {
112
110
  }
113
111
 
114
112
  /** @param {string} path */
115
- function shortenPath(path) {
113
+ function shortenPath(path: string) {
116
114
  if (path.startsWith("/home/")) {
117
115
  const parts = path.split("/");
118
116
  return "~/" + parts.slice(3).join("/");
@@ -1,3 +1,4 @@
1
+ /// <reference lib="dom" />
1
2
  /**
2
3
  * Platform.js — Platform Abstraction Layer (PAL)
3
4
  *
@@ -11,11 +12,12 @@
11
12
  * See spec/desktop.md §3 for the full StudioPlatform interface.
12
13
  */
13
14
 
14
- /** @type {{ __jxPlatform?: StudioPlatform }} */
15
- const g = /** @type {{ __jxPlatform?: StudioPlatform }} */ (/** @type {unknown} */ (globalThis));
15
+ import type { StudioPlatform } from "./types";
16
+
17
+ const g = globalThis as unknown as { __jxPlatform?: StudioPlatform };
16
18
 
17
19
  /** @param {StudioPlatform} platform */
18
- export function registerPlatform(platform) {
20
+ export function registerPlatform(platform: StudioPlatform) {
19
21
  g.__jxPlatform = platform;
20
22
  }
21
23
 
@@ -24,7 +24,7 @@ export function createDevServerPlatform() {
24
24
  *
25
25
  * @param {string} rel
26
26
  */
27
- function serverPath(rel) {
27
+ function serverPath(rel: string) {
28
28
  const r = rel.replaceAll("\\", "/");
29
29
  if (!_projectRoot) return r;
30
30
  if (r === ".") return _projectRoot;
@@ -36,7 +36,7 @@ export function createDevServerPlatform() {
36
36
  *
37
37
  * @param {string} path
38
38
  */
39
- function stripRoot(path) {
39
+ function stripRoot(path: string) {
40
40
  const p = path.replaceAll("\\", "/");
41
41
  if (!_projectRoot) return p;
42
42
  return p.startsWith(_projectRoot + "/") ? p.slice(_projectRoot.length + 1) : p;
@@ -60,7 +60,7 @@ export function createDevServerPlatform() {
60
60
  *
61
61
  * @param {string} [root]
62
62
  */
63
- async activate(root) {
63
+ async activate(root?: string) {
64
64
  const r = root ?? _projectRoot;
65
65
  await fetch("/__studio/activate", {
66
66
  method: "POST",
@@ -79,13 +79,14 @@ export function createDevServerPlatform() {
79
79
 
80
80
  let dirHandle;
81
81
  try {
82
- dirHandle =
83
- await /** @type {{ showDirectoryPicker(opts: { mode: string }): Promise<FileSystemDirectoryHandle> }} */ (
84
- window
85
- ).showDirectoryPicker({ mode: "readwrite" });
86
- } catch (/** @type {unknown} */ e) {
82
+ dirHandle = await (
83
+ window as unknown as {
84
+ showDirectoryPicker(opts: { mode: string }): Promise<FileSystemDirectoryHandle>;
85
+ }
86
+ ).showDirectoryPicker({ mode: "readwrite" });
87
+ } catch (e) {
87
88
  // User cancelled the picker
88
- if (/** @type {Error} */ (e).name === "AbortError") return null;
89
+ if ((e as Error).name === "AbortError") return null;
89
90
  throw e;
90
91
  }
91
92
 
@@ -105,8 +106,10 @@ export function createDevServerPlatform() {
105
106
  if (!sitesRes.ok) throw new Error("Failed to fetch site list from server");
106
107
  const sites = await sitesRes.json();
107
108
  const match = sites.find(
108
- /** @param {{ config: unknown; path: string }} s */ (s) =>
109
- JSON.stringify(s.config) === JSON.stringify(config),
109
+ /** @param {{ config: unknown; path: string }} s */ (s: {
110
+ config: unknown;
111
+ path: string;
112
+ }) => JSON.stringify(s.config) === JSON.stringify(config),
110
113
  );
111
114
 
112
115
  if (!match) {
@@ -164,7 +167,13 @@ export function createDevServerPlatform() {
164
167
  * directory: string;
165
168
  * }} opts
166
169
  */
167
- async createProject(opts) {
170
+ async createProject(opts: {
171
+ name: string;
172
+ description?: string;
173
+ url?: string;
174
+ adapter?: string;
175
+ directory: string;
176
+ }) {
168
177
  const res = await fetch("/__studio/create-project", {
169
178
  method: "POST",
170
179
  headers: { "Content-Type": "application/json" },
@@ -180,7 +189,7 @@ export function createDevServerPlatform() {
180
189
  // ─── File operations ──────────────────────────────────────────────────
181
190
 
182
191
  /** @param {string} dir */
183
- async listDirectory(dir) {
192
+ async listDirectory(dir: string) {
184
193
  const res = await fetch(`/__studio/files?dir=${encodeURIComponent(serverPath(dir))}`);
185
194
  if (!res.ok) throw new Error(`Failed to list directory: ${dir}`);
186
195
  const entries = await res.json();
@@ -189,7 +198,7 @@ export function createDevServerPlatform() {
189
198
  },
190
199
 
191
200
  /** @param {string} path */
192
- async readFile(path) {
201
+ async readFile(path: string) {
193
202
  const res = await fetch(`/__studio/file?path=${encodeURIComponent(serverPath(path))}`);
194
203
  if (!res.ok) throw new Error(`Failed to read file: ${path}`);
195
204
  const data = await res.json();
@@ -200,7 +209,7 @@ export function createDevServerPlatform() {
200
209
  * @param {string} path
201
210
  * @param {string} content
202
211
  */
203
- async writeFile(path, content) {
212
+ async writeFile(path: string, content: string) {
204
213
  const res = await fetch(`/__studio/file?path=${encodeURIComponent(serverPath(path))}`, {
205
214
  method: "PUT",
206
215
  body: content,
@@ -214,7 +223,7 @@ export function createDevServerPlatform() {
214
223
  * @param {string} path — project-relative destination path
215
224
  * @param {File | Blob | ArrayBuffer} data — file content
216
225
  */
217
- async uploadFile(path, data) {
226
+ async uploadFile(path: string, data: File | Blob | ArrayBuffer) {
218
227
  const res = await fetch(
219
228
  `/__studio/file/upload?path=${encodeURIComponent(serverPath(path))}`,
220
229
  { method: "POST", body: data },
@@ -224,7 +233,7 @@ export function createDevServerPlatform() {
224
233
  },
225
234
 
226
235
  /** @param {string} path */
227
- async deleteFile(path) {
236
+ async deleteFile(path: string) {
228
237
  const res = await fetch(`/__studio/file?path=${encodeURIComponent(serverPath(path))}`, {
229
238
  method: "DELETE",
230
239
  });
@@ -235,7 +244,7 @@ export function createDevServerPlatform() {
235
244
  * @param {string} from
236
245
  * @param {string} to
237
246
  */
238
- async renameFile(from, to) {
247
+ async renameFile(from: string, to: string) {
239
248
  const res = await fetch("/__studio/file/rename", {
240
249
  method: "POST",
241
250
  headers: { "Content-Type": "application/json" },
@@ -245,7 +254,7 @@ export function createDevServerPlatform() {
245
254
  },
246
255
 
247
256
  /** @param {string} _path */
248
- async createDirectory(_path) {
257
+ async createDirectory(_path: string) {
249
258
  // The server creates directories implicitly when writing files.
250
259
  // Write a placeholder and delete it, or rely on mkdir behavior.
251
260
  // For now, use the writeFile + delete approach if directory creation
@@ -255,7 +264,7 @@ export function createDevServerPlatform() {
255
264
  // ─── Component discovery ──────────────────────────────────────────────
256
265
 
257
266
  /** @param {string} dir */
258
- async discoverComponents(dir) {
267
+ async discoverComponents(dir: string) {
259
268
  const scanDir = dir || _projectRoot;
260
269
  if (!scanDir) return [];
261
270
  const url = `/__studio/components?dir=${encodeURIComponent(scanDir)}`;
@@ -267,7 +276,7 @@ export function createDevServerPlatform() {
267
276
  // ─── Package management ──────────────────────────────────────────────
268
277
 
269
278
  /** @param {string} name */
270
- async addPackage(name) {
279
+ async addPackage(name: string) {
271
280
  const res = await fetch("/__studio/packages/add", {
272
281
  method: "POST",
273
282
  headers: { "Content-Type": "application/json" },
@@ -278,7 +287,7 @@ export function createDevServerPlatform() {
278
287
  },
279
288
 
280
289
  /** @param {string} name */
281
- async removePackage(name) {
290
+ async removePackage(name: string) {
282
291
  const res = await fetch("/__studio/packages/remove", {
283
292
  method: "POST",
284
293
  headers: { "Content-Type": "application/json" },
@@ -300,7 +309,7 @@ export function createDevServerPlatform() {
300
309
  * @param {string} action
301
310
  * @param {unknown} payload
302
311
  */
303
- async codeService(action, payload) {
312
+ async codeService(action: string, payload: unknown) {
304
313
  try {
305
314
  const res = await fetch(`/__studio/code/${action}`, {
306
315
  method: "POST",
@@ -322,7 +331,7 @@ export function createDevServerPlatform() {
322
331
  *
323
332
  * @param {string} filePath — absolute system path
324
333
  */
325
- async resolveSiteContext(filePath) {
334
+ async resolveSiteContext(filePath: string) {
326
335
  const res = await fetch(`/__studio/resolve-site?path=${encodeURIComponent(filePath)}`);
327
336
  if (!res.ok) return { sitePath: null };
328
337
  return await res.json();
@@ -331,7 +340,7 @@ export function createDevServerPlatform() {
331
340
  // ─── File location ────────────────────────────────────────────────────
332
341
 
333
342
  /** @param {string} name */
334
- async locateFile(name) {
343
+ async locateFile(name: string) {
335
344
  try {
336
345
  const res = await fetch("/__studio/locate", {
337
346
  method: "POST",
@@ -344,7 +353,7 @@ export function createDevServerPlatform() {
344
353
  },
345
354
 
346
355
  /** @param {string} query */
347
- async searchFiles(query) {
356
+ async searchFiles(query: string) {
348
357
  const glob = `**/*${query}*.{json,md}`;
349
358
  const res = await fetch(
350
359
  `/__studio/files?dir=${encodeURIComponent(serverPath("."))}&glob=${encodeURIComponent(glob)}`,
@@ -362,7 +371,7 @@ export function createDevServerPlatform() {
362
371
  * @param {string} prototype
363
372
  * @param {string} base
364
373
  */
365
- async fetchPluginSchema(src, prototype, base) {
374
+ async fetchPluginSchema(src: string, prototype: string, base: string) {
366
375
  const params = new URLSearchParams({ src });
367
376
  if (prototype) params.set("prototype", prototype);
368
377
  if (base) params.set("base", base);
@@ -387,7 +396,7 @@ export function createDevServerPlatform() {
387
396
  },
388
397
 
389
398
  /** @param {number} [limit] */
390
- async gitLog(limit) {
399
+ async gitLog(limit?: number) {
391
400
  const q = limit ? `?limit=${limit}` : "";
392
401
  const res = await fetch(`/__studio/git/log${q}`);
393
402
  if (!res.ok) throw new Error(await res.text());
@@ -395,7 +404,7 @@ export function createDevServerPlatform() {
395
404
  },
396
405
 
397
406
  /** @param {string[]} files */
398
- async gitStage(files) {
407
+ async gitStage(files: string[]) {
399
408
  const res = await fetch("/__studio/git/stage", {
400
409
  method: "POST",
401
410
  headers: { "Content-Type": "application/json" },
@@ -406,7 +415,7 @@ export function createDevServerPlatform() {
406
415
  },
407
416
 
408
417
  /** @param {string[]} files */
409
- async gitUnstage(files) {
418
+ async gitUnstage(files: string[]) {
410
419
  const res = await fetch("/__studio/git/unstage", {
411
420
  method: "POST",
412
421
  headers: { "Content-Type": "application/json" },
@@ -417,7 +426,7 @@ export function createDevServerPlatform() {
417
426
  },
418
427
 
419
428
  /** @param {string} message */
420
- async gitCommit(message) {
429
+ async gitCommit(message: string) {
421
430
  const res = await fetch("/__studio/git/commit", {
422
431
  method: "POST",
423
432
  headers: { "Content-Type": "application/json" },
@@ -428,7 +437,7 @@ export function createDevServerPlatform() {
428
437
  },
429
438
 
430
439
  /** @param {{ setUpstream?: boolean }} [opts] */
431
- async gitPush(opts) {
440
+ async gitPush(opts?: { setUpstream?: boolean }) {
432
441
  const res = await fetch("/__studio/git/push", {
433
442
  method: "POST",
434
443
  headers: { "Content-Type": "application/json" },
@@ -451,7 +460,7 @@ export function createDevServerPlatform() {
451
460
  },
452
461
 
453
462
  /** @param {string} branch */
454
- async gitCheckout(branch) {
463
+ async gitCheckout(branch: string) {
455
464
  const res = await fetch("/__studio/git/checkout", {
456
465
  method: "POST",
457
466
  headers: { "Content-Type": "application/json" },
@@ -462,7 +471,7 @@ export function createDevServerPlatform() {
462
471
  },
463
472
 
464
473
  /** @param {string} name */
465
- async gitCreateBranch(name) {
474
+ async gitCreateBranch(name: string) {
466
475
  const res = await fetch("/__studio/git/create-branch", {
467
476
  method: "POST",
468
477
  headers: { "Content-Type": "application/json" },
@@ -473,14 +482,14 @@ export function createDevServerPlatform() {
473
482
  },
474
483
 
475
484
  /** @param {string} path */
476
- async gitDiff(path) {
485
+ async gitDiff(path: string) {
477
486
  const res = await fetch(`/__studio/git/diff?path=${encodeURIComponent(path)}`);
478
487
  if (!res.ok) throw new Error(await res.text());
479
488
  return await res.json();
480
489
  },
481
490
 
482
491
  /** @param {{ path: string; ref?: string }} opts */
483
- async gitShow(opts) {
492
+ async gitShow(opts: { path: string; ref?: string }) {
484
493
  const params = new URLSearchParams({ path: opts.path });
485
494
  if (opts.ref) params.set("ref", opts.ref);
486
495
  const res = await fetch(`/__studio/git/show?${params}`);
@@ -490,7 +499,7 @@ export function createDevServerPlatform() {
490
499
  },
491
500
 
492
501
  /** @param {string[]} files */
493
- async gitDiscard(files) {
502
+ async gitDiscard(files: string[]) {
494
503
  const res = await fetch("/__studio/git/discard", {
495
504
  method: "POST",
496
505
  headers: { "Content-Type": "application/json" },
@@ -501,7 +510,7 @@ export function createDevServerPlatform() {
501
510
  },
502
511
 
503
512
  /** @param {string} url */
504
- async gitClone(url) {
513
+ async gitClone(url: string) {
505
514
  const res = await fetch("/__studio/git/clone", {
506
515
  method: "POST",
507
516
  headers: { "Content-Type": "application/json" },
@@ -520,7 +529,7 @@ export function createDevServerPlatform() {
520
529
  * @param {string} name
521
530
  * @param {string} url
522
531
  */
523
- async gitAddRemote(name, url) {
532
+ async gitAddRemote(name: string, url: string) {
524
533
  const res = await fetch("/__studio/git/add-remote", {
525
534
  method: "POST",
526
535
  headers: { "Content-Type": "application/json" },
@@ -537,7 +546,7 @@ export function createDevServerPlatform() {
537
546
  },
538
547
 
539
548
  /** @param {{ message: string; systemPrompt?: string }} opts */
540
- async aiCreateSession(opts) {
549
+ async aiCreateSession(opts: { message: string; systemPrompt?: string }) {
541
550
  const res = await fetch("/__studio/ai/session", {
542
551
  method: "POST",
543
552
  headers: { "Content-Type": "application/json" },
@@ -548,7 +557,7 @@ export function createDevServerPlatform() {
548
557
  },
549
558
 
550
559
  /** @param {string} id @param {string} message */
551
- async aiSendMessage(id, message) {
560
+ async aiSendMessage(id: string, message: string) {
552
561
  const res = await fetch(`/__studio/ai/session/${id}/message`, {
553
562
  method: "POST",
554
563
  headers: { "Content-Type": "application/json" },
@@ -559,17 +568,17 @@ export function createDevServerPlatform() {
559
568
  },
560
569
 
561
570
  /** @param {string} id */
562
- aiStreamUrl(id) {
571
+ aiStreamUrl(id: string) {
563
572
  return `/__studio/ai/session/${id}/stream`;
564
573
  },
565
574
 
566
575
  /** @param {string} id */
567
- async aiStopSession(id) {
576
+ async aiStopSession(id: string) {
568
577
  await fetch(`/__studio/ai/session/${id}/stop`, { method: "POST" });
569
578
  },
570
579
 
571
580
  /** @param {string} id */
572
- async aiDeleteSession(id) {
581
+ async aiDeleteSession(id: string) {
573
582
  await fetch(`/__studio/ai/session/${id}`, { method: "DELETE" });
574
583
  },
575
584
  };