@officexapp/catalogs-cli 0.2.6 → 0.2.8

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 +299 -33
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -853,20 +853,64 @@ function buildPreviewHtml(schema, port) {
853
853
  .dev-banner .stub-tags { margin-left: auto; display: flex; gap: 6px; }
854
854
  .dev-banner .stub-tag { background: rgba(255,255,255,0.1); border-radius: 3px; padding: 1px 6px; font-size: 11px; color: #fbbf24; }
855
855
 
856
- /* Dev page nav */
857
- .dev-page-nav {
858
- position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%); z-index: 99998;
859
- background: rgba(26,26,46,0.95); backdrop-filter: blur(12px); border-radius: 16px;
860
- padding: 6px; display: flex; gap: 4px; box-shadow: 0 8px 32px rgba(0,0,0,0.3);
861
- max-width: calc(100vw - 32px); overflow-x: auto;
856
+ /* Pages mindmap overlay */
857
+ .pages-overlay {
858
+ position: fixed; inset: 0; z-index: 99990; background: rgba(10,10,20,0.92);
859
+ backdrop-filter: blur(8px); display: none; align-items: center; justify-content: center;
860
+ font-family: var(--font-display);
861
+ }
862
+ .pages-overlay.open { display: flex; }
863
+ .pages-overlay .close-btn {
864
+ position: absolute; top: 16px; right: 16px; background: rgba(255,255,255,0.1);
865
+ border: none; color: white; width: 32px; height: 32px; border-radius: 8px;
866
+ cursor: pointer; font-size: 18px; display: flex; align-items: center; justify-content: center;
867
+ }
868
+ .pages-overlay .close-btn:hover { background: rgba(255,255,255,0.2); }
869
+ .mindmap-container { position: relative; padding: 40px; }
870
+ .mindmap-container svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; }
871
+ .mindmap-nodes { position: relative; display: flex; flex-wrap: wrap; gap: 24px; justify-content: center; align-items: flex-start; max-width: 900px; }
872
+ .mindmap-node {
873
+ background: rgba(255,255,255,0.08); border: 1.5px solid rgba(255,255,255,0.15);
874
+ border-radius: 14px; padding: 14px 20px; min-width: 140px; text-align: center;
875
+ cursor: pointer; transition: all 0.2s ease; position: relative;
862
876
  }
863
- .dev-page-nav button {
864
- padding: 6px 14px; border-radius: 12px; font-size: 12px; font-family: var(--font-display);
865
- font-weight: 500; border: none; cursor: pointer; white-space: nowrap;
866
- transition: all 0.2s ease; color: rgba(255,255,255,0.6); background: transparent;
877
+ .mindmap-node:hover { background: rgba(255,255,255,0.14); border-color: rgba(255,255,255,0.3); transform: translateY(-2px); }
878
+ .mindmap-node.entry { border-color: #4ade80; box-shadow: 0 0 0 3px rgba(74,222,128,0.15); }
879
+ .mindmap-node.current { border-color: var(--theme-color); box-shadow: 0 0 0 3px rgba(99,102,241,0.25); }
880
+ .mindmap-node .node-title { color: white; font-size: 13px; font-weight: 600; margin-bottom: 2px; }
881
+ .mindmap-node .node-id { color: rgba(255,255,255,0.35); font-size: 10px; font-family: monospace; }
882
+ .mindmap-node .node-badge {
883
+ position: absolute; top: -8px; right: -8px; background: #4ade80; color: #0a2010;
884
+ font-size: 9px; font-weight: 700; padding: 1px 6px; border-radius: 6px; text-transform: uppercase;
885
+ }
886
+ .mindmap-node .node-components { color: rgba(255,255,255,0.3); font-size: 10px; margin-top: 4px; }
887
+ .mindmap-edge-label {
888
+ position: absolute; background: rgba(251,191,36,0.15); color: #fbbf24;
889
+ font-size: 9px; padding: 1px 6px; border-radius: 4px; white-space: nowrap; pointer-events: none;
890
+ }
891
+ .dev-banner .stub-tag.clickable { cursor: pointer; transition: all 0.15s ease; }
892
+ .dev-banner .stub-tag.clickable:hover { background: rgba(255,255,255,0.2); color: #a5b4fc; }
893
+
894
+ /* Element inspector (local dev) */
895
+ .inspector-highlight {
896
+ position: fixed; z-index: 99997; pointer-events: none; border: 2px solid #6366f1;
897
+ background: rgba(99,102,241,0.06); border-radius: 8px; transition: all 80ms ease-out;
898
+ }
899
+ .inspector-tooltip {
900
+ position: fixed; z-index: 99998; pointer-events: none; background: #1e1b4b;
901
+ color: #e0e7ff; font-family: 'SF Mono', 'Fira Code', monospace; font-size: 11px;
902
+ padding: 6px 10px; border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.4);
903
+ max-width: 360px;
904
+ }
905
+ .inspector-tooltip .ref { font-weight: 700; color: #a5b4fc; }
906
+ .inspector-tooltip .type { color: rgba(165,180,252,0.5); margin-left: 6px; }
907
+ .inspector-tooltip .hint { color: rgba(165,180,252,0.3); font-size: 9px; margin-top: 3px; }
908
+ .inspector-active-banner {
909
+ position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%); z-index: 99998;
910
+ background: rgba(99,102,241,0.92); color: white; font-size: 11px; font-weight: 600;
911
+ padding: 6px 14px; border-radius: 10px; font-family: system-ui; pointer-events: none;
912
+ box-shadow: 0 4px 16px rgba(0,0,0,0.3);
867
913
  }
868
- .dev-page-nav button:hover { color: white; background: rgba(255,255,255,0.1); }
869
- .dev-page-nav button.active { color: white; background: var(--theme-color); }
870
914
 
871
915
  /* Checkout stub */
872
916
  .checkout-stub {
@@ -893,8 +937,16 @@ function buildPreviewHtml(schema, port) {
893
937
  <span class="stub-tags">
894
938
  <span class="stub-tag">Checkout: stubbed</span>
895
939
  <span class="stub-tag">Analytics: off</span>
940
+ <span class="stub-tag clickable" id="pages-btn">Pages</span>
896
941
  </span>
897
942
  </div>
943
+ <div class="pages-overlay" id="pages-overlay">
944
+ <button class="close-btn" id="pages-close">&times;</button>
945
+ <div class="mindmap-container" id="mindmap-container"></div>
946
+ </div>
947
+ <div class="inspector-highlight" id="inspector-highlight" style="display:none"></div>
948
+ <div class="inspector-tooltip" id="inspector-tooltip" style="display:none"></div>
949
+ <div class="inspector-active-banner" id="inspector-banner" style="display:none">Inspector active &mdash; hover elements, click to copy</div>
898
950
  <div id="catalog-root"></div>
899
951
 
900
952
  <script id="__catalog_data" type="application/json">${schemaJson}</script>
@@ -1384,6 +1436,12 @@ function buildPreviewHtml(schema, port) {
1384
1436
  const [formState, setFormState] = React.useState({});
1385
1437
  const [history, setHistory] = React.useState([]);
1386
1438
 
1439
+ // Expose navigation for mindmap
1440
+ React.useEffect(() => {
1441
+ window.__devNavigateTo = (id) => { setCurrentPageId(id); setHistory([]); window.scrollTo({ top: 0, behavior: 'smooth' }); };
1442
+ window.__devSetCurrentPage && window.__devSetCurrentPage(currentPageId);
1443
+ }, [currentPageId]);
1444
+
1387
1445
  const page = currentPageId ? pages[currentPageId] : null;
1388
1446
  const isCover = page?.layout === 'cover';
1389
1447
  const isLastPage = (() => {
@@ -1419,12 +1477,12 @@ function buildPreviewHtml(schema, port) {
1419
1477
  );
1420
1478
  }
1421
1479
 
1422
- const components = page.components || [];
1480
+ const components = (page.components || []).filter(c => !c.hidden && !c.props?.hidden);
1423
1481
  const bgImage = page.background_image || catalog.settings?.theme?.background_image;
1424
1482
 
1425
1483
  // Cover page layout
1426
1484
  if (isCover) {
1427
- return h('div', null,
1485
+ return h('div', { 'data-page-id': currentPageId },
1428
1486
  h('div', {
1429
1487
  className: 'cf-page cf-noise min-h-screen flex items-center justify-center relative overflow-hidden',
1430
1488
  style: {
@@ -1435,7 +1493,9 @@ function buildPreviewHtml(schema, port) {
1435
1493
  h('div', { className: 'cf-cover-overlay absolute inset-0' }),
1436
1494
  h('div', { className: 'cf-cover-content relative max-w-2xl mx-auto px-6 py-20 text-center text-white' },
1437
1495
  h('div', { className: 'page-enter-active space-y-7 flex flex-col items-stretch text-center' },
1438
- ...components.map((comp, i) => h(RenderComponent, { key: comp.id || i, comp, isCover: true, formState, onFieldChange })),
1496
+ ...components.map((comp, i) => h('div', { key: comp.id || i, 'data-component-id': comp.id, 'data-component-type': comp.type },
1497
+ h(RenderComponent, { comp, isCover: true, formState, onFieldChange })
1498
+ )),
1439
1499
  // CTA button
1440
1500
  h('div', { className: 'mt-8' },
1441
1501
  h('button', {
@@ -1446,9 +1506,7 @@ function buildPreviewHtml(schema, port) {
1446
1506
  )
1447
1507
  )
1448
1508
  )
1449
- ),
1450
- // Page nav
1451
- h(PageNav, { pageKeys, pages, currentPageId, onSelect: (id) => { setCurrentPageId(id); setHistory([]); } })
1509
+ )
1452
1510
  );
1453
1511
  }
1454
1512
 
@@ -1457,7 +1515,7 @@ function buildPreviewHtml(schema, port) {
1457
1515
  const progressSteps = catalog.settings?.progress_steps;
1458
1516
  const topBarEnabled = topBar?.enabled !== false && catalog.settings?.top_bar;
1459
1517
 
1460
- return h('div', null,
1518
+ return h('div', { 'data-page-id': currentPageId },
1461
1519
  h('div', {
1462
1520
  className: 'cf-page min-h-screen',
1463
1521
  style: { background: 'linear-gradient(180deg, #f8f9fc 0%, #f0f2f7 100%)' },
@@ -1482,7 +1540,9 @@ function buildPreviewHtml(schema, port) {
1482
1540
  h('div', { className: 'max-w-2xl mx-auto px-6 pb-8', style: { paddingTop: topBarEnabled ? '100px' : '60px' } },
1483
1541
  page.description ? h('p', { className: 'text-sm text-gray-400 mb-8 text-center font-medium tracking-wide', style: { fontFamily: 'var(--font-display)' } }, page.description) : null,
1484
1542
  h('div', { className: 'page-enter-active space-y-5' },
1485
- ...components.map((comp, i) => h(RenderComponent, { key: comp.id || i, comp, isCover: false, formState, onFieldChange })),
1543
+ ...components.map((comp, i) => h('div', { key: comp.id || i, 'data-component-id': comp.id, 'data-component-type': comp.type },
1544
+ h(RenderComponent, { comp, isCover: false, formState, onFieldChange })
1545
+ )),
1486
1546
  // Navigation button
1487
1547
  !page.hide_navigation ? h('div', { className: 'mt-8' },
1488
1548
  h('button', {
@@ -1500,9 +1560,7 @@ function buildPreviewHtml(schema, port) {
1500
1560
  ),
1501
1561
  h('div', { className: 'mt-10 text-center text-[11px] text-gray-300 font-medium tracking-wide', style: { fontFamily: 'var(--font-display)' } }, 'Powered by Catalog Kit'),
1502
1562
  )
1503
- ),
1504
- // Page nav
1505
- h(PageNav, { pageKeys, pages, currentPageId, onSelect: (id) => { setCurrentPageId(id); setHistory([]); } })
1563
+ )
1506
1564
  );
1507
1565
  }
1508
1566
 
@@ -1532,16 +1590,6 @@ function buildPreviewHtml(schema, port) {
1532
1590
  );
1533
1591
  }
1534
1592
 
1535
- function PageNav({ pageKeys, pages, currentPageId, onSelect }) {
1536
- return h('div', { className: 'dev-page-nav' },
1537
- ...pageKeys.map(key => h('button', {
1538
- key,
1539
- className: key === currentPageId ? 'active' : '',
1540
- onClick: () => onSelect(key),
1541
- }, pages[key].title || key))
1542
- );
1543
- }
1544
-
1545
1593
  // --- Auto-reload via SSE ---
1546
1594
  const evtSource = new EventSource('/__dev_sse');
1547
1595
  evtSource.onmessage = (e) => {
@@ -1553,6 +1601,224 @@ function buildPreviewHtml(schema, port) {
1553
1601
  // --- Mount ---
1554
1602
  const root = ReactDOM.createRoot(document.getElementById('catalog-root'));
1555
1603
  root.render(h(CatalogPreview, { catalog: schema }));
1604
+
1605
+ // --- Pages mindmap ---
1606
+ (function initPagesMindmap() {
1607
+ const btn = document.getElementById('pages-btn');
1608
+ const overlay = document.getElementById('pages-overlay');
1609
+ const closeBtn = document.getElementById('pages-close');
1610
+ const container = document.getElementById('mindmap-container');
1611
+ let currentPageRef = { id: schema.routing?.entry || Object.keys(schema.pages || {})[0] };
1612
+
1613
+ // Expose setter for CatalogPreview to update current page
1614
+ window.__devSetCurrentPage = (id) => { currentPageRef.id = id; };
1615
+
1616
+ btn.addEventListener('click', () => {
1617
+ overlay.classList.add('open');
1618
+ renderMindmap();
1619
+ });
1620
+ closeBtn.addEventListener('click', () => overlay.classList.remove('open'));
1621
+ overlay.addEventListener('click', (e) => { if (e.target === overlay) overlay.classList.remove('open'); });
1622
+
1623
+ function renderMindmap() {
1624
+ const pages = schema.pages || {};
1625
+ const routing = schema.routing || {};
1626
+ const edges = routing.edges || [];
1627
+ const entry = routing.entry || Object.keys(pages)[0];
1628
+ const pageIds = Object.keys(pages);
1629
+
1630
+ // Build adjacency for layout (BFS layers)
1631
+ const adj = {};
1632
+ pageIds.forEach(id => { adj[id] = []; });
1633
+ edges.forEach(e => { if (adj[e.from]) adj[e.from].push(e.to); });
1634
+
1635
+ // BFS to assign layers
1636
+ const layers = {};
1637
+ const visited = new Set();
1638
+ const queue = [entry];
1639
+ visited.add(entry);
1640
+ layers[entry] = 0;
1641
+ while (queue.length > 0) {
1642
+ const id = queue.shift();
1643
+ for (const next of (adj[id] || [])) {
1644
+ if (!visited.has(next)) {
1645
+ visited.add(next);
1646
+ layers[next] = (layers[id] || 0) + 1;
1647
+ queue.push(next);
1648
+ }
1649
+ }
1650
+ }
1651
+ // Assign orphans
1652
+ pageIds.forEach(id => { if (layers[id] === undefined) layers[id] = 999; });
1653
+
1654
+ // Group by layer
1655
+ const layerGroups = {};
1656
+ pageIds.forEach(id => {
1657
+ const l = layers[id];
1658
+ if (!layerGroups[l]) layerGroups[l] = [];
1659
+ layerGroups[l].push(id);
1660
+ });
1661
+ const sortedLayers = Object.keys(layerGroups).map(Number).sort((a, b) => a - b);
1662
+
1663
+ // Render nodes in rows
1664
+ let html = '<div style="display:flex;flex-direction:column;align-items:center;gap:32px;">';
1665
+ const nodePositions = {};
1666
+ let rowIdx = 0;
1667
+ for (const layer of sortedLayers) {
1668
+ const ids = layerGroups[layer];
1669
+ html += '<div style="display:flex;gap:20px;justify-content:center;flex-wrap:wrap;">';
1670
+ ids.forEach(id => {
1671
+ const page = pages[id];
1672
+ const isEntry = id === entry;
1673
+ const isCurrent = id === currentPageRef.id;
1674
+ const compCount = (page.components || []).length;
1675
+ const cls = 'mindmap-node' + (isEntry ? ' entry' : '') + (isCurrent ? ' current' : '');
1676
+ html += '<div class="' + cls + '" data-node-id="' + id + '">';
1677
+ if (isEntry) html += '<span class="node-badge">entry</span>';
1678
+ html += '<div class="node-title">' + (page.title || id) + '</div>';
1679
+ html += '<div class="node-id">' + id + '</div>';
1680
+ html += '<div class="node-components">' + compCount + ' component' + (compCount !== 1 ? 's' : '') + '</div>';
1681
+ html += '</div>';
1682
+ });
1683
+ html += '</div>';
1684
+ rowIdx++;
1685
+ }
1686
+ html += '</div>';
1687
+
1688
+ container.innerHTML = html;
1689
+
1690
+ // Draw SVG edges after layout
1691
+ requestAnimationFrame(() => {
1692
+ const containerRect = container.getBoundingClientRect();
1693
+ const nodeEls = container.querySelectorAll('[data-node-id]');
1694
+ const nodeRects = {};
1695
+ nodeEls.forEach(el => { nodeRects[el.dataset.nodeId] = el.getBoundingClientRect(); });
1696
+
1697
+ let svgContent = '';
1698
+ edges.forEach(edge => {
1699
+ const fromRect = nodeRects[edge.from];
1700
+ const toRect = nodeRects[edge.to];
1701
+ if (!fromRect || !toRect) return;
1702
+ const x1 = fromRect.left + fromRect.width / 2 - containerRect.left;
1703
+ const y1 = fromRect.top + fromRect.height - containerRect.top;
1704
+ const x2 = toRect.left + toRect.width / 2 - containerRect.left;
1705
+ const y2 = toRect.top - containerRect.top;
1706
+ const midY = (y1 + y2) / 2;
1707
+ const hasConditions = edge.conditions && edge.conditions.length > 0;
1708
+ const color = hasConditions ? '#fbbf24' : 'rgba(255,255,255,0.25)';
1709
+ svgContent += '<path d="M' + x1 + ',' + y1 + ' C' + x1 + ',' + midY + ' ' + x2 + ',' + midY + ' ' + x2 + ',' + y2 + '" fill="none" stroke="' + color + '" stroke-width="2" stroke-dasharray="' + (hasConditions ? '6,4' : 'none') + '"/>';
1710
+ // Arrow
1711
+ svgContent += '<polygon points="' + (x2-4) + ',' + (y2-6) + ' ' + x2 + ',' + y2 + ' ' + (x2+4) + ',' + (y2-6) + '" fill="' + color + '"/>';
1712
+ // Condition label
1713
+ if (hasConditions) {
1714
+ const lx = (x1 + x2) / 2;
1715
+ const ly = midY - 8;
1716
+ const label = edge.conditions.map(c => c.field + ' ' + c.operator + ' ' + c.value).join(', ');
1717
+ svgContent += '<text x="' + lx + '" y="' + ly + '" text-anchor="middle" fill="#fbbf24" font-size="9" font-family="monospace">' + label + '</text>';
1718
+ }
1719
+ });
1720
+
1721
+ const svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1722
+ svgEl.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;overflow:visible;';
1723
+ svgEl.innerHTML = svgContent;
1724
+ // Remove old svg
1725
+ const oldSvg = container.querySelector('svg');
1726
+ if (oldSvg) oldSvg.remove();
1727
+ container.insertBefore(svgEl, container.firstChild);
1728
+ });
1729
+
1730
+ // Click nodes to navigate
1731
+ container.querySelectorAll('[data-node-id]').forEach(el => {
1732
+ el.addEventListener('click', () => {
1733
+ const id = el.dataset.nodeId;
1734
+ window.__devNavigateTo && window.__devNavigateTo(id);
1735
+ overlay.classList.remove('open');
1736
+ });
1737
+ });
1738
+ }
1739
+ })();
1740
+
1741
+ // --- Element Inspector (Shift+Alt) ---
1742
+ (function initInspector() {
1743
+ const highlight = document.getElementById('inspector-highlight');
1744
+ const tooltip = document.getElementById('inspector-tooltip');
1745
+ const banner = document.getElementById('inspector-banner');
1746
+ let active = false;
1747
+
1748
+ document.addEventListener('keydown', (e) => {
1749
+ if (e.shiftKey && e.altKey && !active) {
1750
+ active = true;
1751
+ banner.style.display = 'block';
1752
+ document.body.style.cursor = 'crosshair';
1753
+ }
1754
+ });
1755
+ document.addEventListener('keyup', (e) => {
1756
+ if (!e.shiftKey || !e.altKey) {
1757
+ active = false;
1758
+ banner.style.display = 'none';
1759
+ highlight.style.display = 'none';
1760
+ tooltip.style.display = 'none';
1761
+ document.body.style.cursor = '';
1762
+ }
1763
+ });
1764
+ window.addEventListener('blur', () => {
1765
+ active = false;
1766
+ banner.style.display = 'none';
1767
+ highlight.style.display = 'none';
1768
+ tooltip.style.display = 'none';
1769
+ document.body.style.cursor = '';
1770
+ });
1771
+
1772
+ document.addEventListener('mousemove', (e) => {
1773
+ if (!active) return;
1774
+ let el = e.target;
1775
+ // Walk up to find data-component-id
1776
+ while (el && !el.dataset?.componentId) el = el.parentElement;
1777
+ if (!el) {
1778
+ highlight.style.display = 'none';
1779
+ tooltip.style.display = 'none';
1780
+ return;
1781
+ }
1782
+ const compId = el.dataset.componentId;
1783
+ const compType = el.dataset.componentType || 'unknown';
1784
+ let pageEl = el;
1785
+ while (pageEl && !pageEl.dataset?.pageId) pageEl = pageEl.parentElement;
1786
+ const pageId = pageEl?.dataset?.pageId || 'unknown';
1787
+ const rect = el.getBoundingClientRect();
1788
+ highlight.style.display = 'block';
1789
+ highlight.style.top = (rect.top - 2) + 'px';
1790
+ highlight.style.left = (rect.left - 2) + 'px';
1791
+ highlight.style.width = (rect.width + 4) + 'px';
1792
+ highlight.style.height = (rect.height + 4) + 'px';
1793
+ const ref = pageId + '/' + compId;
1794
+ tooltip.style.display = 'block';
1795
+ tooltip.style.top = Math.max(8, rect.top - 44) + 'px';
1796
+ tooltip.style.left = Math.max(8, Math.min(rect.left, window.innerWidth - 340)) + 'px';
1797
+ tooltip.innerHTML = '<span class="ref">' + ref + '</span><span class="type">(' + compType + ')</span><div class="hint">click to copy</div>';
1798
+ tooltip.dataset.ref = ref;
1799
+ tooltip.dataset.compId = compId;
1800
+ tooltip.dataset.compType = compType;
1801
+ tooltip.dataset.pageId = pageId;
1802
+ });
1803
+
1804
+ document.addEventListener('click', (e) => {
1805
+ if (!active || tooltip.style.display === 'none') return;
1806
+ e.stopPropagation();
1807
+ e.preventDefault();
1808
+ const data = {
1809
+ ref: tooltip.dataset.ref,
1810
+ page_id: tooltip.dataset.pageId,
1811
+ component_id: tooltip.dataset.compId,
1812
+ component_type: tooltip.dataset.compType,
1813
+ schema_path: 'schema.pages.' + tooltip.dataset.pageId + '.components[id="' + tooltip.dataset.compId + '"]',
1814
+ catalog_slug: schema.slug || '',
1815
+ };
1816
+ navigator.clipboard.writeText(JSON.stringify(data, null, 2)).then(() => {
1817
+ tooltip.innerHTML = '<span style="color:#86efac;font-weight:700;">Copied!</span>';
1818
+ setTimeout(() => { tooltip.style.display = 'none'; }, 1200);
1819
+ });
1820
+ }, true);
1821
+ })();
1556
1822
  </script>
1557
1823
  </body>
1558
1824
  </html>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@officexapp/catalogs-cli",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "CLI for Catalog Kit — upload videos, push catalogs, manage assets",
5
5
  "type": "module",
6
6
  "bin": {