@jxsuite/studio 0.19.0 → 0.20.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 (93) hide show
  1. package/dist/studio.css +98 -98
  2. package/dist/studio.js +12577 -12183
  3. package/dist/studio.js.map +146 -144
  4. package/dist/workers/editor.worker.js +79 -79
  5. package/dist/workers/json.worker.js +109 -109
  6. package/dist/workers/ts.worker.js +82 -82
  7. package/package.json +17 -6
  8. package/src/browse/browse.js +25 -19
  9. package/src/canvas/canvas-diff.js +24 -16
  10. package/src/canvas/canvas-helpers.js +24 -19
  11. package/src/canvas/canvas-live-render.js +94 -39
  12. package/src/canvas/canvas-render.js +119 -73
  13. package/src/canvas/canvas-utils.js +38 -15
  14. package/src/canvas/nested-site-style.js +50 -0
  15. package/src/editor/component-inline-edit.js +14 -13
  16. package/src/editor/content-inline-edit.js +22 -10
  17. package/src/editor/context-menu.js +1 -1
  18. package/src/editor/convert-to-component.js +12 -10
  19. package/src/editor/inline-edit.js +66 -28
  20. package/src/editor/insertion-helper.js +16 -12
  21. package/src/editor/shortcuts.js +4 -2
  22. package/src/editor/slash-menu.js +6 -4
  23. package/src/files/components.js +1 -1
  24. package/src/files/file-ops.js +37 -16
  25. package/src/files/files.js +78 -52
  26. package/src/github/github-auth.js +122 -0
  27. package/src/github/github-publish.js +115 -0
  28. package/src/markdown/md-convert.js +172 -107
  29. package/src/new-project/new-project-modal.js +204 -0
  30. package/src/panels/activity-bar.js +22 -20
  31. package/src/panels/ai-panel.js +399 -0
  32. package/src/panels/block-action-bar.js +5 -5
  33. package/src/panels/canvas-dnd.js +8 -2
  34. package/src/panels/data-explorer.js +19 -13
  35. package/src/panels/dnd.js +144 -92
  36. package/src/panels/editors.js +55 -35
  37. package/src/panels/elements-panel.js +33 -19
  38. package/src/panels/events-panel.js +15 -10
  39. package/src/panels/git-panel.js +469 -98
  40. package/src/panels/head-panel.js +121 -81
  41. package/src/panels/imports-panel.js +88 -64
  42. package/src/panels/layers-panel.js +64 -43
  43. package/src/panels/left-panel.js +85 -47
  44. package/src/panels/overlays.js +1 -0
  45. package/src/panels/panel-events.js +45 -30
  46. package/src/panels/preview-render.js +23 -18
  47. package/src/panels/properties-panel.js +365 -249
  48. package/src/panels/pseudo-preview.js +2 -2
  49. package/src/panels/quick-search.js +7 -5
  50. package/src/panels/right-panel.js +26 -2
  51. package/src/panels/shared.js +4 -4
  52. package/src/panels/signals-panel.js +295 -166
  53. package/src/panels/statusbar.js +4 -4
  54. package/src/panels/style-inputs.js +67 -35
  55. package/src/panels/style-panel.js +233 -132
  56. package/src/panels/style-utils.js +34 -16
  57. package/src/panels/stylebook-layers-panel.js +12 -10
  58. package/src/panels/stylebook-panel.js +134 -66
  59. package/src/panels/toolbar.js +333 -109
  60. package/src/panels/welcome-screen.js +121 -0
  61. package/src/platform.js +2 -4
  62. package/src/platforms/devserver.js +113 -7
  63. package/src/resize-edges.js +98 -0
  64. package/src/services/cem-export.js +12 -7
  65. package/src/services/code-services.js +30 -12
  66. package/src/settings/content-types-editor.js +7 -7
  67. package/src/settings/css-vars-editor.js +30 -24
  68. package/src/settings/defs-editor.js +12 -7
  69. package/src/settings/general-settings.js +85 -3
  70. package/src/settings/head-editor.js +30 -24
  71. package/src/settings/schema-field-ui.js +58 -39
  72. package/src/site-context.js +41 -31
  73. package/src/state.js +70 -29
  74. package/src/store.js +21 -26
  75. package/src/studio.js +103 -81
  76. package/src/tabs/tab.js +67 -43
  77. package/src/tabs/transact.js +23 -13
  78. package/src/ui/button-group.js +24 -24
  79. package/src/ui/color-selector.js +28 -23
  80. package/src/ui/field-row.js +3 -3
  81. package/src/ui/icons.js +2 -2
  82. package/src/ui/media-picker.js +7 -7
  83. package/src/ui/spectrum.js +5 -1
  84. package/src/ui/unit-selector.js +18 -16
  85. package/src/ui/value-selector.js +21 -15
  86. package/src/ui/widgets.js +22 -19
  87. package/src/utils/canvas-media.js +5 -4
  88. package/src/utils/edit-display.js +31 -20
  89. package/src/utils/google-fonts.js +11 -11
  90. package/src/utils/inherited-style.js +9 -8
  91. package/src/utils/studio-utils.js +7 -7
  92. package/src/view.js +58 -1
  93. package/src/workspace/workspace.js +10 -7
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Welcome screen — shown when no project is loaded and no document tabs are open. Mimics VS Code's
3
+ * welcome tab with start actions and recent projects.
4
+ */
5
+
6
+ import { html, render as litRender, nothing } from "lit-html";
7
+ import { getRecentProjects } from "../recent-projects.js";
8
+ import { platformSupportsClone } from "./git-panel.js";
9
+
10
+ /**
11
+ * @typedef {{
12
+ * openProject: () => void;
13
+ * openRecentProject: (root: string) => void;
14
+ * openNewProject: () => void;
15
+ * cloneRepository: () => void;
16
+ * }} WelcomeCtx
17
+ */
18
+
19
+ /** @type {WelcomeCtx | null} */
20
+ let _ctx = null;
21
+
22
+ /** @param {WelcomeCtx} ctx */
23
+ export function initWelcome(ctx) {
24
+ _ctx = ctx;
25
+ }
26
+
27
+ /** @param {HTMLElement} host */
28
+ export function renderWelcome(host) {
29
+ const ctx = /** @type {WelcomeCtx} */ (_ctx);
30
+ const recent = getRecentProjects();
31
+ const showClone = platformSupportsClone();
32
+
33
+ litRender(
34
+ html`
35
+ <div class="welcome-screen">
36
+ <div class="welcome-content">
37
+ <h1 class="welcome-title">Jx Studio</h1>
38
+ <p class="welcome-subtitle">Visual component builder</p>
39
+
40
+ <div class="welcome-section">
41
+ <h2 class="welcome-section-title">Start</h2>
42
+ <button class="welcome-action" @click=${() => ctx.openNewProject()}>
43
+ <svg
44
+ class="welcome-action-icon"
45
+ width="16"
46
+ height="16"
47
+ viewBox="0 0 16 16"
48
+ fill="currentColor"
49
+ >
50
+ <path
51
+ d="M9.5 1.1l3.4 3.5.1.4v2h-1V6H8.5L8 5.5V2H3.5l-.5.5v11l.5.5H6v1H3.5l-1.5-1.5v-11l1.5-1.5h5.7l.3.1zM9 2v3h2.9L9 2zm4 14h-1v-3H9v-1h3V9h1v3h3v1h-3v3z"
52
+ />
53
+ </svg>
54
+ New Project...
55
+ </button>
56
+ <button class="welcome-action" @click=${() => ctx.openProject()}>
57
+ <svg
58
+ class="welcome-action-icon"
59
+ width="16"
60
+ height="16"
61
+ viewBox="0 0 16 16"
62
+ fill="currentColor"
63
+ >
64
+ <path
65
+ d="M14.5 3H7.71l-.85-.85L6.51 2h-5l-.5.5v11l.5.5h13l.5-.5v-10L14.5 3zm-.51 8.49V13h-12V7h4.49l.35-.15.86-.86H14v5.5zM6.51 6l-.35.15-.86.86H2v-3h4.29l.85.85.36.15H14V6H6.51z"
66
+ />
67
+ </svg>
68
+ Open Project...
69
+ </button>
70
+ ${showClone
71
+ ? html`<button class="welcome-action" @click=${() => ctx.cloneRepository()}>
72
+ <svg
73
+ class="welcome-action-icon"
74
+ width="16"
75
+ height="16"
76
+ viewBox="0 0 16 16"
77
+ fill="currentColor"
78
+ >
79
+ <path
80
+ d="M4.7 12.3a4.2 4.2 0 0 1-1.2-1.7A4.4 4.4 0 0 1 3 8.9a4 4 0 0 1 .3-1.5 3.8 3.8 0 0 1 .8-1.3c.4-.4.8-.7 1.3-.9.5-.2 1-.4 1.6-.4V3l3.5 2.5L7 8V6.2c-.9.1-1.6.5-2.1 1.1a3 3 0 0 0-.8 2.1c0 .4.1.8.2 1.2.1.3.3.7.5 1l-.1.7zM11 6.9c.9-.1 1.6-.5 2.1-1.1a3 3 0 0 0 .8-2.1 3 3 0 0 0-.2-1.2 3.5 3.5 0 0 0-.5-1l.1-.7a4.2 4.2 0 0 1 1.2 1.7c.3.5.4 1.1.4 1.7a4 4 0 0 1-.3 1.5 3.8 3.8 0 0 1-.8 1.3c-.4.4-.8.7-1.3.9-.5.2-1 .4-1.6.4V11L7.5 8.5 11 6v.9z"
81
+ />
82
+ </svg>
83
+ Clone Git Repository...
84
+ </button>`
85
+ : nothing}
86
+ </div>
87
+
88
+ ${recent.length > 0
89
+ ? html`
90
+ <div class="welcome-section">
91
+ <h2 class="welcome-section-title">Recent</h2>
92
+ ${recent.map(
93
+ (p) => html`
94
+ <button
95
+ class="welcome-recent"
96
+ @click=${() => ctx.openRecentProject(p.root)}
97
+ title=${p.root}
98
+ >
99
+ <span class="welcome-recent-name">${p.name}</span>
100
+ <span class="welcome-recent-path">${shortenPath(p.root)}</span>
101
+ </button>
102
+ `,
103
+ )}
104
+ </div>
105
+ `
106
+ : nothing}
107
+ </div>
108
+ </div>
109
+ `,
110
+ host,
111
+ );
112
+ }
113
+
114
+ /** @param {string} path */
115
+ function shortenPath(path) {
116
+ if (path.startsWith("/home/")) {
117
+ const parts = path.split("/");
118
+ return "~/" + parts.slice(3).join("/");
119
+ }
120
+ return path;
121
+ }
package/src/platform.js CHANGED
@@ -11,10 +11,8 @@
11
11
  * See spec/desktop.md §3 for the full StudioPlatform interface.
12
12
  */
13
13
 
14
- /** @typedef {Record<string, any>} StudioPlatform */
15
-
16
- /** @type {any} */
17
- const g = globalThis;
14
+ /** @type {{ __jxPlatform?: StudioPlatform }} */
15
+ const g = /** @type {{ __jxPlatform?: StudioPlatform }} */ (/** @type {unknown} */ (globalThis));
18
16
 
19
17
  /** @param {StudioPlatform} platform */
20
18
  export function registerPlatform(platform) {
@@ -79,10 +79,13 @@ export function createDevServerPlatform() {
79
79
 
80
80
  let dirHandle;
81
81
  try {
82
- dirHandle = await /** @type {any} */ (window).showDirectoryPicker({ mode: "readwrite" });
83
- } catch (/** @type {any} */ e) {
82
+ dirHandle =
83
+ await /** @type {{ showDirectoryPicker(opts: { mode: string }): Promise<FileSystemDirectoryHandle> }} */ (
84
+ window
85
+ ).showDirectoryPicker({ mode: "readwrite" });
86
+ } catch (/** @type {unknown} */ e) {
84
87
  // User cancelled the picker
85
- if (e.name === "AbortError") return null;
88
+ if (/** @type {Error} */ (e).name === "AbortError") return null;
86
89
  throw e;
87
90
  }
88
91
 
@@ -102,7 +105,8 @@ export function createDevServerPlatform() {
102
105
  if (!sitesRes.ok) throw new Error("Failed to fetch site list from server");
103
106
  const sites = await sitesRes.json();
104
107
  const match = sites.find(
105
- /** @param {any} s */ (s) => JSON.stringify(s.config) === JSON.stringify(config),
108
+ /** @param {{ config: unknown; path: string }} s */ (s) =>
109
+ JSON.stringify(s.config) === JSON.stringify(config),
106
110
  );
107
111
 
108
112
  if (!match) {
@@ -149,6 +153,30 @@ export function createDevServerPlatform() {
149
153
  }
150
154
  },
151
155
 
156
+ // ─── Project creation ─────────────────────────────────────────────────
157
+
158
+ /**
159
+ * @param {{
160
+ * name: string;
161
+ * description?: string;
162
+ * url?: string;
163
+ * adapter?: string;
164
+ * directory: string;
165
+ * }} opts
166
+ */
167
+ async createProject(opts) {
168
+ const res = await fetch("/__studio/create-project", {
169
+ method: "POST",
170
+ headers: { "Content-Type": "application/json" },
171
+ body: JSON.stringify(opts),
172
+ });
173
+ if (!res.ok) {
174
+ const data = await res.json();
175
+ throw new Error(data.error || "Failed to create project");
176
+ }
177
+ return await res.json();
178
+ },
179
+
152
180
  // ─── File operations ──────────────────────────────────────────────────
153
181
 
154
182
  /** @param {string} dir */
@@ -270,7 +298,7 @@ export function createDevServerPlatform() {
270
298
 
271
299
  /**
272
300
  * @param {string} action
273
- * @param {any} payload
301
+ * @param {unknown} payload
274
302
  */
275
303
  async codeService(action, payload) {
276
304
  try {
@@ -399,8 +427,13 @@ export function createDevServerPlatform() {
399
427
  return await res.json();
400
428
  },
401
429
 
402
- async gitPush() {
403
- const res = await fetch("/__studio/git/push", { method: "POST" });
430
+ /** @param {{ setUpstream?: boolean }} [opts] */
431
+ async gitPush(opts) {
432
+ const res = await fetch("/__studio/git/push", {
433
+ method: "POST",
434
+ headers: { "Content-Type": "application/json" },
435
+ body: JSON.stringify(opts || {}),
436
+ });
404
437
  if (!res.ok) throw new Error((await res.json()).error);
405
438
  return await res.json();
406
439
  },
@@ -466,5 +499,78 @@ export function createDevServerPlatform() {
466
499
  if (!res.ok) throw new Error((await res.json()).error);
467
500
  return await res.json();
468
501
  },
502
+
503
+ /** @param {string} url */
504
+ async gitClone(url) {
505
+ const res = await fetch("/__studio/git/clone", {
506
+ method: "POST",
507
+ headers: { "Content-Type": "application/json" },
508
+ body: JSON.stringify({ url }),
509
+ });
510
+ if (!res.ok) throw new Error((await res.json()).error);
511
+ return await res.json();
512
+ },
513
+
514
+ async gitInit() {
515
+ const res = await fetch("/__studio/git/init", { method: "POST" });
516
+ if (!res.ok) throw new Error((await res.json()).error);
517
+ },
518
+
519
+ /**
520
+ * @param {string} name
521
+ * @param {string} url
522
+ */
523
+ async gitAddRemote(name, url) {
524
+ const res = await fetch("/__studio/git/add-remote", {
525
+ method: "POST",
526
+ headers: { "Content-Type": "application/json" },
527
+ body: JSON.stringify({ name, url }),
528
+ });
529
+ if (!res.ok) throw new Error((await res.json()).error);
530
+ },
531
+
532
+ // ─── AI Assistant ───────────────────────────────────
533
+
534
+ async aiAuthStatus() {
535
+ const res = await fetch("/__studio/ai/auth-status");
536
+ return await res.json();
537
+ },
538
+
539
+ /** @param {{ message: string; systemPrompt?: string }} opts */
540
+ async aiCreateSession(opts) {
541
+ const res = await fetch("/__studio/ai/session", {
542
+ method: "POST",
543
+ headers: { "Content-Type": "application/json" },
544
+ body: JSON.stringify(opts),
545
+ });
546
+ if (!res.ok) throw new Error((await res.json()).error);
547
+ return await res.json();
548
+ },
549
+
550
+ /** @param {string} id @param {string} message */
551
+ async aiSendMessage(id, message) {
552
+ const res = await fetch(`/__studio/ai/session/${id}/message`, {
553
+ method: "POST",
554
+ headers: { "Content-Type": "application/json" },
555
+ body: JSON.stringify({ message }),
556
+ });
557
+ if (!res.ok) throw new Error((await res.json()).error);
558
+ return await res.json();
559
+ },
560
+
561
+ /** @param {string} id */
562
+ aiStreamUrl(id) {
563
+ return `/__studio/ai/session/${id}/stream`;
564
+ },
565
+
566
+ /** @param {string} id */
567
+ async aiStopSession(id) {
568
+ await fetch(`/__studio/ai/session/${id}/stop`, { method: "POST" });
569
+ },
570
+
571
+ /** @param {string} id */
572
+ async aiDeleteSession(id) {
573
+ await fetch(`/__studio/ai/session/${id}`, { method: "DELETE" });
574
+ },
469
575
  };
470
576
  }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @type {{
3
+ * __jxPlatform?: {
4
+ * windowControls?: {
5
+ * getFrame: () => Promise<{ x: number; y: number; width: number; height: number }>;
6
+ * setFrame: (x: number, y: number, w: number, h: number) => void;
7
+ * };
8
+ * };
9
+ * }}
10
+ */
11
+ const g = /** @type {any} */ (globalThis);
12
+
13
+ const EDGES = [
14
+ "top",
15
+ "bottom",
16
+ "left",
17
+ "right",
18
+ "top-left",
19
+ "top-right",
20
+ "bottom-left",
21
+ "bottom-right",
22
+ ];
23
+
24
+ const MIN_WIDTH = 400;
25
+ const MIN_HEIGHT = 300;
26
+
27
+ let mounted = false;
28
+
29
+ export function mountResizeEdges() {
30
+ const wc = g.__jxPlatform?.windowControls;
31
+ if (!wc || !wc.getFrame || mounted) return;
32
+ mounted = true;
33
+
34
+ const container = document.createElement("div");
35
+ container.id = "resize-edges";
36
+
37
+ for (const edge of EDGES) {
38
+ const el = document.createElement("div");
39
+ el.className = `resize-edge ${edge}`;
40
+ el.addEventListener("mousedown", (e) => startResize(e, edge, wc));
41
+ container.appendChild(el);
42
+ }
43
+
44
+ document.body.appendChild(container);
45
+ }
46
+
47
+ /**
48
+ * @param {MouseEvent} e
49
+ * @param {string} edge
50
+ * @param {{
51
+ * getFrame: () => Promise<{ x: number; y: number; width: number; height: number }>;
52
+ * setFrame: (x: number, y: number, w: number, h: number) => void;
53
+ * }} wc
54
+ */
55
+ async function startResize(e, edge, wc) {
56
+ e.preventDefault();
57
+ e.stopPropagation();
58
+
59
+ const startX = e.screenX;
60
+ const startY = e.screenY;
61
+ const frame = await wc.getFrame();
62
+ const startFrame = { ...frame };
63
+
64
+ /** @param {MouseEvent} me */
65
+ function onMove(me) {
66
+ const dx = me.screenX - startX;
67
+ const dy = me.screenY - startY;
68
+
69
+ let { x, y, width, height } = startFrame;
70
+
71
+ if (edge.includes("right")) {
72
+ width = Math.max(MIN_WIDTH, width + dx);
73
+ }
74
+ if (edge.includes("left")) {
75
+ const newW = Math.max(MIN_WIDTH, width - dx);
76
+ x = x + (width - newW);
77
+ width = newW;
78
+ }
79
+ if (edge.includes("bottom")) {
80
+ height = Math.max(MIN_HEIGHT, height + dy);
81
+ }
82
+ if (edge.includes("top")) {
83
+ const newH = Math.max(MIN_HEIGHT, height - dy);
84
+ y = y + (height - newH);
85
+ height = newH;
86
+ }
87
+
88
+ wc.setFrame(x, y, width, height);
89
+ }
90
+
91
+ function onUp() {
92
+ document.removeEventListener("mousemove", onMove);
93
+ document.removeEventListener("mouseup", onUp);
94
+ }
95
+
96
+ document.addEventListener("mousemove", onMove);
97
+ document.addEventListener("mouseup", onUp);
98
+ }
@@ -1,21 +1,26 @@
1
1
  /** Collect slot elements from the document tree. */
2
- export function collectSlots(/** @type {any} */ node, /** @type {any} */ slots = []) {
2
+ export function collectSlots(
3
+ /** @type {JxMutableNode | null | undefined} */ node,
4
+ /** @type {string[]} */ slots = [],
5
+ ) {
3
6
  if (node?.tagName === "slot") {
4
7
  slots.push(node.attributes?.name || "");
5
8
  }
6
9
  if (Array.isArray(node?.children))
7
- node.children.forEach((/** @type {any} */ c) => collectSlots(c, slots));
10
+ node.children.forEach((/** @type {JxMutableNode | string} */ c) =>
11
+ collectSlots(typeof c === "string" ? undefined : c, slots),
12
+ );
8
13
  return slots;
9
14
  }
10
15
 
11
16
  /**
12
17
  * Generate and download a CEM 2.1.0 manifest for the current document.
13
18
  *
14
- * @param {any} S - Studio state
19
+ * @param {{ document: JxMutableNode }} S - Studio state
15
20
  * @param {{
16
- * defCategory: (d: any) => string;
17
- * normParam: (p: any) => any;
18
- * collectCssParts: (node: any) => any[];
21
+ * defCategory: (d: unknown) => string;
22
+ * normParam: (p: unknown) => unknown;
23
+ * collectCssParts: (node: JxMutableNode) => { name: string }[];
19
24
  * }} helpers
20
25
  */
21
26
  export function exportCemManifest(S, helpers) {
@@ -83,7 +88,7 @@ export function exportCemManifest(S, helpers) {
83
88
 
84
89
  // Slots
85
90
  const slotNames = collectSlots(doc);
86
- const slots = slotNames.map((/** @type {any} */ name) => ({
91
+ const slots = slotNames.map((/** @type {string} */ name) => ({
87
92
  name: name || "",
88
93
  ...(name ? {} : { description: "Default slot" }),
89
94
  }));
@@ -5,8 +5,8 @@ import { getNodeAtPath } from "../store.js";
5
5
  import * as monaco from "monaco-editor/esm/vs/editor/editor.api.js";
6
6
 
7
7
  /**
8
- * @param {any} action
9
- * @param {any} payload
8
+ * @param {string} action
9
+ * @param {unknown} payload
10
10
  */
11
11
  export async function codeService(action, payload) {
12
12
  const platform = getPlatform();
@@ -17,7 +17,7 @@ export async function codeService(action, payload) {
17
17
  /**
18
18
  * Ask the server to locate a document by filename within the project root.
19
19
  *
20
- * @param {any} name
20
+ * @param {string} name
21
21
  */
22
22
  export async function locateDocument(name) {
23
23
  const platform = getPlatform();
@@ -31,8 +31,8 @@ export const pluginSchemaCache = new Map();
31
31
  /**
32
32
  * Fetch and cache the schema for an external $prototype + $src module via the server.
33
33
  *
34
- * @param {any} def
35
- * @param {any} state
34
+ * @param {{ $src?: string; $prototype?: string }} def
35
+ * @param {{ documentPath?: string }} state
36
36
  */
37
37
  export async function fetchPluginSchema(def, state) {
38
38
  if (!def.$src || !def.$prototype) return null;
@@ -56,8 +56,19 @@ export async function fetchPluginSchema(def, state) {
56
56
  }
57
57
 
58
58
  /**
59
- * @param {any} editor
60
- * @param {any[]} diagnostics
59
+ * @typedef {{
60
+ * severity: string;
61
+ * message: string;
62
+ * help?: string;
63
+ * code?: string;
64
+ * url?: string;
65
+ * labels?: { span: { line: number; column: number; length?: number } }[];
66
+ * }} OxLintDiagnostic
67
+ */
68
+
69
+ /**
70
+ * @param {import("monaco-editor").editor.IStandaloneCodeEditor} editor
71
+ * @param {OxLintDiagnostic[]} diagnostics
61
72
  */
62
73
  export function setLintMarkers(editor, diagnostics) {
63
74
  const model = editor.getModel();
@@ -80,19 +91,26 @@ export function setLintMarkers(editor, diagnostics) {
80
91
  };
81
92
  })
82
93
  .filter(Boolean);
83
- monaco.editor.setModelMarkers(model, "oxlint", /** @type {any} */ (markers));
94
+ monaco.editor.setModelMarkers(
95
+ model,
96
+ "oxlint",
97
+ /** @type {import("monaco-editor").editor.IMarkerData[]} */ (markers),
98
+ );
84
99
  }
85
100
 
86
101
  /**
87
- * @param {any} editing
88
- * @param {any} document
102
+ * @param {{ type: string; defName?: string; path?: JxPath; eventKey?: string }} editing
103
+ * @param {JxMutableNode | null | undefined} document
89
104
  */
90
105
  export function getFunctionArgs(editing, document) {
91
106
  if (editing.type === "def") {
92
- return document?.state?.[editing.defName]?.parameters || ["state", "event"];
107
+ const defName = editing.defName;
108
+ return (defName && document?.state?.[defName]?.parameters) || ["state", "event"];
93
109
  } else if (editing.type === "event") {
110
+ if (!document || !editing.path) return ["state", "event"];
94
111
  const node = getNodeAtPath(document, editing.path);
95
- return node?.[editing.eventKey]?.parameters || ["state", "event"];
112
+ const eventKey = editing.eventKey;
113
+ return (eventKey && node?.[eventKey]?.parameters) || ["state", "event"];
96
114
  }
97
115
  return ["state", "event"];
98
116
  }
@@ -29,7 +29,7 @@ let newContentTypeName = "";
29
29
 
30
30
  async function saveProjectConfig() {
31
31
  const platform = getPlatform();
32
- const config = /** @type {any} */ (projectState).projectConfig;
32
+ const config = /** @type {{ projectConfig: ProjectConfig }} */ (projectState).projectConfig;
33
33
  await platform.writeFile("project.json", JSON.stringify(config, null, "\t"));
34
34
  }
35
35
 
@@ -138,7 +138,7 @@ function handleRenameField(oldName, newName, rerender) {
138
138
  const schema = getSelectedSchema();
139
139
  if (!schema?.properties || !newName || schema.properties[newName]) return;
140
140
 
141
- /** @type {Record<string, any>} */
141
+ /** @type {Record<string, unknown>} */
142
142
  const newProps = {};
143
143
  for (const [key, val] of Object.entries(schema.properties)) {
144
144
  newProps[key === oldName ? newName : key] = val;
@@ -276,7 +276,7 @@ function handleRenameNested(parentName, oldChild, newChild, rerender) {
276
276
  const parent = schema?.properties?.[parentName];
277
277
  if (!parent?.properties || !newChild || parent.properties[newChild]) return;
278
278
 
279
- /** @type {Record<string, any>} */
279
+ /** @type {Record<string, unknown>} */
280
280
  const newProps = {};
281
281
  for (const [key, val] of Object.entries(parent.properties)) {
282
282
  newProps[key === oldChild ? newChild : key] = val;
@@ -382,10 +382,10 @@ export function renderContentTypesEditor(container) {
382
382
  size="s"
383
383
  placeholder="content-type-name"
384
384
  .value=${newContentTypeName}
385
- @input=${(/** @type {any} */ e) => {
386
- newContentTypeName = e.target.value;
385
+ @input=${(/** @type {Event} */ e) => {
386
+ newContentTypeName = /** @type {HTMLInputElement} */ (e.target).value;
387
387
  }}
388
- @keydown=${(/** @type {any} */ e) => {
388
+ @keydown=${(/** @type {KeyboardEvent} */ e) => {
389
389
  if (e.key === "Enter") handleNewContentType(rerender);
390
390
  if (e.key === "Escape") {
391
391
  showNewContentType = false;
@@ -445,7 +445,7 @@ export function renderContentTypesEditor(container) {
445
445
  ([name, def]) =>
446
446
  fieldCardTpl(
447
447
  name,
448
- /** @type {any} */ (def),
448
+ /** @type {import("./schema-field-ui.js").SchemaProperty} */ (def),
449
449
  required.includes(name),
450
450
  handlers,
451
451
  contentTypeNames,