@jxsuite/studio 1.0.0 → 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 (86) 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 +53069 -31746
  5. package/dist/studio.js.map +109 -74
  6. package/package.json +11 -9
  7. package/src/canvas/canvas-live-render.ts +21 -2
  8. package/src/canvas/canvas-render.ts +35 -16
  9. package/src/canvas/canvas-utils.ts +118 -72
  10. package/src/canvas/iframe-entry.ts +20 -0
  11. package/src/canvas/iframe-eval.ts +155 -0
  12. package/src/canvas/iframe-host.ts +131 -5
  13. package/src/canvas/iframe-inline-edit.ts +107 -1
  14. package/src/canvas/iframe-protocol.ts +43 -3
  15. package/src/canvas/iframe-render.ts +18 -0
  16. package/src/collab/collab-session.ts +23 -8
  17. package/src/collab/collab-state.ts +6 -3
  18. package/src/component-props.ts +104 -0
  19. package/src/editor/inline-edit-apply.ts +36 -1
  20. package/src/editor/inline-edit.ts +58 -1
  21. package/src/editor/shortcuts.ts +41 -12
  22. package/src/files/file-ops.ts +14 -0
  23. package/src/files/files.ts +53 -1
  24. package/src/grid/cell-editors.ts +288 -0
  25. package/src/grid/cell-popovers.ts +108 -0
  26. package/src/grid/csv-codec.ts +122 -0
  27. package/src/grid/edit-buffer.ts +490 -0
  28. package/src/grid/grid-controller.ts +417 -0
  29. package/src/grid/grid-layout.ts +83 -0
  30. package/src/grid/grid-open.ts +167 -0
  31. package/src/grid/grid-panel.ts +302 -0
  32. package/src/grid/grid-source.ts +210 -0
  33. package/src/grid/grid-view.ts +367 -0
  34. package/src/grid/schema-columns.ts +261 -0
  35. package/src/grid/sources/connector-source.ts +217 -0
  36. package/src/grid/sources/content-source.ts +542 -0
  37. package/src/grid/sources/csv-file-source.ts +247 -0
  38. package/src/grid/tabulator-tables.d.ts +120 -0
  39. package/src/panels/block-action-bar.ts +8 -3
  40. package/src/panels/chat-panel.ts +98 -0
  41. package/src/panels/data-grid.ts +9 -387
  42. package/src/panels/editors.ts +43 -17
  43. package/src/panels/events-panel.ts +137 -44
  44. package/src/panels/formula-workspace.ts +379 -0
  45. package/src/panels/frontmatter-fields.ts +231 -0
  46. package/src/panels/frontmatter-panel.ts +132 -0
  47. package/src/panels/git-panel.ts +1 -1
  48. package/src/panels/head-panel.ts +11 -175
  49. package/src/panels/properties-panel.ts +154 -144
  50. package/src/panels/right-panel.ts +9 -47
  51. package/src/panels/signals-panel.ts +115 -23
  52. package/src/panels/statement-editor.ts +710 -0
  53. package/src/panels/style-panel.ts +25 -1
  54. package/src/panels/stylebook-panel.ts +0 -2
  55. package/src/panels/tab-bar.ts +175 -6
  56. package/src/panels/tab-strip.ts +62 -6
  57. package/src/panels/toolbar.ts +37 -3
  58. package/src/services/ai-project-tools.ts +541 -0
  59. package/src/services/ai-session-store.ts +28 -0
  60. package/src/services/ai-system-prompt.ts +194 -24
  61. package/src/services/ai-tools.ts +88 -21
  62. package/src/services/automation.ts +16 -0
  63. package/src/services/document-assistant.ts +81 -11
  64. package/src/services/gated-registry.ts +72 -0
  65. package/src/services/live-preview.ts +0 -0
  66. package/src/services/preview-eval.ts +68 -0
  67. package/src/services/project-adoption.ts +31 -0
  68. package/src/site-context.ts +2 -0
  69. package/src/store.ts +4 -0
  70. package/src/studio.ts +83 -52
  71. package/src/tabs/patch-ops.ts +25 -0
  72. package/src/tabs/tab.ts +39 -1
  73. package/src/tabs/transact.ts +60 -13
  74. package/src/types.ts +12 -0
  75. package/src/ui/dynamic-slot.ts +272 -0
  76. package/src/ui/expression-editor.ts +423 -125
  77. package/src/ui/field-row.ts +5 -0
  78. package/src/ui/formula-catalog.ts +557 -0
  79. package/src/ui/formula-chips.ts +216 -0
  80. package/src/ui/formula-palette.ts +211 -0
  81. package/src/ui/layers.ts +40 -0
  82. package/src/ui/media-picker.ts +1 -1
  83. package/src/ui/panel-resize.ts +15 -1
  84. package/src/utils/preview-format.ts +26 -0
  85. package/src/view.ts +4 -4
  86. package/src/workspace/workspace.ts +14 -0
@@ -23,7 +23,10 @@ import {
23
23
  import { renderFieldRow } from "../ui/field-row";
24
24
  import { rawTextArea, spTextField } from "../ui/field-input";
25
25
  import { expressionHint, renderExpressionEditor } from "../ui/expression-editor";
26
+ import { renderStatementEditor } from "./statement-editor";
27
+ import { livePreviewExpression } from "../services/live-preview";
26
28
  import { renderMediaPicker } from "../ui/media-picker";
29
+ import { renderOnly } from "../store";
27
30
  import { registerFormControl, renderForm } from "../ui/schema-form";
28
31
  import { resolveContextPointer } from "../services/context-resolver";
29
32
  import type { JsonSchema } from "../ui/schema-form";
@@ -32,6 +35,7 @@ import type {
32
35
  CemEvent,
33
36
  CemParameter,
34
37
  JxMutableNode,
38
+ JxStatement,
35
39
  JxStateDefinition,
36
40
  } from "@jxsuite/schema/types";
37
41
  import { fetchPluginSchema, pluginSchemaCache } from "../services/code-services";
@@ -64,7 +68,7 @@ export interface SignalDef {
64
68
  $handler?: string;
65
69
  type?: string;
66
70
  default?: unknown;
67
- body?: string;
71
+ body?: string | unknown[];
68
72
  parameters?: string[];
69
73
  timing?: string;
70
74
  description?: string;
@@ -234,6 +238,9 @@ export function defHint(_name: string, def: SignalDef | null | undefined) {
234
238
  return expressionHint(def.$expression);
235
239
  }
236
240
  if (def.$prototype === "Function") {
241
+ if (Array.isArray(def.body)) {
242
+ return `${def.body.length} statement${def.body.length === 1 ? "" : "s"}`;
243
+ }
237
244
  if (def.body) {
238
245
  return def.body.length > 20 ? `${def.body.slice(0, 20)}...` : def.body;
239
246
  }
@@ -819,6 +826,22 @@ function renderSignalEditorTemplate(
819
826
  } else if (cat === "expression") {
820
827
  const exprNode = def.$expression || { operator: "=", target: null };
821
828
  fields = html`
829
+ <div style="display:flex;align-items:center;gap:4px">
830
+ <span class="field-label" style="flex:1">Expression</span>
831
+ <sp-action-button
832
+ size="xs"
833
+ quiet
834
+ title="Open in formula workspace"
835
+ @click=${() => {
836
+ ctx.updateSession({
837
+ ui: { editingFormula: { defName: name, type: "def" } },
838
+ });
839
+ ctx.renderCanvas();
840
+ }}
841
+ >
842
+ <sp-icon-full-screen slot="icon"></sp-icon-full-screen>
843
+ </sp-action-button>
844
+ </div>
822
845
  ${renderExpressionEditor(
823
846
  exprNode,
824
847
  (newNode: unknown) =>
@@ -828,7 +851,17 @@ function renderSignalEditorTemplate(
828
851
  ),
829
852
  {
830
853
  allowEventRef: false,
854
+ // Live-context evaluation in the canvas iframe, snapshot fallback (M6). The signals
855
+ // Panel lives in the left panel — re-render it when a fresh live result lands.
856
+ preview: livePreviewExpression(activeTab.value, `def:${name}`, exprNode, null, () =>
857
+ renderOnly("leftPanel"),
858
+ ),
859
+ onInsertDef: (defName, vendored) =>
860
+ transactDoc(activeTab.value, (t) =>
861
+ mutateAddDef(t, defName, vendored as Record<string, JsonValue>),
862
+ ),
831
863
  stateDefs: Object.keys(S.document.state || {}),
864
+ stateEntries: S.document.state || {},
832
865
  },
833
866
  )}
834
867
  `;
@@ -957,6 +990,42 @@ function renderFunctionFields(
957
990
  transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { description: v || undefined })),
958
991
  );
959
992
 
993
+ // Structured bodies (spec §20) are a body MODE of the function category, not a new entity:
994
+ // "Statements" renders the statement-card editor over `body: JxStatement[]`; "Code" is the
995
+ // Textarea/Monaco path over `body: string`. Switching modes replaces the body with the other
996
+ // Representation's empty seed — an explicit mode change, nothing is converted.
997
+ const bodyIsStatements = Array.isArray(def.body);
998
+ const bodyModeToggle = html`
999
+ <sp-action-group size="s" compact class="body-mode-toggle">
1000
+ <sp-action-button
1001
+ size="s"
1002
+ class="body-mode-statements"
1003
+ ?selected=${bodyIsStatements}
1004
+ @click=${() => {
1005
+ if (!bodyIsStatements) {
1006
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { body: [] }));
1007
+ ctx.renderLeftPanel();
1008
+ }
1009
+ }}
1010
+ >
1011
+ Statements
1012
+ </sp-action-button>
1013
+ <sp-action-button
1014
+ size="s"
1015
+ class="body-mode-code"
1016
+ ?selected=${!bodyIsStatements}
1017
+ @click=${() => {
1018
+ if (bodyIsStatements) {
1019
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { body: "" }));
1020
+ ctx.renderLeftPanel();
1021
+ }
1022
+ }}
1023
+ >
1024
+ Code
1025
+ </sp-action-button>
1026
+ </sp-action-group>
1027
+ `;
1028
+
960
1029
  const bodyField = def.$src
961
1030
  ? html`
962
1031
  ${signalFieldRow("Source", def.$src || "", (v: string) =>
@@ -971,29 +1040,52 @@ function renderFunctionFields(
971
1040
  : html`
972
1041
  <div style="display:flex;align-items:center;gap:4px">
973
1042
  <span class="field-label" style="flex:1">Body</span>
974
- <sp-action-button
975
- size="xs"
976
- quiet
977
- title="Open in code editor"
978
- @click=${() => {
979
- ctx.updateSession({
980
- ui: { editingFunction: { defName: name, type: "def" } },
981
- });
982
- ctx.renderCanvas();
983
- }}
984
- >
985
- <sp-icon-code slot="icon"></sp-icon-code>
986
- </sp-action-button>
1043
+ ${bodyModeToggle}
1044
+ ${bodyIsStatements
1045
+ ? nothing
1046
+ : html`
1047
+ <sp-action-button
1048
+ size="xs"
1049
+ quiet
1050
+ title="Open in code editor"
1051
+ @click=${() => {
1052
+ ctx.updateSession({
1053
+ ui: { editingFunction: { defName: name, type: "def" } },
1054
+ });
1055
+ ctx.renderCanvas();
1056
+ }}
1057
+ >
1058
+ <sp-icon-code slot="icon"></sp-icon-code>
1059
+ </sp-action-button>
1060
+ `}
987
1061
  </div>
988
- <textarea
989
- class="field-input"
990
- style="min-height:60px;font-family:var(--font-mono);font-size:var(--spectrum-font-size-50, 11px)"
991
- .value=${def.body || ""}
992
- @input=${(e: Event) => {
993
- const v = (e.target as HTMLInputElement).value;
994
- transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { body: v }));
995
- }}
996
- ></textarea>
1062
+ ${bodyIsStatements
1063
+ ? renderStatementEditor(
1064
+ def.body as JxStatement[],
1065
+ (next) => {
1066
+ transactDoc(activeTab.value, (t) =>
1067
+ mutateUpdateDef(t, name, { body: next as unknown as JsonValue }),
1068
+ );
1069
+ ctx.renderLeftPanel();
1070
+ },
1071
+ {
1072
+ allowEventRef: true,
1073
+ emits: def.emits ?? [],
1074
+ stateDefs: Object.keys(S.document.state || {}),
1075
+ stateEntries: S.document.state || {},
1076
+ },
1077
+ )
1078
+ : html`
1079
+ <textarea
1080
+ class="field-input"
1081
+ style="min-height:60px;font-family:var(--font-mono);font-size:var(--spectrum-font-size-50, 11px)"
1082
+ .value=${typeof def.body === "string" ? def.body : ""}
1083
+ @input=${(e: Event) => {
1084
+ const v = (e.target as HTMLInputElement).value;
1085
+ transactDoc(activeTab.value, (t) => mutateUpdateDef(t, name, { body: v }));
1086
+ }}
1087
+ ></textarea>
1088
+ `}
997
1089
  `;
998
1090
 
999
1091
  return html`