@jxsuite/studio 0.37.1 → 1.1.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 (113) hide show
  1. package/dist/iframe-entry.js +622 -49
  2. package/dist/iframe-entry.js.map +15 -12
  3. package/dist/studio.css +1333 -0
  4. package/dist/studio.js +103709 -81001
  5. package/dist/studio.js.map +201 -157
  6. package/package.json +47 -45
  7. package/src/account-status.ts +39 -0
  8. package/src/browse/browse.ts +10 -14
  9. package/src/canvas/canvas-live-render.ts +21 -2
  10. package/src/canvas/canvas-render.ts +35 -16
  11. package/src/canvas/canvas-utils.ts +118 -72
  12. package/src/canvas/iframe-entry.ts +20 -0
  13. package/src/canvas/iframe-eval.ts +155 -0
  14. package/src/canvas/iframe-host.ts +132 -6
  15. package/src/canvas/iframe-inline-edit.ts +107 -1
  16. package/src/canvas/iframe-protocol.ts +43 -3
  17. package/src/canvas/iframe-render.ts +18 -0
  18. package/src/collab/collab-session.ts +23 -8
  19. package/src/collab/collab-state.ts +6 -3
  20. package/src/component-props.ts +104 -0
  21. package/src/editor/context-menu.ts +1 -1
  22. package/src/editor/inline-edit-apply.ts +36 -1
  23. package/src/editor/inline-edit.ts +58 -1
  24. package/src/editor/repeater-scope.ts +8 -13
  25. package/src/editor/shortcuts.ts +41 -12
  26. package/src/files/file-ops.ts +14 -0
  27. package/src/files/files.ts +56 -1
  28. package/src/format/format-host.ts +63 -1
  29. package/src/grid/cell-editors.ts +288 -0
  30. package/src/grid/cell-popovers.ts +108 -0
  31. package/src/grid/csv-codec.ts +122 -0
  32. package/src/grid/edit-buffer.ts +490 -0
  33. package/src/grid/grid-controller.ts +417 -0
  34. package/src/grid/grid-layout.ts +83 -0
  35. package/src/grid/grid-open.ts +167 -0
  36. package/src/grid/grid-panel.ts +302 -0
  37. package/src/grid/grid-source.ts +210 -0
  38. package/src/grid/grid-view.ts +367 -0
  39. package/src/grid/schema-columns.ts +261 -0
  40. package/src/grid/sources/connector-source.ts +217 -0
  41. package/src/grid/sources/content-source.ts +542 -0
  42. package/src/grid/sources/csv-file-source.ts +247 -0
  43. package/src/grid/tabulator-tables.d.ts +120 -0
  44. package/src/new-project/add-repo-modal.ts +183 -0
  45. package/src/new-project/new-project-modal.ts +22 -3
  46. package/src/page-params.ts +34 -8
  47. package/src/panels/ai-chat/chat-markdown.ts +2 -2
  48. package/src/panels/ai-panel.ts +61 -4
  49. package/src/panels/block-action-bar.ts +8 -3
  50. package/src/panels/chat-panel.ts +98 -0
  51. package/src/panels/data-grid.ts +241 -0
  52. package/src/panels/editors.ts +43 -17
  53. package/src/panels/events-panel.ts +137 -44
  54. package/src/panels/formula-workspace.ts +379 -0
  55. package/src/panels/frontmatter-fields.ts +231 -0
  56. package/src/panels/frontmatter-panel.ts +132 -0
  57. package/src/panels/git-panel.ts +1 -1
  58. package/src/panels/head-panel.ts +11 -175
  59. package/src/panels/properties-panel.ts +154 -144
  60. package/src/panels/right-panel.ts +9 -47
  61. package/src/panels/signals-panel.ts +217 -460
  62. package/src/panels/statement-editor.ts +710 -0
  63. package/src/panels/statusbar.ts +1 -1
  64. package/src/panels/style-panel.ts +25 -1
  65. package/src/panels/stylebook-panel.ts +0 -2
  66. package/src/panels/tab-bar.ts +175 -6
  67. package/src/panels/tab-strip.ts +62 -6
  68. package/src/panels/toolbar.ts +37 -3
  69. package/src/panels/welcome-screen.ts +50 -0
  70. package/src/platform-errors.ts +30 -0
  71. package/src/platforms/cloud.ts +172 -89
  72. package/src/platforms/devserver.ts +172 -0
  73. package/src/services/ai-project-tools.ts +541 -0
  74. package/src/services/ai-session-store.ts +28 -0
  75. package/src/services/ai-system-prompt.ts +194 -24
  76. package/src/services/ai-tools.ts +88 -21
  77. package/src/services/automation.ts +16 -0
  78. package/src/services/context-resolver.ts +73 -0
  79. package/src/services/data-service.ts +155 -0
  80. package/src/services/document-assistant.ts +81 -11
  81. package/src/services/gated-registry.ts +72 -0
  82. package/src/services/live-preview.ts +0 -0
  83. package/src/services/monaco-setup.ts +75 -26
  84. package/src/services/preview-eval.ts +68 -0
  85. package/src/services/project-adoption.ts +31 -0
  86. package/src/settings/contributed-section.ts +406 -0
  87. package/src/settings/extension-sections.ts +145 -0
  88. package/src/settings/schema-field-ui.ts +4 -2
  89. package/src/settings/settings-modal.ts +101 -42
  90. package/src/site-context.ts +12 -1
  91. package/src/store.ts +4 -0
  92. package/src/studio.ts +107 -52
  93. package/src/tabs/patch-ops.ts +25 -0
  94. package/src/tabs/tab.ts +39 -1
  95. package/src/tabs/transact.ts +61 -14
  96. package/src/types.ts +132 -1
  97. package/src/ui/dynamic-slot.ts +272 -0
  98. package/src/ui/expression-editor.ts +423 -125
  99. package/src/ui/field-row.ts +5 -0
  100. package/src/ui/form-controls.ts +322 -0
  101. package/src/ui/formula-catalog.ts +557 -0
  102. package/src/ui/formula-chips.ts +216 -0
  103. package/src/ui/formula-palette.ts +211 -0
  104. package/src/ui/layers.ts +40 -0
  105. package/src/ui/media-picker.ts +1 -1
  106. package/src/ui/panel-resize.ts +15 -1
  107. package/src/ui/progress-modal.ts +2 -2
  108. package/src/ui/schema-form.ts +524 -0
  109. package/src/utils/preview-format.ts +26 -0
  110. package/src/utils/studio-utils.ts +4 -3
  111. package/src/view.ts +4 -4
  112. package/src/workspace/workspace.ts +14 -0
  113. package/src/settings/content-types-editor.ts +0 -599
package/src/tabs/tab.ts CHANGED
@@ -4,14 +4,16 @@ import { formatByName, formatForPath } from "../format/format-host";
4
4
  import { normalizeArrayChildren } from "../state";
5
5
  import type {
6
6
  DocumentStackEntry,
7
+ FormulaEditDef,
7
8
  FunctionEditDef,
8
9
  GitBranchesResult,
9
10
  GitDiffState,
10
11
  GitStatusResult,
11
12
  InlineEditDef,
13
+ JsonValue,
12
14
  } from "../types";
13
15
  import type { JxMutableNode } from "@jxsuite/schema/types";
14
- import type { JxDocOp } from "./patch-ops";
16
+ import type { JxDocOp, JxFmOp } from "./patch-ops";
15
17
 
16
18
  export interface TabUi {
17
19
  rightTab: string;
@@ -20,12 +22,26 @@ export interface TabUi {
20
22
  preview: boolean;
21
23
  /** Show elements inherited from the page's layout (pages with an effective layout only). */
22
24
  showLayout: boolean;
25
+ /** Above-canvas frontmatter Properties accordion expanded (content-collection docs, edit mode). */
26
+ frontmatterOpen: boolean;
23
27
  /** Chosen literal values for dynamic route params (e.g. { sku: "mini-trencher" }). */
24
28
  previewParams: Record<string, string>;
29
+ /**
30
+ * Chosen test values for a component doc's props (state entries), seeded into the canvas render
31
+ * so a non-instantiated component previews with real data (M6) — the previewParams mirror.
32
+ */
33
+ previewProps: Record<string, JsonValue> | null;
25
34
  zoom: number;
35
+ /**
36
+ * Edit-mode content zoom — browser-page-zoom semantics (content reflows at the zoomed effective
37
+ * width while the canvas footprint stays fixed), unlike `zoom` which pan/zooms the whole canvas.
38
+ */
39
+ editZoom: number;
26
40
  activeMedia: string | null;
27
41
  activeSelector: string | null;
28
42
  editingFunction: FunctionEditDef | null;
43
+ /** Full-screen formula workspace target ($expression editing); editingFunction wins if both set. */
44
+ editingFormula: FormulaEditDef | null;
29
45
  featureToggles: Record<string, boolean>;
30
46
  styleSections: Record<string, boolean>;
31
47
  inspectorSections: Record<string, boolean>;
@@ -46,6 +62,18 @@ export interface TabUi {
46
62
  pendingInlineEdit: InlineEditDef | null;
47
63
  }
48
64
 
65
+ /**
66
+ * A live (iframe-evaluated) expression preview retained per editing target (M6). Stored on
67
+ * `session.canvas` beside `scope` — plain wire data (path-key → display-string pairs), rebuilt into
68
+ * an ExpressionPreview by services/live-preview.ts on read.
69
+ */
70
+ export interface StoredLivePreview {
71
+ /** Serialized expression + context the values were computed for (staleness check). */
72
+ key: string;
73
+ values: [string, string][];
74
+ error: string | null;
75
+ }
76
+
49
77
  interface HistorySnapshot {
50
78
  /** Full document at this state (checkpoint), or null when the ops describe the transition. */
51
79
  document: Record<string, unknown> | null;
@@ -57,6 +85,8 @@ interface HistorySnapshot {
57
85
  forwardOps?: JxDocOp[] | null;
58
86
  /** Replayable ops transforming this state back into the previous one. */
59
87
  inverseOps?: JxDocOp[] | null;
88
+ /** Frontmatter key changes in this transaction (before/after values, replayed both ways). */
89
+ fmOps?: JxFmOp[] | null;
60
90
  }
61
91
 
62
92
  export interface Tab {
@@ -89,6 +119,9 @@ export interface Tab {
89
119
  // The bridge as a `dataScope` message and read by the data-explorer panel. Plain data now —
90
120
  // The old live `EffectScope` (with `.stop()`) moved into the iframe realm with buildScope.
91
121
  scope: Record<string, unknown> | null;
122
+ // Latest live (iframe-evaluated) expression previews keyed by editing-target id, stored
123
+ // Beside `scope` the same way (services/live-preview.ts owns reads/writes).
124
+ livePreviews: Record<string, StoredLivePreview> | null;
92
125
  error: string | null;
93
126
  pendingInlineEdit: InlineEditDef | null;
94
127
  };
@@ -109,8 +142,11 @@ function createDefaultUi(canvasMode: string, preview = false) {
109
142
  activeMedia: null,
110
143
  activeSelector: null,
111
144
  canvasMode,
145
+ editZoom: 1,
146
+ editingFormula: null,
112
147
  editingFunction: null,
113
148
  featureToggles: {},
149
+ frontmatterOpen: true,
114
150
  gitBranches: null,
115
151
  gitCommitMessage: "",
116
152
  gitDiffState: null,
@@ -121,6 +157,7 @@ function createDefaultUi(canvasMode: string, preview = false) {
121
157
  pendingInlineEdit: null,
122
158
  preview,
123
159
  previewParams: {},
160
+ previewProps: null,
124
161
  rightTab: "properties",
125
162
  settingsTab: "stylebook",
126
163
  showLayout: true,
@@ -204,6 +241,7 @@ export function createTab({
204
241
  session: reactive({
205
242
  canvas: {
206
243
  error: null,
244
+ livePreviews: null,
207
245
  pendingInlineEdit: null,
208
246
  scope: null,
209
247
  status: "idle",
@@ -8,16 +8,23 @@ import {
8
8
  endRecording,
9
9
  getPatchConsumer,
10
10
  recordDocOp,
11
+ recordFmOp,
11
12
  recordPatch,
12
13
  } from "./patch-ops";
13
14
 
14
- import type { JxDocOp, JxDocOpPair, TransactionRecord } from "./patch-ops";
15
+ import type { JxDocOp, JxDocOpPair, JxFmOp, TransactionRecord } from "./patch-ops";
15
16
 
16
17
  import type { Tab } from "../tabs/tab";
17
18
  import type { JxPath } from "../state";
18
19
 
19
20
  import type { JsonValue } from "../types";
20
- import type { JxEventBinding, JxMutableNode, JxStateObject, JxStyle } from "@jxsuite/schema/types";
21
+ import type {
22
+ JxAttributeValue,
23
+ JxEventBinding,
24
+ JxMutableNode,
25
+ JxStateObject,
26
+ JxStyle,
27
+ } from "@jxsuite/schema/types";
21
28
  import { ensureNestedStyle, getNestedStyle, isEventBinding } from "@jxsuite/schema/guards";
22
29
 
23
30
  const HISTORY_LIMIT = 100;
@@ -205,10 +212,14 @@ function pushHistoryEntry(
205
212
  selectionBefore: JxPath | null,
206
213
  ) {
207
214
  const truncated = tab.history.snapshots.slice(0, tab.history.index + 1);
208
- const useOps = patchHistoryEnabled() && record.invertible && record.docOps.length > 0;
215
+ const useOps =
216
+ patchHistoryEnabled() &&
217
+ record.invertible &&
218
+ (record.docOps.length > 0 || record.fmOps.length > 0);
209
219
  const needCheckpoint = !useOps || truncated.length % CHECKPOINT_INTERVAL === 0;
210
220
  truncated.push({
211
221
  document: needCheckpoint ? jsonClone(raw) : null,
222
+ fmOps: useOps && record.fmOps.length > 0 ? record.fmOps : null,
212
223
  forwardOps: useOps ? record.docOps.map((p) => p.forward) : null,
213
224
  inverseOps: useOps ? record.docOps.map((p) => p.inverse) : null,
214
225
  selection: tab.session.selection ? [...tab.session.selection] : null,
@@ -343,7 +354,7 @@ function applyDocOp(tab: Tab, op: JxDocOp) {
343
354
  toIndex: op.toIndex,
344
355
  toParentPath: op.toParentPath,
345
356
  });
346
- return;
357
+ break;
347
358
  }
348
359
  default: {
349
360
  break;
@@ -471,14 +482,23 @@ export function undo(tab: Tab) {
471
482
  }
472
483
  const entry = tab.history.snapshots[tab.history.index]!;
473
484
  const inverseOps = patchHistoryEnabled() ? entry.inverseOps : null;
474
- if (inverseOps && inverseOps.length > 0) {
485
+ const fmOps = patchHistoryEnabled() ? entry.fmOps : null;
486
+ if ((inverseOps && inverseOps.length > 0) || (fmOps && fmOps.length > 0)) {
475
487
  // Surgical path: apply inverse ops through the normal transaction pipeline (the canvas
476
488
  // Patches in place when it can), in reverse recording order.
477
489
  transactDoc(
478
490
  tab,
479
491
  (t) => {
480
- for (let i = inverseOps.length - 1; i >= 0; i--) {
481
- applyDocOp(t, toRaw(inverseOps[i]) as JxDocOp);
492
+ if (inverseOps) {
493
+ for (let i = inverseOps.length - 1; i >= 0; i--) {
494
+ applyDocOp(t, toRaw(inverseOps[i]) as JxDocOp);
495
+ }
496
+ }
497
+ if (fmOps) {
498
+ for (let i = fmOps.length - 1; i >= 0; i--) {
499
+ const op = toRaw(fmOps[i]) as JxFmOp;
500
+ applyFmState(t, op.field, op.before);
501
+ }
482
502
  }
483
503
  },
484
504
  { origin: "history", skipHistory: true },
@@ -505,12 +525,21 @@ export function redo(tab: Tab) {
505
525
  }
506
526
  const entry = tab.history.snapshots[tab.history.index + 1]!;
507
527
  const forwardOps = patchHistoryEnabled() ? entry.forwardOps : null;
508
- if (forwardOps && forwardOps.length > 0) {
528
+ const fmOps = patchHistoryEnabled() ? entry.fmOps : null;
529
+ if ((forwardOps && forwardOps.length > 0) || (fmOps && fmOps.length > 0)) {
509
530
  transactDoc(
510
531
  tab,
511
532
  (t) => {
512
- for (const op of forwardOps) {
513
- applyDocOp(t, toRaw(op) as JxDocOp);
533
+ if (forwardOps) {
534
+ for (const op of forwardOps) {
535
+ applyDocOp(t, toRaw(op) as JxDocOp);
536
+ }
537
+ }
538
+ if (fmOps) {
539
+ for (const op of fmOps) {
540
+ const raw = toRaw(op) as JxFmOp;
541
+ applyFmState(t, raw.field, raw.after);
542
+ }
514
543
  }
515
544
  },
516
545
  { origin: "history", skipHistory: true },
@@ -746,13 +775,13 @@ export function mutateUpdateStyle(tab: Tab, path: JxPath, prop: string, value: s
746
775
  * @param {Tab} tab
747
776
  * @param {JxPath} path
748
777
  * @param {string} attr
749
- * @param {string | undefined} value
778
+ * @param {JxAttributeValue | undefined} value — a literal, `$ref` binding, or `${}` template.
750
779
  */
751
780
  export function mutateUpdateAttribute(
752
781
  tab: Tab,
753
782
  path: JxPath,
754
783
  attr: string,
755
- value?: string | undefined,
784
+ value?: JxAttributeValue | undefined,
756
785
  ) {
757
786
  const node = getNodeAtPath(tab.doc.document, path);
758
787
  const attrsBefore = cloneValue(node.attributes);
@@ -1196,10 +1225,28 @@ export function mutateRenameSwitchCase(tab: Tab, path: JxPath, oldName: string,
1196
1225
  * @param {JsonValue} value
1197
1226
  */
1198
1227
  export function mutateUpdateFrontmatter(tab: Tab, field: string, value?: JsonValue) {
1199
- if (value === undefined || value === null || value === "") {
1228
+ const fm = tab.doc.content.frontmatter;
1229
+ const before = Object.hasOwn(fm, field) ? cloneValue(fm[field]) : undefined;
1230
+ const deletes = value === undefined || value === null || value === "";
1231
+ if (deletes) {
1232
+ delete fm[field];
1233
+ } else {
1234
+ fm[field] = value;
1235
+ }
1236
+ const after = deletes ? undefined : cloneValue(value);
1237
+ recordFmOp({ after, before, field });
1238
+ tab.doc.dirty = true;
1239
+ }
1240
+
1241
+ /**
1242
+ * Restore a frontmatter key to a recorded history state: `undefined` means the key was absent.
1243
+ * Unlike `mutateUpdateFrontmatter`, values like `""`/`false`/`null` are written verbatim (source
1244
+ * round-trips can put them there) and no fm op is recorded — history replay must not re-record.
1245
+ */
1246
+ function applyFmState(tab: Tab, field: string, value: unknown) {
1247
+ if (value === undefined) {
1200
1248
  delete tab.doc.content.frontmatter[field];
1201
1249
  } else {
1202
1250
  tab.doc.content.frontmatter[field] = value;
1203
1251
  }
1204
- tab.doc.dirty = true;
1205
1252
  }
package/src/types.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  /// <reference lib="dom" />
2
2
  import type { CollabHandle } from "@jxsuite/collab/provider";
3
- import type { JxMutableNode, JxPath, ProjectConfig } from "@jxsuite/schema/types";
3
+ import type {
4
+ ContentTypeSchema,
5
+ JxMutableNode,
6
+ JxPath,
7
+ ProjectConfig,
8
+ } from "@jxsuite/schema/types";
4
9
 
5
10
  // ─── Wire types (the Studio Backend Protocol) ───────────────────────────────
6
11
  /* The request/response shapes every backend serves live in @jxsuite/protocol;
@@ -11,7 +16,16 @@ import type {
11
16
  CfConnection,
12
17
  CodeServiceResult,
13
18
  ComponentMeta,
19
+ DataConnectionsResponse,
20
+ DataConnectionTestResult,
21
+ DataPushResult,
22
+ DataRowDelete,
23
+ DataRowInsert,
24
+ DataRowsQuery,
25
+ DataRowsResult,
26
+ DataRowUpdate,
14
27
  DirEntry,
28
+ ExtensionsInfo,
15
29
  FsEvent,
16
30
  GitBranchesResult,
17
31
  GitLogEntry,
@@ -22,8 +36,11 @@ import type {
22
36
  PackageInfo,
23
37
  PackageOpResult,
24
38
  ProjectListEntry,
39
+ ProjectSchemasResponse,
25
40
  RecentProjectEntry,
26
41
  RenameResult,
42
+ SecretsSetRequest,
43
+ SecretsSetResponse,
27
44
  StarterInfo,
28
45
  } from "@jxsuite/protocol";
29
46
 
@@ -35,8 +52,24 @@ export type {
35
52
  CodeServiceResult,
36
53
  ComponentMeta,
37
54
  ComponentSlotMeta,
55
+ DataColumnMeta,
56
+ DataConnectionInfo,
57
+ DataConnectionsResponse,
58
+ DataConnectionTestResult,
59
+ DataConnectorInfo,
60
+ DataPushRequest,
61
+ DataPushResult,
62
+ DataPushStep,
63
+ DataRowDelete,
64
+ DataRowInsert,
65
+ DataRowsQuery,
66
+ DataRowsResult,
67
+ DataRowUpdate,
38
68
  DirEntry,
39
69
  ErrorBody,
70
+ ExtensionContributionInfo,
71
+ ExtensionProjectBlock,
72
+ ExtensionsInfo,
40
73
  FsEvent,
41
74
  GitBranchesResult,
42
75
  GitFileStatus,
@@ -49,12 +82,36 @@ export type {
49
82
  PackageInfo,
50
83
  PackageOpResult,
51
84
  ProjectListEntry,
85
+ ProjectSchemasResponse,
52
86
  PullRequestInfo,
53
87
  RecentProjectEntry,
54
88
  RenameResult,
89
+ SecretsListResponse,
90
+ SecretsSetRequest,
91
+ SecretsSetResponse,
55
92
  StarterInfo,
56
93
  } from "@jxsuite/protocol";
57
94
 
95
+ /** Repository-access onboarding state returned by `StudioPlatform.getAccountStatus`. */
96
+ export interface AccountStatus {
97
+ /** GitHub App installations (personal + organization) visible to the signed-in user. */
98
+ installations: { id: number; account: string | null }[];
99
+ /** Where to install the App (github.com/apps/<slug>/installations/new), when known. */
100
+ appInstallUrl?: string;
101
+ }
102
+
103
+ /** A repository visible to `StudioPlatform.listRepos` (the add-existing-repository picker). */
104
+ export interface RepoInfo {
105
+ owner: string;
106
+ name: string;
107
+ fullName: string;
108
+ private: boolean;
109
+ defaultBranch: string;
110
+ permission: "admin" | "write" | "read" | "none";
111
+ /** Already recognized as a Jx project (topic-tagged / cataloged). */
112
+ isJxProject: boolean;
113
+ }
114
+
58
115
  export interface StudioPlatform {
59
116
  id: string;
60
117
  projectRoot: string;
@@ -126,8 +183,39 @@ export interface StudioPlatform {
126
183
  searchFiles: (query: string, extensions?: string[]) => Promise<DirEntry[]>;
127
184
  /** List the project's registered format classes (auto-discovered from imports). */
128
185
  listFormats?: () => Promise<unknown[]>;
186
+ /**
187
+ * List the project's enabled extension packages with their project-section contributions
188
+ * (specs/extensions.md §9/§9.1) — the formats route's sibling `extensions` payload. Optional:
189
+ * platforms without it lose descriptor-contributed settings sections.
190
+ */
191
+ listExtensions?: () => Promise<ExtensionsInfo[]>;
192
+ /**
193
+ * Fetch the project's generated entry schemas (project.schema.json / document.schema.json),
194
+ * PRE-BUNDLED into self-contained documents for editor registration. Optional: without it the
195
+ * JSON editor keeps the bundled core schemas.
196
+ */
197
+ fetchProjectSchemas?: () => Promise<ProjectSchemasResponse>;
129
198
  /** Invoke a format capability (parse/serialize) — { format, action, source?, doc?, options? }. */
130
199
  formatAction?: (payload: Record<string, unknown>) => Promise<unknown>;
200
+ // ─── Data surface + secrets (owner console; specs/extensions.md §13) ────────
201
+ // Optional as a family: backends without the connector data routes omit them all, and Studio
202
+ // Hides the data grid and connection/push/test actions. Row CRUD is the ADMIN path — it
203
+ // Intentionally bypasses table permission rules; the backend boundary is the gate.
204
+ /** List connector connections with configured state, table names, and provider metadata. */
205
+ dataConnections?: () => Promise<DataConnectionsResponse>;
206
+ /** Probe one connection through the backend's connector registry. */
207
+ dataConnectionTest?: (connection: string) => Promise<DataConnectionTestResult>;
208
+ /** Additive schema push; `dryRun` compiles the plan without applying it. */
209
+ dataPush?: (opts?: { connection?: string; dryRun?: boolean }) => Promise<DataPushResult>;
210
+ /** Page a table's rows with introspected column metadata. */
211
+ dataRows?: (query: DataRowsQuery) => Promise<DataRowsResult>;
212
+ dataInsertRow?: (req: DataRowInsert) => Promise<{ row: Record<string, unknown> }>;
213
+ dataUpdateRow?: (req: DataRowUpdate) => Promise<{ row: Record<string, unknown> }>;
214
+ dataDeleteRow?: (req: DataRowDelete) => Promise<{ ok: boolean }>;
215
+ /** Configured secret env-var NAMES — never values. */
216
+ listSecrets?: () => Promise<string[]>;
217
+ /** Write/remove secrets in the backend store (.dev.vars locally); names-only response. */
218
+ setSecrets?: (req: SecretsSetRequest) => Promise<SecretsSetResponse>;
131
219
  fetchPluginSchema: (src: string, prototype?: string, base?: string) => Promise<unknown>;
132
220
  /**
133
221
  * Resolve a class-prototype config through the backend's `/__jx_resolve__` pipeline (with the
@@ -235,6 +323,25 @@ export interface StudioPlatform {
235
323
  // ─── Identity & hosting connections (publish surface) ───────────────────────
236
324
  /** The signed-in user's identity, when the platform has one (cloud). */
237
325
  getUser?: () => Promise<{ login: string; name?: string; avatarUrl?: string } | null>;
326
+ /**
327
+ * Repository-access onboarding state: the platform's GitHub App installations visible to this
328
+ * user and where to install the App. Cloud-only; when the list is empty Studio's welcome screen
329
+ * prompts the user to install the App (without it, no repos are reachable). Null = unknown (don't
330
+ * prompt).
331
+ */
332
+ getAccountStatus?: () => Promise<AccountStatus | null>;
333
+ /**
334
+ * Browse every repository the platform's account link can reach — personal and organization repos
335
+ * covered by a GitHub App installation on cloud. Backs the "Add Existing Repository" picker;
336
+ * local platforms omit it (the OS picker / clone flow covers them).
337
+ */
338
+ listRepos?: () => Promise<RepoInfo[]>;
339
+ /**
340
+ * Adopt an existing repository as a Jx project and return its catalogue root key (openable via
341
+ * the recent-projects path). Rejects with a structured message when the repository carries no
342
+ * project.json.
343
+ */
344
+ importProject?: (opts: { owner: string; name: string }) => Promise<{ root: string }>;
238
345
  /**
239
346
  * Open a pull request for the current branch. Cloud platforms implement it against their session;
240
347
  * local platforms omit it and Studio falls back to a direct GitHub API call with the user's
@@ -273,6 +380,18 @@ export interface StudioPlatform {
273
380
 
274
381
  // ─── Studio Types ───────────────────────────────────────────────────────────
275
382
 
383
+ /**
384
+ * A project.json `content` section entry, as the studio consumes it. The parser extension owns the
385
+ * full shape (its project fragment schema is the validation source of truth); the studio only reads
386
+ * these fields.
387
+ */
388
+ export interface ContentSectionEntry {
389
+ source?: string;
390
+ format?: string;
391
+ schema?: ContentTypeSchema;
392
+ $elements?: unknown[];
393
+ }
394
+
276
395
  export interface CanvasPanel {
277
396
  mediaName: string;
278
397
  element: HTMLElement;
@@ -308,6 +427,18 @@ export interface FunctionEditDef {
308
427
  parameters?: string[];
309
428
  }
310
429
 
430
+ /**
431
+ * Identifies which document position's `$expression` the full-screen formula workspace is editing:
432
+ * a state entry (`type: "def"` + defName) or an element event binding (`type: "event"` + path +
433
+ * eventKey). Mirrors FunctionEditDef, the Monaco function editor's target shape.
434
+ */
435
+ export interface FormulaEditDef {
436
+ type: "def" | "event";
437
+ defName?: string;
438
+ path?: JxPath;
439
+ eventKey?: string;
440
+ }
441
+
311
442
  export interface GitDiffState {
312
443
  filePath: string;
313
444
  originalContent: string;