@obelism/content 0.17.0 → 0.18.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.
package/dist/bridge.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
 
3
- var preview = require('./preview-mpwfmnla.js');
3
+ var preview = require('./preview-ijk3ixwb.js');
4
4
  var site = require('./site.js');
5
5
  var config = require('./config-ci2hxmas.js');
6
6
 
@@ -229,6 +229,7 @@ const onlyWhileEditingSite = ()=>Promise.reject(new Error('Only while signed in
229
229
  const INERT_METHODS = {
230
230
  prompt: inertPrompt,
231
231
  createPage: ()=>Promise.reject(new Error('New pages are made on the site itself')),
232
+ createProductPage: ()=>Promise.reject(new Error('Product pages are made on the site itself')),
232
233
  view: onlyWhileEditingSite,
233
234
  applyJson: onlyWhileEditingSite,
234
235
  openForm: (_container, options = {})=>{
@@ -286,6 +287,7 @@ const createBridge = (win, cmsOrigin)=>{
286
287
  mode: isEditing ? 'editor' : null,
287
288
  entryId: params.get(preview.PREVIEW_PARAMS.entry),
288
289
  environment: params.get(preview.PREVIEW_PARAMS.environment),
290
+ product: null,
289
291
  on: emitter.on,
290
292
  getState: ()=>state,
291
293
  ...INERT_METHODS,
@@ -478,6 +480,7 @@ const wrapScriptBridge = (script, params)=>{
478
480
  mode: script.isEditing ? 'editor' : null,
479
481
  entryId: script.entryId,
480
482
  environment: params.get(preview.PREVIEW_PARAMS.environment),
483
+ product: null,
481
484
  on: (event, listener)=>{
482
485
  if (event === 'update' && !forwardingUpdates) {
483
486
  forwardingUpdates = true;
@@ -537,7 +540,11 @@ const DEFAULT_PROMPT_LABELS = {
537
540
  pagesEmpty: 'No pages match',
538
541
  pageCurrent: 'This page',
539
542
  pageDraft: 'Draft',
540
- pageChanged: 'Changed'
543
+ pageChanged: 'Changed',
544
+ addProductPage: 'Add a product page',
545
+ addingProductPage: 'Making the product page…',
546
+ addedProductPage: 'Opening the product page…',
547
+ productPageHint: 'This product has no page yet. Add one for what the store doesn’t say.'
541
548
  };
542
549
  // Icons (Lucide), drawn in the text colour.
543
550
  const ICONS = {
@@ -872,6 +879,12 @@ const mountPromptBox = (doc, bridge, options = {})=>{
872
879
  className: 'action chip',
873
880
  type: 'button'
874
881
  }, icon(doc, 'newPage'), labels.newPage);
882
+ // A product the site marks (editableProduct) without a page of its own:
883
+ // one click makes its product page, which the page then shows.
884
+ const addProduct = element(doc, 'button', {
885
+ className: 'action chip product',
886
+ type: 'button'
887
+ }, icon(doc, 'newPage'), labels.addProductPage);
875
888
  const cancelNew = element(doc, 'button', {
876
889
  type: 'button',
877
890
  title: labels.newPageCancel
@@ -921,7 +934,7 @@ const mountPromptBox = (doc, bridge, options = {})=>{
921
934
  });
922
935
  const toolbar = element(doc, 'div', {
923
936
  className: 'toolbar'
924
- }, scope, newScope, modes, undo, newPage, element(doc, 'span', {
937
+ }, scope, newScope, modes, undo, addProduct, newPage, element(doc, 'span', {
925
938
  className: 'spacer'
926
939
  }), minimize, close, divider, send);
927
940
  // The pages (on the site): a chip naming this page, opening a search
@@ -1032,7 +1045,9 @@ const mountPromptBox = (doc, bridge, options = {})=>{
1032
1045
  // A page of the site that isn't an entry (no `editable(entry)`, like a
1033
1046
  // hand-written /about) has nothing to edit: only New page.
1034
1047
  const pageless = site && bridge.entryId === null;
1035
- const onlyNew = pageless && canCreate;
1048
+ // …unless it shows a product: then Add a product page first.
1049
+ const productHere = pageless && canCreate && bridge.product !== null;
1050
+ const onlyNew = pageless && canCreate && !productHere;
1036
1051
  if (!canCreate) creating = false;
1037
1052
  if (onlyNew) creating = true;
1038
1053
  if (creating || pageless || !site) form = false;
@@ -1040,16 +1055,19 @@ const mountPromptBox = (doc, bridge, options = {})=>{
1040
1055
  const showForm = open && available && form;
1041
1056
  if (showForm && !formHandle) mountForm();
1042
1057
  else if (!showForm && formHandle) unmountForm();
1043
- pillText.textContent = onlyNew ? labels.newPage : labels.open;
1058
+ const offerProduct = productHere && !creating;
1059
+ pillText.textContent = offerProduct ? labels.addProductPage : onlyNew ? labels.newPage : labels.open;
1044
1060
  cancelNew.hidden = onlyNew;
1045
- scope.hidden = creating;
1061
+ scope.hidden = creating || offerProduct;
1046
1062
  newScope.hidden = !creating;
1063
+ addProduct.hidden = !offerProduct;
1064
+ addProduct.disabled = busy;
1047
1065
  newPage.hidden = creating || form || !canCreate;
1048
1066
  newPage.disabled = busy;
1049
1067
  if (minimized) picking = false;
1050
1068
  pathRow.hidden = !creating || picking || minimized;
1051
1069
  pathInput.disabled = busy;
1052
- input.hidden = form || picking || minimized;
1070
+ input.hidden = form || picking || minimized || offerProduct;
1053
1071
  formBox.hidden = !form || picking || minimized;
1054
1072
  formBox.classList.toggle('loading', formLoading);
1055
1073
  toolbar.hidden = picking;
@@ -1069,8 +1087,8 @@ const mountPromptBox = (doc, bridge, options = {})=>{
1069
1087
  undo.hidden = creating || !state.canUndo;
1070
1088
  undo.disabled = busy;
1071
1089
  // The form applies edits as they are made.
1072
- send.hidden = form || minimized;
1073
- divider.hidden = form || minimized;
1090
+ send.hidden = form || minimized || offerProduct;
1091
+ divider.hidden = form || minimized || offerProduct;
1074
1092
  const toggleLabel = minimized ? labels.expand : labels.minimize;
1075
1093
  minimize.title = toggleLabel;
1076
1094
  minimize.setAttribute('aria-label', toggleLabel);
@@ -1093,7 +1111,7 @@ const mountPromptBox = (doc, bridge, options = {})=>{
1093
1111
  pageChipText.textContent = pages?.find(({ entryId })=>entryId === bridge.entryId)?.name ?? labels.pages;
1094
1112
  const loadingForm = form && formLoading && !picking;
1095
1113
  const working = state.prompting || loadingForm;
1096
- const note = state.prompting ? creating ? labels.creating : labels.working : loadingForm ? labels.formLoading : message?.text ?? (site && !creating && !picking && !state.selectedUid ? labels.selectHint : '');
1114
+ const note = state.prompting ? creating ? labels.creating : offerProduct ? labels.addingProductPage : labels.working : loadingForm ? labels.formLoading : message?.text ?? (offerProduct ? labels.productPageHint : site && !creating && !picking && !state.selectedUid ? labels.selectHint : '');
1097
1115
  status.textContent = note;
1098
1116
  status.hidden = !note || minimized;
1099
1117
  status.className = `status${working ? ' working' : ''}${!busy && !loadingForm && message?.error ? ' error' : ''}`;
@@ -1303,6 +1321,20 @@ const mountPromptBox = (doc, bridge, options = {})=>{
1303
1321
  pillSignOut.addEventListener('click', ()=>bridge.signOut());
1304
1322
  clearBlock.addEventListener('click', ()=>bridge.select(null));
1305
1323
  newPage.addEventListener('click', ()=>setCreating(true));
1324
+ addProduct.addEventListener('click', async ()=>{
1325
+ message = null;
1326
+ try {
1327
+ // The bridge reloads the page; this shows until it does.
1328
+ await bridge.createProductPage();
1329
+ message = {
1330
+ text: labels.addedProductPage,
1331
+ error: false
1332
+ };
1333
+ } catch (error) {
1334
+ message = failure(error);
1335
+ }
1336
+ refresh();
1337
+ });
1306
1338
  cancelNew.addEventListener('click', ()=>setCreating(false));
1307
1339
  promptMode.addEventListener('click', ()=>setForm(false));
1308
1340
  formMode.addEventListener('click', ()=>setForm(true));
@@ -1483,7 +1515,7 @@ const FORM_BURST_MS = 1000;
1483
1515
  // How long the page waits for the form's edits to settle before it shows
1484
1516
  // them.
1485
1517
  const FORM_RENDER_MS = 300;
1486
- const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})=>{
1518
+ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}`, productPath: ownProductPath } = {})=>{
1487
1519
  const doc = win.document;
1488
1520
  const emitter = createEmitter();
1489
1521
  const cleanup = [];
@@ -1493,6 +1525,7 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
1493
1525
  // Entries changed here since the last publish.
1494
1526
  const touched = new Set();
1495
1527
  let session = null;
1528
+ const productPath = (handle, locale)=>ownProductPath ? ownProductPath(handle, locale) : (session?.productPath ?? '/products/{handle}').replaceAll('{handle}', encodeURIComponent(handle));
1496
1529
  // The open form's, if any; and when the form last changed content, for
1497
1530
  // undo steps (-Infinity: the next edit starts a new step).
1498
1531
  let closeForm = null;
@@ -1537,6 +1570,16 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
1537
1570
  return data;
1538
1571
  };
1539
1572
  const pageEntryId = ()=>doc.querySelector(`[${preview.ENTRY_ATTRIBUTE}]`)?.getAttribute(preview.ENTRY_ATTRIBUTE) ?? null;
1573
+ // The product the page shows (editableProduct), if it marks one.
1574
+ const pageProduct = ()=>{
1575
+ const element = doc.querySelector(`[${preview.PRODUCT_ATTRIBUTE}]`);
1576
+ const id = element?.getAttribute(preview.PRODUCT_ATTRIBUTE);
1577
+ const handle = element?.getAttribute(preview.PRODUCT_HANDLE_ATTRIBUTE);
1578
+ return id && handle ? {
1579
+ id,
1580
+ handle
1581
+ } : null;
1582
+ };
1540
1583
  const entryFor = (uid)=>{
1541
1584
  const block = uid ? doc.querySelector(blockSelector(uid)) : null;
1542
1585
  return block?.closest(`[${preview.ENTRY_ATTRIBUTE}]`)?.getAttribute(preview.ENTRY_ATTRIBUTE) ?? pageEntryId();
@@ -1644,6 +1687,9 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
1644
1687
  get entryId () {
1645
1688
  return pageEntryId();
1646
1689
  },
1690
+ get product () {
1691
+ return pageProduct();
1692
+ },
1647
1693
  environment: null,
1648
1694
  on: emitter.on,
1649
1695
  getState: ()=>state,
@@ -1713,6 +1759,34 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
1713
1759
  });
1714
1760
  }
1715
1761
  },
1762
+ async createProductPage (product = pageProduct() ?? undefined) {
1763
+ if (!product) {
1764
+ throw new Error('Nothing on this page is marked as a product: add editableProduct(product)');
1765
+ }
1766
+ if (!has('create')) throw new Error('You may not add pages here');
1767
+ if (state.unsaved) {
1768
+ throw new Error('Save or undo your changes before adding a page');
1769
+ }
1770
+ setState({
1771
+ prompting: true
1772
+ });
1773
+ try {
1774
+ const made = await api('product-pages', {
1775
+ product: {
1776
+ id: product.id,
1777
+ handle: product.handle
1778
+ }
1779
+ });
1780
+ // The site's product route finds its page now (drafts, while
1781
+ // editing): show it.
1782
+ win.location.reload();
1783
+ return made;
1784
+ } finally{
1785
+ setState({
1786
+ prompting: false
1787
+ });
1788
+ }
1789
+ },
1716
1790
  async view (options = {}) {
1717
1791
  const { uid, entryId } = target(options.uid);
1718
1792
  const entry = await load(entryId);
@@ -1866,7 +1940,8 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
1866
1940
  contentType: page.contentType,
1867
1941
  status: page.status,
1868
1942
  path,
1869
- url: pagePath(path, code)
1943
+ url: page.product ? productPath(page.product.handle, code) : pagePath(path, code),
1944
+ product: page.product ?? null
1870
1945
  }
1871
1946
  ];
1872
1947
  });
@@ -2011,7 +2086,7 @@ const peekContentBridge = ()=>typeof window === 'undefined' ? null : window[KEY]
2011
2086
  // if (bridge.isEditing) bridge.on('update', ({ entry }) => render(entry))
2012
2087
  //
2013
2088
  // The first call's options win: there is one bridge per page.
2014
- const getContentBridge = ({ url, prompt = true, pagePath } = {})=>{
2089
+ const getContentBridge = ({ url, prompt = true, pagePath, productPath } = {})=>{
2015
2090
  if (typeof window === 'undefined') return SERVER_BRIDGE;
2016
2091
  const win = window;
2017
2092
  if (win[KEY]) return win[KEY];
@@ -2024,7 +2099,8 @@ const getContentBridge = ({ url, prompt = true, pagePath } = {})=>{
2024
2099
  // own `/cms/login` (@obelism/content/site).
2025
2100
  const sitePath = bridge.isEditing || win.parent !== win ? null : readSiteEditing(win.document);
2026
2101
  if (sitePath) bridge = createSiteBridge(win, sitePath, {
2027
- pagePath
2102
+ pagePath,
2103
+ productPath
2028
2104
  });
2029
2105
  if (bridge.isEditing && prompt) {
2030
2106
  const unmount = mountPromptBox(win.document, bridge, prompt === true ? {} : prompt);
package/dist/bridge.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { P as PREVIEW_PARAMS, U as UID_ATTRIBUTE, E as ENTRY_ATTRIBUTE } from './preview-h7rwp06v.mjs';
1
+ import { P as PREVIEW_PARAMS, U as UID_ATTRIBUTE, E as ENTRY_ATTRIBUTE, a as PRODUCT_ATTRIBUTE, b as PRODUCT_HANDLE_ATTRIBUTE } from './preview-jcmxxj92.mjs';
2
2
  import { SITE_EDITING_COOKIE, SITE_REQUEST_HEADER } from './site.mjs';
3
3
  import { n as normalizeUrl, D as DEFAULT_URL } from './config-c6qktlz6.mjs';
4
4
 
@@ -227,6 +227,7 @@ const onlyWhileEditingSite = ()=>Promise.reject(new Error('Only while signed in
227
227
  const INERT_METHODS = {
228
228
  prompt: inertPrompt,
229
229
  createPage: ()=>Promise.reject(new Error('New pages are made on the site itself')),
230
+ createProductPage: ()=>Promise.reject(new Error('Product pages are made on the site itself')),
230
231
  view: onlyWhileEditingSite,
231
232
  applyJson: onlyWhileEditingSite,
232
233
  openForm: (_container, options = {})=>{
@@ -284,6 +285,7 @@ const createBridge = (win, cmsOrigin)=>{
284
285
  mode: isEditing ? 'editor' : null,
285
286
  entryId: params.get(PREVIEW_PARAMS.entry),
286
287
  environment: params.get(PREVIEW_PARAMS.environment),
288
+ product: null,
287
289
  on: emitter.on,
288
290
  getState: ()=>state,
289
291
  ...INERT_METHODS,
@@ -476,6 +478,7 @@ const wrapScriptBridge = (script, params)=>{
476
478
  mode: script.isEditing ? 'editor' : null,
477
479
  entryId: script.entryId,
478
480
  environment: params.get(PREVIEW_PARAMS.environment),
481
+ product: null,
479
482
  on: (event, listener)=>{
480
483
  if (event === 'update' && !forwardingUpdates) {
481
484
  forwardingUpdates = true;
@@ -535,7 +538,11 @@ const DEFAULT_PROMPT_LABELS = {
535
538
  pagesEmpty: 'No pages match',
536
539
  pageCurrent: 'This page',
537
540
  pageDraft: 'Draft',
538
- pageChanged: 'Changed'
541
+ pageChanged: 'Changed',
542
+ addProductPage: 'Add a product page',
543
+ addingProductPage: 'Making the product page…',
544
+ addedProductPage: 'Opening the product page…',
545
+ productPageHint: 'This product has no page yet. Add one for what the store doesn’t say.'
539
546
  };
540
547
  // Icons (Lucide), drawn in the text colour.
541
548
  const ICONS = {
@@ -870,6 +877,12 @@ const mountPromptBox = (doc, bridge, options = {})=>{
870
877
  className: 'action chip',
871
878
  type: 'button'
872
879
  }, icon(doc, 'newPage'), labels.newPage);
880
+ // A product the site marks (editableProduct) without a page of its own:
881
+ // one click makes its product page, which the page then shows.
882
+ const addProduct = element(doc, 'button', {
883
+ className: 'action chip product',
884
+ type: 'button'
885
+ }, icon(doc, 'newPage'), labels.addProductPage);
873
886
  const cancelNew = element(doc, 'button', {
874
887
  type: 'button',
875
888
  title: labels.newPageCancel
@@ -919,7 +932,7 @@ const mountPromptBox = (doc, bridge, options = {})=>{
919
932
  });
920
933
  const toolbar = element(doc, 'div', {
921
934
  className: 'toolbar'
922
- }, scope, newScope, modes, undo, newPage, element(doc, 'span', {
935
+ }, scope, newScope, modes, undo, addProduct, newPage, element(doc, 'span', {
923
936
  className: 'spacer'
924
937
  }), minimize, close, divider, send);
925
938
  // The pages (on the site): a chip naming this page, opening a search
@@ -1030,7 +1043,9 @@ const mountPromptBox = (doc, bridge, options = {})=>{
1030
1043
  // A page of the site that isn't an entry (no `editable(entry)`, like a
1031
1044
  // hand-written /about) has nothing to edit: only New page.
1032
1045
  const pageless = site && bridge.entryId === null;
1033
- const onlyNew = pageless && canCreate;
1046
+ // …unless it shows a product: then Add a product page first.
1047
+ const productHere = pageless && canCreate && bridge.product !== null;
1048
+ const onlyNew = pageless && canCreate && !productHere;
1034
1049
  if (!canCreate) creating = false;
1035
1050
  if (onlyNew) creating = true;
1036
1051
  if (creating || pageless || !site) form = false;
@@ -1038,16 +1053,19 @@ const mountPromptBox = (doc, bridge, options = {})=>{
1038
1053
  const showForm = open && available && form;
1039
1054
  if (showForm && !formHandle) mountForm();
1040
1055
  else if (!showForm && formHandle) unmountForm();
1041
- pillText.textContent = onlyNew ? labels.newPage : labels.open;
1056
+ const offerProduct = productHere && !creating;
1057
+ pillText.textContent = offerProduct ? labels.addProductPage : onlyNew ? labels.newPage : labels.open;
1042
1058
  cancelNew.hidden = onlyNew;
1043
- scope.hidden = creating;
1059
+ scope.hidden = creating || offerProduct;
1044
1060
  newScope.hidden = !creating;
1061
+ addProduct.hidden = !offerProduct;
1062
+ addProduct.disabled = busy;
1045
1063
  newPage.hidden = creating || form || !canCreate;
1046
1064
  newPage.disabled = busy;
1047
1065
  if (minimized) picking = false;
1048
1066
  pathRow.hidden = !creating || picking || minimized;
1049
1067
  pathInput.disabled = busy;
1050
- input.hidden = form || picking || minimized;
1068
+ input.hidden = form || picking || minimized || offerProduct;
1051
1069
  formBox.hidden = !form || picking || minimized;
1052
1070
  formBox.classList.toggle('loading', formLoading);
1053
1071
  toolbar.hidden = picking;
@@ -1067,8 +1085,8 @@ const mountPromptBox = (doc, bridge, options = {})=>{
1067
1085
  undo.hidden = creating || !state.canUndo;
1068
1086
  undo.disabled = busy;
1069
1087
  // The form applies edits as they are made.
1070
- send.hidden = form || minimized;
1071
- divider.hidden = form || minimized;
1088
+ send.hidden = form || minimized || offerProduct;
1089
+ divider.hidden = form || minimized || offerProduct;
1072
1090
  const toggleLabel = minimized ? labels.expand : labels.minimize;
1073
1091
  minimize.title = toggleLabel;
1074
1092
  minimize.setAttribute('aria-label', toggleLabel);
@@ -1091,7 +1109,7 @@ const mountPromptBox = (doc, bridge, options = {})=>{
1091
1109
  pageChipText.textContent = pages?.find(({ entryId })=>entryId === bridge.entryId)?.name ?? labels.pages;
1092
1110
  const loadingForm = form && formLoading && !picking;
1093
1111
  const working = state.prompting || loadingForm;
1094
- const note = state.prompting ? creating ? labels.creating : labels.working : loadingForm ? labels.formLoading : message?.text ?? (site && !creating && !picking && !state.selectedUid ? labels.selectHint : '');
1112
+ const note = state.prompting ? creating ? labels.creating : offerProduct ? labels.addingProductPage : labels.working : loadingForm ? labels.formLoading : message?.text ?? (offerProduct ? labels.productPageHint : site && !creating && !picking && !state.selectedUid ? labels.selectHint : '');
1095
1113
  status.textContent = note;
1096
1114
  status.hidden = !note || minimized;
1097
1115
  status.className = `status${working ? ' working' : ''}${!busy && !loadingForm && message?.error ? ' error' : ''}`;
@@ -1301,6 +1319,20 @@ const mountPromptBox = (doc, bridge, options = {})=>{
1301
1319
  pillSignOut.addEventListener('click', ()=>bridge.signOut());
1302
1320
  clearBlock.addEventListener('click', ()=>bridge.select(null));
1303
1321
  newPage.addEventListener('click', ()=>setCreating(true));
1322
+ addProduct.addEventListener('click', async ()=>{
1323
+ message = null;
1324
+ try {
1325
+ // The bridge reloads the page; this shows until it does.
1326
+ await bridge.createProductPage();
1327
+ message = {
1328
+ text: labels.addedProductPage,
1329
+ error: false
1330
+ };
1331
+ } catch (error) {
1332
+ message = failure(error);
1333
+ }
1334
+ refresh();
1335
+ });
1304
1336
  cancelNew.addEventListener('click', ()=>setCreating(false));
1305
1337
  promptMode.addEventListener('click', ()=>setForm(false));
1306
1338
  formMode.addEventListener('click', ()=>setForm(true));
@@ -1481,7 +1513,7 @@ const FORM_BURST_MS = 1000;
1481
1513
  // How long the page waits for the form's edits to settle before it shows
1482
1514
  // them.
1483
1515
  const FORM_RENDER_MS = 300;
1484
- const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})=>{
1516
+ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}`, productPath: ownProductPath } = {})=>{
1485
1517
  const doc = win.document;
1486
1518
  const emitter = createEmitter();
1487
1519
  const cleanup = [];
@@ -1491,6 +1523,7 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
1491
1523
  // Entries changed here since the last publish.
1492
1524
  const touched = new Set();
1493
1525
  let session = null;
1526
+ const productPath = (handle, locale)=>ownProductPath ? ownProductPath(handle, locale) : (session?.productPath ?? '/products/{handle}').replaceAll('{handle}', encodeURIComponent(handle));
1494
1527
  // The open form's, if any; and when the form last changed content, for
1495
1528
  // undo steps (-Infinity: the next edit starts a new step).
1496
1529
  let closeForm = null;
@@ -1535,6 +1568,16 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
1535
1568
  return data;
1536
1569
  };
1537
1570
  const pageEntryId = ()=>doc.querySelector(`[${ENTRY_ATTRIBUTE}]`)?.getAttribute(ENTRY_ATTRIBUTE) ?? null;
1571
+ // The product the page shows (editableProduct), if it marks one.
1572
+ const pageProduct = ()=>{
1573
+ const element = doc.querySelector(`[${PRODUCT_ATTRIBUTE}]`);
1574
+ const id = element?.getAttribute(PRODUCT_ATTRIBUTE);
1575
+ const handle = element?.getAttribute(PRODUCT_HANDLE_ATTRIBUTE);
1576
+ return id && handle ? {
1577
+ id,
1578
+ handle
1579
+ } : null;
1580
+ };
1538
1581
  const entryFor = (uid)=>{
1539
1582
  const block = uid ? doc.querySelector(blockSelector(uid)) : null;
1540
1583
  return block?.closest(`[${ENTRY_ATTRIBUTE}]`)?.getAttribute(ENTRY_ATTRIBUTE) ?? pageEntryId();
@@ -1642,6 +1685,9 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
1642
1685
  get entryId () {
1643
1686
  return pageEntryId();
1644
1687
  },
1688
+ get product () {
1689
+ return pageProduct();
1690
+ },
1645
1691
  environment: null,
1646
1692
  on: emitter.on,
1647
1693
  getState: ()=>state,
@@ -1711,6 +1757,34 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
1711
1757
  });
1712
1758
  }
1713
1759
  },
1760
+ async createProductPage (product = pageProduct() ?? undefined) {
1761
+ if (!product) {
1762
+ throw new Error('Nothing on this page is marked as a product: add editableProduct(product)');
1763
+ }
1764
+ if (!has('create')) throw new Error('You may not add pages here');
1765
+ if (state.unsaved) {
1766
+ throw new Error('Save or undo your changes before adding a page');
1767
+ }
1768
+ setState({
1769
+ prompting: true
1770
+ });
1771
+ try {
1772
+ const made = await api('product-pages', {
1773
+ product: {
1774
+ id: product.id,
1775
+ handle: product.handle
1776
+ }
1777
+ });
1778
+ // The site's product route finds its page now (drafts, while
1779
+ // editing): show it.
1780
+ win.location.reload();
1781
+ return made;
1782
+ } finally{
1783
+ setState({
1784
+ prompting: false
1785
+ });
1786
+ }
1787
+ },
1714
1788
  async view (options = {}) {
1715
1789
  const { uid, entryId } = target(options.uid);
1716
1790
  const entry = await load(entryId);
@@ -1864,7 +1938,8 @@ const createSiteBridge = (win, basePath, { pagePath = (path)=>`/${path}` } = {})
1864
1938
  contentType: page.contentType,
1865
1939
  status: page.status,
1866
1940
  path,
1867
- url: pagePath(path, code)
1941
+ url: page.product ? productPath(page.product.handle, code) : pagePath(path, code),
1942
+ product: page.product ?? null
1868
1943
  }
1869
1944
  ];
1870
1945
  });
@@ -2009,7 +2084,7 @@ const peekContentBridge = ()=>typeof window === 'undefined' ? null : window[KEY]
2009
2084
  // if (bridge.isEditing) bridge.on('update', ({ entry }) => render(entry))
2010
2085
  //
2011
2086
  // The first call's options win: there is one bridge per page.
2012
- const getContentBridge = ({ url, prompt = true, pagePath } = {})=>{
2087
+ const getContentBridge = ({ url, prompt = true, pagePath, productPath } = {})=>{
2013
2088
  if (typeof window === 'undefined') return SERVER_BRIDGE;
2014
2089
  const win = window;
2015
2090
  if (win[KEY]) return win[KEY];
@@ -2022,7 +2097,8 @@ const getContentBridge = ({ url, prompt = true, pagePath } = {})=>{
2022
2097
  // own `/cms/login` (@obelism/content/site).
2023
2098
  const sitePath = bridge.isEditing || win.parent !== win ? null : readSiteEditing(win.document);
2024
2099
  if (sitePath) bridge = createSiteBridge(win, sitePath, {
2025
- pagePath
2100
+ pagePath,
2101
+ productPath
2026
2102
  });
2027
2103
  if (bridge.isEditing && prompt) {
2028
2104
  const unmount = mountPromptBox(win.document, bridge, prompt === true ? {} : prompt);
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RichTextFormat, E as Entry, a as RedirectList, b as Redirect, c as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock, L as Link, S as ShopifyItem, d as EntryStub, e as RichTextNode } from './types-e5nvgudo.js';
2
- export { C as ContentTypeDefinition, f as ContentTypeKind, g as Environment, F as FieldDefinition, h as FieldType, i as LinkTarget, j as RichTextDocument, k as RichTextMark } from './types-e5nvgudo.js';
1
+ import { R as RichTextFormat, E as Entry, a as RedirectList, b as Redirect, c as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock, S as ShopifyItem, L as LinkTarget, d as Link, e as EntryStub, f as RichTextNode } from './types-mtjco3a5.js';
2
+ export { C as ContentTypeDefinition, g as ContentTypeKind, h as Environment, F as FieldDefinition, i as FieldType, j as RichTextDocument, k as RichTextMark } from './types-mtjco3a5.js';
3
3
 
4
4
  type ContentClientOptions = {
5
5
  token: string;
@@ -60,6 +60,8 @@ declare const DEFAULT_URL = "https://content.obelism.studio";
60
60
 
61
61
  declare const UID_ATTRIBUTE = "data-cms-uid";
62
62
  declare const ENTRY_ATTRIBUTE = "data-cms-entry";
63
+ declare const PRODUCT_ATTRIBUTE = "data-cms-product";
64
+ declare const PRODUCT_HANDLE_ATTRIBUTE = "data-cms-product-handle";
63
65
  type Editable = {
64
66
  'data-cms-uid'?: string;
65
67
  'data-cms-entry'?: string;
@@ -70,6 +72,14 @@ declare const editable: (target: {
70
72
  id: string;
71
73
  fields: unknown;
72
74
  } | null | undefined) => Editable;
75
+ type EditableProduct = {
76
+ 'data-cms-product'?: string;
77
+ 'data-cms-product-handle'?: string;
78
+ };
79
+ declare const editableProduct: (product: {
80
+ id: string;
81
+ handle: string;
82
+ } | null | undefined) => EditableProduct;
73
83
 
74
84
  declare class ContentApiError extends Error {
75
85
  readonly status: number;
@@ -98,8 +108,9 @@ declare const pickImproveVariant: <TBlock extends Block>(block: ImproveBlock<TBl
98
108
 
99
109
  type LinkHrefOptions = {
100
110
  url?: ((path: string) => string) | undefined;
111
+ product?: ((product: ShopifyItem, page: LinkTarget | null) => string) | undefined;
101
112
  };
102
- declare const linkHref: (link: Link | null | undefined, { url }?: LinkHrefOptions) => string | null;
113
+ declare const linkHref: (link: Link | null | undefined, { url, product, }?: LinkHrefOptions) => string | null;
103
114
 
104
115
  declare const PREVIEW_PARAMS: {
105
116
  readonly preview: "cms_preview";
@@ -184,6 +195,6 @@ type SitemapOptions = LocalizedUrlOptions & {
184
195
  };
185
196
  declare const sitemapXml: (entries: Iterable<Page>, { exclude, ...options }: SitemapOptions) => string;
186
197
 
187
- export { Asset, Block, ContentApiError, ContentClient, DEFAULT_URL, ENTRY_ATTRIBUTE, Entry, EntryList, EntryStub, IMPROVE_BLOCK, ImproveBlock, Link, ModelFile, PREVIEW_PARAMS, Redirect, RedirectList, RichTextFormat, RichTextNode, SAFE_HREF, SHOPIFY_STOREFRONT_API_VERSION, ShopifyItem, ShopifyStorefrontError, UID_ATTRIBUTE, alternateLinks, editable, escapeHtml, fetchShopifyCollection, fetchShopifyProducts, headingLevel, imageSrcSet, imageUrl, isImproveBlock, linkHref, pickImproveVariant, readPreviewParams, richTextToHtml, richTextToPlainText, sitemapXml };
198
+ export { Asset, Block, ContentApiError, ContentClient, DEFAULT_URL, ENTRY_ATTRIBUTE, Entry, EntryList, EntryStub, IMPROVE_BLOCK, ImproveBlock, Link, LinkTarget, ModelFile, PREVIEW_PARAMS, PRODUCT_ATTRIBUTE, PRODUCT_HANDLE_ATTRIBUTE, Redirect, RedirectList, RichTextFormat, RichTextNode, SAFE_HREF, SHOPIFY_STOREFRONT_API_VERSION, ShopifyItem, ShopifyStorefrontError, UID_ATTRIBUTE, alternateLinks, editable, editableProduct, escapeHtml, fetchShopifyCollection, fetchShopifyProducts, headingLevel, imageSrcSet, imageUrl, isImproveBlock, linkHref, pickImproveVariant, readPreviewParams, richTextToHtml, richTextToPlainText, sitemapXml };
188
199
  export type { AlternateLink, ContentClientOptions, EntriesQuery, ImageTransform, LinkHrefOptions, LocalizedUrlOptions, PageLookup, PreviewParams, RedirectsQuery, RequestOptions, RichTextHtmlOptions, ShopifyCollection, ShopifyImage, ShopifyMoney, ShopifyProduct, ShopifyStorefrontOptions, SitemapOptions };
189
200
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/links.ts","../src/preview.ts","../src/shopify.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":"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","names":[]}
1
+ {"version":3,"file":"index.d.mts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/links.ts","../src/preview.ts","../src/shopify.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RichTextFormat, E as Entry, a as RedirectList, b as Redirect, c as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock, L as Link, S as ShopifyItem, d as EntryStub, e as RichTextNode } from './types-e5nvgudo.js';
2
- export { C as ContentTypeDefinition, f as ContentTypeKind, g as Environment, F as FieldDefinition, h as FieldType, i as LinkTarget, j as RichTextDocument, k as RichTextMark } from './types-e5nvgudo.js';
1
+ import { R as RichTextFormat, E as Entry, a as RedirectList, b as Redirect, c as EntryList, M as ModelFile, A as Asset, B as Block, I as ImproveBlock, S as ShopifyItem, L as LinkTarget, d as Link, e as EntryStub, f as RichTextNode } from './types-mtjco3a5.js';
2
+ export { C as ContentTypeDefinition, g as ContentTypeKind, h as Environment, F as FieldDefinition, i as FieldType, j as RichTextDocument, k as RichTextMark } from './types-mtjco3a5.js';
3
3
 
4
4
  type ContentClientOptions = {
5
5
  token: string;
@@ -60,6 +60,8 @@ declare const DEFAULT_URL = "https://content.obelism.studio";
60
60
 
61
61
  declare const UID_ATTRIBUTE = "data-cms-uid";
62
62
  declare const ENTRY_ATTRIBUTE = "data-cms-entry";
63
+ declare const PRODUCT_ATTRIBUTE = "data-cms-product";
64
+ declare const PRODUCT_HANDLE_ATTRIBUTE = "data-cms-product-handle";
63
65
  type Editable = {
64
66
  'data-cms-uid'?: string;
65
67
  'data-cms-entry'?: string;
@@ -70,6 +72,14 @@ declare const editable: (target: {
70
72
  id: string;
71
73
  fields: unknown;
72
74
  } | null | undefined) => Editable;
75
+ type EditableProduct = {
76
+ 'data-cms-product'?: string;
77
+ 'data-cms-product-handle'?: string;
78
+ };
79
+ declare const editableProduct: (product: {
80
+ id: string;
81
+ handle: string;
82
+ } | null | undefined) => EditableProduct;
73
83
 
74
84
  declare class ContentApiError extends Error {
75
85
  readonly status: number;
@@ -98,8 +108,9 @@ declare const pickImproveVariant: <TBlock extends Block>(block: ImproveBlock<TBl
98
108
 
99
109
  type LinkHrefOptions = {
100
110
  url?: ((path: string) => string) | undefined;
111
+ product?: ((product: ShopifyItem, page: LinkTarget | null) => string) | undefined;
101
112
  };
102
- declare const linkHref: (link: Link | null | undefined, { url }?: LinkHrefOptions) => string | null;
113
+ declare const linkHref: (link: Link | null | undefined, { url, product, }?: LinkHrefOptions) => string | null;
103
114
 
104
115
  declare const PREVIEW_PARAMS: {
105
116
  readonly preview: "cms_preview";
@@ -184,6 +195,6 @@ type SitemapOptions = LocalizedUrlOptions & {
184
195
  };
185
196
  declare const sitemapXml: (entries: Iterable<Page>, { exclude, ...options }: SitemapOptions) => string;
186
197
 
187
- export { Asset, Block, ContentApiError, ContentClient, DEFAULT_URL, ENTRY_ATTRIBUTE, Entry, EntryList, EntryStub, IMPROVE_BLOCK, ImproveBlock, Link, ModelFile, PREVIEW_PARAMS, Redirect, RedirectList, RichTextFormat, RichTextNode, SAFE_HREF, SHOPIFY_STOREFRONT_API_VERSION, ShopifyItem, ShopifyStorefrontError, UID_ATTRIBUTE, alternateLinks, editable, escapeHtml, fetchShopifyCollection, fetchShopifyProducts, headingLevel, imageSrcSet, imageUrl, isImproveBlock, linkHref, pickImproveVariant, readPreviewParams, richTextToHtml, richTextToPlainText, sitemapXml };
198
+ export { Asset, Block, ContentApiError, ContentClient, DEFAULT_URL, ENTRY_ATTRIBUTE, Entry, EntryList, EntryStub, IMPROVE_BLOCK, ImproveBlock, Link, LinkTarget, ModelFile, PREVIEW_PARAMS, PRODUCT_ATTRIBUTE, PRODUCT_HANDLE_ATTRIBUTE, Redirect, RedirectList, RichTextFormat, RichTextNode, SAFE_HREF, SHOPIFY_STOREFRONT_API_VERSION, ShopifyItem, ShopifyStorefrontError, UID_ATTRIBUTE, alternateLinks, editable, editableProduct, escapeHtml, fetchShopifyCollection, fetchShopifyProducts, headingLevel, imageSrcSet, imageUrl, isImproveBlock, linkHref, pickImproveVariant, readPreviewParams, richTextToHtml, richTextToPlainText, sitemapXml };
188
199
  export type { AlternateLink, ContentClientOptions, EntriesQuery, ImageTransform, LinkHrefOptions, LocalizedUrlOptions, PageLookup, PreviewParams, RedirectsQuery, RequestOptions, RichTextHtmlOptions, ShopifyCollection, ShopifyImage, ShopifyMoney, ShopifyProduct, ShopifyStorefrontOptions, SitemapOptions };
189
200
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/links.ts","../src/preview.ts","../src/shopify.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":"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","names":[]}
1
+ {"version":3,"file":"index.d.ts","sources":["../src/client.ts","../src/config.ts","../src/editable.ts","../src/errors.ts","../src/images.ts","../src/improve.ts","../src/links.ts","../src/preview.ts","../src/shopify.ts","../src/richText.ts","../src/sitemap.ts"],"mappings":"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","names":[]}