@cancia/toolbar 0.12.0 → 0.12.1

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/cancia.js CHANGED
@@ -145,6 +145,7 @@ function inlineToShorthand(el) {
145
145
  }
146
146
 
147
147
  // src/state.ts
148
+ import { parseLinkValue } from "@cancia/astro/schema";
148
149
  var state = {
149
150
  config: null,
150
151
  cmsData: {},
@@ -174,20 +175,6 @@ function setPending(key, lang, value) {
174
175
  const full = pendingKey(key, lang);
175
176
  state.pending.set(full, { key, lang, value });
176
177
  }
177
- function parseLinkOverlay(raw) {
178
- if (!raw) return { label: "", href: "" };
179
- const trimmed = raw.trim();
180
- if (trimmed.startsWith("{")) {
181
- try {
182
- const p = JSON.parse(trimmed);
183
- if (p && typeof p === "object") {
184
- return { label: String(p.label ?? ""), href: String(p.href ?? "") };
185
- }
186
- } catch {
187
- }
188
- }
189
- return { label: raw, href: "" };
190
- }
191
178
  function applyOverlay() {
192
179
  document.querySelectorAll("[data-cms]").forEach((el) => {
193
180
  if (el.dataset.cmsList !== void 0) return;
@@ -205,7 +192,7 @@ function applyOverlay() {
205
192
  return;
206
193
  }
207
194
  if (el.dataset.cmsType === "link") {
208
- const link = parseLinkOverlay(savedValue);
195
+ const link = parseLinkValue(savedValue);
209
196
  if (link.href && el.tagName === "A") el.setAttribute("href", link.href);
210
197
  const labelEls = el.querySelectorAll("[data-cms-label]");
211
198
  if (labelEls.length > 0) labelEls.forEach((n) => n.textContent = link.label);
@@ -383,19 +370,23 @@ async function reorderList(listName, ids) {
383
370
  );
384
371
  if (!res.ok) throw new Error(`Cancia: failed to reorder list (${res.status})`);
385
372
  }
373
+ async function flushEdits(edits, save) {
374
+ const results = await Promise.allSettled(edits.map((edit) => save(edit)));
375
+ const saved = [];
376
+ const failed = [];
377
+ results.forEach((result, i) => {
378
+ (result.status === "fulfilled" ? saved : failed).push(edits[i]);
379
+ });
380
+ return { saved, failed };
381
+ }
386
382
  async function flushPending() {
387
383
  const entries = Array.from(state.pending.values());
388
- const results = await Promise.allSettled(
389
- entries.map(({ key, lang, value }) => saveEntry(key, lang, value))
384
+ const { saved, failed } = await flushEdits(
385
+ entries,
386
+ ({ key, lang, value }) => saveEntry(key, lang, value)
390
387
  );
391
- results.forEach((result, i) => {
392
- if (result.status === "fulfilled") {
393
- const { key, lang } = entries[i];
394
- state.pending.delete(`${key}.${lang}`);
395
- }
396
- });
397
- const failed = results.filter((r) => r.status === "rejected").length;
398
- if (failed > 0) throw new Error(`Cancia: ${failed} save(s) failed`);
388
+ for (const { key, lang } of saved) state.pending.delete(`${key}.${lang}`);
389
+ if (failed.length > 0) throw new Error(`Cancia: ${failed.length} save(s) failed`);
399
390
  }
400
391
 
401
392
  // src/tokens.ts
@@ -1110,9 +1101,11 @@ function onPendingChange(cb) {
1110
1101
 
1111
1102
  // src/popup.ts
1112
1103
  import {
1104
+ isSafeHref,
1113
1105
  portableTextToRows,
1114
1106
  rowsToPortableText
1115
1107
  } from "@cancia/astro/richtext";
1108
+ import { parseLinkValue as parseLinkValue2 } from "@cancia/astro/schema";
1116
1109
  var popupEl = null;
1117
1110
  var outsideListener = null;
1118
1111
  var keyListener = null;
@@ -1286,29 +1279,11 @@ function buildTextPopup(key, anchorEl, onClose) {
1286
1279
  return wrap;
1287
1280
  }
1288
1281
  function isSafeHrefValue(href) {
1289
- const trimmed = href.trim();
1290
- if (trimmed === "") return true;
1291
- if (/^(https?:|mailto:|tel:)/i.test(trimmed)) return true;
1292
- const schemeMatch = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed);
1293
- if (schemeMatch) {
1294
- const firstSep = trimmed.search(/[/?#]/);
1295
- if (firstSep === -1 || schemeMatch[1].length < firstSep) return false;
1296
- }
1297
- return true;
1282
+ if (href.trim() === "") return true;
1283
+ return isSafeHref(href);
1298
1284
  }
1299
1285
  function parseLink(raw) {
1300
- if (!raw) return { label: "", href: "" };
1301
- const trimmed = raw.trim();
1302
- if (trimmed.startsWith("{")) {
1303
- try {
1304
- const p = JSON.parse(trimmed);
1305
- if (p && typeof p === "object") {
1306
- return { label: String(p.label ?? ""), href: String(p.href ?? "") };
1307
- }
1308
- } catch {
1309
- }
1310
- }
1311
- return { label: raw, href: "" };
1286
+ return parseLinkValue2(raw);
1312
1287
  }
1313
1288
  function buildLinkPopup(key, anchorEl, onClose) {
1314
1289
  const langs = state.config?.languages ?? ["en"];
@@ -1899,6 +1874,7 @@ function closePopup() {
1899
1874
  }
1900
1875
 
1901
1876
  // src/list-panel.ts
1877
+ import { isDraft } from "@cancia/astro/schema";
1902
1878
  var PANEL_Z = v("z-panel");
1903
1879
  var TOOLBAR_RESERVE = "100px";
1904
1880
  var BACKDROP_Z = v("z-overlay");
@@ -2239,7 +2215,7 @@ function renderEntries(body, schema, activeLocale, entriesInLocale, translations
2239
2215
  const titleValue = row.entry.data[schema.titleField];
2240
2216
  const title = typeof titleValue === "string" && titleValue.trim().length > 0 ? titleValue : `(untitled ${schema.labelSingular.toLowerCase()})`;
2241
2217
  const { subtitle, thumbnail } = derivePreview(schema, row.entry.data);
2242
- const isDraftRow = !!schema.draftField && row.entry.data[schema.draftField] === true;
2218
+ const isDraftRow = isDraft(schema, row.entry.data);
2243
2219
  const draftBadge = isDraftRow ? `<span style="
2244
2220
  flex-shrink: 0; margin-left: ${v("space-2")}; padding: 1px 6px;
2245
2221
  font-size: 11px; font-weight: 600; line-height: 1.5;