@jxsuite/studio 0.37.1 → 1.0.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.
- package/dist/studio.js +77654 -76269
- package/dist/studio.js.map +128 -119
- package/package.json +44 -44
- package/src/account-status.ts +39 -0
- package/src/browse/browse.ts +10 -14
- package/src/canvas/iframe-host.ts +1 -1
- package/src/editor/context-menu.ts +1 -1
- package/src/editor/repeater-scope.ts +8 -13
- package/src/files/files.ts +3 -0
- package/src/format/format-host.ts +63 -1
- package/src/new-project/add-repo-modal.ts +183 -0
- package/src/new-project/new-project-modal.ts +22 -3
- package/src/page-params.ts +34 -8
- package/src/panels/ai-chat/chat-markdown.ts +2 -2
- package/src/panels/ai-panel.ts +61 -4
- package/src/panels/data-grid.ts +619 -0
- package/src/panels/signals-panel.ts +102 -437
- package/src/panels/statusbar.ts +1 -1
- package/src/panels/welcome-screen.ts +50 -0
- package/src/platform-errors.ts +30 -0
- package/src/platforms/cloud.ts +172 -89
- package/src/platforms/devserver.ts +172 -0
- package/src/services/context-resolver.ts +73 -0
- package/src/services/data-service.ts +155 -0
- package/src/services/monaco-setup.ts +75 -26
- package/src/settings/contributed-section.ts +406 -0
- package/src/settings/extension-sections.ts +145 -0
- package/src/settings/schema-field-ui.ts +4 -2
- package/src/settings/settings-modal.ts +101 -42
- package/src/site-context.ts +10 -1
- package/src/studio.ts +24 -0
- package/src/tabs/transact.ts +1 -1
- package/src/types.ts +120 -1
- package/src/ui/form-controls.ts +322 -0
- package/src/ui/progress-modal.ts +2 -2
- package/src/ui/schema-form.ts +524 -0
- package/src/utils/studio-utils.ts +4 -3
- package/src/settings/content-types-editor.ts +0 -599
|
@@ -2,41 +2,129 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Settings modal — site-wide project settings (CSS variables, definitions, content types, head,
|
|
4
4
|
* general). Modeled after VS Code / Obsidian settings panels: left sidebar nav + right content
|
|
5
|
-
* area.
|
|
5
|
+
* area. Sections come from a registry: built-ins register at module init, and extensions add
|
|
6
|
+
* descriptor-contributed sections through `registerSettingsSection`.
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
import { html } from "lit-html";
|
|
9
10
|
import { classMap } from "lit-html/directives/class-map.js";
|
|
10
11
|
import { ref } from "lit-html/directives/ref.js";
|
|
11
12
|
import { renderDefsEditor } from "./defs-editor";
|
|
12
|
-
import { renderContentTypesEditor } from "./content-types-editor";
|
|
13
13
|
import { renderCssVarsEditor } from "./css-vars-editor";
|
|
14
14
|
import { renderHeadEditor } from "./head-editor";
|
|
15
15
|
import { renderGeneralSettings } from "./general-settings";
|
|
16
16
|
import { renderDependenciesEditor } from "./dependencies-editor";
|
|
17
17
|
import { openModal } from "../ui/layers";
|
|
18
18
|
|
|
19
|
+
// ─── Section registry ─────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
/** A settings-modal section: nav entry plus a renderer for the content area. */
|
|
22
|
+
export interface SettingsSection {
|
|
23
|
+
key: string;
|
|
24
|
+
label: string;
|
|
25
|
+
/** Nav icon name (reserved for future nav treatments). */
|
|
26
|
+
icon?: string | undefined;
|
|
27
|
+
/** Sort position — lower orders render higher in the nav. */
|
|
28
|
+
order: number;
|
|
29
|
+
render: (container: HTMLElement) => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const sectionRegistry = new Map<string, SettingsSection>();
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Register (or replace) a settings section. Extensions use this hook to contribute
|
|
36
|
+
* descriptor-driven sections; built-ins register below at module init.
|
|
37
|
+
*
|
|
38
|
+
* @param {SettingsSection} section
|
|
39
|
+
*/
|
|
40
|
+
export function registerSettingsSection(section: SettingsSection): void {
|
|
41
|
+
sectionRegistry.set(section.key, section);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Remove a registered section — used when a descriptor-contributed section's extension is disabled
|
|
46
|
+
* (see ./extension-sections). Built-ins are never unregistered.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} key
|
|
49
|
+
*/
|
|
50
|
+
export function unregisterSettingsSection(key: string): void {
|
|
51
|
+
sectionRegistry.delete(key);
|
|
52
|
+
if (_activeSection === key) {
|
|
53
|
+
_activeSection = "general";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Registered sections sorted by order (registration order breaks ties). */
|
|
58
|
+
function sortedSections(): SettingsSection[] {
|
|
59
|
+
return [...sectionRegistry.values()].toSorted((a, b) => a.order - b.order);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Built-in sections — orders preserve the historical display order
|
|
63
|
+
registerSettingsSection({
|
|
64
|
+
icon: "sp-icon-properties",
|
|
65
|
+
key: "general",
|
|
66
|
+
label: "General",
|
|
67
|
+
order: 10,
|
|
68
|
+
render: renderGeneralSettings,
|
|
69
|
+
});
|
|
70
|
+
registerSettingsSection({
|
|
71
|
+
icon: "sp-icon-file-single-web-page",
|
|
72
|
+
key: "head",
|
|
73
|
+
label: "Head",
|
|
74
|
+
order: 20,
|
|
75
|
+
render: renderHeadEditor,
|
|
76
|
+
});
|
|
77
|
+
registerSettingsSection({
|
|
78
|
+
icon: "sp-icon-brush",
|
|
79
|
+
key: "cssVars",
|
|
80
|
+
label: "CSS Variables",
|
|
81
|
+
order: 30,
|
|
82
|
+
render: renderCssVarsEditor,
|
|
83
|
+
});
|
|
84
|
+
registerSettingsSection({
|
|
85
|
+
icon: "sp-icon-data",
|
|
86
|
+
key: "definitions",
|
|
87
|
+
label: "Definitions",
|
|
88
|
+
order: 40,
|
|
89
|
+
render: renderDefsEditor,
|
|
90
|
+
});
|
|
91
|
+
// Content Types is no longer a built-in: @jxsuite/parser contributes it (order 50) through its
|
|
92
|
+
// Content class descriptor's $studio.settings block, registered via ./extension-sections.
|
|
93
|
+
registerSettingsSection({
|
|
94
|
+
icon: "sp-icon-box",
|
|
95
|
+
key: "dependencies",
|
|
96
|
+
label: "Dependencies",
|
|
97
|
+
order: 60,
|
|
98
|
+
render: renderDependenciesEditor,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ─── Modal state ──────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
19
103
|
let _handle: ReturnType<typeof openModal> | null = null;
|
|
20
104
|
|
|
21
105
|
let _activeSection = "general";
|
|
22
106
|
|
|
23
107
|
let _contentEl: HTMLElement | null = null;
|
|
24
108
|
|
|
25
|
-
const sections = [
|
|
26
|
-
{ icon: "sp-icon-properties", key: "general", label: "General" },
|
|
27
|
-
{ icon: "sp-icon-file-single-web-page", key: "head", label: "Head" },
|
|
28
|
-
{ icon: "sp-icon-brush", key: "cssVars", label: "CSS Variables" },
|
|
29
|
-
{ icon: "sp-icon-data", key: "definitions", label: "Definitions" },
|
|
30
|
-
{ icon: "sp-icon-view-grid", key: "contentTypes", label: "Content Types" },
|
|
31
|
-
{ icon: "sp-icon-box", key: "dependencies", label: "Dependencies" },
|
|
32
|
-
];
|
|
33
|
-
|
|
34
109
|
export function openSettingsModal() {
|
|
35
110
|
if (_handle) {
|
|
36
111
|
return;
|
|
37
112
|
}
|
|
38
113
|
_activeSection = "general";
|
|
39
114
|
renderModal();
|
|
115
|
+
// Refresh descriptor-contributed sections (cached payloads make this cheap) and rerender the
|
|
116
|
+
// Nav once they land. Lazy import breaks the settings-modal ↔ extension-sections module cycle.
|
|
117
|
+
void import("./extension-sections")
|
|
118
|
+
.then(async ({ syncExtensionSettingsSections }) => {
|
|
119
|
+
await syncExtensionSettingsSections();
|
|
120
|
+
if (_handle) {
|
|
121
|
+
renderModal();
|
|
122
|
+
renderActiveSection();
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
.catch(() => {
|
|
126
|
+
// Contributed sections are optional — the built-ins render regardless.
|
|
127
|
+
});
|
|
40
128
|
}
|
|
41
129
|
|
|
42
130
|
export function closeSettingsModal() {
|
|
@@ -73,7 +161,7 @@ function renderModal() {
|
|
|
73
161
|
</div>
|
|
74
162
|
<div class="settings-modal-body">
|
|
75
163
|
<nav class="settings-modal-nav">
|
|
76
|
-
${
|
|
164
|
+
${sortedSections().map(
|
|
77
165
|
(s) => html`
|
|
78
166
|
<button
|
|
79
167
|
class=${classMap({
|
|
@@ -111,34 +199,5 @@ function renderActiveSection() {
|
|
|
111
199
|
if (!_handle || !_contentEl) {
|
|
112
200
|
return;
|
|
113
201
|
}
|
|
114
|
-
|
|
115
|
-
switch (_activeSection) {
|
|
116
|
-
case "general": {
|
|
117
|
-
renderGeneralSettings(_contentEl);
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
case "head": {
|
|
121
|
-
renderHeadEditor(_contentEl);
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
case "cssVars": {
|
|
125
|
-
renderCssVarsEditor(_contentEl);
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
case "definitions": {
|
|
129
|
-
renderDefsEditor(_contentEl);
|
|
130
|
-
break;
|
|
131
|
-
}
|
|
132
|
-
case "contentTypes": {
|
|
133
|
-
renderContentTypesEditor(_contentEl);
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
case "dependencies": {
|
|
137
|
-
renderDependenciesEditor(_contentEl);
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
default: {
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
202
|
+
sectionRegistry.get(_activeSection)?.render(_contentEl);
|
|
144
203
|
}
|
package/src/site-context.ts
CHANGED
|
@@ -258,7 +258,9 @@ function fillSlots(
|
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
/**
|
|
261
|
-
* Update the project's project.json with a partial patch and persist to disk.
|
|
261
|
+
* Update the project's project.json with a partial patch and persist to disk. A patch touching
|
|
262
|
+
* `extensions` invalidates the format/extensions caches and refreshes the editor's per-project
|
|
263
|
+
* schemas plus the contributed settings sections — the enabled-extension surface just changed.
|
|
262
264
|
*
|
|
263
265
|
* @param {Partial<ProjectConfig>} patch - Fields to merge into the current projectConfig
|
|
264
266
|
*/
|
|
@@ -270,4 +272,11 @@ export async function updateSiteConfig(patch: Partial<ProjectConfig>) {
|
|
|
270
272
|
} as ProjectConfig;
|
|
271
273
|
await platform.writeFile("project.json", JSON.stringify(config, null, 2));
|
|
272
274
|
setProjectState({ ...requireProjectState(), projectConfig: config });
|
|
275
|
+
if ("extensions" in patch) {
|
|
276
|
+
const { loadFormats, refreshExtensionUi, refreshFormats } =
|
|
277
|
+
await import("./format/format-host");
|
|
278
|
+
refreshFormats();
|
|
279
|
+
void loadFormats();
|
|
280
|
+
refreshExtensionUi(platform);
|
|
281
|
+
}
|
|
273
282
|
}
|
package/src/studio.ts
CHANGED
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
documentExtensions,
|
|
64
64
|
formatForPath,
|
|
65
65
|
loadFormats,
|
|
66
|
+
refreshExtensionUi,
|
|
66
67
|
refreshFormats,
|
|
67
68
|
} from "./format/format-host";
|
|
68
69
|
import {
|
|
@@ -110,6 +111,8 @@ import { cloneRepository, renderGitPanel } from "./panels/git-panel";
|
|
|
110
111
|
// By Bun's bundler despite sideEffects declarations in Spectrum's package.json.
|
|
111
112
|
import { components as _swc } from "./ui/spectrum";
|
|
112
113
|
import "./ui/panel-resize.js";
|
|
114
|
+
// Built-in schema-form controls (schema-builder, secret) register on import
|
|
115
|
+
import "./ui/form-controls.js";
|
|
113
116
|
import { initLayers } from "./ui/layers";
|
|
114
117
|
import { initShortcuts } from "./editor/shortcuts";
|
|
115
118
|
import { renderActivityBar, mount as mountActivityBar } from "./panels/activity-bar";
|
|
@@ -131,10 +134,12 @@ import {
|
|
|
131
134
|
} from "./panels/block-action-bar";
|
|
132
135
|
import { initCssData } from "./panels/style-utils";
|
|
133
136
|
import { initQuickSearch } from "./panels/quick-search";
|
|
137
|
+
import { hydrateAccountStatus } from "./account-status";
|
|
134
138
|
import { hydrateProjectList } from "./project-list";
|
|
135
139
|
import { addRecentProject, hydrateRecentProjects, removeRecentProject } from "./recent-projects";
|
|
136
140
|
import { hydrateSettings } from "./services/settings-store";
|
|
137
141
|
import { initWelcome } from "./panels/welcome-screen";
|
|
142
|
+
import { openAddRepoModal } from "./new-project/add-repo-modal";
|
|
138
143
|
import { openNewProjectModal } from "./new-project/new-project-modal";
|
|
139
144
|
import type { DocumentStackEntry, GitDiffState } from "./types";
|
|
140
145
|
import type { JxPath } from "./state";
|
|
@@ -471,6 +476,16 @@ initCanvasRender({
|
|
|
471
476
|
});
|
|
472
477
|
|
|
473
478
|
initWelcome({
|
|
479
|
+
addExistingRepo: async () => {
|
|
480
|
+
const result = await openAddRepoModal();
|
|
481
|
+
if (result) {
|
|
482
|
+
// The catalogue gained an entry; refresh it before navigating into the project.
|
|
483
|
+
void hydrateProjectList().then(() => {
|
|
484
|
+
render();
|
|
485
|
+
});
|
|
486
|
+
void openRecentProject(result.root);
|
|
487
|
+
}
|
|
488
|
+
},
|
|
474
489
|
cloneRepository: () => cloneRepository({ openRecentProject }),
|
|
475
490
|
openNewProject: async () => {
|
|
476
491
|
const result = await openNewProjectModal();
|
|
@@ -653,6 +668,7 @@ if (_projectParam) {
|
|
|
653
668
|
selectedPath: siteCtx.fileRelPath || null,
|
|
654
669
|
});
|
|
655
670
|
|
|
671
|
+
refreshExtensionUi(platform);
|
|
656
672
|
await autoSyncProjectOnOpen();
|
|
657
673
|
await ensureDependenciesInstalled();
|
|
658
674
|
await loadComponentRegistry();
|
|
@@ -769,6 +785,13 @@ void hydrateProjectList().then(() => {
|
|
|
769
785
|
render();
|
|
770
786
|
});
|
|
771
787
|
|
|
788
|
+
// Hydrate the account onboarding status (GitHub-App installation coverage on cloud), then refresh
|
|
789
|
+
// The welcome screen's install prompt. No-op on platforms without getAccountStatus.
|
|
790
|
+
// oxlint-disable-next-line unicorn/prefer-top-level-await -- deliberate fire-and-forget: hydration must not block initial render
|
|
791
|
+
void hydrateAccountStatus().then(() => {
|
|
792
|
+
render();
|
|
793
|
+
});
|
|
794
|
+
|
|
772
795
|
// Hydrate user settings (AI connection parameters) from the backend store, then re-render so
|
|
773
796
|
// Key-gated surfaces (assistant gate, New Project Import/Agent tabs) see the stored key.
|
|
774
797
|
// oxlint-disable-next-line unicorn/prefer-top-level-await -- deliberate fire-and-forget: hydration must not block initial render
|
|
@@ -819,6 +842,7 @@ async function openRecentProject(root: string) {
|
|
|
819
842
|
// "No format class imported" until a reload. Mirrors openProject in files.ts.
|
|
820
843
|
refreshFormats();
|
|
821
844
|
void loadFormats();
|
|
845
|
+
refreshExtensionUi(platform);
|
|
822
846
|
const content = await platform.readFile("project.json");
|
|
823
847
|
const config = JSON.parse(content) as ProjectConfig;
|
|
824
848
|
|
package/src/tabs/transact.ts
CHANGED
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 {
|
|
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;
|