@officexapp/catalogs-cli 0.4.11 → 0.4.13
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/index.js +423 -24
- package/dist/renderer/index.js +18 -9
- package/dist/renderer/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -860,6 +860,11 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
860
860
|
const schemaJson = JSON.stringify(schema).replace(/<\/script/gi, "<\\/script");
|
|
861
861
|
const themeColor = schema.settings?.theme?.primary_color || "#6366f1";
|
|
862
862
|
const stripeEnabled = devConfig?.stripeEnabled ?? false;
|
|
863
|
+
const prodUrl = devConfig?.prodUrl || "";
|
|
864
|
+
const schemaVersion = schema.schema_version || "1.0";
|
|
865
|
+
const pageIds = Object.keys(schema.pages || {});
|
|
866
|
+
const routingEdges = schema.routing?.edges || [];
|
|
867
|
+
const routingEntry = schema.routing?.entry || pageIds[0] || "";
|
|
863
868
|
let validationHtml = "";
|
|
864
869
|
if (validation && (validation.errors.length > 0 || validation.warnings.length > 0)) {
|
|
865
870
|
const items = [
|
|
@@ -875,6 +880,18 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
875
880
|
${items}
|
|
876
881
|
</div>`;
|
|
877
882
|
}
|
|
883
|
+
const pagesGraphJson = JSON.stringify({
|
|
884
|
+
pages: pageIds.map((id) => ({
|
|
885
|
+
id,
|
|
886
|
+
title: schema.pages[id]?.title || schema.pages[id]?.settings?.title || id
|
|
887
|
+
})),
|
|
888
|
+
edges: routingEdges.map((e) => ({
|
|
889
|
+
from: e.from,
|
|
890
|
+
to: e.to,
|
|
891
|
+
label: e.conditions ? "conditional" : e.is_default ? "default" : ""
|
|
892
|
+
})),
|
|
893
|
+
entry: routingEntry
|
|
894
|
+
}).replace(/<\/script/gi, "<\\/script");
|
|
878
895
|
return `<!DOCTYPE html>
|
|
879
896
|
<html lang="en">
|
|
880
897
|
<head>
|
|
@@ -884,7 +901,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
884
901
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
885
902
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
886
903
|
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800;900&family=DM+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&display=swap" rel="stylesheet" />
|
|
887
|
-
<link rel="stylesheet" href="/__renderer/index.css" />
|
|
904
|
+
<link rel="stylesheet" href="/__renderer/styles/index.css" />
|
|
888
905
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
889
906
|
<script type="importmap">
|
|
890
907
|
{
|
|
@@ -893,34 +910,125 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
893
910
|
"react-dom": "https://esm.sh/react-dom@18.3.1",
|
|
894
911
|
"react-dom/client": "https://esm.sh/react-dom@18.3.1/client",
|
|
895
912
|
"react/jsx-runtime": "https://esm.sh/react@18.3.1/jsx-runtime",
|
|
896
|
-
"hls.js": "https://esm.sh/hls.js@1.5.17"
|
|
913
|
+
"hls.js": "https://esm.sh/hls.js@1.5.17",
|
|
914
|
+
"tldraw": "https://esm.sh/tldraw@2.4.1?external=react,react-dom",
|
|
915
|
+
"@tldraw/tldraw": "https://esm.sh/tldraw@2.4.1?external=react,react-dom"
|
|
897
916
|
}
|
|
898
917
|
}
|
|
899
918
|
</script>
|
|
919
|
+
<link rel="stylesheet" href="https://esm.sh/tldraw@2.4.1/tldraw.css" />
|
|
900
920
|
<style>
|
|
901
|
-
/* Dev
|
|
902
|
-
#
|
|
921
|
+
/* Dev toolbar \u2014 expanded */
|
|
922
|
+
#__dev_toolbar {
|
|
903
923
|
position: fixed; top: 0; left: 0; right: 0; z-index: 10000;
|
|
904
924
|
background: linear-gradient(135deg, #1e1b4b 0%, #312e81 100%);
|
|
905
925
|
color: #c7d2fe; font-family: system-ui, sans-serif;
|
|
906
|
-
font-size: 12px; padding:
|
|
907
|
-
display: flex; align-items: center;
|
|
926
|
+
font-size: 12px; padding: 0 12px; height: 32px;
|
|
927
|
+
display: flex; align-items: center; gap: 6px;
|
|
908
928
|
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
|
909
929
|
}
|
|
910
|
-
#
|
|
911
|
-
#
|
|
912
|
-
|
|
930
|
+
#__dev_toolbar a { color: #a5b4fc; text-decoration: none; }
|
|
931
|
+
#__dev_toolbar a:hover { text-decoration: underline; }
|
|
932
|
+
#__dev_toolbar .tb-sep { width: 1px; height: 16px; background: rgba(165,180,252,0.2); margin: 0 4px; }
|
|
933
|
+
#__dev_toolbar .tb-btn {
|
|
934
|
+
background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.1);
|
|
935
|
+
color: #c7d2fe; border-radius: 6px; padding: 2px 8px; cursor: pointer;
|
|
936
|
+
font-size: 11px; font-family: inherit; display: flex; align-items: center; gap: 4px;
|
|
937
|
+
transition: background 0.15s;
|
|
938
|
+
}
|
|
939
|
+
#__dev_toolbar .tb-btn:hover { background: rgba(255,255,255,0.15); }
|
|
940
|
+
#__dev_toolbar .tb-btn.active { background: rgba(99,102,241,0.4); border-color: #6366f1; }
|
|
941
|
+
#__dev_toolbar .tb-right { margin-left: auto; display: flex; align-items: center; gap: 6px; }
|
|
942
|
+
body.toolbar-expanded { padding-top: 32px; }
|
|
943
|
+
|
|
944
|
+
/* Minimized pill */
|
|
945
|
+
#__dev_pill {
|
|
946
|
+
position: fixed; top: 8px; right: 8px; z-index: 10000;
|
|
947
|
+
background: linear-gradient(135deg, #1e1b4b 0%, #312e81 100%);
|
|
948
|
+
color: #e0e7ff; font-family: system-ui, sans-serif;
|
|
949
|
+
font-size: 11px; padding: 4px 10px; border-radius: 20px;
|
|
950
|
+
cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
951
|
+
display: none; align-items: center; gap: 6px;
|
|
952
|
+
border: 1px solid rgba(99,102,241,0.3);
|
|
953
|
+
}
|
|
954
|
+
#__dev_pill:hover { background: linear-gradient(135deg, #312e81 0%, #4338ca 100%); }
|
|
955
|
+
|
|
956
|
+
/* Pages graph panel */
|
|
957
|
+
#__dev_pages_panel {
|
|
958
|
+
position: fixed; top: 32px; left: 0; right: 0; z-index: 9999;
|
|
959
|
+
height: 0; overflow: hidden; transition: height 0.25s ease;
|
|
960
|
+
background: #0f0e26; border-bottom: 1px solid rgba(99,102,241,0.2);
|
|
961
|
+
}
|
|
962
|
+
#__dev_pages_panel.open { height: 40vh; }
|
|
963
|
+
#__dev_pages_canvas {
|
|
964
|
+
width: 100%; height: 100%; position: relative;
|
|
965
|
+
overflow: auto; padding: 20px;
|
|
966
|
+
}
|
|
967
|
+
.pg-node {
|
|
968
|
+
position: absolute; background: #1e1b4b; border: 2px solid #4338ca;
|
|
969
|
+
border-radius: 10px; padding: 8px 14px; color: #e0e7ff;
|
|
970
|
+
font-size: 12px; font-family: system-ui, sans-serif;
|
|
971
|
+
cursor: pointer; white-space: nowrap; min-width: 80px; text-align: center;
|
|
972
|
+
transition: border-color 0.15s, box-shadow 0.15s;
|
|
973
|
+
}
|
|
974
|
+
.pg-node:hover { border-color: #818cf8; box-shadow: 0 0 12px rgba(99,102,241,0.3); }
|
|
975
|
+
.pg-node.entry { border-color: #4ade80; }
|
|
976
|
+
.pg-node.current { border-color: ${themeColor}; box-shadow: 0 0 16px ${themeColor}44; }
|
|
977
|
+
.pg-edge-label {
|
|
978
|
+
position: absolute; color: #818cf8; font-size: 10px;
|
|
979
|
+
font-family: system-ui, sans-serif; pointer-events: none;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/* Debug badge colors */
|
|
983
|
+
.debug-none { color: #6b7280; }
|
|
984
|
+
.debug-slim { color: #fbbf24; }
|
|
985
|
+
.debug-verbose { color: #ef4444; }
|
|
913
986
|
</style>
|
|
914
987
|
</head>
|
|
915
|
-
<body>
|
|
916
|
-
<!-- Dev
|
|
917
|
-
<div id="
|
|
918
|
-
<
|
|
919
|
-
<span>
|
|
920
|
-
|
|
921
|
-
|
|
988
|
+
<body class="toolbar-expanded">
|
|
989
|
+
<!-- Dev Toolbar (expanded) -->
|
|
990
|
+
<div id="__dev_toolbar">
|
|
991
|
+
<strong style="color:#e0e7ff">${schema.slug || "catalog"}</strong>
|
|
992
|
+
<span style="color:#818cf8;font-size:10px">v${schemaVersion}</span>
|
|
993
|
+
|
|
994
|
+
<div class="tb-sep"></div>
|
|
995
|
+
|
|
996
|
+
<button class="tb-btn" id="__tb_pages" title="Page graph">
|
|
997
|
+
<span style="font-size:13px">📄</span> Pages
|
|
998
|
+
</button>
|
|
999
|
+
|
|
1000
|
+
<button class="tb-btn" id="__tb_inspect" title="Toggle element inspector">
|
|
1001
|
+
<span style="font-size:13px">🎯</span> Inspect
|
|
1002
|
+
</button>
|
|
1003
|
+
|
|
1004
|
+
<button class="tb-btn" id="__tb_debug" title="Cycle debug mode: none / slim / verbose">
|
|
1005
|
+
<span style="font-size:13px">🐛</span> Debug: <span id="__tb_debug_label" class="debug-none">none</span>
|
|
1006
|
+
</button>
|
|
1007
|
+
|
|
1008
|
+
<div class="tb-right">
|
|
1009
|
+
${stripeEnabled ? '<span style="color:#4ade80">●</span> <span>Stripe</span>' : '<span style="color:#fbbf24">○</span> <span>Stripe stubbed</span>'}
|
|
1010
|
+
|
|
1011
|
+
<div class="tb-sep"></div>
|
|
922
1012
|
<a href="/__dev_events" target="_blank">Events</a>
|
|
923
|
-
|
|
1013
|
+
|
|
1014
|
+
<div class="tb-sep"></div>
|
|
1015
|
+
${prodUrl ? `<a href="${prodUrl}" target="_blank" title="Open production URL">↗ Prod</a>` : '<span style="color:#6b7280;cursor:default" title="Push catalog to see prod URL">↗ Prod</span>'}
|
|
1016
|
+
|
|
1017
|
+
<div class="tb-sep"></div>
|
|
1018
|
+
<button class="tb-btn" id="__tb_minimize" title="Minimize toolbar" style="padding:2px 6px;font-size:14px;line-height:1">—</button>
|
|
1019
|
+
</div>
|
|
1020
|
+
</div>
|
|
1021
|
+
|
|
1022
|
+
<!-- Minimized pill -->
|
|
1023
|
+
<div id="__dev_pill">
|
|
1024
|
+
<strong>${schema.slug || "catalog"}</strong>
|
|
1025
|
+
<span style="font-size:13px">▲</span>
|
|
1026
|
+
</div>
|
|
1027
|
+
|
|
1028
|
+
<!-- Pages graph panel -->
|
|
1029
|
+
<div id="__dev_pages_panel">
|
|
1030
|
+
<svg id="__dev_pages_svg" style="position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:1"></svg>
|
|
1031
|
+
<div id="__dev_pages_canvas"></div>
|
|
924
1032
|
</div>
|
|
925
1033
|
|
|
926
1034
|
<!-- React mount point -->
|
|
@@ -928,7 +1036,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
928
1036
|
|
|
929
1037
|
${validationHtml}
|
|
930
1038
|
|
|
931
|
-
<!--
|
|
1039
|
+
<!-- Dev toolbar logic + CatalogRenderer mount -->
|
|
932
1040
|
<script type="module">
|
|
933
1041
|
import React from "react";
|
|
934
1042
|
import { createRoot } from "react-dom/client";
|
|
@@ -936,11 +1044,232 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
936
1044
|
|
|
937
1045
|
const schema = ${schemaJson};
|
|
938
1046
|
const port = ${port};
|
|
1047
|
+
const pagesGraph = ${pagesGraphJson};
|
|
1048
|
+
|
|
1049
|
+
// ============ Toolbar State ============
|
|
1050
|
+
|
|
1051
|
+
// --- Minimize/Expand ---
|
|
1052
|
+
const toolbar = document.getElementById("__dev_toolbar");
|
|
1053
|
+
const pill = document.getElementById("__dev_pill");
|
|
1054
|
+
const pagesPanel = document.getElementById("__dev_pages_panel");
|
|
1055
|
+
let minimized = sessionStorage.getItem("__dev_toolbar_minimized") === "true";
|
|
1056
|
+
|
|
1057
|
+
function applyMinimized() {
|
|
1058
|
+
if (minimized) {
|
|
1059
|
+
toolbar.style.display = "none";
|
|
1060
|
+
pill.style.display = "flex";
|
|
1061
|
+
document.body.classList.remove("toolbar-expanded");
|
|
1062
|
+
if (pagesPanel.classList.contains("open")) pagesPanel.classList.remove("open");
|
|
1063
|
+
} else {
|
|
1064
|
+
toolbar.style.display = "flex";
|
|
1065
|
+
pill.style.display = "none";
|
|
1066
|
+
document.body.classList.add("toolbar-expanded");
|
|
1067
|
+
}
|
|
1068
|
+
sessionStorage.setItem("__dev_toolbar_minimized", String(minimized));
|
|
1069
|
+
}
|
|
1070
|
+
applyMinimized();
|
|
1071
|
+
|
|
1072
|
+
document.getElementById("__tb_minimize").addEventListener("click", () => {
|
|
1073
|
+
minimized = true; applyMinimized();
|
|
1074
|
+
});
|
|
1075
|
+
pill.addEventListener("click", () => {
|
|
1076
|
+
minimized = false; applyMinimized();
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
// --- Debug Mode Toggle ---
|
|
1080
|
+
const debugModes = ["none", "slim", "verbose"];
|
|
1081
|
+
const debugColors = { none: "debug-none", slim: "debug-slim", verbose: "debug-verbose" };
|
|
1082
|
+
let debugIndex = debugModes.indexOf(new URL(location.href).searchParams.get("debug_mode") || "none");
|
|
1083
|
+
if (debugIndex < 0) debugIndex = 0;
|
|
1084
|
+
const debugLabel = document.getElementById("__tb_debug_label");
|
|
1085
|
+
const debugBtn = document.getElementById("__tb_debug");
|
|
1086
|
+
|
|
1087
|
+
function applyDebug() {
|
|
1088
|
+
const mode = debugModes[debugIndex];
|
|
1089
|
+
debugLabel.textContent = mode;
|
|
1090
|
+
debugLabel.className = debugColors[mode];
|
|
1091
|
+
debugBtn.classList.toggle("active", mode !== "none");
|
|
1092
|
+
const u = new URL(location.href);
|
|
1093
|
+
if (mode === "none") u.searchParams.delete("debug_mode");
|
|
1094
|
+
else u.searchParams.set("debug_mode", mode);
|
|
1095
|
+
history.replaceState(null, "", u.toString());
|
|
1096
|
+
}
|
|
1097
|
+
applyDebug();
|
|
1098
|
+
|
|
1099
|
+
debugBtn.addEventListener("click", () => {
|
|
1100
|
+
debugIndex = (debugIndex + 1) % debugModes.length;
|
|
1101
|
+
applyDebug();
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
// --- Element Inspector Toggle ---
|
|
1105
|
+
let inspectorActive = false;
|
|
1106
|
+
const inspectBtn = document.getElementById("__tb_inspect");
|
|
1107
|
+
|
|
1108
|
+
inspectBtn.addEventListener("click", () => {
|
|
1109
|
+
inspectorActive = !inspectorActive;
|
|
1110
|
+
window.__devInspectorActive = inspectorActive;
|
|
1111
|
+
inspectBtn.classList.toggle("active", inspectorActive);
|
|
1112
|
+
document.body.style.cursor = inspectorActive ? "crosshair" : "";
|
|
1113
|
+
window.dispatchEvent(new CustomEvent("dev:inspector-toggle"));
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
// --- Pages Graph Panel ---
|
|
1117
|
+
let pagesOpen = false;
|
|
1118
|
+
const pagesBtn = document.getElementById("__tb_pages");
|
|
1119
|
+
const pagesCanvas = document.getElementById("__dev_pages_canvas");
|
|
1120
|
+
const pagesSvg = document.getElementById("__dev_pages_svg");
|
|
1121
|
+
let currentPageId = pagesGraph.entry;
|
|
1122
|
+
|
|
1123
|
+
function buildPagesGraph() {
|
|
1124
|
+
pagesCanvas.innerHTML = "";
|
|
1125
|
+
pagesSvg.innerHTML = "";
|
|
1126
|
+
if (pagesGraph.pages.length === 0) {
|
|
1127
|
+
pagesCanvas.innerHTML = '<div style="color:#818cf8;padding:20px;font-size:13px">No pages defined</div>';
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// BFS layout: levels from entry
|
|
1132
|
+
const adj = {};
|
|
1133
|
+
pagesGraph.pages.forEach(p => { adj[p.id] = []; });
|
|
1134
|
+
pagesGraph.edges.forEach(e => {
|
|
1135
|
+
if (e.from && e.to && adj[e.from]) adj[e.from].push(e.to);
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
const levels = {};
|
|
1139
|
+
const visited = new Set();
|
|
1140
|
+
const queue = [pagesGraph.entry];
|
|
1141
|
+
visited.add(pagesGraph.entry);
|
|
1142
|
+
levels[pagesGraph.entry] = 0;
|
|
1143
|
+
|
|
1144
|
+
while (queue.length > 0) {
|
|
1145
|
+
const node = queue.shift();
|
|
1146
|
+
for (const child of (adj[node] || [])) {
|
|
1147
|
+
if (!visited.has(child)) {
|
|
1148
|
+
visited.add(child);
|
|
1149
|
+
levels[child] = (levels[node] || 0) + 1;
|
|
1150
|
+
queue.push(child);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// Place unvisited nodes at max level + 1
|
|
1156
|
+
let maxLevel = Math.max(0, ...Object.values(levels));
|
|
1157
|
+
pagesGraph.pages.forEach(p => {
|
|
1158
|
+
if (levels[p.id] === undefined) {
|
|
1159
|
+
levels[p.id] = maxLevel + 1;
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
// Group by level for positioning
|
|
1164
|
+
const byLevel = {};
|
|
1165
|
+
pagesGraph.pages.forEach(p => {
|
|
1166
|
+
const l = levels[p.id];
|
|
1167
|
+
if (!byLevel[l]) byLevel[l] = [];
|
|
1168
|
+
byLevel[l].push(p);
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
const nodeWidth = 120;
|
|
1172
|
+
const nodeHeight = 36;
|
|
1173
|
+
const levelGapY = 80;
|
|
1174
|
+
const nodeGapX = 160;
|
|
1175
|
+
const positions = {};
|
|
1176
|
+
|
|
1177
|
+
Object.keys(byLevel).sort((a, b) => a - b).forEach(level => {
|
|
1178
|
+
const nodes = byLevel[level];
|
|
1179
|
+
const totalWidth = nodes.length * nodeGapX;
|
|
1180
|
+
const startX = (pagesCanvas.offsetWidth || 600) / 2 - totalWidth / 2 + nodeGapX / 2 - nodeWidth / 2;
|
|
1181
|
+
nodes.forEach((node, i) => {
|
|
1182
|
+
const x = startX + i * nodeGapX;
|
|
1183
|
+
const y = 20 + level * levelGapY;
|
|
1184
|
+
positions[node.id] = { x, y };
|
|
1185
|
+
|
|
1186
|
+
const div = document.createElement("div");
|
|
1187
|
+
div.className = "pg-node" +
|
|
1188
|
+
(node.id === pagesGraph.entry ? " entry" : "") +
|
|
1189
|
+
(node.id === currentPageId ? " current" : "");
|
|
1190
|
+
div.style.left = x + "px";
|
|
1191
|
+
div.style.top = y + "px";
|
|
1192
|
+
div.textContent = node.title;
|
|
1193
|
+
div.title = "Navigate to: " + node.id;
|
|
1194
|
+
div.addEventListener("click", () => {
|
|
1195
|
+
window.dispatchEvent(new CustomEvent("dev:navigate", { detail: { pageId: node.id } }));
|
|
1196
|
+
currentPageId = node.id;
|
|
1197
|
+
buildPagesGraph();
|
|
1198
|
+
});
|
|
1199
|
+
pagesCanvas.appendChild(div);
|
|
1200
|
+
});
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
// Draw edges as SVG arrows
|
|
1204
|
+
pagesGraph.edges.forEach(edge => {
|
|
1205
|
+
if (!positions[edge.from] || !positions[edge.to]) return;
|
|
1206
|
+
const from = positions[edge.from];
|
|
1207
|
+
const to = positions[edge.to];
|
|
1208
|
+
const x1 = from.x + nodeWidth / 2;
|
|
1209
|
+
const y1 = from.y + nodeHeight;
|
|
1210
|
+
const x2 = to.x + nodeWidth / 2;
|
|
1211
|
+
const y2 = to.y;
|
|
1212
|
+
|
|
1213
|
+
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
1214
|
+
line.setAttribute("x1", x1);
|
|
1215
|
+
line.setAttribute("y1", y1);
|
|
1216
|
+
line.setAttribute("x2", x2);
|
|
1217
|
+
line.setAttribute("y2", y2);
|
|
1218
|
+
line.setAttribute("stroke", "#4338ca");
|
|
1219
|
+
line.setAttribute("stroke-width", "2");
|
|
1220
|
+
line.setAttribute("marker-end", "url(#arrowhead)");
|
|
1221
|
+
pagesSvg.appendChild(line);
|
|
1222
|
+
|
|
1223
|
+
// Edge label
|
|
1224
|
+
if (edge.label) {
|
|
1225
|
+
const lbl = document.createElement("div");
|
|
1226
|
+
lbl.className = "pg-edge-label";
|
|
1227
|
+
lbl.style.left = ((x1 + x2) / 2 - 20) + "px";
|
|
1228
|
+
lbl.style.top = ((y1 + y2) / 2 - 8) + "px";
|
|
1229
|
+
lbl.textContent = edge.label;
|
|
1230
|
+
pagesCanvas.appendChild(lbl);
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1233
|
+
|
|
1234
|
+
// SVG arrowhead marker
|
|
1235
|
+
if (!pagesSvg.querySelector("defs")) {
|
|
1236
|
+
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
1237
|
+
const marker = document.createElementNS("http://www.w3.org/2000/svg", "marker");
|
|
1238
|
+
marker.setAttribute("id", "arrowhead");
|
|
1239
|
+
marker.setAttribute("markerWidth", "10");
|
|
1240
|
+
marker.setAttribute("markerHeight", "7");
|
|
1241
|
+
marker.setAttribute("refX", "9");
|
|
1242
|
+
marker.setAttribute("refY", "3.5");
|
|
1243
|
+
marker.setAttribute("orient", "auto");
|
|
1244
|
+
const poly = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
|
|
1245
|
+
poly.setAttribute("points", "0 0, 10 3.5, 0 7");
|
|
1246
|
+
poly.setAttribute("fill", "#4338ca");
|
|
1247
|
+
marker.appendChild(poly);
|
|
1248
|
+
defs.appendChild(marker);
|
|
1249
|
+
pagesSvg.appendChild(defs);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
pagesBtn.addEventListener("click", () => {
|
|
1254
|
+
pagesOpen = !pagesOpen;
|
|
1255
|
+
pagesPanel.classList.toggle("open", pagesOpen);
|
|
1256
|
+
pagesBtn.classList.toggle("active", pagesOpen);
|
|
1257
|
+
if (pagesOpen) {
|
|
1258
|
+
// Slight delay so the panel has dimensions for layout
|
|
1259
|
+
requestAnimationFrame(() => buildPagesGraph());
|
|
1260
|
+
}
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
// Update current page highlight when catalog navigates
|
|
1264
|
+
window.addEventListener("dev:page-changed", (e) => {
|
|
1265
|
+
currentPageId = e.detail?.pageId || currentPageId;
|
|
1266
|
+
if (pagesOpen) buildPagesGraph();
|
|
1267
|
+
});
|
|
1268
|
+
|
|
1269
|
+
// ============ Dev Services ============
|
|
939
1270
|
|
|
940
|
-
// --- Dev Services ---
|
|
941
1271
|
const devTracker = {
|
|
942
1272
|
track(payload) {
|
|
943
|
-
// Post to dev event endpoint
|
|
944
1273
|
fetch("/__dev_event", {
|
|
945
1274
|
method: "POST",
|
|
946
1275
|
headers: { "Content-Type": "application/json" },
|
|
@@ -960,6 +1289,8 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
960
1289
|
}).catch(() => {});
|
|
961
1290
|
},
|
|
962
1291
|
onPageChange(pageId) {
|
|
1292
|
+
currentPageId = pageId;
|
|
1293
|
+
window.dispatchEvent(new CustomEvent("dev:page-changed", { detail: { pageId } }));
|
|
963
1294
|
fetch("/__dev_event", {
|
|
964
1295
|
method: "POST",
|
|
965
1296
|
headers: { "Content-Type": "application/json" },
|
|
@@ -968,7 +1299,8 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
968
1299
|
},
|
|
969
1300
|
};
|
|
970
1301
|
|
|
971
|
-
//
|
|
1302
|
+
// ============ Mount CatalogRenderer ============
|
|
1303
|
+
|
|
972
1304
|
const root = createRoot(document.getElementById("root"));
|
|
973
1305
|
root.render(
|
|
974
1306
|
React.createElement(CatalogRenderer, {
|
|
@@ -979,6 +1311,20 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
979
1311
|
})
|
|
980
1312
|
);
|
|
981
1313
|
|
|
1314
|
+
// Listen for page navigation from graph panel
|
|
1315
|
+
window.addEventListener("dev:navigate", (e) => {
|
|
1316
|
+
const pageId = e.detail?.pageId;
|
|
1317
|
+
if (pageId) {
|
|
1318
|
+
// Try multiple approaches to navigate
|
|
1319
|
+
if (typeof window.__dev_navigateTo === "function") {
|
|
1320
|
+
window.__dev_navigateTo(pageId);
|
|
1321
|
+
} else {
|
|
1322
|
+
// Use URL hash as fallback
|
|
1323
|
+
window.location.hash = pageId;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
|
|
982
1328
|
// --- Auto-reload via SSE ---
|
|
983
1329
|
const sse = new EventSource("/__dev_sse");
|
|
984
1330
|
sse.onmessage = (e) => {
|
|
@@ -987,7 +1333,6 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
987
1333
|
|
|
988
1334
|
// --- Checkout redirect override for local dev ---
|
|
989
1335
|
${stripeEnabled ? `
|
|
990
|
-
// Override checkout endpoint to use local dev server
|
|
991
1336
|
const _origFetch = window.fetch;
|
|
992
1337
|
window.fetch = function(url, opts) {
|
|
993
1338
|
if (typeof url === "string" && url.includes("/checkout/session")) {
|
|
@@ -996,7 +1341,6 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
|
|
|
996
1341
|
return _origFetch.apply(this, arguments);
|
|
997
1342
|
};
|
|
998
1343
|
` : `
|
|
999
|
-
// Stub checkout \u2014 no Stripe key
|
|
1000
1344
|
const _origFetch = window.fetch;
|
|
1001
1345
|
window.fetch = function(url, opts) {
|
|
1002
1346
|
if (typeof url === "string" && url.includes("/checkout/session")) {
|
|
@@ -1034,7 +1378,22 @@ async function catalogDev(file, opts) {
|
|
|
1034
1378
|
}
|
|
1035
1379
|
}
|
|
1036
1380
|
const stripeEnabled = stripeSecretKey.length > 0;
|
|
1037
|
-
|
|
1381
|
+
let prodUrl = "";
|
|
1382
|
+
const cliConfig = getConfig();
|
|
1383
|
+
if (cliConfig.token) {
|
|
1384
|
+
try {
|
|
1385
|
+
const listRes = await fetch(`${cliConfig.api_url}/api/v1/catalogs`, {
|
|
1386
|
+
headers: { Authorization: `Bearer ${cliConfig.token}` }
|
|
1387
|
+
});
|
|
1388
|
+
if (listRes.ok) {
|
|
1389
|
+
const listData = await listRes.json();
|
|
1390
|
+
const catalogs = listData.data || [];
|
|
1391
|
+
globalThis.__dev_catalogs = catalogs;
|
|
1392
|
+
}
|
|
1393
|
+
} catch {
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
const devConfig = { stripeEnabled, port, prodUrl };
|
|
1038
1397
|
const spinner = ora5("Loading catalog schema...").start();
|
|
1039
1398
|
let schema;
|
|
1040
1399
|
try {
|
|
@@ -1054,9 +1413,37 @@ async function catalogDev(file, opts) {
|
|
|
1054
1413
|
let validation = deepValidateCatalog(schema);
|
|
1055
1414
|
const localBaseUrl = `http://localhost:${port}/assets`;
|
|
1056
1415
|
schema = resolveLocalAssets(schema, catalogDir, localBaseUrl);
|
|
1416
|
+
if (globalThis.__dev_catalogs && schema.slug) {
|
|
1417
|
+
const catalogs = globalThis.__dev_catalogs;
|
|
1418
|
+
const catalog2 = catalogs.find((c) => c.slug === schema.slug);
|
|
1419
|
+
if (catalog2) {
|
|
1420
|
+
if (catalog2.url) {
|
|
1421
|
+
devConfig.prodUrl = catalog2.url;
|
|
1422
|
+
} else {
|
|
1423
|
+
try {
|
|
1424
|
+
const meRes = await fetch(`${cliConfig.api_url}/api/v1/me`, {
|
|
1425
|
+
headers: { Authorization: `Bearer ${cliConfig.token}` }
|
|
1426
|
+
});
|
|
1427
|
+
if (meRes.ok) {
|
|
1428
|
+
const meData = await meRes.json();
|
|
1429
|
+
const subdomain = meData.data?.subdomain || meData.data?.app_slug;
|
|
1430
|
+
if (subdomain) {
|
|
1431
|
+
devConfig.prodUrl = `https://${subdomain}.catalogkit.cc/${schema.slug}`;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
} catch {
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
if (!devConfig.prodUrl && catalog2.catalog_id) {
|
|
1438
|
+
devConfig.prodUrl = `https://catalogkit.cc/c/${catalog2.catalog_id}`;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
delete globalThis.__dev_catalogs;
|
|
1442
|
+
}
|
|
1057
1443
|
spinner.succeed(`Loaded: ${schema.slug || file}`);
|
|
1058
1444
|
console.log(` Pages: ${Object.keys(schema.pages || {}).length}`);
|
|
1059
1445
|
console.log(` Entry: ${schema.routing?.entry || "first page"}`);
|
|
1446
|
+
if (devConfig.prodUrl) console.log(` Prod: ${devConfig.prodUrl}`);
|
|
1060
1447
|
console.log();
|
|
1061
1448
|
const sseClients = /* @__PURE__ */ new Set();
|
|
1062
1449
|
const eventSseClients = /* @__PURE__ */ new Set();
|
|
@@ -1118,6 +1505,18 @@ async function catalogDev(file, opts) {
|
|
|
1118
1505
|
res.end(JSON.stringify(eventLog.slice(-limit)));
|
|
1119
1506
|
return;
|
|
1120
1507
|
}
|
|
1508
|
+
if (url.pathname === "/__dev_meta" && req.method === "GET") {
|
|
1509
|
+
res.writeHead(200, { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" });
|
|
1510
|
+
res.end(JSON.stringify({
|
|
1511
|
+
slug: schema.slug || "",
|
|
1512
|
+
schema_version: schema.schema_version || "1.0",
|
|
1513
|
+
prod_url: devConfig.prodUrl || null,
|
|
1514
|
+
pages_count: Object.keys(schema.pages || {}).length,
|
|
1515
|
+
entry: schema.routing?.entry || null,
|
|
1516
|
+
stripe_enabled: stripeEnabled
|
|
1517
|
+
}));
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1121
1520
|
if (url.pathname === "/__dev_event" && req.method === "POST") {
|
|
1122
1521
|
let body = "";
|
|
1123
1522
|
req.on("data", (chunk) => {
|
package/dist/renderer/index.js
CHANGED
|
@@ -5298,22 +5298,33 @@ var ElementInspector = ({ context }) => {
|
|
|
5298
5298
|
const onKeyUp = (e) => {
|
|
5299
5299
|
keysDown.delete(e.key);
|
|
5300
5300
|
if (!e.shiftKey || !e.altKey) {
|
|
5301
|
-
|
|
5302
|
-
|
|
5301
|
+
if (!window.__devInspectorActive) {
|
|
5302
|
+
setActive(false);
|
|
5303
|
+
setTooltip(null);
|
|
5304
|
+
}
|
|
5303
5305
|
}
|
|
5304
5306
|
};
|
|
5305
5307
|
const onBlur = () => {
|
|
5306
5308
|
keysDown.clear();
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
+
if (!window.__devInspectorActive) {
|
|
5310
|
+
setActive(false);
|
|
5311
|
+
setTooltip(null);
|
|
5312
|
+
}
|
|
5313
|
+
};
|
|
5314
|
+
const onDevToggle = () => {
|
|
5315
|
+
const isOn = !!window.__devInspectorActive;
|
|
5316
|
+
setActive(isOn);
|
|
5317
|
+
if (!isOn) setTooltip(null);
|
|
5309
5318
|
};
|
|
5310
5319
|
window.addEventListener("keydown", onKeyDown);
|
|
5311
5320
|
window.addEventListener("keyup", onKeyUp);
|
|
5312
5321
|
window.addEventListener("blur", onBlur);
|
|
5322
|
+
window.addEventListener("dev:inspector-toggle", onDevToggle);
|
|
5313
5323
|
return () => {
|
|
5314
5324
|
window.removeEventListener("keydown", onKeyDown);
|
|
5315
5325
|
window.removeEventListener("keyup", onKeyUp);
|
|
5316
5326
|
window.removeEventListener("blur", onBlur);
|
|
5327
|
+
window.removeEventListener("dev:inspector-toggle", onDevToggle);
|
|
5317
5328
|
};
|
|
5318
5329
|
}, []);
|
|
5319
5330
|
const onMouseMove = useCallback7(
|
|
@@ -6225,6 +6236,7 @@ var CatalogRenderer = ({ catalog: rawCatalog, variantSlug, userId, trackingEnabl
|
|
|
6225
6236
|
const pageId = e.state?.pageId;
|
|
6226
6237
|
const prevHistory = e.state?.history || [];
|
|
6227
6238
|
if (pageId && catalog.pages[pageId]) {
|
|
6239
|
+
setShowCheckout(false);
|
|
6228
6240
|
isNavigatingRef.current = true;
|
|
6229
6241
|
setPageTransitioning(true);
|
|
6230
6242
|
setCurrentPageIdRaw(pageId);
|
|
@@ -6695,14 +6707,11 @@ var CatalogRenderer = ({ catalog: rawCatalog, variantSlug, userId, trackingEnabl
|
|
|
6695
6707
|
);
|
|
6696
6708
|
const handleBack = useCallback8(() => {
|
|
6697
6709
|
if (history.length > 0) {
|
|
6698
|
-
const newHistory = history.slice(0, -1);
|
|
6699
6710
|
const prevPageId = history[history.length - 1];
|
|
6700
6711
|
track("page_back", { page_id: currentPageId, value: { from_page: currentPageId, to_page: prevPageId } });
|
|
6701
|
-
|
|
6702
|
-
setCurrentPageId(prevPageId);
|
|
6703
|
-
window.history.replaceState({ pageId: prevPageId, history: newHistory }, "");
|
|
6712
|
+
window.history.back();
|
|
6704
6713
|
}
|
|
6705
|
-
}, [history,
|
|
6714
|
+
}, [history, currentPageId, track]);
|
|
6706
6715
|
useEffect8(() => {
|
|
6707
6716
|
if (!submitted) {
|
|
6708
6717
|
localStorage.setItem(saveKey, JSON.stringify({ formState, currentPageId, history }));
|