@jxsuite/studio 0.19.0 → 0.21.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 (99) hide show
  1. package/dist/studio.css +98 -98
  2. package/dist/studio.js +13476 -12330
  3. package/dist/studio.js.map +152 -148
  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 +19 -7
  8. package/src/browse/browse-modal.js +16 -13
  9. package/src/browse/browse.js +44 -35
  10. package/src/canvas/canvas-diff.js +24 -16
  11. package/src/canvas/canvas-helpers.js +24 -19
  12. package/src/canvas/canvas-live-render.js +113 -39
  13. package/src/canvas/canvas-render.js +119 -73
  14. package/src/canvas/canvas-utils.js +51 -21
  15. package/src/canvas/nested-site-style.js +50 -0
  16. package/src/editor/component-inline-edit.js +14 -13
  17. package/src/editor/content-inline-edit.js +22 -10
  18. package/src/editor/context-menu.js +28 -14
  19. package/src/editor/convert-targets.js +60 -0
  20. package/src/editor/convert-to-component.js +20 -19
  21. package/src/editor/convert-to-repeater.js +226 -0
  22. package/src/editor/inline-edit.js +69 -28
  23. package/src/editor/insertion-helper.js +16 -12
  24. package/src/editor/shortcuts.js +14 -4
  25. package/src/editor/slash-menu.js +40 -19
  26. package/src/files/components.js +1 -1
  27. package/src/files/file-ops.js +37 -16
  28. package/src/files/files.js +322 -68
  29. package/src/github/github-auth.js +122 -0
  30. package/src/github/github-publish.js +126 -0
  31. package/src/markdown/md-convert.js +172 -107
  32. package/src/new-project/new-project-modal.js +204 -0
  33. package/src/panels/activity-bar.js +27 -25
  34. package/src/panels/ai-panel.js +406 -0
  35. package/src/panels/block-action-bar.js +77 -28
  36. package/src/panels/canvas-dnd.js +8 -2
  37. package/src/panels/data-explorer.js +19 -13
  38. package/src/panels/dnd.js +144 -92
  39. package/src/panels/editors.js +55 -35
  40. package/src/panels/elements-panel.js +33 -19
  41. package/src/panels/events-panel.js +15 -10
  42. package/src/panels/git-panel.js +475 -99
  43. package/src/panels/head-panel.js +121 -81
  44. package/src/panels/imports-panel.js +94 -65
  45. package/src/panels/layers-panel.js +64 -43
  46. package/src/panels/left-panel.js +105 -48
  47. package/src/panels/overlays.js +1 -0
  48. package/src/panels/panel-events.js +45 -30
  49. package/src/panels/preview-render.js +23 -18
  50. package/src/panels/properties-panel.js +378 -257
  51. package/src/panels/pseudo-preview.js +2 -2
  52. package/src/panels/quick-search.js +11 -9
  53. package/src/panels/right-panel.js +42 -16
  54. package/src/panels/shared.js +4 -4
  55. package/src/panels/signals-panel.js +415 -166
  56. package/src/panels/statusbar.js +10 -6
  57. package/src/panels/style-inputs.js +67 -35
  58. package/src/panels/style-panel.js +239 -134
  59. package/src/panels/style-utils.js +34 -16
  60. package/src/panels/stylebook-layers-panel.js +12 -10
  61. package/src/panels/stylebook-panel.js +134 -66
  62. package/src/panels/tab-strip.js +5 -2
  63. package/src/panels/toolbar.js +333 -109
  64. package/src/panels/welcome-screen.js +121 -0
  65. package/src/platform.js +2 -4
  66. package/src/platforms/devserver.js +113 -7
  67. package/src/resize-edges.js +98 -0
  68. package/src/services/cem-export.js +12 -7
  69. package/src/services/code-services.js +30 -12
  70. package/src/settings/content-types-editor.js +8 -8
  71. package/src/settings/css-vars-editor.js +30 -24
  72. package/src/settings/defs-editor.js +12 -7
  73. package/src/settings/general-settings.js +85 -3
  74. package/src/settings/head-editor.js +30 -24
  75. package/src/settings/schema-field-ui.js +58 -39
  76. package/src/settings/settings-modal.js +12 -9
  77. package/src/site-context.js +41 -31
  78. package/src/state.js +70 -29
  79. package/src/store.js +36 -35
  80. package/src/studio.js +120 -90
  81. package/src/tabs/tab.js +67 -43
  82. package/src/tabs/transact.js +23 -13
  83. package/src/ui/button-group.js +24 -24
  84. package/src/ui/color-selector.js +28 -23
  85. package/src/ui/field-row.js +3 -3
  86. package/src/ui/icons.js +2 -2
  87. package/src/ui/layers.js +31 -1
  88. package/src/ui/media-picker.js +7 -7
  89. package/src/ui/spectrum.js +5 -1
  90. package/src/ui/unit-selector.js +18 -16
  91. package/src/ui/value-selector.js +21 -15
  92. package/src/ui/widgets.js +22 -19
  93. package/src/utils/canvas-media.js +5 -4
  94. package/src/utils/edit-display.js +32 -26
  95. package/src/utils/google-fonts.js +11 -11
  96. package/src/utils/inherited-style.js +9 -8
  97. package/src/utils/studio-utils.js +18 -18
  98. package/src/view.js +58 -1
  99. package/src/workspace/workspace.js +29 -7
@@ -0,0 +1,122 @@
1
+ /**
2
+ * GitHub OAuth Device Flow authentication for Jx Studio.
3
+ *
4
+ * Uses the Device Flow so the user authenticates in their browser without needing a server-side
5
+ * redirect endpoint.
6
+ */
7
+
8
+ import { html } from "lit-html";
9
+ import { showDialog } from "../ui/layers.js";
10
+
11
+ const CLIENT_ID = "Ov23liYVlMFpgjOEPXJH";
12
+ const STORAGE_KEY = "jx_github_token";
13
+
14
+ export function getGithubToken() {
15
+ return localStorage.getItem(STORAGE_KEY);
16
+ }
17
+
18
+ export function clearGithubToken() {
19
+ localStorage.removeItem(STORAGE_KEY);
20
+ }
21
+
22
+ /**
23
+ * Authenticate with GitHub via the Device Flow. Shows a dialog with the user code and polls until
24
+ * the user completes authorization or cancels.
25
+ *
26
+ * @returns {Promise<string | null>} The access token, or null if cancelled.
27
+ */
28
+ export async function authenticateGithub() {
29
+ const existing = getGithubToken();
30
+ if (existing) return existing;
31
+
32
+ const codeRes = await fetch("https://github.com/login/device/code", {
33
+ method: "POST",
34
+ headers: {
35
+ "Content-Type": "application/json",
36
+ Accept: "application/json",
37
+ },
38
+ body: JSON.stringify({ client_id: CLIENT_ID, scope: "repo" }),
39
+ });
40
+
41
+ if (!codeRes.ok) throw new Error("Failed to initiate GitHub device flow");
42
+ const codeData = await codeRes.json();
43
+
44
+ const { device_code, user_code, verification_uri, interval = 5 } = codeData;
45
+
46
+ return showDialog((done) => {
47
+ let cancelled = false;
48
+ /** @type {ReturnType<typeof setTimeout> | null} */
49
+ let pollTimer = null;
50
+
51
+ const poll = async () => {
52
+ if (cancelled) return;
53
+ try {
54
+ const tokenRes = await fetch("https://github.com/login/oauth/access_token", {
55
+ method: "POST",
56
+ headers: {
57
+ "Content-Type": "application/json",
58
+ Accept: "application/json",
59
+ },
60
+ body: JSON.stringify({
61
+ client_id: CLIENT_ID,
62
+ device_code,
63
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
64
+ }),
65
+ });
66
+ const tokenData = await tokenRes.json();
67
+
68
+ if (tokenData.access_token) {
69
+ localStorage.setItem(STORAGE_KEY, tokenData.access_token);
70
+ done(tokenData.access_token);
71
+ return;
72
+ }
73
+
74
+ if (tokenData.error === "authorization_pending") {
75
+ pollTimer = setTimeout(poll, interval * 1000);
76
+ return;
77
+ }
78
+
79
+ if (tokenData.error === "slow_down") {
80
+ pollTimer = setTimeout(poll, (interval + 5) * 1000);
81
+ return;
82
+ }
83
+
84
+ // expired_token or access_denied
85
+ done(null);
86
+ } catch {
87
+ pollTimer = setTimeout(poll, interval * 1000);
88
+ }
89
+ };
90
+
91
+ pollTimer = setTimeout(poll, interval * 1000);
92
+
93
+ return html`
94
+ <sp-dialog-wrapper
95
+ open
96
+ headline="Sign in to GitHub"
97
+ cancel-label="Cancel"
98
+ @cancel=${() => {
99
+ cancelled = true;
100
+ if (pollTimer) clearTimeout(pollTimer);
101
+ done(null);
102
+ }}
103
+ @close=${() => {
104
+ cancelled = true;
105
+ if (pollTimer) clearTimeout(pollTimer);
106
+ done(null);
107
+ }}
108
+ >
109
+ <div class="github-auth-dialog">
110
+ <p>Enter this code on GitHub to authorize Jx Studio:</p>
111
+ <div class="github-auth-code">${user_code}</div>
112
+ <p>
113
+ <a href="${verification_uri}" target="_blank" rel="noopener"
114
+ >Open ${verification_uri}</a
115
+ >
116
+ </p>
117
+ <p class="github-auth-waiting">Waiting for authorization…</p>
118
+ </div>
119
+ </sp-dialog-wrapper>
120
+ `;
121
+ });
122
+ }
@@ -0,0 +1,126 @@
1
+ /** Publish a local project to GitHub — creates a new repo and pushes. */
2
+
3
+ import { html } from "lit-html";
4
+ import { ref } from "lit-html/directives/ref.js";
5
+ import { showDialog } from "../ui/layers.js";
6
+ import { authenticateGithub } from "./github-auth.js";
7
+ import { getPlatform } from "../platform.js";
8
+ import { refreshGitStatus } from "../panels/git-panel.js";
9
+ import { statusMessage } from "../panels/statusbar.js";
10
+
11
+ /**
12
+ * Full "Publish to GitHub" flow: 1. Authenticate (or reuse stored token) 2. Prompt for repo name /
13
+ * visibility 3. Create the repo via GitHub API 4. Add remote + push
14
+ *
15
+ * @param {{ projectName: string }} opts
16
+ * @returns {Promise<boolean>} True if published successfully
17
+ */
18
+ export async function publishToGithub({ projectName }) {
19
+ const token = await authenticateGithub();
20
+ if (!token) return false;
21
+
22
+ const repoOpts = await showDialog((done) => {
23
+ /** @type {HTMLInputElement | null} */
24
+ let _nameInput = null;
25
+ /** @type {HTMLInputElement | null} */
26
+ let _descInput = null;
27
+ /** @type {HTMLInputElement | null} */
28
+ let _privateToggle = null;
29
+
30
+ return html`
31
+ <sp-dialog-wrapper
32
+ open
33
+ headline="Publish to GitHub"
34
+ confirm-label="Create Repository"
35
+ cancel-label="Cancel"
36
+ @confirm=${() => {
37
+ done({
38
+ name: _nameInput?.value || projectName,
39
+ description: _descInput?.value || "",
40
+ isPrivate: _privateToggle?.checked ?? true,
41
+ });
42
+ }}
43
+ @cancel=${() => done(null)}
44
+ @close=${() => done(null)}
45
+ >
46
+ <div class="github-publish-dialog">
47
+ <sp-field-label for="repo-name">Repository name</sp-field-label>
48
+ <sp-textfield
49
+ id="repo-name"
50
+ name="repo-name"
51
+ value="${projectName}"
52
+ placeholder="my-project"
53
+ ${ref((el) => {
54
+ _nameInput = /** @type {HTMLInputElement | null} */ (el || null);
55
+ })}
56
+ ></sp-textfield>
57
+
58
+ <sp-field-label for="repo-desc">Description (optional)</sp-field-label>
59
+ <sp-textfield
60
+ id="repo-desc"
61
+ name="repo-desc"
62
+ placeholder="A brief description"
63
+ ${ref((el) => {
64
+ _descInput = /** @type {HTMLInputElement | null} */ (el || null);
65
+ })}
66
+ ></sp-textfield>
67
+
68
+ <sp-field-label>Visibility</sp-field-label>
69
+ <sp-switch
70
+ name="repo-private"
71
+ checked
72
+ ${ref((el) => {
73
+ _privateToggle = /** @type {HTMLInputElement | null} */ (el || null);
74
+ })}
75
+ >Private repository</sp-switch
76
+ >
77
+ </div>
78
+ </sp-dialog-wrapper>
79
+ `;
80
+ });
81
+
82
+ if (!repoOpts) return false;
83
+
84
+ statusMessage("Creating GitHub repository…");
85
+
86
+ const createRes = await fetch("https://api.github.com/user/repos", {
87
+ method: "POST",
88
+ headers: {
89
+ Authorization: `Bearer ${token}`,
90
+ Accept: "application/vnd.github+json",
91
+ "Content-Type": "application/json",
92
+ },
93
+ body: JSON.stringify({
94
+ name: repoOpts.name,
95
+ description: repoOpts.description,
96
+ private: repoOpts.isPrivate,
97
+ auto_init: false,
98
+ }),
99
+ });
100
+
101
+ if (!createRes.ok) {
102
+ const err = await createRes.json();
103
+ const msg = err.errors?.[0]?.message || err.message || "Failed to create repository";
104
+ statusMessage(`Error: ${msg}`);
105
+ return false;
106
+ }
107
+
108
+ const repo = await createRes.json();
109
+ const platform = getPlatform();
110
+
111
+ statusMessage("Setting remote and pushing…");
112
+
113
+ await platform.gitAddRemote("origin", repo.clone_url);
114
+
115
+ statusMessage("Pushing to GitHub…");
116
+ try {
117
+ await platform.gitPush({ setUpstream: true });
118
+ } catch (/** @type {unknown} */ e) {
119
+ statusMessage(`Push failed: ${/** @type {Error} */ (e).message}`);
120
+ return false;
121
+ }
122
+
123
+ await refreshGitStatus();
124
+ statusMessage(`Published to GitHub: ${repo.html_url}`);
125
+ return true;
126
+ }