@officexapp/catalogs-cli 0.4.1 → 0.4.3

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 (2) hide show
  1. package/dist/index.js +131 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -322,6 +322,7 @@ function deepValidateCatalog(schema) {
322
322
  const edges = routing.edges || [];
323
323
  const entry = routing.entry;
324
324
  const KNOWN_TYPES = /* @__PURE__ */ new Set([
325
+ // Display / layout
325
326
  "heading",
326
327
  "paragraph",
327
328
  "image",
@@ -333,23 +334,50 @@ function deepValidateCatalog(schema) {
333
334
  "pricing_card",
334
335
  "testimonial",
335
336
  "faq",
337
+ "accordion",
336
338
  "timeline",
337
339
  "file_download",
338
340
  "iframe",
341
+ "table",
342
+ "social_links",
343
+ "tabs",
344
+ "countdown",
345
+ "comparison_table",
346
+ "progress_bar",
347
+ "modal",
348
+ "section_collapse",
349
+ "subform",
350
+ // Inputs
339
351
  "short_text",
352
+ "long_text",
353
+ "rich_text",
340
354
  "email",
341
355
  "phone",
342
356
  "url",
357
+ "address",
343
358
  "number",
344
- "password",
345
- "long_text",
359
+ "currency",
360
+ "date",
361
+ "datetime",
362
+ "time",
363
+ "date_range",
364
+ "dropdown",
365
+ "multiselect",
346
366
  "multiple_choice",
347
367
  "checkboxes",
348
- "dropdown",
349
- "slider",
350
- "star_rating",
368
+ "picture_choice",
351
369
  "switch",
352
370
  "checkbox",
371
+ "choice_matrix",
372
+ "ranking",
373
+ "star_rating",
374
+ "slider",
375
+ "opinion_scale",
376
+ "file_upload",
377
+ "signature",
378
+ "password",
379
+ "location",
380
+ // Special
353
381
  "payment"
354
382
  ]);
355
383
  if (entry && !pageIds.has(entry)) {
@@ -1340,7 +1368,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
1340
1368
  }
1341
1369
 
1342
1370
  case 'html':
1343
- return h('div', { className: compClass, style: compStyle, dangerouslySetInnerHTML: { __html: props.content || '' } });
1371
+ return h(HtmlBlock, { content: props.content || '', className: compClass, style: compStyle, formState });
1344
1372
 
1345
1373
  case 'banner': {
1346
1374
  const variants = {
@@ -1976,6 +2004,38 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
1976
2004
  );
1977
2005
  }
1978
2006
 
2007
+ // --- HtmlBlock: renders HTML content and executes inline <script> tags ---
2008
+ function HtmlBlock({ content, className, style, formState }) {
2009
+ const ref = React.useRef(null);
2010
+ const executedRef = React.useRef(new Set());
2011
+ // Template interpolation: replace {{field_id}} with form state values
2012
+ const interpolated = React.useMemo(() =>
2013
+ (content || '').replace(/{{(w+)}}/g, (_, id) => formState?.[id] ?? ''),
2014
+ [content, formState]
2015
+ );
2016
+ React.useEffect(() => {
2017
+ const container = ref.current;
2018
+ if (!container) return;
2019
+ const scripts = container.querySelectorAll('script');
2020
+ scripts.forEach((orig) => {
2021
+ const key = orig.src || orig.textContent || '';
2022
+ if (executedRef.current.has(key)) return;
2023
+ executedRef.current.add(key);
2024
+ if (orig.src) {
2025
+ const s = document.createElement('script');
2026
+ s.src = orig.src;
2027
+ if (orig.type) s.type = orig.type;
2028
+ s.async = true;
2029
+ container.appendChild(s);
2030
+ } else if (orig.textContent) {
2031
+ try { new Function(orig.textContent)(); }
2032
+ catch (e) { console.error('[CatalogKit:dev] Inline script error:', e); }
2033
+ }
2034
+ });
2035
+ }, [interpolated]);
2036
+ return h('div', { ref, className: 'prose prose-sm max-w-none ' + (className || ''), style, dangerouslySetInnerHTML: { __html: interpolated } });
2037
+ }
2038
+
1979
2039
  function ActionButton({ action, themeColor, onAction }) {
1980
2040
  const st = action.style || 'primary';
1981
2041
  const hasSide = !!action.side_statement;
@@ -2021,7 +2081,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2021
2081
  );
2022
2082
  }
2023
2083
 
2024
- function StickyBottomBar({ config, page, formState, cartItems, themeColor, onNext, onAction, onBack, historyLen }) {
2084
+ function StickyBottomBar({ config, page, formState, cartItems, themeColor, onNext, onAction, onFieldAndNavigate, onBack, historyLen }) {
2025
2085
  const [visible, setVisible] = React.useState(!config.delay_ms);
2026
2086
  const [scrollDir, setScrollDir] = React.useState('down');
2027
2087
  React.useEffect(() => {
@@ -2044,15 +2104,27 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2044
2104
  glass_dark: { backgroundColor: 'rgba(0,0,0,0.85)', backdropFilter: 'blur(16px)', color: 'white' },
2045
2105
  gradient: { background: 'linear-gradient(135deg, ' + themeColor + ' 0%, ' + themeColor + 'dd 100%)', color: 'white' },
2046
2106
  };
2047
- const handlePrimary = () => {
2048
- const dispatch = config.primary_action?.dispatch;
2049
- if (!dispatch || dispatch === 'next') { onNext(); return; }
2050
- if (dispatch.startsWith('action:')) {
2051
- const actionId = dispatch.slice(7);
2107
+ const dispatchAction = (act) => {
2108
+ const cmd = act?.action || 'next';
2109
+ if (cmd === 'next') { onNext(); return; }
2110
+ if (cmd.startsWith('action:')) {
2111
+ const actionId = cmd.slice(7);
2052
2112
  const action = page.actions?.find(a => a.id === actionId);
2053
2113
  if (action) onAction(action); else onNext();
2054
- } else { onNext(); }
2114
+ return;
2115
+ }
2116
+ if (cmd.startsWith('field:')) {
2117
+ const parts = cmd.slice(6).split(':');
2118
+ if (parts.length >= 2) { onFieldAndNavigate(parts[0], parts.slice(1).join(':')); }
2119
+ return;
2120
+ }
2121
+ onNext();
2055
2122
  };
2123
+ const primaryLabel = config.primary?.label
2124
+ ? interpolate(config.primary.label)
2125
+ : config.button_text || page.submit_label || 'Continue';
2126
+ const secondaryAction = config.secondary;
2127
+ const secondaryLabel = secondaryAction?.label ? interpolate(secondaryAction.label) : null;
2056
2128
  return h('div', {
2057
2129
  className: 'cf-sticky-bar' + (show ? '' : ' hidden'),
2058
2130
  style: bgStyles[config.style || 'solid'] || bgStyles.solid,
@@ -2066,12 +2138,19 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2066
2138
  h('div', { className: 'flex items-center gap-3' },
2067
2139
  config.cart_badge && cartItems.length > 0
2068
2140
  ? h('span', { className: 'bg-red-500 text-white text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center' }, cartItems.length) : null,
2141
+ secondaryLabel
2142
+ ? h('button', {
2143
+ className: 'text-sm font-medium hover:opacity-80 transition-opacity',
2144
+ style: { color: config.style === 'glass_dark' ? 'rgba(255,255,255,0.6)' : '#6b7280' },
2145
+ onClick: () => dispatchAction(secondaryAction),
2146
+ }, secondaryLabel)
2147
+ : null,
2069
2148
  h('button', {
2070
2149
  className: 'cf-btn-primary text-white text-sm',
2071
2150
  style: { backgroundColor: config.style === 'gradient' ? 'rgba(255,255,255,0.9)' : themeColor, color: config.style === 'gradient' ? themeColor : 'white' },
2072
2151
  disabled: config.disabled,
2073
- onClick: handlePrimary,
2074
- }, interpolate(config.primary_action?.label || page.submit_label || 'Continue'))
2152
+ onClick: () => dispatchAction(config.primary),
2153
+ }, primaryLabel)
2075
2154
  )
2076
2155
  )
2077
2156
  );
@@ -2746,6 +2825,24 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
2746
2825
  navigateTo(nextPageId);
2747
2826
  }, [currentPageId, routing, formState, pages, addToCart, navigateTo, runValidation]);
2748
2827
 
2828
+ // --- Field + Navigate (for sticky bar field: dispatch) ---
2829
+ const handleFieldAndNavigate = React.useCallback((fieldId, value) => {
2830
+ const newFormState = { ...formState, [fieldId]: value };
2831
+ setFormState(newFormState);
2832
+ const page = pages[currentPageId];
2833
+ if (page) {
2834
+ const errors = validatePage(page, newFormState, devContext);
2835
+ setValidationErrors(errors);
2836
+ if (errors.length > 0) {
2837
+ const el = document.querySelector('[data-component-id="' + errors[0].componentId + '"]');
2838
+ if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' });
2839
+ return;
2840
+ }
2841
+ }
2842
+ const nextPageId = getNextPage(routing, currentPageId, newFormState, devContext);
2843
+ navigateTo(nextPageId);
2844
+ }, [currentPageId, routing, formState, pages, navigateTo]);
2845
+
2749
2846
  // --- Resume prompt ---
2750
2847
  if (showResumeModal) {
2751
2848
  return h('div', { className: 'cf-resume-backdrop' },
@@ -3037,7 +3134,7 @@ function buildPreviewHtml(schema, port, validation, devConfig) {
3037
3134
  ? h(StickyBottomBar, {
3038
3135
  config: { ...(catalog.settings?.sticky_bar || {}), ...(page.sticky_bar || {}) },
3039
3136
  page, formState, cartItems, themeColor,
3040
- onNext: handleNext, onAction: handleAction, onBack: handleBack,
3137
+ onNext: handleNext, onAction: handleAction, onFieldAndNavigate: handleFieldAndNavigate, onBack: handleBack,
3041
3138
  historyLen: history.length,
3042
3139
  })
3043
3140
  : null
@@ -4307,6 +4404,24 @@ async function whoami() {
4307
4404
  }
4308
4405
 
4309
4406
  // src/index.ts
4407
+ var NODE_MIN = 18;
4408
+ var NODE_MAX = 22;
4409
+ var nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
4410
+ if (nodeMajor < NODE_MIN || nodeMajor > NODE_MAX) {
4411
+ console.error(`
4412
+ \x1B[31m\u2716 Unsupported Node.js version: v${process.versions.node}\x1B[0m`);
4413
+ console.error(` Catalog Kit CLI requires Node.js ${NODE_MIN}\u2013${NODE_MAX} (LTS recommended).`);
4414
+ if (nodeMajor > NODE_MAX) {
4415
+ console.error(` Node ${nodeMajor}+ breaks the tsx ESM loader hooks used to load TypeScript catalog files.`);
4416
+ }
4417
+ console.error(`
4418
+ \x1B[1mFix:\x1B[0m Install a compatible version with NVM (Node Version Manager):`);
4419
+ console.error(` curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash`);
4420
+ console.error(` nvm install 22`);
4421
+ console.error(` nvm use 22
4422
+ `);
4423
+ process.exit(1);
4424
+ }
4310
4425
  var __dirname = dirname3(fileURLToPath(import.meta.url));
4311
4426
  var { version } = JSON.parse(readFileSync5(join4(__dirname, "../package.json"), "utf-8"));
4312
4427
  var program = new Command();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@officexapp/catalogs-cli",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "CLI for Catalog Kit — upload videos, push catalogs, manage assets",
5
5
  "type": "module",
6
6
  "bin": {