@upstart.gg/vite-plugins 0.0.38 → 0.0.40

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 (64) hide show
  1. package/dist/upstart-editor-api.d.ts +79 -0
  2. package/dist/upstart-editor-api.d.ts.map +1 -0
  3. package/dist/upstart-editor-api.js +208 -0
  4. package/dist/upstart-editor-api.js.map +1 -0
  5. package/dist/vite-plugin-upstart-attrs.d.ts +3 -3
  6. package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -1
  7. package/dist/vite-plugin-upstart-attrs.js +227 -25
  8. package/dist/vite-plugin-upstart-attrs.js.map +1 -1
  9. package/dist/vite-plugin-upstart-branding/plugin.d.ts +17 -0
  10. package/dist/vite-plugin-upstart-branding/plugin.d.ts.map +1 -0
  11. package/dist/vite-plugin-upstart-branding/plugin.js +41 -0
  12. package/dist/vite-plugin-upstart-branding/plugin.js.map +1 -0
  13. package/dist/vite-plugin-upstart-branding/runtime.d.ts +10 -0
  14. package/dist/vite-plugin-upstart-branding/runtime.d.ts.map +1 -0
  15. package/dist/vite-plugin-upstart-branding/runtime.js +118 -0
  16. package/dist/vite-plugin-upstart-branding/runtime.js.map +1 -0
  17. package/dist/vite-plugin-upstart-branding/types.d.ts +14 -0
  18. package/dist/vite-plugin-upstart-branding/types.d.ts.map +1 -0
  19. package/dist/vite-plugin-upstart-branding/types.js +1 -0
  20. package/dist/vite-plugin-upstart-editor/plugin.d.ts +3 -3
  21. package/dist/vite-plugin-upstart-editor/plugin.d.ts.map +1 -1
  22. package/dist/vite-plugin-upstart-editor/plugin.js +3 -16
  23. package/dist/vite-plugin-upstart-editor/plugin.js.map +1 -1
  24. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +25 -11
  25. package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
  26. package/dist/vite-plugin-upstart-editor/runtime/error-handler.d.ts +5 -0
  27. package/dist/vite-plugin-upstart-editor/runtime/error-handler.d.ts.map +1 -0
  28. package/dist/vite-plugin-upstart-editor/runtime/error-handler.js +16 -0
  29. package/dist/vite-plugin-upstart-editor/runtime/error-handler.js.map +1 -0
  30. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +1 -1
  31. package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -1
  32. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts +2 -1
  33. package/dist/vite-plugin-upstart-editor/runtime/index.d.ts.map +1 -1
  34. package/dist/vite-plugin-upstart-editor/runtime/index.js +42 -7
  35. package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
  36. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts +6 -1
  37. package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -1
  38. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +423 -129
  39. package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -1
  40. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +18 -10
  41. package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
  42. package/dist/vite-plugin-upstart-theme.d.ts +3 -3
  43. package/dist/vite-plugin-upstart-theme.d.ts.map +1 -1
  44. package/dist/vite-plugin-upstart-theme.js +1 -3
  45. package/dist/vite-plugin-upstart-theme.js.map +1 -1
  46. package/package.json +12 -4
  47. package/src/tests/upstart-editor-api.test.ts +98 -174
  48. package/src/tests/vite-plugin-upstart-attrs.test.ts +408 -105
  49. package/src/tests/vite-plugin-upstart-branding.test.ts +90 -0
  50. package/src/tests/vite-plugin-upstart-editor.test.ts +1 -2
  51. package/src/upstart-editor-api.ts +90 -29
  52. package/src/vite-plugin-upstart-attrs.ts +376 -38
  53. package/src/vite-plugin-upstart-branding/plugin.ts +59 -0
  54. package/src/vite-plugin-upstart-branding/runtime.ts +128 -0
  55. package/src/vite-plugin-upstart-branding/types.ts +10 -0
  56. package/src/vite-plugin-upstart-editor/plugin.ts +4 -19
  57. package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +25 -12
  58. package/src/vite-plugin-upstart-editor/runtime/error-handler.ts +12 -0
  59. package/src/vite-plugin-upstart-editor/runtime/hover-overlay.ts +1 -1
  60. package/src/vite-plugin-upstart-editor/runtime/index.ts +39 -5
  61. package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +518 -141
  62. package/src/vite-plugin-upstart-editor/runtime/types.ts +18 -4
  63. package/src/vite-plugin-upstart-theme.ts +0 -3
  64. package/src/vite-plugin-upstart-editor/PLAN.md +0 -1391
@@ -0,0 +1,128 @@
1
+ const UPSTART_ICON_SVG = `<svg width="18" height="18" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_upstart_branding)">
3
+ <path d="M452 0H60C26.8629 0 0 26.8629 0 60V452C0 485.137 26.8629 512 60 512H452C485.137 512 512 485.137 512 452V60C512 26.8629 485.137 0 452 0Z" fill="#817FCC"/>
4
+ <path d="M346.505 112H410L377.739 307.306C374.122 329.235 365.764 348.423 352.664 364.87C339.564 381.316 322.896 394.141 302.66 403.343C282.424 412.448 259.841 417 234.913 417C209.984 417 188.966 412.448 171.858 403.343C154.75 394.141 142.383 381.316 134.758 364.87C127.133 348.423 125.129 329.235 128.746 307.306L161.006 112H224.501L192.974 301.872C191.214 313.326 192.094 323.508 195.613 332.416C199.23 341.325 205.194 348.325 213.503 353.416C221.813 358.506 232.078 361.052 244.297 361.052C256.615 361.052 267.76 358.506 277.731 353.416C287.801 348.325 296.061 341.325 302.513 332.416C309.063 323.508 313.218 313.326 314.978 301.872L346.505 112Z" fill="white"/>
5
+ <path d="M101 119C101 115.134 104.134 112 108 112H164C167.866 112 171 115.134 171 119V160C171 163.866 167.866 167 164 167H108C104.134 167 101 163.866 101 160V119Z" fill="white"/>
6
+ </g>
7
+ <defs><clipPath id="clip0_upstart_branding"><rect width="512" height="512" fill="white"/></clipPath></defs>
8
+ </svg>`;
9
+
10
+ const BADGE_ID = "upstart-branding-badge";
11
+ const DISMISS_DELAY_MS = 4000;
12
+
13
+ let isInitialized = false;
14
+
15
+ /**
16
+ * Initialize the Upstart branding badge.
17
+ * Injects a fixed-position badge at the bottom center of the viewport.
18
+ * The badge slides up and fades in, then dismisses after ~4 seconds or on scroll.
19
+ */
20
+ export function initUpstartBranding(): void {
21
+ if (typeof window === "undefined" || typeof document === "undefined") {
22
+ return;
23
+ }
24
+
25
+ if (isInitialized) {
26
+ return;
27
+ }
28
+
29
+ isInitialized = true;
30
+
31
+ // Delay initialization to wait for React hydration
32
+ if ("scheduler" in globalThis) {
33
+ // @ts-expect-error not yet in TS types
34
+ globalThis.scheduler.postTask(() => injectBranding(), { delay: 250, priority: "background" });
35
+ } else {
36
+ setTimeout(() => injectBranding(), 250);
37
+ }
38
+ }
39
+
40
+ function injectBranding(): void {
41
+ const style = document.createElement("style");
42
+ style.textContent = `
43
+ @keyframes upstart-branding-slide-in {
44
+ from {
45
+ opacity: 0;
46
+ transform: translateX(-50%) translateY(20px);
47
+ }
48
+ to {
49
+ opacity: 1;
50
+ transform: translateX(-50%) translateY(0);
51
+ }
52
+ }
53
+
54
+ #${BADGE_ID} {
55
+ position: fixed;
56
+ bottom: 16px;
57
+ left: 50%;
58
+ transform: translateX(-50%);
59
+ z-index: 2147483647;
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 6px;
63
+ padding: 8px 14px;
64
+ background: rgba(30, 30, 30, 0.75);
65
+ backdrop-filter: blur(12px);
66
+ -webkit-backdrop-filter: blur(12px);
67
+ border-radius: 9999px;
68
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.18), 0 1px 4px rgba(0, 0, 0, 0.1);
69
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
70
+ font-size: 13px;
71
+ font-weight: 500;
72
+ color: rgba(255, 255, 255, 0.9);
73
+ text-decoration: none;
74
+ cursor: pointer;
75
+ pointer-events: auto;
76
+ animation: upstart-branding-slide-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
77
+ transition: opacity 0.4s ease, transform 0.4s ease;
78
+ white-space: nowrap;
79
+ line-height: 1;
80
+ }
81
+
82
+ #${BADGE_ID}:hover {
83
+ background: rgba(30, 30, 30, 0.9);
84
+ color: #fff;
85
+ }
86
+
87
+ #${BADGE_ID}.upstart-branding-hiding {
88
+ opacity: 0;
89
+ transform: translateX(-50%) translateY(12px);
90
+ pointer-events: none;
91
+ }
92
+
93
+ #${BADGE_ID} svg {
94
+ flex-shrink: 0;
95
+ border-radius: 3px;
96
+ }
97
+ `;
98
+ document.head.appendChild(style);
99
+
100
+ const badge = document.createElement("a");
101
+ badge.id = BADGE_ID;
102
+ badge.href = "https://upstart.gg";
103
+ badge.target = "_blank";
104
+ badge.rel = "noopener noreferrer";
105
+ badge.innerHTML = `${UPSTART_ICON_SVG}<span>Made with Upstart</span>`;
106
+
107
+ document.body.appendChild(badge);
108
+
109
+ let dismissed = false;
110
+ function dismiss() {
111
+ if (dismissed) return;
112
+ dismissed = true;
113
+ badge.classList.add("upstart-branding-hiding");
114
+ setTimeout(() => {
115
+ badge.remove();
116
+ style.remove();
117
+ }, 500);
118
+ window.removeEventListener("scroll", onScroll);
119
+ clearTimeout(timer);
120
+ }
121
+
122
+ const timer = setTimeout(dismiss, DISMISS_DELAY_MS);
123
+
124
+ function onScroll() {
125
+ dismiss();
126
+ }
127
+ window.addEventListener("scroll", onScroll, { passive: true, once: true });
128
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Build-time plugin options for the Upstart branding badge.
3
+ */
4
+ export interface UpstartBrandingPluginOptions {
5
+ /**
6
+ * Enable or disable the branding badge.
7
+ * @default false
8
+ */
9
+ enabled?: boolean;
10
+ }
@@ -36,12 +36,7 @@ export const upstartEditor = createUnplugin<UpstartEditorPluginOptions>((options
36
36
  return null;
37
37
  }
38
38
 
39
- if (!/\.(t|j)sx?$/.test(cleanId)) {
40
- return null;
41
- }
42
-
43
- const filename = path.basename(cleanId);
44
- if (!filename.startsWith("main.") && !filename.startsWith("index.")) {
39
+ if (!cleanId.includes("entry.client.tsx")) {
45
40
  return null;
46
41
  }
47
42
 
@@ -49,21 +44,11 @@ export const upstartEditor = createUnplugin<UpstartEditorPluginOptions>((options
49
44
  return null;
50
45
  }
51
46
 
52
- const injection = [
53
- `import { initUpstartEditor } from ${JSON.stringify(runtimePath)};`,
54
- "",
55
- "if (typeof window !== 'undefined') {",
56
- " if (document.readyState === 'loading') {",
57
- " document.addEventListener('DOMContentLoaded', () => initUpstartEditor());",
58
- " } else {",
59
- " initUpstartEditor();",
60
- " }",
61
- "}",
62
- "",
63
- ].join("\n");
47
+ const imports = `import { initUpstartEditor } from ${JSON.stringify(runtimePath)};`;
48
+ const injection = "requestIdleCallback(initUpstartEditor);";
64
49
 
65
50
  return {
66
- code: `${injection}${code}`,
51
+ code: `${imports}${code}${injection}`,
67
52
  map: null,
68
53
  };
69
54
  },
@@ -33,39 +33,52 @@ function handleClick(event: MouseEvent): void {
33
33
  return;
34
34
  }
35
35
 
36
+ console.debug("[Upstart Editor] Click event:", event);
37
+
36
38
  const target = event.target as HTMLElement | null;
37
39
  if (!target) {
40
+ console.warn("[Upstart Editor] Click target is not an HTMLElement");
38
41
  return;
39
42
  }
40
43
 
44
+ const activeLink = target.closest("a[data-upstart-editor-active]");
45
+ const isFormControlClick = Boolean(
46
+ (target as HTMLElement | null)?.closest("input, textarea, select, button"),
47
+ );
48
+ if (activeLink || !isFormControlClick) {
49
+ event.preventDefault();
50
+ }
51
+
41
52
  if (target.closest("[contenteditable='true']")) {
53
+ console.info("[Upstart Editor] Click ignored: target is inside a contenteditable element");
42
54
  return;
43
55
  }
44
56
 
45
- const component = target.closest<HTMLElement>("[data-upstart-component]");
46
- if (!component) {
57
+ const element = target.closest<HTMLElement>("[data-upstart-hash]");
58
+ // const i18nKey = target.closest<HTMLElement>("[data-upstart-i18n]");
59
+
60
+ if (!element) {
61
+ console.info("[Upstart Editor] Click ignored: no ancestral element with data-upstart-hash found");
47
62
  return;
48
63
  }
49
64
 
50
- event.preventDefault();
51
65
  event.stopPropagation();
52
66
 
53
- const hash = component.dataset.upstartHash;
54
- const componentName = component.dataset.upstartComponent;
55
- const filePath = component.dataset.upstartFile ?? "";
56
-
57
- if (!hash || !componentName) {
58
- return;
59
- }
67
+ console.log("Element clicked dataset:", element.dataset);
60
68
 
61
- const rect = component.getBoundingClientRect();
69
+ const hash = element.dataset.upstartHash as string;
70
+ const componentName = element.dataset.upstartComponent;
71
+ const filePath = element.dataset.upstartFile ?? "";
72
+ const classNameId = element.dataset.upstartClassnameId ?? "";
73
+ const rect = element.getBoundingClientRect();
62
74
 
63
75
  sendToParent({
64
76
  type: "element-clicked",
65
77
  hash,
66
78
  componentName,
67
79
  filePath,
68
- currentClassName: component.className,
80
+ classNameId,
81
+ currentClassName: element.className,
69
82
  bounds: {
70
83
  top: rect.top,
71
84
  left: rect.left,
@@ -0,0 +1,12 @@
1
+ import { sendToParent } from "./utils.js";
2
+
3
+ export function initErrorHandler() {
4
+ // Global error handler for uncaught errors in the editor
5
+ window.addEventListener("error", (event) => {
6
+ console.error("[Upstart Editor] Uncaught error in editor:", event.error);
7
+ sendToParent({
8
+ type: "editor-error",
9
+ error: event.error,
10
+ });
11
+ });
12
+ }
@@ -49,7 +49,7 @@ function handleMouseOver(event: MouseEvent): void {
49
49
  }
50
50
 
51
51
  const component = target.closest<HTMLElement>("[data-upstart-component]");
52
- if (!component) {
52
+ if (!component || component.dataset.upstartEditorActive) {
53
53
  return;
54
54
  }
55
55
 
@@ -1,6 +1,7 @@
1
1
  import { initClickHandler } from "./click-handler.js";
2
2
  import { initHoverOverlay, hideOverlays } from "./hover-overlay.js";
3
- import { initTextEditor, destroyAllActiveEditors } from "./text-editor.js";
3
+ import { initErrorHandler } from "./error-handler.js";
4
+ import { initTextEditor, activateAllEditors, destroyAllActiveEditors } from "./text-editor.js";
4
5
  import { sendToParent } from "./utils.js";
5
6
  import type { EditorMode, UpstartParentMessage } from "./types.js";
6
7
 
@@ -19,7 +20,7 @@ export function getCurrentMode(): EditorMode {
19
20
  */
20
21
  export function setMode(mode: EditorMode): void {
21
22
  currentMode = mode;
22
-
23
+ console.log(`[Upstart Editor] Setting mode to: ${mode}`);
23
24
  if (mode === "edit") {
24
25
  enableEditMode();
25
26
  } else {
@@ -32,17 +33,18 @@ export function setMode(mode: EditorMode): void {
32
33
  */
33
34
  export function initUpstartEditor(): void {
34
35
  if (typeof window === "undefined") {
36
+ console.warn("[Upstart Editor] Cannot initialize editor: not running in a browser environment");
35
37
  return;
36
38
  }
37
39
 
38
40
  if (isInitialized) {
41
+ console.log("[Upstart Editor] Editor is already initialized");
39
42
  return;
40
43
  }
41
44
 
42
45
  try {
43
46
  console.log("[Upstart Editor] Initializing...");
44
47
 
45
- currentMode = "preview";
46
48
  isInitialized = true;
47
49
 
48
50
  window.addEventListener("message", handleParentMessage);
@@ -50,6 +52,7 @@ export function initUpstartEditor(): void {
50
52
  initTextEditor();
51
53
  initClickHandler();
52
54
  initHoverOverlay();
55
+ initErrorHandler();
53
56
 
54
57
  sendToParent({ type: "editor-ready" });
55
58
  } catch (error) {
@@ -61,20 +64,50 @@ export function initUpstartEditor(): void {
61
64
  }
62
65
  }
63
66
 
67
+ const ALLOWED_ORIGINS = ["http://localhost:8080", /upstart.gg$/];
68
+
69
+ const matchAllowedOrigins = (origin: string) => {
70
+ return ALLOWED_ORIGINS.some((allowedOrigin) => {
71
+ if (typeof allowedOrigin === "string") {
72
+ return origin === allowedOrigin;
73
+ } else if (allowedOrigin instanceof RegExp) {
74
+ return allowedOrigin.test(origin);
75
+ }
76
+ return false;
77
+ });
78
+ };
79
+
64
80
  function handleParentMessage(event: MessageEvent): void {
65
81
  const message = event.data as UpstartParentMessage | undefined;
66
82
 
67
- if (!message || message.source !== "upstart-editor-parent") {
83
+ console.log("[Upstart Editor] Received message from parent:", { event, message });
84
+
85
+ if (!message || !matchAllowedOrigins(event.origin)) {
86
+ console.warn("[Upstart Editor] Ignoring message from unknown source:", event.origin);
68
87
  return;
69
88
  }
70
89
 
71
90
  if (message.type === "set-mode") {
72
- setMode(message.mode);
91
+ console.log("Setting editor mode to:", message.mode);
92
+ if ("scheduler" in globalThis) {
93
+ // @ts-expect-error not yet in TS types
94
+ globalThis.scheduler.postTask(
95
+ () => {
96
+ setMode(message.mode);
97
+ },
98
+ { delay: 250, priority: "background" },
99
+ );
100
+ } else {
101
+ setTimeout(() => {
102
+ setMode(message.mode);
103
+ }, 250);
104
+ }
73
105
  }
74
106
  }
75
107
 
76
108
  function enableEditMode(): void {
77
109
  console.log("[Upstart Editor] Edit mode enabled");
110
+ activateAllEditors();
78
111
  }
79
112
 
80
113
  function disableEditMode(): void {
@@ -85,6 +118,7 @@ function disableEditMode(): void {
85
118
 
86
119
  export { initTextEditor } from "./text-editor.js";
87
120
  export { initClickHandler } from "./click-handler.js";
121
+ export { initErrorHandler } from "./error-handler.js";
88
122
  export { initHoverOverlay } from "./hover-overlay.js";
89
123
  export { sendToParent } from "./utils.js";
90
124
  export type { EditorMessage, UpstartEditorMessage } from "./types.js";