@micacms/editor-ui 0.3.0 → 0.4.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.
Files changed (50) hide show
  1. package/dist/assets/{ChangesView-D7n3k873.js → ChangesView-DoLhwPxu.js} +2 -2
  2. package/dist/assets/{ChangesView-D7n3k873.js.map → ChangesView-DoLhwPxu.js.map} +1 -1
  3. package/dist/assets/{CollectionView-Cu_ytsrZ.js → CollectionView-D3W4tRXa.js} +2 -2
  4. package/dist/assets/{CollectionView-Cu_ytsrZ.js.map → CollectionView-D3W4tRXa.js.map} +1 -1
  5. package/dist/assets/{ConfirmDialog.vue_vue_type_script_setup_true_lang-C4yhYMWf.js → ConfirmDialog.vue_vue_type_script_setup_true_lang-BhOQwoeA.js} +2 -2
  6. package/dist/assets/{ConfirmDialog.vue_vue_type_script_setup_true_lang-C4yhYMWf.js.map → ConfirmDialog.vue_vue_type_script_setup_true_lang-BhOQwoeA.js.map} +1 -1
  7. package/dist/assets/{DashboardView-7y5LY2BH.js → DashboardView-JZ2Eaw1_.js} +2 -2
  8. package/dist/assets/{DashboardView-7y5LY2BH.js.map → DashboardView-JZ2Eaw1_.js.map} +1 -1
  9. package/dist/assets/{EntryView-IyBqYorV.js → EntryView-BwERaZkJ.js} +2 -2
  10. package/dist/assets/{EntryView-IyBqYorV.js.map → EntryView-BwERaZkJ.js.map} +1 -1
  11. package/dist/assets/InterVariable-DiVDrmQJ.woff2 +0 -0
  12. package/dist/assets/LoginView-BCrrdqnf.js +2 -0
  13. package/dist/assets/LoginView-BCrrdqnf.js.map +1 -0
  14. package/dist/assets/{MediaView-BVODAshv.js → MediaView-KlQHfWAi.js} +2 -2
  15. package/dist/assets/{MediaView-BVODAshv.js.map → MediaView-KlQHfWAi.js.map} +1 -1
  16. package/dist/assets/{NotFoundView-K-fMCruW.js → NotFoundView-C9bEHUGC.js} +2 -2
  17. package/dist/assets/{NotFoundView-K-fMCruW.js.map → NotFoundView-C9bEHUGC.js.map} +1 -1
  18. package/dist/assets/{ObjectField-CAJPaAKa.js → ObjectField-gxr2a2pp.js} +2 -2
  19. package/dist/assets/{ObjectField-CAJPaAKa.js.map → ObjectField-gxr2a2pp.js.map} +1 -1
  20. package/dist/assets/{PageHeader.vue_vue_type_script_setup_true_lang-IPw0xrLa.js → PageHeader.vue_vue_type_script_setup_true_lang-B-fBJAjn.js} +2 -2
  21. package/dist/assets/{PageHeader.vue_vue_type_script_setup_true_lang-IPw0xrLa.js.map → PageHeader.vue_vue_type_script_setup_true_lang-B-fBJAjn.js.map} +1 -1
  22. package/dist/assets/{RepeaterField-BAnvha76.js → RepeaterField-obOWml_P.js} +2 -2
  23. package/dist/assets/{RepeaterField-BAnvha76.js.map → RepeaterField-obOWml_P.js.map} +1 -1
  24. package/dist/assets/{SingletonView-C3AqQe6a.js → SingletonView-BNJCcl7V.js} +2 -2
  25. package/dist/assets/{SingletonView-C3AqQe6a.js.map → SingletonView-BNJCcl7V.js.map} +1 -1
  26. package/dist/assets/{SystemView-DKA_QdVz.js → SystemView-CB8CT0Pa.js} +2 -2
  27. package/dist/assets/{SystemView-DKA_QdVz.js.map → SystemView-CB8CT0Pa.js.map} +1 -1
  28. package/dist/assets/{arrow-right-s8aI3aOk.js → arrow-right-DJTAYrz5.js} +2 -2
  29. package/dist/assets/{arrow-right-s8aI3aOk.js.map → arrow-right-DJTAYrz5.js.map} +1 -1
  30. package/dist/assets/{arrow-up-BeQh5rSe.js → arrow-up-CibOjHj-.js} +2 -2
  31. package/dist/assets/{arrow-up-BeQh5rSe.js.map → arrow-up-CibOjHj-.js.map} +1 -1
  32. package/dist/assets/{chevron-down-BgnDSY3E.js → chevron-down-Bmt8sf-v.js} +2 -2
  33. package/dist/assets/{chevron-down-BgnDSY3E.js.map → chevron-down-Bmt8sf-v.js.map} +1 -1
  34. package/dist/assets/{index-9WVbsgdk.js → index-Colfqb-9.js} +26 -26
  35. package/dist/assets/index-Colfqb-9.js.map +1 -0
  36. package/dist/assets/index-bRiiEobC.css +1 -0
  37. package/dist/assets/{plus-D6xxK_qW.js → plus-D_aoCFbi.js} +2 -2
  38. package/dist/assets/{plus-D6xxK_qW.js.map → plus-D_aoCFbi.js.map} +1 -1
  39. package/dist/assets/{useField-CQv2TS4g.js → useField-BDSV5ZPo.js} +2 -2
  40. package/dist/assets/{useField-CQv2TS4g.js.map → useField-BDSV5ZPo.js.map} +1 -1
  41. package/dist/assets/{useUnsavedChanges-bO5Ha9y6.js → useUnsavedChanges-B0WVqgI4.js} +5 -5
  42. package/dist/assets/useUnsavedChanges-B0WVqgI4.js.map +1 -0
  43. package/dist/fonts/InterVariable-LICENSE.txt +92 -0
  44. package/dist/index.html +2 -2
  45. package/package.json +2 -2
  46. package/dist/assets/LoginView-C8sJvqmb.js +0 -2
  47. package/dist/assets/LoginView-C8sJvqmb.js.map +0 -1
  48. package/dist/assets/index-9WVbsgdk.js.map +0 -1
  49. package/dist/assets/index-DDeMbcSl.css +0 -1
  50. package/dist/assets/useUnsavedChanges-bO5Ha9y6.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"EntryView-IyBqYorV.js","sources":["../../src/views/EntryView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Entry editor.\n *\n * One Save button, and Saved is not Published: a save lands in the site's\n * draft workspace and collects on the Publish page until somebody publishes\n * it. Unfinished work stays in this form and never reaches the server, which\n * is what the unsaved-changes guard is for.\n *\n * `revision` is how two editors stay out of each other's way. The form\n * remembers which version of the entry it opened and sends that token back with\n * the save. If someone else saved in the meantime the server answers 409, and\n * this view says so plainly rather than overwriting their work.\n *\n * An entry is addressed by its slug, so changing the slug field moves the entry\n * — files and all — and the route follows it.\n */\n\nimport { computed, onMounted, ref } from \"vue\";\nimport { useRouter } from \"vue-router\";\n\nimport { defaultValues, type FieldIssue } from \"@micacms/core\";\n\nimport { api, ApiError, type EntryDetail } from \"@/api\";\nimport ConfirmDialog from \"@/components/ConfirmDialog.vue\";\nimport FieldList from \"@/components/fields/FieldList.vue\";\nimport PageHeader from \"@/components/PageHeader.vue\";\nimport { useUnsavedChanges } from \"@/composables/useUnsavedChanges\";\nimport { useSchema } from \"@/stores/schema\";\nimport { useStaging } from \"@/stores/staging\";\nimport { useToast } from \"@/stores/toast\";\n\nconst props = defineProps<{ collection: string; slug?: string }>();\n\nconst router = useRouter();\nconst schema = useSchema();\nconst staging = useStaging();\nconst toast = useToast();\n\nconst definition = computed(() => schema.collection(props.collection));\n\nconst entry = ref<EntryDetail | null>(null);\nconst values = ref<Record<string, unknown>>({});\nconst issues = ref<FieldIssue[]>([]);\nconst loading = ref(true);\nconst saving = ref(false);\nconst conflict = ref(false);\nconst confirmDelete = ref(false);\nconst references = ref<Array<{ collection: string; title: string }>>([]);\n\n/**\n * The version this form was opened against.\n *\n * `null` on a new entry, which asserts to the server that nothing is there —\n * so two people creating the same slug is a conflict, not a silent overwrite.\n */\nconst revision = ref<string | null>(null);\n\nconst unsaved = useUnsavedChanges(values);\nconst dirty = unsaved.dirty;\n\nconst isNew = computed(() => !props.slug);\n\nconst title = computed(() => {\n const def = definition.value;\n if (!def) return \"Entry\";\n const value = values.value[def.titleField];\n if (typeof value === \"string\" && value.trim()) return value.trim();\n return isNew.value ? `New ${def.labelSingular.toLowerCase()}` : \"Untitled\";\n});\n\n/**\n * Where this entry lives in the repository — the path a reviewer will see.\n *\n * From the server, not assembled here: an entry with rich text or media is a\n * directory, and one without is a single `.json` file.\n */\nconst location = computed(() => entry.value?.path ?? \"Not saved yet\");\n\nfunction setBaseline(detail: EntryDetail): void {\n entry.value = detail;\n values.value = structuredClone(detail.data);\n revision.value = detail.revision;\n issues.value = [];\n conflict.value = false;\n unsaved.markClean();\n}\n\nasync function open(slug: string): Promise<void> {\n setBaseline(await api.entries.get(props.collection, slug));\n}\n\nonMounted(async () => {\n const def = definition.value;\n if (!def) {\n loading.value = false;\n return;\n }\n\n try {\n if (props.slug) {\n await open(props.slug);\n } else {\n // A fresh entry starts as a full skeleton so every input is bound. The\n // skeleton itself is not an edit, so it becomes the baseline.\n values.value = defaultValues(def.fields);\n revision.value = null;\n unsaved.markClean();\n }\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not open this entry.\");\n await router.replace({ name: \"collection\", params: { collection: props.collection } });\n } finally {\n loading.value = false;\n }\n});\n\nasync function save(): Promise<void> {\n saving.value = true;\n issues.value = [];\n\n const payload = { data: values.value, revision: revision.value };\n\n try {\n const saved = props.slug\n ? await api.entries.update(props.collection, props.slug, payload)\n : await api.entries.create(props.collection, payload);\n\n // The server owns the slug — it derives it, de-duplicates it, and may have\n // moved the entry. Re-open at whatever it decided rather than assuming.\n unsaved.markClean();\n toast.success(saved.renamed ? `Saved as “${saved.slug}”.` : \"Saved.\");\n void staging.refresh();\n\n if (saved.slug !== props.slug) {\n await router.replace({\n name: \"entry\",\n params: { collection: props.collection, slug: saved.slug },\n });\n return;\n }\n\n await open(saved.slug);\n } catch (error) {\n if (error instanceof ApiError && error.status === 409) {\n // Nothing is lost: the form still holds the editor's work, and reloading\n // is a deliberate act rather than something that happens under them.\n conflict.value = true;\n toast.error(\"Someone else saved this entry while you were editing it.\");\n } else if (error instanceof ApiError && error.issues.length > 0) {\n issues.value = error.issues;\n toast.error(error.message, `${error.issues.length} field(s) need attention.`);\n // Take the editor to the first problem rather than leaving them hunting.\n requestAnimationFrame(() => {\n document\n .querySelector('[aria-invalid=\"true\"], .field-error')\n ?.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n });\n } else {\n toast.error(error instanceof ApiError ? error.message : \"Could not save this entry.\");\n }\n } finally {\n saving.value = false;\n }\n}\n\n/** Takes the version now in the repository, discarding this form's edits. */\nasync function reload(): Promise<void> {\n if (!props.slug) return;\n try {\n await open(props.slug);\n toast.success(\"Reloaded from the repository.\");\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not reload this entry.\");\n }\n}\n\nasync function askDelete(): Promise<void> {\n references.value = [];\n if (props.slug) {\n try {\n const { references: found } = await api.entries.references(props.collection, props.slug);\n references.value = found.map((item) => ({\n collection: schema.collection(item.collection)?.label ?? item.collection,\n title: item.title,\n }));\n } catch {\n // The warning is a courtesy; a failure to fetch it must not block deletion.\n }\n }\n confirmDelete.value = true;\n}\n\nasync function remove(): Promise<void> {\n if (!props.slug) return;\n try {\n await api.entries.remove(props.collection, props.slug);\n unsaved.release();\n toast.success(\"Deleted.\");\n void staging.refresh();\n await router.replace({ name: \"collection\", params: { collection: props.collection } });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not delete this entry.\");\n } finally {\n confirmDelete.value = false;\n }\n}\n</script>\n\n<template>\n <template v-if=\"definition\">\n <PageHeader\n :title=\"title\"\n :subtitle=\"location\"\n :back=\"{ name: 'collection', params: { collection } }\"\n :back-label=\"definition.label\"\n >\n <span :class=\"['badge', dirty ? 'border-accent bg-accent-soft text-accent' : 'text-success']\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />\n {{ dirty ? \"Unsaved\" : \"Saved\" }}\n </span>\n <button type=\"button\" class=\"btn-primary\" :disabled=\"saving || !dirty\" @click=\"save\">\n {{ saving ? \"Saving…\" : \"Save changes\" }}\n </button>\n </PageHeader>\n\n <div class=\"mx-auto max-w-3xl px-4 py-6 sm:px-6\">\n <p v-if=\"loading\" class=\"text-sm text-muted\">Loading…</p>\n\n <template v-else>\n <div\n v-if=\"conflict\"\n role=\"alert\"\n class=\"mb-5 rounded-md border border-warning/15 bg-warning-soft p-4\"\n >\n <p class=\"text-sm font-medium text-warning\">This entry changed while you were editing</p>\n <p class=\"mt-1 text-xs text-muted\">\n Your changes are still here and have not been saved. Copy anything you need, then load\n the current version — or open the repository to see what changed.\n </p>\n <button type=\"button\" class=\"btn-secondary mt-2.5\" @click=\"reload\">\n Discard mine and reload\n </button>\n </div>\n\n <div\n v-if=\"issues.length\"\n role=\"alert\"\n class=\"mb-5 rounded-md border border-danger/15 bg-danger-soft p-4\"\n >\n <p class=\"text-sm font-medium text-danger\">\n {{ issues.length }} field{{ issues.length === 1 ? \"\" : \"s\" }} need attention\n </p>\n <ul class=\"mt-1.5 space-y-0.5 text-xs text-danger\">\n <li v-for=\"issue in issues\" :key=\"issue.path\">{{ issue.message }}</li>\n </ul>\n </div>\n\n <form novalidate @submit.prevent=\"save\">\n <FieldList\n v-model=\"values\"\n :fields=\"definition.fields\"\n :issues=\"issues\"\n :disabled=\"saving\"\n :collection=\"collection\"\n :entry-slug=\"slug\"\n />\n\n <!-- Enter in a text input should save, not do nothing. -->\n <button type=\"submit\" class=\"sr-only\">Save</button>\n </form>\n\n <div v-if=\"!isNew\" class=\"mt-10 border-t border-line pt-5\">\n <button type=\"button\" class=\"btn-danger\" @click=\"askDelete\">\n Delete this {{ definition.labelSingular.toLowerCase() }}\n </button>\n </div>\n </template>\n </div>\n\n <ConfirmDialog\n :open=\"confirmDelete\"\n :title=\"`Delete “${title}”?`\"\n confirm-label=\"Delete\"\n destructive\n @cancel=\"confirmDelete = false\"\n @confirm=\"remove\"\n >\n <!-- Honest about what a delete is now: a pending change. It reaches the\n repository's history when the pending set is published, and the\n history keeps it from then on. -->\n <p>\n This removes <code>{{ location }}</code>. Like any edit, the removal joins the pending\n changes and reaches the repository when they are published.\n </p>\n\n <div v-if=\"references.length\" class=\"mt-3 rounded-md border border-warning/15 bg-warning-soft p-4\">\n <p class=\"text-sm font-medium text-warning\">\n {{ references.length }} other entr{{ references.length === 1 ? \"y\" : \"ies\" }} link to this\n one:\n </p>\n <ul class=\"mt-1 list-inside list-disc text-xs\">\n <li v-for=\"(item, index) in references\" :key=\"index\">\n {{ item.title }} ({{ item.collection }})\n </li>\n </ul>\n </div>\n </ConfirmDialog>\n </template>\n\n <div v-else class=\"p-8 text-center text-sm text-muted\">\n There is no collection called \"{{ collection }}\".\n </div>\n</template>\n"],"names":["props","__props","router","useRouter","schema","useSchema","staging","useStaging","toast","useToast","definition","computed","entry","ref","values","issues","loading","saving","conflict","confirmDelete","references","revision","unsaved","useUnsavedChanges","dirty","isNew","title","def","value","location","_a","setBaseline","detail","open","slug","api","onMounted","defaultValues","error","ApiError","save","payload","saved","reload","askDelete","found","item","remove","_createElementBlock","_Fragment","_createVNode","PageHeader","_createElementVNode","_unref","_createTextVNode","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_hoisted_4","_cache","_hoisted_5","_hoisted_6","_toDisplayString","_hoisted_7","_renderList","issue","FieldList","$event","_hoisted_8","ConfirmDialog","_hoisted_9","_hoisted_10","_hoisted_11","index","_hoisted_12"],"mappings":"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"}
1
+ {"version":3,"file":"EntryView-BwERaZkJ.js","sources":["../../src/views/EntryView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Entry editor.\n *\n * One Save button, and Saved is not Published: a save lands in the site's\n * draft workspace and collects on the Publish page until somebody publishes\n * it. Unfinished work stays in this form and never reaches the server, which\n * is what the unsaved-changes guard is for.\n *\n * `revision` is how two editors stay out of each other's way. The form\n * remembers which version of the entry it opened and sends that token back with\n * the save. If someone else saved in the meantime the server answers 409, and\n * this view says so plainly rather than overwriting their work.\n *\n * An entry is addressed by its slug, so changing the slug field moves the entry\n * — files and all — and the route follows it.\n */\n\nimport { computed, onMounted, ref } from \"vue\";\nimport { useRouter } from \"vue-router\";\n\nimport { defaultValues, type FieldIssue } from \"@micacms/core\";\n\nimport { api, ApiError, type EntryDetail } from \"@/api\";\nimport ConfirmDialog from \"@/components/ConfirmDialog.vue\";\nimport FieldList from \"@/components/fields/FieldList.vue\";\nimport PageHeader from \"@/components/PageHeader.vue\";\nimport { useUnsavedChanges } from \"@/composables/useUnsavedChanges\";\nimport { useSchema } from \"@/stores/schema\";\nimport { useStaging } from \"@/stores/staging\";\nimport { useToast } from \"@/stores/toast\";\n\nconst props = defineProps<{ collection: string; slug?: string }>();\n\nconst router = useRouter();\nconst schema = useSchema();\nconst staging = useStaging();\nconst toast = useToast();\n\nconst definition = computed(() => schema.collection(props.collection));\n\nconst entry = ref<EntryDetail | null>(null);\nconst values = ref<Record<string, unknown>>({});\nconst issues = ref<FieldIssue[]>([]);\nconst loading = ref(true);\nconst saving = ref(false);\nconst conflict = ref(false);\nconst confirmDelete = ref(false);\nconst references = ref<Array<{ collection: string; title: string }>>([]);\n\n/**\n * The version this form was opened against.\n *\n * `null` on a new entry, which asserts to the server that nothing is there —\n * so two people creating the same slug is a conflict, not a silent overwrite.\n */\nconst revision = ref<string | null>(null);\n\nconst unsaved = useUnsavedChanges(values);\nconst dirty = unsaved.dirty;\n\nconst isNew = computed(() => !props.slug);\n\nconst title = computed(() => {\n const def = definition.value;\n if (!def) return \"Entry\";\n const value = values.value[def.titleField];\n if (typeof value === \"string\" && value.trim()) return value.trim();\n return isNew.value ? `New ${def.labelSingular.toLowerCase()}` : \"Untitled\";\n});\n\n/**\n * Where this entry lives in the repository — the path a reviewer will see.\n *\n * From the server, not assembled here: an entry with rich text or media is a\n * directory, and one without is a single `.json` file.\n */\nconst location = computed(() => entry.value?.path ?? \"Not saved yet\");\n\nfunction setBaseline(detail: EntryDetail): void {\n entry.value = detail;\n values.value = structuredClone(detail.data);\n revision.value = detail.revision;\n issues.value = [];\n conflict.value = false;\n unsaved.markClean();\n}\n\nasync function open(slug: string): Promise<void> {\n setBaseline(await api.entries.get(props.collection, slug));\n}\n\nonMounted(async () => {\n const def = definition.value;\n if (!def) {\n loading.value = false;\n return;\n }\n\n try {\n if (props.slug) {\n await open(props.slug);\n } else {\n // A fresh entry starts as a full skeleton so every input is bound. The\n // skeleton itself is not an edit, so it becomes the baseline.\n values.value = defaultValues(def.fields);\n revision.value = null;\n unsaved.markClean();\n }\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not open this entry.\");\n await router.replace({ name: \"collection\", params: { collection: props.collection } });\n } finally {\n loading.value = false;\n }\n});\n\nasync function save(): Promise<void> {\n saving.value = true;\n issues.value = [];\n\n const payload = { data: values.value, revision: revision.value };\n\n try {\n const saved = props.slug\n ? await api.entries.update(props.collection, props.slug, payload)\n : await api.entries.create(props.collection, payload);\n\n // The server owns the slug — it derives it, de-duplicates it, and may have\n // moved the entry. Re-open at whatever it decided rather than assuming.\n unsaved.markClean();\n toast.success(saved.renamed ? `Saved as “${saved.slug}”.` : \"Saved.\");\n void staging.refresh();\n\n if (saved.slug !== props.slug) {\n await router.replace({\n name: \"entry\",\n params: { collection: props.collection, slug: saved.slug },\n });\n return;\n }\n\n await open(saved.slug);\n } catch (error) {\n if (error instanceof ApiError && error.status === 409) {\n // Nothing is lost: the form still holds the editor's work, and reloading\n // is a deliberate act rather than something that happens under them.\n conflict.value = true;\n toast.error(\"Someone else saved this entry while you were editing it.\");\n } else if (error instanceof ApiError && error.issues.length > 0) {\n issues.value = error.issues;\n toast.error(error.message, `${error.issues.length} field(s) need attention.`);\n // Take the editor to the first problem rather than leaving them hunting.\n requestAnimationFrame(() => {\n document\n .querySelector('[aria-invalid=\"true\"], .field-error')\n ?.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n });\n } else {\n toast.error(error instanceof ApiError ? error.message : \"Could not save this entry.\");\n }\n } finally {\n saving.value = false;\n }\n}\n\n/** Takes the version now in the repository, discarding this form's edits. */\nasync function reload(): Promise<void> {\n if (!props.slug) return;\n try {\n await open(props.slug);\n toast.success(\"Reloaded from the repository.\");\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not reload this entry.\");\n }\n}\n\nasync function askDelete(): Promise<void> {\n references.value = [];\n if (props.slug) {\n try {\n const { references: found } = await api.entries.references(props.collection, props.slug);\n references.value = found.map((item) => ({\n collection: schema.collection(item.collection)?.label ?? item.collection,\n title: item.title,\n }));\n } catch {\n // The warning is a courtesy; a failure to fetch it must not block deletion.\n }\n }\n confirmDelete.value = true;\n}\n\nasync function remove(): Promise<void> {\n if (!props.slug) return;\n try {\n await api.entries.remove(props.collection, props.slug);\n unsaved.release();\n toast.success(\"Deleted.\");\n void staging.refresh();\n await router.replace({ name: \"collection\", params: { collection: props.collection } });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not delete this entry.\");\n } finally {\n confirmDelete.value = false;\n }\n}\n</script>\n\n<template>\n <template v-if=\"definition\">\n <PageHeader\n :title=\"title\"\n :subtitle=\"location\"\n :back=\"{ name: 'collection', params: { collection } }\"\n :back-label=\"definition.label\"\n >\n <span :class=\"['badge', dirty ? 'border-accent bg-accent-soft text-accent' : 'text-success']\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />\n {{ dirty ? \"Unsaved\" : \"Saved\" }}\n </span>\n <button type=\"button\" class=\"btn-primary\" :disabled=\"saving || !dirty\" @click=\"save\">\n {{ saving ? \"Saving…\" : \"Save changes\" }}\n </button>\n </PageHeader>\n\n <div class=\"mx-auto max-w-3xl px-4 py-6 sm:px-6\">\n <p v-if=\"loading\" class=\"text-sm text-muted\">Loading…</p>\n\n <template v-else>\n <div\n v-if=\"conflict\"\n role=\"alert\"\n class=\"mb-5 rounded-md border border-warning/15 bg-warning-soft p-4\"\n >\n <p class=\"text-sm font-medium text-warning\">This entry changed while you were editing</p>\n <p class=\"mt-1 text-xs text-muted\">\n Your changes are still here and have not been saved. Copy anything you need, then load\n the current version — or open the repository to see what changed.\n </p>\n <button type=\"button\" class=\"btn-secondary mt-2.5\" @click=\"reload\">\n Discard mine and reload\n </button>\n </div>\n\n <div\n v-if=\"issues.length\"\n role=\"alert\"\n class=\"mb-5 rounded-md border border-danger/15 bg-danger-soft p-4\"\n >\n <p class=\"text-sm font-medium text-danger\">\n {{ issues.length }} field{{ issues.length === 1 ? \"\" : \"s\" }} need attention\n </p>\n <ul class=\"mt-1.5 space-y-0.5 text-xs text-danger\">\n <li v-for=\"issue in issues\" :key=\"issue.path\">{{ issue.message }}</li>\n </ul>\n </div>\n\n <form novalidate @submit.prevent=\"save\">\n <FieldList\n v-model=\"values\"\n :fields=\"definition.fields\"\n :issues=\"issues\"\n :disabled=\"saving\"\n :collection=\"collection\"\n :entry-slug=\"slug\"\n />\n\n <!-- Enter in a text input should save, not do nothing. -->\n <button type=\"submit\" class=\"sr-only\">Save</button>\n </form>\n\n <div v-if=\"!isNew\" class=\"mt-10 border-t border-line pt-5\">\n <button type=\"button\" class=\"btn-danger\" @click=\"askDelete\">\n Delete this {{ definition.labelSingular.toLowerCase() }}\n </button>\n </div>\n </template>\n </div>\n\n <ConfirmDialog\n :open=\"confirmDelete\"\n :title=\"`Delete “${title}”?`\"\n confirm-label=\"Delete\"\n destructive\n @cancel=\"confirmDelete = false\"\n @confirm=\"remove\"\n >\n <!-- Honest about what a delete is now: a pending change. It reaches the\n repository's history when the pending set is published, and the\n history keeps it from then on. -->\n <p>\n This removes <code>{{ location }}</code>. Like any edit, the removal joins the pending\n changes and reaches the repository when they are published.\n </p>\n\n <div v-if=\"references.length\" class=\"mt-3 rounded-md border border-warning/15 bg-warning-soft p-4\">\n <p class=\"text-sm font-medium text-warning\">\n {{ references.length }} other entr{{ references.length === 1 ? \"y\" : \"ies\" }} link to this\n one:\n </p>\n <ul class=\"mt-1 list-inside list-disc text-xs\">\n <li v-for=\"(item, index) in references\" :key=\"index\">\n {{ item.title }} ({{ item.collection }})\n </li>\n </ul>\n </div>\n </ConfirmDialog>\n </template>\n\n <div v-else class=\"p-8 text-center text-sm text-muted\">\n There is no collection called \"{{ collection }}\".\n </div>\n</template>\n"],"names":["props","__props","router","useRouter","schema","useSchema","staging","useStaging","toast","useToast","definition","computed","entry","ref","values","issues","loading","saving","conflict","confirmDelete","references","revision","unsaved","useUnsavedChanges","dirty","isNew","title","def","value","location","_a","setBaseline","detail","open","slug","api","onMounted","defaultValues","error","ApiError","save","payload","saved","reload","askDelete","found","item","remove","_createElementBlock","_Fragment","_createVNode","PageHeader","_createElementVNode","_unref","_createTextVNode","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_hoisted_4","_cache","_hoisted_5","_hoisted_6","_toDisplayString","_hoisted_7","_renderList","issue","FieldList","$event","_hoisted_8","ConfirmDialog","_hoisted_9","_hoisted_10","_hoisted_11","index","_hoisted_12"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{d as _,o as h,c as a,a as t,_ as b,t as u,b as d,e as v,u as w,f as k,r as f,g as c,h as i}from"./index-Colfqb-9.js";const S={key:0,class:"grid min-h-full place-items-center p-6"},q={key:1,class:"grid min-h-full place-items-center p-6"},T={class:"w-full max-w-sm"},C={class:"card space-y-4 p-5"},M={key:0,role:"alert",class:"rounded-md border border-danger/15 bg-danger-soft px-4 py-3 text-sm text-danger"},R=["disabled"],V={key:1,class:"text-center text-xs text-muted"},B={class:"font-medium"},N={key:0,class:"mt-4 text-center text-xs text-muted"},E=_({__name:"LoginView",setup(A){const r=w(),m=k(),l=f(!1),o=f(null),p=c(()=>{var e;return(e=r.config.value)==null?void 0:e.provider}),n=c(()=>{var e;return((e=r.config.value)==null?void 0:e.authRequired)===!1}),y=c(()=>{const e=m.query.redirect;return typeof e=="string"&&e.startsWith("/admin")?e:"/admin/"}),x={rate_limited:"Too many sign-in attempts. Wait a few minutes, then sign in again.",expired:"That sign-in attempt expired or was not started here. Please try again.",failed:"Sign-in could not be completed. Please try again.",not_admitted:"You are signed in at your provider, but that account has no access to this site. Ask whoever runs it to add you.",too_large:"Sign-in could not be completed: your account details are too large."};h(()=>{const e=m.query.error;typeof e=="string"&&(o.value=x[e]??"Sign-in could not be completed."),n.value&&!o.value&&g()});function g(){l.value=!0,r.signIn(y.value)}return(e,s)=>n.value&&!o.value?(i(),a("div",S,[...s[0]||(s[0]=[t("p",{class:"text-sm text-muted"},"Opening the editor…",-1)])])):(i(),a("div",q,[t("div",T,[s[3]||(s[3]=t("div",{class:"mb-7 flex flex-col items-center gap-4 text-center"},[t("img",{src:b,alt:"micacms",class:"h-8 w-auto"}),t("div",null,[t("h1",{class:"text-lg font-semibold tracking-tight"},"Sign in to Mica"),t("p",{class:"mt-0.5 text-sm text-muted"},"Manage your website content.")])],-1)),t("div",C,[o.value?(i(),a("p",M,u(o.value),1)):d("",!0),t("button",{type:"button",class:"btn-primary w-full",disabled:l.value,onClick:g},u(l.value?"Redirecting…":n.value?"Continue":"Sign in"),9,R),p.value&&!n.value?(i(),a("p",V,[s[1]||(s[1]=v(" You will be taken to ",-1)),t("span",B,u(p.value),1),s[2]||(s[2]=v(" to sign in. ",-1))])):d("",!0)]),n.value?d("",!0):(i(),a("p",N," Mica does not hold your password. Trouble signing in is a question for whoever runs your identity provider; no access once you are in is a question for a site administrator. "))])]))}});export{E as default};
2
+ //# sourceMappingURL=LoginView-BCrrdqnf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LoginView-BCrrdqnf.js","sources":["../../src/views/LoginView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Sign in.\n *\n * There is no form. Mica does not hold passwords, so there is nothing for an\n * editor to type here — the button hands the browser to the identity provider,\n * which does the actual authenticating and sends it back.\n *\n * The error banner exists because the round trip can fail after the provider\n * is satisfied: someone can authenticate perfectly well and still not be\n * admitted to this site, and that has to read as \"ask for access\", not as\n * \"your password was wrong\".\n *\n * In local mode this screen is not reached at all: there is no provider, no\n * account and nobody to be, and the session starts as the page is served. The\n * redirect below is the fallback for the one way in that skips that — a\n * browser still holding `/admin/login` from an earlier session.\n */\n\nimport { computed, onMounted, ref } from \"vue\";\nimport { useRoute } from \"vue-router\";\n\nimport { useSession } from \"@/stores/session\";\n\nconst session = useSession();\nconst route = useRoute();\n\nconst busy = ref(false);\n\n/** Set by the callback when it bounces the browser back here with a reason. */\nconst error = ref<string | null>(null);\n\nconst provider = computed(() => session.config.value?.provider);\n/** Local mode: no provider to send anybody to, so there is nothing to show. */\nconst authDisabled = computed(() => session.config.value?.authRequired === false);\n\n/** Where to land afterwards. Always a path on this server. */\nconst returnTo = computed(() => {\n const redirect = route.query[\"redirect\"];\n return typeof redirect === \"string\" && redirect.startsWith(\"/admin\")\n ? redirect\n : \"/admin/\";\n});\n\n/** The callback sends a code, never a sentence, so the URL carries no address (#150). */\nconst REFUSALS: Record<string, string> = {\n rate_limited: \"Too many sign-in attempts. Wait a few minutes, then sign in again.\",\n expired: \"That sign-in attempt expired or was not started here. Please try again.\",\n failed: \"Sign-in could not be completed. Please try again.\",\n not_admitted: \"You are signed in at your provider, but that account has no access to this site. Ask whoever runs it to add you.\",\n too_large: \"Sign-in could not be completed: your account details are too large.\",\n};\n\nonMounted(() => {\n const reason = route.query[\"error\"];\n if (typeof reason === \"string\") error.value = REFUSALS[reason] ?? \"Sign-in could not be completed.\";\n\n // No provider, no question to ask. Straight to the editor.\n if (authDisabled.value && !error.value) signIn();\n});\n\nfunction signIn(): void {\n busy.value = true;\n session.signIn(returnTo.value);\n}\n</script>\n\n<template>\n <!-- Local mode: a held breath, not a screen. -->\n <div v-if=\"authDisabled && !error\" class=\"grid min-h-full place-items-center p-6\">\n <p class=\"text-sm text-muted\">Opening the editor…</p>\n </div>\n\n <div v-else class=\"grid min-h-full place-items-center p-6\">\n <div class=\"w-full max-w-sm\">\n <div class=\"mb-7 flex flex-col items-center gap-4 text-center\">\n <img src=\"/brand/micacms-wordmark.svg\" alt=\"micacms\" class=\"h-8 w-auto\" />\n <div>\n <h1 class=\"text-lg font-semibold tracking-tight\">Sign in to Mica</h1>\n <p class=\"mt-0.5 text-sm text-muted\">Manage your website content.</p>\n </div>\n </div>\n\n <div class=\"card space-y-4 p-5\">\n <p v-if=\"error\" role=\"alert\" class=\"rounded-md border border-danger/15 bg-danger-soft px-4 py-3 text-sm text-danger\">\n {{ error }}\n </p>\n\n <button type=\"button\" class=\"btn-primary w-full\" :disabled=\"busy\" @click=\"signIn\">\n {{ busy ? \"Redirecting…\" : authDisabled ? \"Continue\" : \"Sign in\" }}\n </button>\n\n <p v-if=\"provider && !authDisabled\" class=\"text-center text-xs text-muted\">\n You will be taken to <span class=\"font-medium\">{{ provider }}</span> to sign in.\n </p>\n </div>\n\n <p v-if=\"!authDisabled\" class=\"mt-4 text-center text-xs text-muted\">\n Mica does not hold your password. Trouble signing in is a question for\n whoever runs your identity provider; no access once you are in is a\n question for a site administrator.\n </p>\n </div>\n </div>\n</template>\n"],"names":["session","useSession","route","useRoute","busy","ref","error","provider","computed","_a","authDisabled","returnTo","redirect","REFUSALS","onMounted","reason","signIn","_openBlock","_createElementBlock","_hoisted_1","_cache","_createElementVNode","_hoisted_2","_hoisted_3","_imports_0","_hoisted_4","_hoisted_5","_toDisplayString","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9"],"mappings":"6kBAwBA,MAAMA,EAAUC,EAAA,EACVC,EAAQC,EAAA,EAERC,EAAOC,EAAI,EAAK,EAGhBC,EAAQD,EAAmB,IAAI,EAE/BE,EAAWC,EAAS,IAAA,OAAM,OAAAC,EAAAT,EAAQ,OAAO,QAAf,YAAAS,EAAsB,SAAQ,EAExDC,EAAeF,EAAS,IAAA,OAAM,QAAAC,EAAAT,EAAQ,OAAO,QAAf,YAAAS,EAAsB,gBAAiB,GAAK,EAG1EE,EAAWH,EAAS,IAAM,CAC9B,MAAMI,EAAWV,EAAM,MAAM,SAC7B,OAAO,OAAOU,GAAa,UAAYA,EAAS,WAAW,QAAQ,EAC/DA,EACA,SACN,CAAC,EAGKC,EAAmC,CACvC,aAAc,qEACd,QAAS,0EACT,OAAQ,oDACR,aAAc,mHACd,UAAW,qEAAA,EAGbC,EAAU,IAAM,CACd,MAAMC,EAASb,EAAM,MAAM,MACvB,OAAOa,GAAW,aAAgB,MAAQF,EAASE,CAAM,GAAK,mCAG9DL,EAAa,OAAS,CAACJ,EAAM,OAAOU,EAAA,CAC1C,CAAC,EAED,SAASA,GAAe,CACtBZ,EAAK,MAAQ,GACbJ,EAAQ,OAAOW,EAAS,KAAK,CAC/B,cAKaD,EAAA,QAAiBJ,EAAA,OAA5BW,IAAAC,EAEM,MAFNC,EAEM,CAAA,GAAAC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAA,CADJC,EAAqD,IAAA,CAAlD,MAAM,oBAAA,EAAqB,sBAAmB,EAAA,CAAA,OAGnDJ,IAAAC,EA8BM,MA9BNI,EA8BM,CA7BJD,EA4BM,MA5BNE,EA4BM,aA3BJF,EAMM,MAAA,CAND,MAAM,qDAAmD,CAC5DA,EAA0E,MAAA,CAArE,IAAAG,EAAkC,IAAI,UAAU,MAAM,YAAA,GAC3DH,EAGM,MAAA,KAAA,CAFJA,EAAqE,KAAA,CAAjE,MAAM,sCAAA,EAAuC,iBAAe,EAChEA,EAAqE,IAAA,CAAlE,MAAM,2BAAA,EAA4B,8BAA4B,CAAA,SAIrEA,EAYM,MAZNI,EAYM,CAXKnB,EAAA,WAATY,EAEI,IAFJQ,EAEIC,EADCrB,EAAA,KAAK,EAAA,CAAA,YAGVe,EAES,SAAA,CAFD,KAAK,SAAS,MAAM,qBAAsB,SAAUjB,EAAA,MAAO,QAAOY,CAAA,EACrEW,EAAAvB,EAAA,qBAAwBM,EAAA,MAAY,WAAA,SAAA,EAAA,EAAAkB,CAAA,EAGhCrB,EAAA,QAAaG,EAAA,OAAtBO,IAAAC,EAEI,IAFJW,EAEI,eAFuE,yBACpD,EAAA,GAAAR,EAA+C,OAA/CS,EAA+CH,EAAlBpB,EAAA,KAAQ,EAAA,CAAA,gBAAU,gBACtE,EAAA,EAAA,eAGQG,EAAA,oBAAVQ,EAII,IAJJa,EAAoE,iLAIpE"}
@@ -1,2 +1,2 @@
1
- import{d as I,o as P,c as o,j as E,w as O,l as p,a as t,s as W,v as q,t as r,F as _,k as N,i as v,b as S,z as b,C as V,r as c,g as w,A as B,n as G,h as n,e as H,m as J}from"./index-9WVbsgdk.js";import{_ as K}from"./PageHeader.vue_vue_type_script_setup_true_lang-IPw0xrLa.js";import{f as k}from"./format-Cpnr2As6.js";const Q={class:"btn-primary cursor-pointer"},R=["disabled"],X={class:"mb-3 flex flex-wrap items-center gap-2"},Y={key:0,class:"py-12 text-center text-sm text-muted"},Z={key:1,class:"card px-4 py-12 text-center"},ee={class:"text-sm font-medium"},te={key:2,class:"grid gap-5 lg:grid-cols-[1fr_20rem]"},se={class:"space-y-6"},le={class:"mb-2 font-mono text-xs text-muted"},ae={class:"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4"},oe=["aria-pressed","onClick"],ne={class:"block aspect-4/3 bg-canvas"},ie=["src"],re={key:1,class:"flex h-full w-full items-center justify-center text-xs font-semibold uppercase text-muted"},de={class:"block px-2 py-1.5"},ue={class:"block truncate text-xs font-medium"},ce={class:"block text-[11px] text-muted"},pe={key:0,class:"card h-fit p-4 lg:sticky lg:top-5"},fe={class:"mt-3 overflow-hidden rounded-lg border border-line bg-canvas"},me=["src"],ve={key:1,class:"p-6 text-center text-xs text-muted"},he={class:"mt-4 space-y-1 text-xs text-muted"},ge={class:"flex justify-between gap-3"},ye={key:0,class:"flex justify-between gap-3"},xe={class:"flex justify-between gap-3"},_e={class:"mt-3 border-t border-line pt-3"},be={class:"mt-1 break-all font-mono text-[11px]"},we={class:"mt-3 flex gap-2"},ke=["href"],De=I({__name:"MediaView",setup(Ce){const f=G(),u=c([]),y=c(!0),h=c(!1),m=c(""),g=c(!1),a=c(null),x=c(null),C=l=>l.mimeType.startsWith("image/"),L=l=>{const e=l.split("/");return e.pop(),e.join("/")||"/"},T=w(()=>{const l=m.value.trim().toLowerCase();return l?u.value.filter(e=>e.path.toLowerCase().includes(l)):u.value}),U=w(()=>{const l=new Map;for(const e of T.value){const s=L(e.path),d=l.get(s);d?d.push(e):l.set(s,[e])}return[...l].sort(([e],[s])=>e.localeCompare(s))}),A=w(()=>u.value.reduce((l,e)=>l+e.size,0));async function $(){y.value=!0;try{u.value=(await v.media.list()).items;const l=u.value.find(e=>{var s;return e.path===((s=a.value)==null?void 0:s.path)});a.value=l??null}catch(l){f.error(l instanceof B?l.message:"Could not load the media library.")}finally{y.value=!1}}P($);async function j(l){const e=Array.from(l??[]);if(e.length!==0){h.value=!0;try{const{items:s}=await v.media.upload(e);f.success(`Uploaded ${s.length} file${s.length===1?"":"s"}.`),J().refresh(),await $(),a.value=u.value.find(d=>{var i;return d.path===((i=s[0])==null?void 0:i.path)})??null}catch(s){f.error(s instanceof B?s.message:"The upload failed.")}finally{h.value=!1,x.value&&(x.value.value="")}}}async function F(l){try{await navigator.clipboard.writeText(l),f.success("Path copied.")}catch{f.error("Could not copy. Select the path and copy it by hand.")}}return(l,e)=>(n(),o(_,null,[E(K,{title:"Media",subtitle:`${u.value.length} file${u.value.length===1?"":"s"} · ${p(k)(A.value)}`},{default:O(()=>[t("label",Q,[t("input",{ref_key:"fileInput",ref:x,type:"file",multiple:"",class:"sr-only",disabled:h.value,onChange:e[0]||(e[0]=s=>j(s.target.files))},null,40,R),H(" "+r(h.value?"Uploading…":"Upload files"),1)])]),_:1},8,["subtitle"]),t("div",{class:V(["mx-auto max-w-5xl px-4 py-5 sm:px-6",g.value&&"rounded-md outline-2 outline-dashed outline-ink"]),onDragover:e[3]||(e[3]=b(s=>g.value=!0,["prevent"])),onDragleave:e[4]||(e[4]=b(s=>g.value=!1,["prevent"])),onDrop:e[5]||(e[5]=b(s=>{var d;g.value=!1,j(((d=s.dataTransfer)==null?void 0:d.files)??null)},["prevent"]))},[t("div",X,[W(t("input",{"onUpdate:modelValue":e[1]||(e[1]=s=>m.value=s),type:"search",placeholder:"Search by name or folder…",class:"input max-w-64","aria-label":"Search media"},null,512),[[q,m.value]])]),y.value?(n(),o("p",Y,"Loading…")):T.value.length===0?(n(),o("div",Z,[t("p",ee,r(m.value?`Nothing matches "${m.value}".`:"No files yet."),1),e[6]||(e[6]=t("p",{class:"mt-1 text-sm text-muted"}," Drag files anywhere on this page to upload them. ",-1))])):(n(),o("div",te,[t("div",se,[(n(!0),o(_,null,N(U.value,([s,d])=>(n(),o("section",{key:s},[t("h2",le,r(s),1),t("ul",ae,[(n(!0),o(_,null,N(d,i=>{var z,D;return n(),o("li",{key:i.path},[t("button",{type:"button","aria-pressed":((z=a.value)==null?void 0:z.path)===i.path,class:V(["block w-full overflow-hidden rounded-lg border text-left transition",((D=a.value)==null?void 0:D.path)===i.path?"border-accent ring-2 ring-accent/25":"border-line hover:border-accent/40"]),onClick:Te=>{var M;return a.value=((M=a.value)==null?void 0:M.path)===i.path?null:i}},[t("span",ne,[C(i)?(n(),o("img",{key:0,src:p(v).media.url(i.path),alt:"",loading:"lazy",class:"h-full w-full object-cover"},null,8,ie)):(n(),o("span",re,r(i.mimeType.split("/")[1]),1))]),t("span",de,[t("span",ue,r(i.filename),1),t("span",ce,r(p(k)(i.size)),1)])],10,oe)])}),128))])]))),128))]),a.value?(n(),o("aside",pe,[e[11]||(e[11]=t("h2",{class:"text-sm font-semibold"},"File details",-1)),t("div",fe,[C(a.value)?(n(),o("img",{key:0,src:p(v).media.url(a.value.path),alt:"",class:"max-h-48 w-full object-contain"},null,8,me)):(n(),o("p",ve,r(a.value.mimeType),1))]),t("dl",he,[t("div",ge,[e[7]||(e[7]=t("dt",null,"Type",-1)),t("dd",null,r(a.value.mimeType),1)]),a.value.width?(n(),o("div",ye,[e[8]||(e[8]=t("dt",null,"Dimensions",-1)),t("dd",null,r(a.value.width)+" × "+r(a.value.height),1)])):S("",!0),t("div",xe,[e[9]||(e[9]=t("dt",null,"Size",-1)),t("dd",null,r(p(k)(a.value.size)),1)])]),t("div",_e,[e[10]||(e[10]=t("p",{class:"field-label"},"Path in the repository",-1)),t("p",be,r(a.value.path),1)]),t("div",we,[t("button",{type:"button",class:"btn-secondary flex-1",onClick:e[2]||(e[2]=s=>F(a.value.path))}," Copy path "),t("a",{href:p(v).media.url(a.value.path),target:"_blank",rel:"noopener noreferrer",class:"btn-secondary flex-1"}," Open ",8,ke)])])):S("",!0)]))],34)],64))}});export{De as default};
2
- //# sourceMappingURL=MediaView-BVODAshv.js.map
1
+ import{d as I,o as P,c as o,j as E,w as O,l as p,a as t,s as W,v as q,t as r,F as _,k as N,i as v,b as S,z as b,C as V,r as c,g as w,A as B,n as G,h as n,e as H,m as J}from"./index-Colfqb-9.js";import{_ as K}from"./PageHeader.vue_vue_type_script_setup_true_lang-B-fBJAjn.js";import{f as k}from"./format-Cpnr2As6.js";const Q={class:"btn-primary cursor-pointer"},R=["disabled"],X={class:"mb-3 flex flex-wrap items-center gap-2"},Y={key:0,class:"py-12 text-center text-sm text-muted"},Z={key:1,class:"card px-4 py-12 text-center"},ee={class:"text-sm font-medium"},te={key:2,class:"grid gap-5 lg:grid-cols-[1fr_20rem]"},se={class:"space-y-6"},le={class:"mb-2 font-mono text-xs text-muted"},ae={class:"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4"},oe=["aria-pressed","onClick"],ne={class:"block aspect-4/3 bg-canvas"},ie=["src"],re={key:1,class:"flex h-full w-full items-center justify-center text-xs font-semibold uppercase text-muted"},de={class:"block px-2 py-1.5"},ue={class:"block truncate text-xs font-medium"},ce={class:"block text-[11px] text-muted"},pe={key:0,class:"card h-fit p-4 lg:sticky lg:top-5"},fe={class:"mt-3 overflow-hidden rounded-lg border border-line bg-canvas"},me=["src"],ve={key:1,class:"p-6 text-center text-xs text-muted"},he={class:"mt-4 space-y-1 text-xs text-muted"},ge={class:"flex justify-between gap-3"},ye={key:0,class:"flex justify-between gap-3"},xe={class:"flex justify-between gap-3"},_e={class:"mt-3 border-t border-line pt-3"},be={class:"mt-1 break-all font-mono text-[11px]"},we={class:"mt-3 flex gap-2"},ke=["href"],De=I({__name:"MediaView",setup(Ce){const f=G(),u=c([]),y=c(!0),h=c(!1),m=c(""),g=c(!1),a=c(null),x=c(null),C=l=>l.mimeType.startsWith("image/"),L=l=>{const e=l.split("/");return e.pop(),e.join("/")||"/"},T=w(()=>{const l=m.value.trim().toLowerCase();return l?u.value.filter(e=>e.path.toLowerCase().includes(l)):u.value}),U=w(()=>{const l=new Map;for(const e of T.value){const s=L(e.path),d=l.get(s);d?d.push(e):l.set(s,[e])}return[...l].sort(([e],[s])=>e.localeCompare(s))}),A=w(()=>u.value.reduce((l,e)=>l+e.size,0));async function $(){y.value=!0;try{u.value=(await v.media.list()).items;const l=u.value.find(e=>{var s;return e.path===((s=a.value)==null?void 0:s.path)});a.value=l??null}catch(l){f.error(l instanceof B?l.message:"Could not load the media library.")}finally{y.value=!1}}P($);async function j(l){const e=Array.from(l??[]);if(e.length!==0){h.value=!0;try{const{items:s}=await v.media.upload(e);f.success(`Uploaded ${s.length} file${s.length===1?"":"s"}.`),J().refresh(),await $(),a.value=u.value.find(d=>{var i;return d.path===((i=s[0])==null?void 0:i.path)})??null}catch(s){f.error(s instanceof B?s.message:"The upload failed.")}finally{h.value=!1,x.value&&(x.value.value="")}}}async function F(l){try{await navigator.clipboard.writeText(l),f.success("Path copied.")}catch{f.error("Could not copy. Select the path and copy it by hand.")}}return(l,e)=>(n(),o(_,null,[E(K,{title:"Media",subtitle:`${u.value.length} file${u.value.length===1?"":"s"} · ${p(k)(A.value)}`},{default:O(()=>[t("label",Q,[t("input",{ref_key:"fileInput",ref:x,type:"file",multiple:"",class:"sr-only",disabled:h.value,onChange:e[0]||(e[0]=s=>j(s.target.files))},null,40,R),H(" "+r(h.value?"Uploading…":"Upload files"),1)])]),_:1},8,["subtitle"]),t("div",{class:V(["mx-auto max-w-5xl px-4 py-5 sm:px-6",g.value&&"rounded-md outline-2 outline-dashed outline-ink"]),onDragover:e[3]||(e[3]=b(s=>g.value=!0,["prevent"])),onDragleave:e[4]||(e[4]=b(s=>g.value=!1,["prevent"])),onDrop:e[5]||(e[5]=b(s=>{var d;g.value=!1,j(((d=s.dataTransfer)==null?void 0:d.files)??null)},["prevent"]))},[t("div",X,[W(t("input",{"onUpdate:modelValue":e[1]||(e[1]=s=>m.value=s),type:"search",placeholder:"Search by name or folder…",class:"input max-w-64","aria-label":"Search media"},null,512),[[q,m.value]])]),y.value?(n(),o("p",Y,"Loading…")):T.value.length===0?(n(),o("div",Z,[t("p",ee,r(m.value?`Nothing matches "${m.value}".`:"No files yet."),1),e[6]||(e[6]=t("p",{class:"mt-1 text-sm text-muted"}," Drag files anywhere on this page to upload them. ",-1))])):(n(),o("div",te,[t("div",se,[(n(!0),o(_,null,N(U.value,([s,d])=>(n(),o("section",{key:s},[t("h2",le,r(s),1),t("ul",ae,[(n(!0),o(_,null,N(d,i=>{var z,D;return n(),o("li",{key:i.path},[t("button",{type:"button","aria-pressed":((z=a.value)==null?void 0:z.path)===i.path,class:V(["block w-full overflow-hidden rounded-lg border text-left transition",((D=a.value)==null?void 0:D.path)===i.path?"border-accent ring-2 ring-accent/25":"border-line hover:border-accent/40"]),onClick:Te=>{var M;return a.value=((M=a.value)==null?void 0:M.path)===i.path?null:i}},[t("span",ne,[C(i)?(n(),o("img",{key:0,src:p(v).media.url(i.path),alt:"",loading:"lazy",class:"h-full w-full object-cover"},null,8,ie)):(n(),o("span",re,r(i.mimeType.split("/")[1]),1))]),t("span",de,[t("span",ue,r(i.filename),1),t("span",ce,r(p(k)(i.size)),1)])],10,oe)])}),128))])]))),128))]),a.value?(n(),o("aside",pe,[e[11]||(e[11]=t("h2",{class:"text-sm font-semibold"},"File details",-1)),t("div",fe,[C(a.value)?(n(),o("img",{key:0,src:p(v).media.url(a.value.path),alt:"",class:"max-h-48 w-full object-contain"},null,8,me)):(n(),o("p",ve,r(a.value.mimeType),1))]),t("dl",he,[t("div",ge,[e[7]||(e[7]=t("dt",null,"Type",-1)),t("dd",null,r(a.value.mimeType),1)]),a.value.width?(n(),o("div",ye,[e[8]||(e[8]=t("dt",null,"Dimensions",-1)),t("dd",null,r(a.value.width)+" × "+r(a.value.height),1)])):S("",!0),t("div",xe,[e[9]||(e[9]=t("dt",null,"Size",-1)),t("dd",null,r(p(k)(a.value.size)),1)])]),t("div",_e,[e[10]||(e[10]=t("p",{class:"field-label"},"Path in the repository",-1)),t("p",be,r(a.value.path),1)]),t("div",we,[t("button",{type:"button",class:"btn-secondary flex-1",onClick:e[2]||(e[2]=s=>F(a.value.path))}," Copy path "),t("a",{href:p(v).media.url(a.value.path),target:"_blank",rel:"noopener noreferrer",class:"btn-secondary flex-1"}," Open ",8,ke)])])):S("",!0)]))],34)],64))}});export{De as default};
2
+ //# sourceMappingURL=MediaView-KlQHfWAi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MediaView-BVODAshv.js","sources":["../../src/views/MediaView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Media library — a view of the files in the content repository.\n *\n * See what is there, and add to it. Two things it deliberately does not offer:\n *\n * - **Alt text.** Not a property of a file: the same photograph means\n * different things in different places, so it belongs to the field that\n * uses the image, next to the content it describes.\n * - **Rename and delete.** A media file is referenced by its path, so moving\n * or removing one is a change to every entry that points at it — a job for\n * a commit and a review, not for a button that quietly leaves gaps on the\n * website.\n *\n * Files are grouped by directory, which is meaningful here: an entry's images\n * sit beside the entry.\n */\n\nimport { computed, onMounted, ref } from \"vue\";\n\nimport { api, ApiError, type MediaItem } from \"@/api\";\nimport PageHeader from \"@/components/PageHeader.vue\";\nimport { formatBytes } from \"@/lib/format\";\nimport { useStaging } from \"@/stores/staging\";\nimport { useToast } from \"@/stores/toast\";\n\nconst toast = useToast();\n\nconst items = ref<MediaItem[]>([]);\nconst loading = ref(true);\nconst uploading = ref(false);\nconst search = ref(\"\");\nconst dragging = ref(false);\nconst selected = ref<MediaItem | null>(null);\nconst fileInput = ref<HTMLInputElement | null>(null);\n\nconst isImage = (item: MediaItem): boolean => item.mimeType.startsWith(\"image/\");\n\n/** The directory a file sits in; `media` is the shared one. */\nconst folderOf = (path: string): string => {\n const parts = path.split(\"/\");\n parts.pop();\n return parts.join(\"/\") || \"/\";\n};\n\nconst matching = computed(() => {\n const term = search.value.trim().toLowerCase();\n return term ? items.value.filter((item) => item.path.toLowerCase().includes(term)) : items.value;\n});\n\n/** Grouped by directory, in path order — the order the repository has them. */\nconst folders = computed(() => {\n const groups = new Map<string, MediaItem[]>();\n for (const item of matching.value) {\n const folder = folderOf(item.path);\n const group = groups.get(folder);\n if (group) group.push(item);\n else groups.set(folder, [item]);\n }\n return [...groups].sort(([a], [b]) => a.localeCompare(b));\n});\n\nconst totalBytes = computed(() => items.value.reduce((sum, item) => sum + item.size, 0));\n\nasync function load(): Promise<void> {\n loading.value = true;\n try {\n items.value = (await api.media.list()).items;\n // The chosen file may have gone from the repository between loads.\n const stillThere = items.value.find((item) => item.path === selected.value?.path);\n selected.value = stillThere ?? null;\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not load the media library.\");\n } finally {\n loading.value = false;\n }\n}\n\nonMounted(load);\n\nasync function upload(files: FileList | File[] | null): Promise<void> {\n const list = Array.from(files ?? []);\n if (list.length === 0) return;\n\n uploading.value = true;\n try {\n const { items: uploaded } = await api.media.upload(list);\n toast.success(`Uploaded ${uploaded.length} file${uploaded.length === 1 ? \"\" : \"s\"}.`);\n void useStaging().refresh();\n await load();\n selected.value = items.value.find((item) => item.path === uploaded[0]?.path) ?? null;\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"The upload failed.\");\n } finally {\n uploading.value = false;\n if (fileInput.value) fileInput.value.value = \"\";\n }\n}\n\nasync function copyPath(path: string): Promise<void> {\n try {\n await navigator.clipboard.writeText(path);\n toast.success(\"Path copied.\");\n } catch {\n // Clipboard access is denied in some browsers unless the page is focused.\n toast.error(\"Could not copy. Select the path and copy it by hand.\");\n }\n}\n</script>\n\n<template>\n <PageHeader\n title=\"Media\"\n :subtitle=\"`${items.length} file${items.length === 1 ? '' : 's'} · ${formatBytes(totalBytes)}`\"\n >\n <label class=\"btn-primary cursor-pointer\">\n <input\n ref=\"fileInput\"\n type=\"file\"\n multiple\n class=\"sr-only\"\n :disabled=\"uploading\"\n @change=\"upload(($event.target as HTMLInputElement).files)\"\n />\n {{ uploading ? \"Uploading…\" : \"Upload files\" }}\n </label>\n </PageHeader>\n\n <div\n class=\"mx-auto max-w-5xl px-4 py-5 sm:px-6\"\n :class=\"dragging && 'rounded-md outline-2 outline-dashed outline-ink'\"\n @dragover.prevent=\"dragging = true\"\n @dragleave.prevent=\"dragging = false\"\n @drop.prevent=\"\n dragging = false;\n upload($event.dataTransfer?.files ?? null);\n \"\n >\n <div class=\"mb-3 flex flex-wrap items-center gap-2\">\n <input\n v-model=\"search\"\n type=\"search\"\n placeholder=\"Search by name or folder…\"\n class=\"input max-w-64\"\n aria-label=\"Search media\"\n />\n </div>\n\n <p v-if=\"loading\" class=\"py-12 text-center text-sm text-muted\">Loading…</p>\n\n <div v-else-if=\"matching.length === 0\" class=\"card px-4 py-12 text-center\">\n <p class=\"text-sm font-medium\">\n {{ search ? `Nothing matches \"${search}\".` : \"No files yet.\" }}\n </p>\n <p class=\"mt-1 text-sm text-muted\">\n Drag files anywhere on this page to upload them.\n </p>\n </div>\n\n <div v-else class=\"grid gap-5 lg:grid-cols-[1fr_20rem]\">\n <div class=\"space-y-6\">\n <section v-for=\"[folder, files] in folders\" :key=\"folder\">\n <h2 class=\"mb-2 font-mono text-xs text-muted\">{{ folder }}</h2>\n\n <ul class=\"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4\">\n <li v-for=\"item in files\" :key=\"item.path\">\n <button\n type=\"button\"\n :aria-pressed=\"selected?.path === item.path\"\n :class=\"[\n 'block w-full overflow-hidden rounded-lg border text-left transition',\n selected?.path === item.path\n ? 'border-accent ring-2 ring-accent/25'\n : 'border-line hover:border-accent/40',\n ]\"\n @click=\"selected = selected?.path === item.path ? null : item\"\n >\n <span class=\"block aspect-4/3 bg-canvas\">\n <img\n v-if=\"isImage(item)\"\n :src=\"api.media.url(item.path)\"\n alt=\"\"\n loading=\"lazy\"\n class=\"h-full w-full object-cover\"\n />\n <span\n v-else\n class=\"flex h-full w-full items-center justify-center text-xs font-semibold uppercase text-muted\"\n >\n {{ item.mimeType.split(\"/\")[1] }}\n </span>\n </span>\n\n <span class=\"block px-2 py-1.5\">\n <span class=\"block truncate text-xs font-medium\">{{ item.filename }}</span>\n <span class=\"block text-[11px] text-muted\">{{ formatBytes(item.size) }}</span>\n </span>\n </button>\n </li>\n </ul>\n </section>\n </div>\n\n <aside v-if=\"selected\" class=\"card h-fit p-4 lg:sticky lg:top-5\">\n <h2 class=\"text-sm font-semibold\">File details</h2>\n\n <div class=\"mt-3 overflow-hidden rounded-lg border border-line bg-canvas\">\n <img\n v-if=\"isImage(selected)\"\n :src=\"api.media.url(selected.path)\"\n alt=\"\"\n class=\"max-h-48 w-full object-contain\"\n />\n <p v-else class=\"p-6 text-center text-xs text-muted\">\n {{ selected.mimeType }}\n </p>\n </div>\n\n <dl class=\"mt-4 space-y-1 text-xs text-muted\">\n <div class=\"flex justify-between gap-3\">\n <dt>Type</dt>\n <dd>{{ selected.mimeType }}</dd>\n </div>\n <div v-if=\"selected.width\" class=\"flex justify-between gap-3\">\n <dt>Dimensions</dt>\n <dd>{{ selected.width }} × {{ selected.height }}</dd>\n </div>\n <div class=\"flex justify-between gap-3\">\n <dt>Size</dt>\n <dd>{{ formatBytes(selected.size) }}</dd>\n </div>\n </dl>\n\n <!-- The path is the file's identity: it is what a field stores and what\n appears in the diff, so it is worth being able to copy. -->\n <div class=\"mt-3 border-t border-line pt-3\">\n <p class=\"field-label\">Path in the repository</p>\n <p class=\"mt-1 break-all font-mono text-[11px]\">{{ selected.path }}</p>\n </div>\n\n <div class=\"mt-3 flex gap-2\">\n <button type=\"button\" class=\"btn-secondary flex-1\" @click=\"copyPath(selected.path)\">\n Copy path\n </button>\n <a\n :href=\"api.media.url(selected.path)\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"btn-secondary flex-1\"\n >\n Open\n </a>\n </div>\n </aside>\n </div>\n </div>\n</template>\n"],"names":["toast","useToast","items","ref","loading","uploading","search","dragging","selected","fileInput","isImage","item","folderOf","path","parts","matching","computed","term","folders","groups","folder","group","a","b","totalBytes","sum","load","api","stillThere","_a","error","ApiError","onMounted","upload","files","list","uploaded","useStaging","copyPath","_createVNode","PageHeader","_unref","formatBytes","_createElementVNode","_hoisted_1","$event","_createTextVNode","_normalizeClass","_cache","_withModifiers","_hoisted_3","_createElementBlock","_hoisted_4","_openBlock","_hoisted_5","_hoisted_6","_toDisplayString","_hoisted_7","_hoisted_8","_Fragment","_renderList","_hoisted_9","_hoisted_10","_b","_hoisted_12","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29"],"mappings":"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"}
1
+ {"version":3,"file":"MediaView-KlQHfWAi.js","sources":["../../src/views/MediaView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Media library — a view of the files in the content repository.\n *\n * See what is there, and add to it. Two things it deliberately does not offer:\n *\n * - **Alt text.** Not a property of a file: the same photograph means\n * different things in different places, so it belongs to the field that\n * uses the image, next to the content it describes.\n * - **Rename and delete.** A media file is referenced by its path, so moving\n * or removing one is a change to every entry that points at it — a job for\n * a commit and a review, not for a button that quietly leaves gaps on the\n * website.\n *\n * Files are grouped by directory, which is meaningful here: an entry's images\n * sit beside the entry.\n */\n\nimport { computed, onMounted, ref } from \"vue\";\n\nimport { api, ApiError, type MediaItem } from \"@/api\";\nimport PageHeader from \"@/components/PageHeader.vue\";\nimport { formatBytes } from \"@/lib/format\";\nimport { useStaging } from \"@/stores/staging\";\nimport { useToast } from \"@/stores/toast\";\n\nconst toast = useToast();\n\nconst items = ref<MediaItem[]>([]);\nconst loading = ref(true);\nconst uploading = ref(false);\nconst search = ref(\"\");\nconst dragging = ref(false);\nconst selected = ref<MediaItem | null>(null);\nconst fileInput = ref<HTMLInputElement | null>(null);\n\nconst isImage = (item: MediaItem): boolean => item.mimeType.startsWith(\"image/\");\n\n/** The directory a file sits in; `media` is the shared one. */\nconst folderOf = (path: string): string => {\n const parts = path.split(\"/\");\n parts.pop();\n return parts.join(\"/\") || \"/\";\n};\n\nconst matching = computed(() => {\n const term = search.value.trim().toLowerCase();\n return term ? items.value.filter((item) => item.path.toLowerCase().includes(term)) : items.value;\n});\n\n/** Grouped by directory, in path order — the order the repository has them. */\nconst folders = computed(() => {\n const groups = new Map<string, MediaItem[]>();\n for (const item of matching.value) {\n const folder = folderOf(item.path);\n const group = groups.get(folder);\n if (group) group.push(item);\n else groups.set(folder, [item]);\n }\n return [...groups].sort(([a], [b]) => a.localeCompare(b));\n});\n\nconst totalBytes = computed(() => items.value.reduce((sum, item) => sum + item.size, 0));\n\nasync function load(): Promise<void> {\n loading.value = true;\n try {\n items.value = (await api.media.list()).items;\n // The chosen file may have gone from the repository between loads.\n const stillThere = items.value.find((item) => item.path === selected.value?.path);\n selected.value = stillThere ?? null;\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not load the media library.\");\n } finally {\n loading.value = false;\n }\n}\n\nonMounted(load);\n\nasync function upload(files: FileList | File[] | null): Promise<void> {\n const list = Array.from(files ?? []);\n if (list.length === 0) return;\n\n uploading.value = true;\n try {\n const { items: uploaded } = await api.media.upload(list);\n toast.success(`Uploaded ${uploaded.length} file${uploaded.length === 1 ? \"\" : \"s\"}.`);\n void useStaging().refresh();\n await load();\n selected.value = items.value.find((item) => item.path === uploaded[0]?.path) ?? null;\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"The upload failed.\");\n } finally {\n uploading.value = false;\n if (fileInput.value) fileInput.value.value = \"\";\n }\n}\n\nasync function copyPath(path: string): Promise<void> {\n try {\n await navigator.clipboard.writeText(path);\n toast.success(\"Path copied.\");\n } catch {\n // Clipboard access is denied in some browsers unless the page is focused.\n toast.error(\"Could not copy. Select the path and copy it by hand.\");\n }\n}\n</script>\n\n<template>\n <PageHeader\n title=\"Media\"\n :subtitle=\"`${items.length} file${items.length === 1 ? '' : 's'} · ${formatBytes(totalBytes)}`\"\n >\n <label class=\"btn-primary cursor-pointer\">\n <input\n ref=\"fileInput\"\n type=\"file\"\n multiple\n class=\"sr-only\"\n :disabled=\"uploading\"\n @change=\"upload(($event.target as HTMLInputElement).files)\"\n />\n {{ uploading ? \"Uploading…\" : \"Upload files\" }}\n </label>\n </PageHeader>\n\n <div\n class=\"mx-auto max-w-5xl px-4 py-5 sm:px-6\"\n :class=\"dragging && 'rounded-md outline-2 outline-dashed outline-ink'\"\n @dragover.prevent=\"dragging = true\"\n @dragleave.prevent=\"dragging = false\"\n @drop.prevent=\"\n dragging = false;\n upload($event.dataTransfer?.files ?? null);\n \"\n >\n <div class=\"mb-3 flex flex-wrap items-center gap-2\">\n <input\n v-model=\"search\"\n type=\"search\"\n placeholder=\"Search by name or folder…\"\n class=\"input max-w-64\"\n aria-label=\"Search media\"\n />\n </div>\n\n <p v-if=\"loading\" class=\"py-12 text-center text-sm text-muted\">Loading…</p>\n\n <div v-else-if=\"matching.length === 0\" class=\"card px-4 py-12 text-center\">\n <p class=\"text-sm font-medium\">\n {{ search ? `Nothing matches \"${search}\".` : \"No files yet.\" }}\n </p>\n <p class=\"mt-1 text-sm text-muted\">\n Drag files anywhere on this page to upload them.\n </p>\n </div>\n\n <div v-else class=\"grid gap-5 lg:grid-cols-[1fr_20rem]\">\n <div class=\"space-y-6\">\n <section v-for=\"[folder, files] in folders\" :key=\"folder\">\n <h2 class=\"mb-2 font-mono text-xs text-muted\">{{ folder }}</h2>\n\n <ul class=\"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4\">\n <li v-for=\"item in files\" :key=\"item.path\">\n <button\n type=\"button\"\n :aria-pressed=\"selected?.path === item.path\"\n :class=\"[\n 'block w-full overflow-hidden rounded-lg border text-left transition',\n selected?.path === item.path\n ? 'border-accent ring-2 ring-accent/25'\n : 'border-line hover:border-accent/40',\n ]\"\n @click=\"selected = selected?.path === item.path ? null : item\"\n >\n <span class=\"block aspect-4/3 bg-canvas\">\n <img\n v-if=\"isImage(item)\"\n :src=\"api.media.url(item.path)\"\n alt=\"\"\n loading=\"lazy\"\n class=\"h-full w-full object-cover\"\n />\n <span\n v-else\n class=\"flex h-full w-full items-center justify-center text-xs font-semibold uppercase text-muted\"\n >\n {{ item.mimeType.split(\"/\")[1] }}\n </span>\n </span>\n\n <span class=\"block px-2 py-1.5\">\n <span class=\"block truncate text-xs font-medium\">{{ item.filename }}</span>\n <span class=\"block text-[11px] text-muted\">{{ formatBytes(item.size) }}</span>\n </span>\n </button>\n </li>\n </ul>\n </section>\n </div>\n\n <aside v-if=\"selected\" class=\"card h-fit p-4 lg:sticky lg:top-5\">\n <h2 class=\"text-sm font-semibold\">File details</h2>\n\n <div class=\"mt-3 overflow-hidden rounded-lg border border-line bg-canvas\">\n <img\n v-if=\"isImage(selected)\"\n :src=\"api.media.url(selected.path)\"\n alt=\"\"\n class=\"max-h-48 w-full object-contain\"\n />\n <p v-else class=\"p-6 text-center text-xs text-muted\">\n {{ selected.mimeType }}\n </p>\n </div>\n\n <dl class=\"mt-4 space-y-1 text-xs text-muted\">\n <div class=\"flex justify-between gap-3\">\n <dt>Type</dt>\n <dd>{{ selected.mimeType }}</dd>\n </div>\n <div v-if=\"selected.width\" class=\"flex justify-between gap-3\">\n <dt>Dimensions</dt>\n <dd>{{ selected.width }} × {{ selected.height }}</dd>\n </div>\n <div class=\"flex justify-between gap-3\">\n <dt>Size</dt>\n <dd>{{ formatBytes(selected.size) }}</dd>\n </div>\n </dl>\n\n <!-- The path is the file's identity: it is what a field stores and what\n appears in the diff, so it is worth being able to copy. -->\n <div class=\"mt-3 border-t border-line pt-3\">\n <p class=\"field-label\">Path in the repository</p>\n <p class=\"mt-1 break-all font-mono text-[11px]\">{{ selected.path }}</p>\n </div>\n\n <div class=\"mt-3 flex gap-2\">\n <button type=\"button\" class=\"btn-secondary flex-1\" @click=\"copyPath(selected.path)\">\n Copy path\n </button>\n <a\n :href=\"api.media.url(selected.path)\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"btn-secondary flex-1\"\n >\n Open\n </a>\n </div>\n </aside>\n </div>\n </div>\n</template>\n"],"names":["toast","useToast","items","ref","loading","uploading","search","dragging","selected","fileInput","isImage","item","folderOf","path","parts","matching","computed","term","folders","groups","folder","group","a","b","totalBytes","sum","load","api","stillThere","_a","error","ApiError","onMounted","upload","files","list","uploaded","useStaging","copyPath","_createVNode","PageHeader","_unref","formatBytes","_createElementVNode","_hoisted_1","$event","_createTextVNode","_normalizeClass","_cache","_withModifiers","_hoisted_3","_createElementBlock","_hoisted_4","_openBlock","_hoisted_5","_hoisted_6","_toDisplayString","_hoisted_7","_hoisted_8","_Fragment","_renderList","_hoisted_9","_hoisted_10","_b","_hoisted_12","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{Y as s,h as n,c as a,a as e,j as r,w as d,e as i,p as m}from"./index-9WVbsgdk.js";const l={},c={class:"grid min-h-[60vh] place-items-center p-6 text-center"};function p(x,t){const o=m("RouterLink");return n(),a("div",c,[e("div",null,[t[1]||(t[1]=e("p",{class:"text-sm font-semibold text-muted"},"Not found",-1)),t[2]||(t[2]=e("h1",{class:"mt-1 text-xl font-semibold tracking-tight"},"This page does not exist",-1)),t[3]||(t[3]=e("p",{class:"mt-1.5 text-sm text-muted"}," The link may be out of date, or the content may have been deleted. ",-1)),r(o,{to:{name:"dashboard"},class:"btn-primary mt-5"},{default:d(()=>[...t[0]||(t[0]=[i("Back to overview",-1)])]),_:1})])])}const f=s(l,[["render",p]]);export{f as default};
2
- //# sourceMappingURL=NotFoundView-K-fMCruW.js.map
1
+ import{Y as s,h as n,c as a,a as e,j as r,w as d,e as i,p as m}from"./index-Colfqb-9.js";const l={},c={class:"grid min-h-[60vh] place-items-center p-6 text-center"};function p(x,t){const o=m("RouterLink");return n(),a("div",c,[e("div",null,[t[1]||(t[1]=e("p",{class:"text-sm font-semibold text-muted"},"Not found",-1)),t[2]||(t[2]=e("h1",{class:"mt-1 text-xl font-semibold tracking-tight"},"This page does not exist",-1)),t[3]||(t[3]=e("p",{class:"mt-1.5 text-sm text-muted"}," The link may be out of date, or the content may have been deleted. ",-1)),r(o,{to:{name:"dashboard"},class:"btn-primary mt-5"},{default:d(()=>[...t[0]||(t[0]=[i("Back to overview",-1)])]),_:1})])])}const f=s(l,[["render",p]]);export{f as default};
2
+ //# sourceMappingURL=NotFoundView-C9bEHUGC.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NotFoundView-K-fMCruW.js","sources":["../../src/views/NotFoundView.vue"],"sourcesContent":["<template>\n <div class=\"grid min-h-[60vh] place-items-center p-6 text-center\">\n <div>\n <p class=\"text-sm font-semibold text-muted\">Not found</p>\n <h1 class=\"mt-1 text-xl font-semibold tracking-tight\">This page does not exist</h1>\n <p class=\"mt-1.5 text-sm text-muted\">\n The link may be out of date, or the content may have been deleted.\n </p>\n <RouterLink :to=\"{ name: 'dashboard' }\" class=\"btn-primary mt-5\">Back to overview</RouterLink>\n </div>\n </div>\n</template>\n"],"names":["_hoisted_1","_openBlock","_createElementBlock","_createElementVNode","_cache","_createVNode","_component_RouterLink"],"mappings":"oGACOA,EAAA,CAAA,MAAM,sDAAsD,0CAAjE,OAAAC,EAAA,EAAAC,EASM,MATNF,EASM,CARJG,EAOM,MAAA,KAAA,CANJC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAD,EAAyD,IAAA,CAAtD,MAAM,kCAAkC,EAAC,YAAS,EAAA,GACrDC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAD,EAAmF,KAAA,CAA/E,MAAM,2CAA2C,EAAC,2BAAwB,EAAA,GAC9EC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAD,EAEI,IAAA,CAFD,MAAM,2BAA2B,EAAC,uEAErC,EAAA,GACAE,EAA8FC,EAAA,CAAjF,GAAI,CAAA,KAAA,WAAA,EAAuB,MAAM,+BAAmB,IAAgB,CAAA,GAAAF,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAA,GAAhB,mBAAgB,EAAA"}
1
+ {"version":3,"file":"NotFoundView-C9bEHUGC.js","sources":["../../src/views/NotFoundView.vue"],"sourcesContent":["<template>\n <div class=\"grid min-h-[60vh] place-items-center p-6 text-center\">\n <div>\n <p class=\"text-sm font-semibold text-muted\">Not found</p>\n <h1 class=\"mt-1 text-xl font-semibold tracking-tight\">This page does not exist</h1>\n <p class=\"mt-1.5 text-sm text-muted\">\n The link may be out of date, or the content may have been deleted.\n </p>\n <RouterLink :to=\"{ name: 'dashboard' }\" class=\"btn-primary mt-5\">Back to overview</RouterLink>\n </div>\n </div>\n</template>\n"],"names":["_hoisted_1","_openBlock","_createElementBlock","_createElementVNode","_cache","_createVNode","_component_RouterLink"],"mappings":"oGACOA,EAAA,CAAA,MAAM,sDAAsD,0CAAjE,OAAAC,EAAA,EAAAC,EASM,MATNF,EASM,CARJG,EAOM,MAAA,KAAA,CANJC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAD,EAAyD,IAAA,CAAtD,MAAM,kCAAkC,EAAC,YAAS,EAAA,GACrDC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAD,EAAmF,KAAA,CAA/E,MAAM,2CAA2C,EAAC,2BAAwB,EAAA,GAC9EC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAD,EAEI,IAAA,CAFD,MAAM,2BAA2B,EAAC,uEAErC,EAAA,GACAE,EAA8FC,EAAA,CAAjF,GAAI,CAAA,KAAA,WAAA,EAAuB,MAAM,+BAAmB,IAAgB,CAAA,GAAAF,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAA,GAAhB,mBAAgB,EAAA"}
@@ -1,2 +1,2 @@
1
- import{d as C,r as k,h as a,c as d,a as m,t as n,l as o,x,w,b as u,C as y,S as B,s as S,Z as D,j as N,g as c}from"./index-9WVbsgdk.js";import{_ as j}from"./useUnsavedChanges-bO5Ha9y6.js";import{u as $}from"./useField-CQv2TS4g.js";import{C as z}from"./chevron-down-BgnDSY3E.js";import"./arrow-right-s8aI3aOk.js";import"./plus-D6xxK_qW.js";const F={class:"card overflow-hidden"},I={class:"sr-only"},E={class:"text-sm font-semibold"},O={key:0,class:"badge bg-danger-soft text-danger"},U={class:"border-t border-line p-4 sm:p-5"},W={key:0,class:"mb-4 text-xs text-muted"},Z={key:0,class:"px-4 pb-3 text-xs font-medium text-danger"},L=C({__name:"ObjectField",props:{field:{},path:{},issues:{},disabled:{type:Boolean},modelValue:{},collection:{},entrySlug:{}},emits:["update:modelValue"],setup(t,{emit:g}){const s=t,V=g,e=c(()=>s.field),{label:p,error:v}=$(s),f=k(!e.value.collapsed),_=c(()=>s.modelValue&&typeof s.modelValue=="object"?s.modelValue:{}),b=c(()=>{var r;return((r=s.issues)==null?void 0:r.some(l=>l.path.startsWith(`${s.path}.`)))??!1}),i=c(()=>f.value||b.value);return(r,l)=>(a(),d("fieldset",F,[m("legend",I,n(o(p)),1),(a(),x(B(e.value.collapsible?"button":"div"),{type:e.value.collapsible?"button":void 0,"aria-expanded":e.value.collapsible?i.value:void 0,class:y(["flex w-full items-center gap-2 px-4 py-3 text-left",e.value.collapsible&&"hover:bg-canvas"]),onClick:l[0]||(l[0]=h=>e.value.collapsible?f.value=!i.value:void 0)},{default:w(()=>[m("span",E,n(o(p)),1),b.value?(a(),d("span",O," Needs attention ")):u("",!0),e.value.collapsible?(a(),x(o(z),{key:1,size:16,class:y(["ml-auto h-4 w-4 text-muted transition",i.value&&"rotate-180"]),"aria-hidden":"true"},null,8,["class"])):u("",!0)]),_:1},8,["type","aria-expanded","class"])),S(m("div",U,[e.value.description?(a(),d("p",W,n(e.value.description),1)):u("",!0),N(j,{fields:e.value.fields,"model-value":_.value,path:t.path,issues:t.issues,disabled:t.disabled||e.value.readonly,collection:t.collection,"entry-slug":t.entrySlug,"onUpdate:modelValue":l[1]||(l[1]=h=>V("update:modelValue",h))},null,8,["fields","model-value","path","issues","disabled","collection","entry-slug"])],512),[[D,i.value]]),o(v)?(a(),d("p",Z,n(o(v)),1)):u("",!0)]))}});export{L as default};
2
- //# sourceMappingURL=ObjectField-CAJPaAKa.js.map
1
+ import{d as C,r as k,h as a,c as d,a as m,t as n,l as o,x,w,b as u,C as y,S as B,s as S,Z as D,j as N,g as c}from"./index-Colfqb-9.js";import{_ as j}from"./useUnsavedChanges-B0WVqgI4.js";import{u as $}from"./useField-BDSV5ZPo.js";import{C as z}from"./chevron-down-Bmt8sf-v.js";import"./arrow-right-DJTAYrz5.js";import"./plus-D_aoCFbi.js";const F={class:"card overflow-hidden"},I={class:"sr-only"},E={class:"text-sm font-semibold"},O={key:0,class:"badge bg-danger-soft text-danger"},U={class:"border-t border-line p-4 sm:p-5"},W={key:0,class:"mb-4 text-xs text-muted"},Z={key:0,class:"px-4 pb-3 text-xs font-medium text-danger"},L=C({__name:"ObjectField",props:{field:{},path:{},issues:{},disabled:{type:Boolean},modelValue:{},collection:{},entrySlug:{}},emits:["update:modelValue"],setup(t,{emit:g}){const s=t,V=g,e=c(()=>s.field),{label:p,error:v}=$(s),f=k(!e.value.collapsed),_=c(()=>s.modelValue&&typeof s.modelValue=="object"?s.modelValue:{}),b=c(()=>{var r;return((r=s.issues)==null?void 0:r.some(l=>l.path.startsWith(`${s.path}.`)))??!1}),i=c(()=>f.value||b.value);return(r,l)=>(a(),d("fieldset",F,[m("legend",I,n(o(p)),1),(a(),x(B(e.value.collapsible?"button":"div"),{type:e.value.collapsible?"button":void 0,"aria-expanded":e.value.collapsible?i.value:void 0,class:y(["flex w-full items-center gap-2 px-4 py-3 text-left",e.value.collapsible&&"hover:bg-canvas"]),onClick:l[0]||(l[0]=h=>e.value.collapsible?f.value=!i.value:void 0)},{default:w(()=>[m("span",E,n(o(p)),1),b.value?(a(),d("span",O," Needs attention ")):u("",!0),e.value.collapsible?(a(),x(o(z),{key:1,size:16,class:y(["ml-auto h-4 w-4 text-muted transition",i.value&&"rotate-180"]),"aria-hidden":"true"},null,8,["class"])):u("",!0)]),_:1},8,["type","aria-expanded","class"])),S(m("div",U,[e.value.description?(a(),d("p",W,n(e.value.description),1)):u("",!0),N(j,{fields:e.value.fields,"model-value":_.value,path:t.path,issues:t.issues,disabled:t.disabled||e.value.readonly,collection:t.collection,"entry-slug":t.entrySlug,"onUpdate:modelValue":l[1]||(l[1]=h=>V("update:modelValue",h))},null,8,["fields","model-value","path","issues","disabled","collection","entry-slug"])],512),[[D,i.value]]),o(v)?(a(),d("p",Z,n(o(v)),1)):u("",!0)]))}});export{L as default};
2
+ //# sourceMappingURL=ObjectField-gxr2a2pp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectField-CAJPaAKa.js","sources":["../../src/components/fields/ObjectField.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Nested object. Recurses through `FieldList`, so nesting depth is unbounded.\n */\n\nimport { computed, ref } from \"vue\";\nimport { ChevronDown } from \"@lucide/vue\";\n\nimport type { ObjectField } from \"@micacms/core\";\n\nimport FieldList from \"./FieldList.vue\";\nimport { useField, type FieldProps } from \"@/composables/useField\";\n\nconst props = defineProps<\n FieldProps & { modelValue: unknown; collection?: string; entrySlug?: string }\n>();\nconst emit = defineEmits<{ \"update:modelValue\": [Record<string, unknown>] }>();\n\nconst field = computed(() => props.field as ObjectField);\nconst { label, error } = useField(props);\n\nconst open = ref(!field.value.collapsed);\n\nconst value = computed<Record<string, unknown>>(() =>\n props.modelValue && typeof props.modelValue === \"object\"\n ? (props.modelValue as Record<string, unknown>)\n : {},\n);\n\n// A collapsed group that hides an error is a trap; force it open.\nconst hasIssueInside = computed(\n () => props.issues?.some((issue) => issue.path.startsWith(`${props.path}.`)) ?? false,\n);\n\nconst expanded = computed(() => open.value || hasIssueInside.value);\n</script>\n\n<template>\n <fieldset class=\"card overflow-hidden\">\n <legend class=\"sr-only\">{{ label }}</legend>\n\n <component\n :is=\"field.collapsible ? 'button' : 'div'\"\n :type=\"field.collapsible ? 'button' : undefined\"\n :aria-expanded=\"field.collapsible ? expanded : undefined\"\n :class=\"[\n 'flex w-full items-center gap-2 px-4 py-3 text-left',\n field.collapsible && 'hover:bg-canvas',\n ]\"\n @click=\"field.collapsible ? (open = !expanded) : undefined\"\n >\n <span class=\"text-sm font-semibold\">{{ label }}</span>\n\n <span\n v-if=\"hasIssueInside\"\n class=\"badge bg-danger-soft text-danger\"\n >\n Needs attention\n </span>\n\n <ChevronDown\n v-if=\"field.collapsible\"\n :size=\"16\"\n :class=\"['ml-auto h-4 w-4 text-muted transition', expanded && 'rotate-180']\"\n aria-hidden=\"true\"\n />\n </component>\n\n <div v-show=\"expanded\" class=\"border-t border-line p-4 sm:p-5\">\n <p v-if=\"field.description\" class=\"mb-4 text-xs text-muted\">\n {{ field.description }}\n </p>\n\n <FieldList\n :fields=\"field.fields\"\n :model-value=\"value\"\n :path=\"path\"\n :issues=\"issues\"\n :disabled=\"disabled || field.readonly\"\n :collection=\"collection\"\n :entry-slug=\"entrySlug\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </div>\n\n <p v-if=\"error\" class=\"px-4 pb-3 text-xs font-medium text-danger\">{{ error }}</p>\n </fieldset>\n</template>\n"],"names":["props","__props","emit","__emit","field","computed","label","error","useField","open","ref","value","hasIssueInside","_a","issue","expanded","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_toDisplayString","_unref","_createBlock","_resolveDynamicComponent","_normalizeClass","_cache","$event","_hoisted_3","_hoisted_4","ChevronDown","_withDirectives","_hoisted_5","_hoisted_6","_createVNode","FieldList","_hoisted_7"],"mappings":"gyBAaA,MAAMA,EAAQC,EAGRC,EAAOC,EAEPC,EAAQC,EAAS,IAAML,EAAM,KAAoB,EACjD,CAAE,MAAAM,EAAO,MAAAC,GAAUC,EAASR,CAAK,EAEjCS,EAAOC,EAAI,CAACN,EAAM,MAAM,SAAS,EAEjCO,EAAQN,EAAkC,IAC9CL,EAAM,YAAc,OAAOA,EAAM,YAAe,SAC3CA,EAAM,WACP,CAAA,CAAC,EAIDY,EAAiBP,EACrB,IAAA,OAAM,QAAAQ,EAAAb,EAAM,SAAN,YAAAa,EAAc,KAAMC,GAAUA,EAAM,KAAK,WAAW,GAAGd,EAAM,IAAI,GAAG,KAAM,GAAA,EAG5Ee,EAAWV,EAAS,IAAMI,EAAK,OAASG,EAAe,KAAK,gBAIhEI,EAAA,EAAAC,EAgDW,WAhDXC,EAgDW,CA/CTC,EAA4C,SAA5CC,EAA4CC,EAAjBC,EAAAhB,CAAA,CAAK,EAAA,CAAA,OAEhCiB,EAyBYC,EAxBLpB,EAAA,MAAM,YAAW,SAAA,KAAA,EAAA,CACrB,KAAMA,EAAA,MAAM,qBAAyB,OACrC,gBAAeA,EAAA,MAAM,YAAcW,EAAA,MAAW,OAC9C,MAAKU,EAAA,sDAA0ErB,EAAA,MAAM,aAAW,iBAAA,GAIhG,QAAKsB,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAC,GAAEvB,QAAM,YAAeK,EAAA,MAAI,CAAIM,EAAA,MAAY,OAAA,aAEjD,IAAsD,CAAtDI,EAAsD,OAAtDS,EAAsDP,EAAfC,EAAAhB,CAAA,CAAK,EAAA,CAAA,EAGpCM,EAAA,WADRK,EAKO,OALPY,EAGC,mBAED,YAGQzB,EAAA,MAAM,iBADdmB,EAKED,EAAAQ,CAAA,EAAA,OAHC,KAAM,GACN,iDAAiDf,EAAA,OAAQ,YAAA,CAAA,EAC1D,cAAY,MAAA,0EAIhBgB,EAAAZ,EAeM,MAfNa,EAeM,CAdK5B,EAAA,MAAM,aAAfY,EAAA,EAAAC,EAEI,IAFJgB,EAEIZ,EADCjB,EAAA,MAAM,WAAW,EAAA,CAAA,YAGtB8B,EASEC,EAAA,CARC,OAAQ/B,EAAA,MAAM,OACd,cAAaO,EAAA,MACb,KAAMV,EAAA,KACN,OAAQA,EAAA,OACR,SAAUA,EAAA,UAAYG,EAAA,MAAM,SAC5B,WAAYH,EAAA,WACZ,aAAYA,EAAA,UACZ,sBAAkByB,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAC,GAAEzB,EAAI,oBAAsByB,CAAM,EAAA,kGAb5CZ,EAAA,KAAQ,CAAA,GAiBZO,EAAAf,CAAA,OAATU,EAAiF,IAAjFmB,EAAiFf,EAAZC,EAAAf,CAAA,CAAK,EAAA,CAAA"}
1
+ {"version":3,"file":"ObjectField-gxr2a2pp.js","sources":["../../src/components/fields/ObjectField.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Nested object. Recurses through `FieldList`, so nesting depth is unbounded.\n */\n\nimport { computed, ref } from \"vue\";\nimport { ChevronDown } from \"@lucide/vue\";\n\nimport type { ObjectField } from \"@micacms/core\";\n\nimport FieldList from \"./FieldList.vue\";\nimport { useField, type FieldProps } from \"@/composables/useField\";\n\nconst props = defineProps<\n FieldProps & { modelValue: unknown; collection?: string; entrySlug?: string }\n>();\nconst emit = defineEmits<{ \"update:modelValue\": [Record<string, unknown>] }>();\n\nconst field = computed(() => props.field as ObjectField);\nconst { label, error } = useField(props);\n\nconst open = ref(!field.value.collapsed);\n\nconst value = computed<Record<string, unknown>>(() =>\n props.modelValue && typeof props.modelValue === \"object\"\n ? (props.modelValue as Record<string, unknown>)\n : {},\n);\n\n// A collapsed group that hides an error is a trap; force it open.\nconst hasIssueInside = computed(\n () => props.issues?.some((issue) => issue.path.startsWith(`${props.path}.`)) ?? false,\n);\n\nconst expanded = computed(() => open.value || hasIssueInside.value);\n</script>\n\n<template>\n <fieldset class=\"card overflow-hidden\">\n <legend class=\"sr-only\">{{ label }}</legend>\n\n <component\n :is=\"field.collapsible ? 'button' : 'div'\"\n :type=\"field.collapsible ? 'button' : undefined\"\n :aria-expanded=\"field.collapsible ? expanded : undefined\"\n :class=\"[\n 'flex w-full items-center gap-2 px-4 py-3 text-left',\n field.collapsible && 'hover:bg-canvas',\n ]\"\n @click=\"field.collapsible ? (open = !expanded) : undefined\"\n >\n <span class=\"text-sm font-semibold\">{{ label }}</span>\n\n <span\n v-if=\"hasIssueInside\"\n class=\"badge bg-danger-soft text-danger\"\n >\n Needs attention\n </span>\n\n <ChevronDown\n v-if=\"field.collapsible\"\n :size=\"16\"\n :class=\"['ml-auto h-4 w-4 text-muted transition', expanded && 'rotate-180']\"\n aria-hidden=\"true\"\n />\n </component>\n\n <div v-show=\"expanded\" class=\"border-t border-line p-4 sm:p-5\">\n <p v-if=\"field.description\" class=\"mb-4 text-xs text-muted\">\n {{ field.description }}\n </p>\n\n <FieldList\n :fields=\"field.fields\"\n :model-value=\"value\"\n :path=\"path\"\n :issues=\"issues\"\n :disabled=\"disabled || field.readonly\"\n :collection=\"collection\"\n :entry-slug=\"entrySlug\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </div>\n\n <p v-if=\"error\" class=\"px-4 pb-3 text-xs font-medium text-danger\">{{ error }}</p>\n </fieldset>\n</template>\n"],"names":["props","__props","emit","__emit","field","computed","label","error","useField","open","ref","value","hasIssueInside","_a","issue","expanded","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_toDisplayString","_unref","_createBlock","_resolveDynamicComponent","_normalizeClass","_cache","$event","_hoisted_3","_hoisted_4","ChevronDown","_withDirectives","_hoisted_5","_hoisted_6","_createVNode","FieldList","_hoisted_7"],"mappings":"gyBAaA,MAAMA,EAAQC,EAGRC,EAAOC,EAEPC,EAAQC,EAAS,IAAML,EAAM,KAAoB,EACjD,CAAE,MAAAM,EAAO,MAAAC,GAAUC,EAASR,CAAK,EAEjCS,EAAOC,EAAI,CAACN,EAAM,MAAM,SAAS,EAEjCO,EAAQN,EAAkC,IAC9CL,EAAM,YAAc,OAAOA,EAAM,YAAe,SAC3CA,EAAM,WACP,CAAA,CAAC,EAIDY,EAAiBP,EACrB,IAAA,OAAM,QAAAQ,EAAAb,EAAM,SAAN,YAAAa,EAAc,KAAMC,GAAUA,EAAM,KAAK,WAAW,GAAGd,EAAM,IAAI,GAAG,KAAM,GAAA,EAG5Ee,EAAWV,EAAS,IAAMI,EAAK,OAASG,EAAe,KAAK,gBAIhEI,EAAA,EAAAC,EAgDW,WAhDXC,EAgDW,CA/CTC,EAA4C,SAA5CC,EAA4CC,EAAjBC,EAAAhB,CAAA,CAAK,EAAA,CAAA,OAEhCiB,EAyBYC,EAxBLpB,EAAA,MAAM,YAAW,SAAA,KAAA,EAAA,CACrB,KAAMA,EAAA,MAAM,qBAAyB,OACrC,gBAAeA,EAAA,MAAM,YAAcW,EAAA,MAAW,OAC9C,MAAKU,EAAA,sDAA0ErB,EAAA,MAAM,aAAW,iBAAA,GAIhG,QAAKsB,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAC,GAAEvB,QAAM,YAAeK,EAAA,MAAI,CAAIM,EAAA,MAAY,OAAA,aAEjD,IAAsD,CAAtDI,EAAsD,OAAtDS,EAAsDP,EAAfC,EAAAhB,CAAA,CAAK,EAAA,CAAA,EAGpCM,EAAA,WADRK,EAKO,OALPY,EAGC,mBAED,YAGQzB,EAAA,MAAM,iBADdmB,EAKED,EAAAQ,CAAA,EAAA,OAHC,KAAM,GACN,iDAAiDf,EAAA,OAAQ,YAAA,CAAA,EAC1D,cAAY,MAAA,0EAIhBgB,EAAAZ,EAeM,MAfNa,EAeM,CAdK5B,EAAA,MAAM,aAAfY,EAAA,EAAAC,EAEI,IAFJgB,EAEIZ,EADCjB,EAAA,MAAM,WAAW,EAAA,CAAA,YAGtB8B,EASEC,EAAA,CARC,OAAQ/B,EAAA,MAAM,OACd,cAAaO,EAAA,MACb,KAAMV,EAAA,KACN,OAAQA,EAAA,OACR,SAAUA,EAAA,UAAYG,EAAA,MAAM,SAC5B,WAAYH,EAAA,WACZ,aAAYA,EAAA,UACZ,sBAAkByB,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAC,GAAEzB,EAAI,oBAAsByB,CAAM,EAAA,kGAb5CZ,EAAA,KAAQ,CAAA,GAiBZO,EAAAf,CAAA,OAATU,EAAiF,IAAjFmB,EAAiFf,EAAZC,EAAAf,CAAA,CAAK,EAAA,CAAA"}
@@ -1,7 +1,7 @@
1
- import{D as r,d as l,c as n,a as t,x as d,w as m,b as o,t as a,E as x,p as u,h as s,j as h,l as f,e as k}from"./index-9WVbsgdk.js";/**
1
+ import{D as r,d as l,c as n,a as t,x as d,w as m,b as o,t as a,E as x,p as u,h as s,j as h,l as f,e as k}from"./index-Colfqb-9.js";/**
2
2
  * @license @lucide/vue v1.37.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
5
5
  * See the LICENSE file in the root directory of this source tree.
6
6
  */const b=[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]],_=r("chevron-left",b),p={class:"border-b border-line bg-raised"},g={class:"mx-auto flex max-w-5xl flex-wrap items-center gap-4 px-4 py-4 sm:px-6"},v={class:"min-w-0 flex-1"},w={class:"truncate font-display text-2xl font-normal leading-8 tracking-tight"},y={key:1,class:"mt-1 truncate text-xs text-muted"},L={class:"flex shrink-0 items-center gap-2"},N=l({__name:"PageHeader",props:{title:{},subtitle:{},back:{},backLabel:{}},setup(e){return(c,B)=>{const i=u("RouterLink");return s(),n("header",p,[t("div",g,[t("div",v,[e.back?(s(),d(i,{key:0,to:e.back,class:"mb-0.5 inline-flex items-center gap-1 text-xs font-medium text-muted hover:text-ink"},{default:m(()=>[h(f(_),{size:14,"aria-hidden":"true"}),k(" "+a(e.backLabel??"Back"),1)]),_:1},8,["to"])):o("",!0),t("h1",w,a(e.title),1),e.subtitle?(s(),n("p",y,a(e.subtitle),1)):o("",!0)]),t("div",L,[x(c.$slots,"default")])])])}}});export{N as _};
7
- //# sourceMappingURL=PageHeader.vue_vue_type_script_setup_true_lang-IPw0xrLa.js.map
7
+ //# sourceMappingURL=PageHeader.vue_vue_type_script_setup_true_lang-B-fBJAjn.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PageHeader.vue_vue_type_script_setup_true_lang-IPw0xrLa.js","sources":["../../../../common/temp/node_modules/.pnpm/@lucide+vue@1.37.0_vue@3.5.42_typescript@5.9.3_/node_modules/@lucide/vue/dist/esm/icons/chevron-left.mjs","../../src/components/PageHeader.vue"],"sourcesContent":["/**\n * @license @lucide/vue v1.37.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [[\"path\", { d: \"m15 18-6-6 6-6\", key: \"1wnfg3\" }]];\nconst ChevronLeft = createLucideIcon(\"chevron-left\", __iconNode);\n\nexport { __iconNode, ChevronLeft as default };\n//# sourceMappingURL=chevron-left.mjs.map\n","<script setup lang=\"ts\">\nimport { ChevronLeft } from \"@lucide/vue\";\n\ndefineProps<{\n title: string;\n subtitle?: string | null;\n /** Rendered as a back link above the title. */\n back?: { name: string; params?: Record<string, string> } | null;\n backLabel?: string;\n}>();\n</script>\n\n<template>\n <header class=\"border-b border-line bg-raised\">\n <div class=\"mx-auto flex max-w-5xl flex-wrap items-center gap-4 px-4 py-4 sm:px-6\">\n <div class=\"min-w-0 flex-1\">\n <RouterLink\n v-if=\"back\"\n :to=\"back\"\n class=\"mb-0.5 inline-flex items-center gap-1 text-xs font-medium text-muted hover:text-ink\"\n >\n <ChevronLeft :size=\"14\" aria-hidden=\"true\" />\n {{ backLabel ?? \"Back\" }}\n </RouterLink>\n\n <h1 class=\"truncate font-display text-2xl font-normal leading-8 tracking-tight\">{{ title }}</h1>\n <p v-if=\"subtitle\" class=\"mt-1 truncate text-xs text-muted\">{{ subtitle }}</p>\n </div>\n\n <div class=\"flex shrink-0 items-center gap-2\">\n <slot />\n </div>\n </div>\n </header>\n</template>\n"],"names":["__iconNode","ChevronLeft","createLucideIcon","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","__props","_createBlock","_component_RouterLink","_createVNode","_unref","_createTextVNode","_hoisted_4","_toDisplayString","_hoisted_5","_hoisted_6","_renderSlot","_ctx"],"mappings":"mIAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASA,MAAMA,EAAa,CAAC,CAAC,OAAQ,CAAE,EAAG,iBAAkB,IAAK,QAAQ,CAAE,CAAC,EAC9DC,EAAcC,EAAiB,eAAgBF,CAAU,ocCG7D,OAAAG,EAAA,EAAAC,EAoBS,SApBTC,EAoBS,CAnBPC,EAkBM,MAlBNC,EAkBM,CAjBJD,EAYM,MAZNE,EAYM,CAVIC,EAAA,UADRC,EAOaC,EAAA,OALV,GAAIF,EAAA,KACL,MAAM,qFAAA,aAEN,IAA6C,CAA7CG,EAA6CC,EAAAZ,CAAA,EAAA,CAA/B,KAAM,GAAI,cAAY,MAAA,GAASa,EAAA,MAC1CL,EAAA,WAAS,MAAA,EAAA,CAAA,CAAA,4BAGdH,EAAgG,KAAhGS,EAAgGC,EAAbP,EAAA,KAAK,EAAA,CAAA,EAC/EA,EAAA,cAATL,EAA8E,IAA9Ea,EAA8ED,EAAfP,EAAA,QAAQ,EAAA,CAAA,cAGzEH,EAEM,MAFNY,EAEM,CADJC,EAAQC,EAAA,OAAA,SAAA,CAAA","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"PageHeader.vue_vue_type_script_setup_true_lang-B-fBJAjn.js","sources":["../../../../common/temp/node_modules/.pnpm/@lucide+vue@1.37.0_vue@3.5.42_typescript@5.9.3_/node_modules/@lucide/vue/dist/esm/icons/chevron-left.mjs","../../src/components/PageHeader.vue"],"sourcesContent":["/**\n * @license @lucide/vue v1.37.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [[\"path\", { d: \"m15 18-6-6 6-6\", key: \"1wnfg3\" }]];\nconst ChevronLeft = createLucideIcon(\"chevron-left\", __iconNode);\n\nexport { __iconNode, ChevronLeft as default };\n//# sourceMappingURL=chevron-left.mjs.map\n","<script setup lang=\"ts\">\nimport { ChevronLeft } from \"@lucide/vue\";\n\ndefineProps<{\n title: string;\n subtitle?: string | null;\n /** Rendered as a back link above the title. */\n back?: { name: string; params?: Record<string, string> } | null;\n backLabel?: string;\n}>();\n</script>\n\n<template>\n <header class=\"border-b border-line bg-raised\">\n <div class=\"mx-auto flex max-w-5xl flex-wrap items-center gap-4 px-4 py-4 sm:px-6\">\n <div class=\"min-w-0 flex-1\">\n <RouterLink\n v-if=\"back\"\n :to=\"back\"\n class=\"mb-0.5 inline-flex items-center gap-1 text-xs font-medium text-muted hover:text-ink\"\n >\n <ChevronLeft :size=\"14\" aria-hidden=\"true\" />\n {{ backLabel ?? \"Back\" }}\n </RouterLink>\n\n <h1 class=\"truncate font-display text-2xl font-normal leading-8 tracking-tight\">{{ title }}</h1>\n <p v-if=\"subtitle\" class=\"mt-1 truncate text-xs text-muted\">{{ subtitle }}</p>\n </div>\n\n <div class=\"flex shrink-0 items-center gap-2\">\n <slot />\n </div>\n </div>\n </header>\n</template>\n"],"names":["__iconNode","ChevronLeft","createLucideIcon","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","__props","_createBlock","_component_RouterLink","_createVNode","_unref","_createTextVNode","_hoisted_4","_toDisplayString","_hoisted_5","_hoisted_6","_renderSlot","_ctx"],"mappings":"mIAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASA,MAAMA,EAAa,CAAC,CAAC,OAAQ,CAAE,EAAG,iBAAkB,IAAK,QAAQ,CAAE,CAAC,EAC9DC,EAAcC,EAAiB,eAAgBF,CAAU,ocCG7D,OAAAG,EAAA,EAAAC,EAoBS,SApBTC,EAoBS,CAnBPC,EAkBM,MAlBNC,EAkBM,CAjBJD,EAYM,MAZNE,EAYM,CAVIC,EAAA,UADRC,EAOaC,EAAA,OALV,GAAIF,EAAA,KACL,MAAM,qFAAA,aAEN,IAA6C,CAA7CG,EAA6CC,EAAAZ,CAAA,EAAA,CAA/B,KAAM,GAAI,cAAY,MAAA,GAASa,EAAA,MAC1CL,EAAA,WAAS,MAAA,EAAA,CAAA,CAAA,4BAGdH,EAAgG,KAAhGS,EAAgGC,EAAbP,EAAA,KAAK,EAAA,CAAA,EAC/EA,EAAA,cAATL,EAA8E,IAA9Ea,EAA8ED,EAAfP,EAAA,QAAQ,EAAA,CAAA,cAGzEH,EAEM,MAFNY,EAEM,CADJC,EAAQC,EAAA,OAAA,SAAA,CAAA","x_google_ignoreList":[0]}
@@ -1,4 +1,4 @@
1
- import{D as w,d as j,h as i,c,a as o,e as V,t as r,l as h,b as f,F as B,k as R,j as v,C as I,s as U,Z as H,g as _,r as P}from"./index-9WVbsgdk.js";import{_ as T,d as q}from"./useUnsavedChanges-bO5Ha9y6.js";import{u as E,h as W}from"./useField-CQv2TS4g.js";import{C as Z}from"./chevron-down-BgnDSY3E.js";import{A as G,a as J}from"./arrow-up-BeQh5rSe.js";import{P as K}from"./plus-D6xxK_qW.js";import"./arrow-right-s8aI3aOk.js";/**
1
+ import{D as w,d as j,h as i,c,a as o,e as V,t as r,l as h,b as f,F as B,k as R,j as v,C as I,s as U,Z as H,g as _,r as P}from"./index-Colfqb-9.js";import{_ as T,d as q}from"./useUnsavedChanges-B0WVqgI4.js";import{u as E,h as W}from"./useField-BDSV5ZPo.js";import{C as Z}from"./chevron-down-Bmt8sf-v.js";import{A as G,a as J}from"./arrow-up-CibOjHj-.js";import{P as K}from"./plus-D_aoCFbi.js";import"./arrow-right-DJTAYrz5.js";/**
2
2
  * @license @lucide/vue v1.37.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -9,4 +9,4 @@ import{D as w,d as j,h as i,c,a as o,e as V,t as r,l as h,b as f,F as B,k as R,j
9
9
  * This source code is licensed under the ISC license.
10
10
  * See the LICENSE file in the root directory of this source tree.
11
11
  */const X=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],Y=w("trash-2",X),ee={class:"field-label mb-1.5"},te={key:0,class:"text-danger","aria-hidden":"true"},se={key:0,class:"field-hint -mt-1 mb-2"},ae={key:1,class:"space-y-2"},le={class:"flex items-center gap-1 bg-canvas/60 px-3 py-2"},oe=["aria-expanded","onClick"],ne={class:"truncate text-sm font-medium"},de={key:0,class:"badge shrink-0 bg-danger-soft text-danger"},ie={class:"flex shrink-0 items-center gap-0.5"},ce=["disabled","onClick"],re={class:"sr-only"},ue=["disabled","onClick"],he={class:"sr-only"},me=["disabled","onClick"],ve={class:"sr-only"},pe=["disabled","onClick"],ye={class:"sr-only"},be={class:"border-t border-line p-4"},fe={key:2,class:"rounded-lg border border-dashed border-line px-4 py-5 text-center text-sm text-muted"},_e=["disabled"],ge={key:3,class:"ml-2 text-xs text-muted"},ke={key:4,class:"field-error"},Ne=j({__name:"RepeaterField",props:{field:{},path:{},issues:{},disabled:{type:Boolean},modelValue:{},collection:{},entrySlug:{}},emits:["update:modelValue"],setup(d,{emit:x}){const p=d,$=x,l=_(()=>p.field),{label:M,error:k}=E(p),m=P(new Set),n=_(()=>Array.isArray(p.modelValue)?p.modelValue:[]),g=_(()=>l.value.max!==void 0&&n.value.length>=l.value.max),z=_(()=>l.value.min!==void 0&&n.value.length<=l.value.min);function y(s,t){var u;const a=l.value.itemLabel,e=a?s[a]:void 0;return typeof e=="string"&&e.trim()?e.trim():`${W(((u=l.value.addLabel)==null?void 0:u.replace(/^Add\s+/i,""))??"Item")} ${t+1}`}function N(s){var t;return((t=p.issues)==null?void 0:t.some(a=>a.path.startsWith(`${p.path}.${s}.`)))??!1}function b(s){$("update:modelValue",s)}function A(){b([...n.value,q(l.value.fields)])}function S(s){const t=n.value[s];if(!t)return;const a=[...n.value];a.splice(s+1,0,structuredClone(t)),b(a)}function D(s){b(n.value.filter((t,a)=>a!==s)),m.value=new Set([...m.value].filter(t=>t!==s).map(t=>t>s?t-1:t))}function C(s,t){const a=s+t;if(a<0||a>=n.value.length)return;const e=[...n.value];[e[s],e[a]]=[e[a],e[s]],b(e)}function F(s,t){b(n.value.map((a,e)=>e===s?t:a))}function L(s){const t=new Set(m.value);t.has(s)?t.delete(s):t.add(s),m.value=t}return(s,t)=>(i(),c("fieldset",null,[o("legend",ee,[V(r(h(M))+" ",1),l.value.required?(i(),c("span",te,"*")):f("",!0)]),l.value.description?(i(),c("p",se,r(l.value.description),1)):f("",!0),n.value.length?(i(),c("ol",ae,[(i(!0),c(B,null,R(n.value,(a,e)=>(i(),c("li",{key:e,class:"card overflow-hidden"},[o("div",le,[o("button",{type:"button",class:"flex min-w-0 flex-1 items-center gap-2 text-left","aria-expanded":!m.value.has(e),onClick:u=>L(e)},[v(h(Z),{size:16,class:I(["h-4 w-4 shrink-0 text-muted transition",!m.value.has(e)&&"rotate-180"]),"aria-hidden":"true"},null,8,["class"]),o("span",ne,r(y(a,e)),1),N(e)?(i(),c("span",de," Needs attention ")):f("",!0)],8,oe),o("div",ie,[o("button",{type:"button",class:"btn-ghost px-1.5 py-1",disabled:d.disabled||e===0,onClick:u=>C(e,-1)},[v(h(G),{size:16,"aria-hidden":"true"}),o("span",re,'Move "'+r(y(a,e))+'" up',1)],8,ce),o("button",{type:"button",class:"btn-ghost px-1.5 py-1",disabled:d.disabled||e===n.value.length-1,onClick:u=>C(e,1)},[v(h(J),{size:16,"aria-hidden":"true"}),o("span",he,'Move "'+r(y(a,e))+'" down',1)],8,ue),o("button",{type:"button",class:"btn-ghost px-1.5 py-1",disabled:d.disabled||g.value,onClick:u=>S(e)},[v(h(Q),{size:16,"aria-hidden":"true"}),o("span",ve,'Duplicate "'+r(y(a,e))+'"',1)],8,me),o("button",{type:"button",class:"btn-ghost px-1.5 py-1 hover:text-danger",disabled:d.disabled||z.value,onClick:u=>D(e)},[v(h(Y),{size:16,"aria-hidden":"true"}),o("span",ye,'Remove "'+r(y(a,e))+'"',1)],8,pe)])]),U(o("div",be,[v(T,{fields:l.value.fields,"model-value":a,path:`${d.path}.${e}`,issues:d.issues,disabled:d.disabled||l.value.readonly,collection:d.collection,"entry-slug":d.entrySlug,"onUpdate:modelValue":u=>F(e,u)},null,8,["fields","model-value","path","issues","disabled","collection","entry-slug","onUpdate:modelValue"])],512),[[H,!m.value.has(e)]])]))),128))])):(i(),c("p",fe," Nothing added yet. ")),o("button",{type:"button",class:"btn-secondary mt-2",disabled:d.disabled||l.value.readonly||g.value,onClick:A},[v(h(K),{size:16,"aria-hidden":"true"}),V(" "+r(l.value.addLabel??"Add item"),1)],8,_e),g.value?(i(),c("span",ge," Maximum of "+r(l.value.max)+" reached. ",1)):f("",!0),h(k)?(i(),c("p",ke,r(h(k)),1)):f("",!0)]))}});export{Ne as default};
12
- //# sourceMappingURL=RepeaterField-BAnvha76.js.map
12
+ //# sourceMappingURL=RepeaterField-obOWml_P.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RepeaterField-BAnvha76.js","sources":["../../../../common/temp/node_modules/.pnpm/@lucide+vue@1.37.0_vue@3.5.42_typescript@5.9.3_/node_modules/@lucide/vue/dist/esm/icons/copy.mjs","../../../../common/temp/node_modules/.pnpm/@lucide+vue@1.37.0_vue@3.5.42_typescript@5.9.3_/node_modules/@lucide/vue/dist/esm/icons/trash-2.mjs","../../src/components/fields/RepeaterField.vue"],"sourcesContent":["/**\n * @license @lucide/vue v1.37.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"rect\", { width: \"14\", height: \"14\", x: \"8\", y: \"8\", rx: \"2\", ry: \"2\", key: \"17jyea\" }],\n [\"path\", { d: \"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\", key: \"zix9uf\" }]\n];\nconst Copy = createLucideIcon(\"copy\", __iconNode);\n\nexport { __iconNode, Copy as default };\n//# sourceMappingURL=copy.mjs.map\n","/**\n * @license @lucide/vue v1.37.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M10 11v6\", key: \"nco0om\" }],\n [\"path\", { d: \"M14 11v6\", key: \"outv1u\" }],\n [\"path\", { d: \"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\", key: \"miytrc\" }],\n [\"path\", { d: \"M3 6h18\", key: \"d0wm0j\" }],\n [\"path\", { d: \"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\", key: \"e791ji\" }]\n];\nconst Trash2 = createLucideIcon(\"trash-2\", __iconNode);\n\nexport { __iconNode, Trash2 as default };\n//# sourceMappingURL=trash-2.mjs.map\n","<script setup lang=\"ts\">\n/**\n * Repeating group of fields.\n *\n * Reordering is done with buttons rather than drag-and-drop: it works with a\n * keyboard, on a tablet, and with a screen reader, none of which are true of a\n * drag handle without a great deal more code.\n */\n\nimport { computed, ref } from \"vue\";\nimport { ArrowDown, ArrowUp, ChevronDown, Copy, Plus, Trash2 } from \"@lucide/vue\";\n\nimport { defaultValues, type RepeaterField } from \"@micacms/core\";\n\nimport FieldList from \"./FieldList.vue\";\nimport { humanise, useField, type FieldProps } from \"@/composables/useField\";\n\nconst props = defineProps<\n FieldProps & { modelValue: unknown; collection?: string; entrySlug?: string }\n>();\nconst emit = defineEmits<{ \"update:modelValue\": [Record<string, unknown>[]] }>();\n\nconst field = computed(() => props.field as RepeaterField);\nconst { label, error } = useField(props);\n\nconst collapsed = ref(new Set<number>());\n\nconst rows = computed<Record<string, unknown>[]>(() =>\n Array.isArray(props.modelValue) ? (props.modelValue as Record<string, unknown>[]) : [],\n);\n\nconst atMax = computed(() => field.value.max !== undefined && rows.value.length >= field.value.max);\nconst atMin = computed(() => field.value.min !== undefined && rows.value.length <= field.value.min);\n\n/** Row summary: the declared `itemLabel` child, else a positional fallback. */\nfunction summaryFor(row: Record<string, unknown>, index: number): string {\n const key = field.value.itemLabel;\n const value = key ? row[key] : undefined;\n if (typeof value === \"string\" && value.trim()) return value.trim();\n return `${humanise(field.value.addLabel?.replace(/^Add\\s+/i, \"\") ?? \"Item\")} ${index + 1}`;\n}\n\nfunction rowHasIssue(index: number): boolean {\n return props.issues?.some((issue) => issue.path.startsWith(`${props.path}.${index}.`)) ?? false;\n}\n\nfunction emitRows(next: Record<string, unknown>[]): void {\n emit(\"update:modelValue\", next);\n}\n\nfunction add(): void {\n // A new row starts as a complete skeleton so every input is bound.\n emitRows([...rows.value, defaultValues(field.value.fields)]);\n}\n\nfunction duplicate(index: number): void {\n const source = rows.value[index];\n if (!source) return;\n const next = [...rows.value];\n next.splice(index + 1, 0, structuredClone(source));\n emitRows(next);\n}\n\nfunction remove(index: number): void {\n emitRows(rows.value.filter((_, position) => position !== index));\n collapsed.value = new Set(\n [...collapsed.value].filter((item) => item !== index).map((item) => (item > index ? item - 1 : item)),\n );\n}\n\nfunction move(index: number, direction: -1 | 1): void {\n const target = index + direction;\n if (target < 0 || target >= rows.value.length) return;\n\n const next = [...rows.value];\n [next[index], next[target]] = [next[target]!, next[index]!];\n emitRows(next);\n}\n\nfunction updateRow(index: number, value: Record<string, unknown>): void {\n emitRows(rows.value.map((row, position) => (position === index ? value : row)));\n}\n\nfunction toggle(index: number): void {\n const next = new Set(collapsed.value);\n if (next.has(index)) next.delete(index);\n else next.add(index);\n collapsed.value = next;\n}\n</script>\n\n<template>\n <fieldset>\n <legend class=\"field-label mb-1.5\">\n {{ label }}\n <span v-if=\"field.required\" class=\"text-danger\" aria-hidden=\"true\">*</span>\n </legend>\n\n <p v-if=\"field.description\" class=\"field-hint -mt-1 mb-2\">{{ field.description }}</p>\n\n <ol v-if=\"rows.length\" class=\"space-y-2\">\n <li v-for=\"(row, index) in rows\" :key=\"index\" class=\"card overflow-hidden\">\n <div class=\"flex items-center gap-1 bg-canvas/60 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex min-w-0 flex-1 items-center gap-2 text-left\"\n :aria-expanded=\"!collapsed.has(index)\"\n @click=\"toggle(index)\"\n >\n <ChevronDown\n :size=\"16\"\n :class=\"[\n 'h-4 w-4 shrink-0 text-muted transition',\n !collapsed.has(index) && 'rotate-180',\n ]\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate text-sm font-medium\">{{ summaryFor(row, index) }}</span>\n <span\n v-if=\"rowHasIssue(index)\"\n class=\"badge shrink-0 bg-danger-soft text-danger\"\n >\n Needs attention\n </span>\n </button>\n\n <div class=\"flex shrink-0 items-center gap-0.5\">\n <button\n type=\"button\"\n class=\"btn-ghost px-1.5 py-1\"\n :disabled=\"disabled || index === 0\"\n @click=\"move(index, -1)\"\n >\n <ArrowUp :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Move \"{{ summaryFor(row, index) }}\" up</span>\n </button>\n\n <button\n type=\"button\"\n class=\"btn-ghost px-1.5 py-1\"\n :disabled=\"disabled || index === rows.length - 1\"\n @click=\"move(index, 1)\"\n >\n <ArrowDown :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Move \"{{ summaryFor(row, index) }}\" down</span>\n </button>\n\n <button\n type=\"button\"\n class=\"btn-ghost px-1.5 py-1\"\n :disabled=\"disabled || atMax\"\n @click=\"duplicate(index)\"\n >\n <Copy :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Duplicate \"{{ summaryFor(row, index) }}\"</span>\n </button>\n\n <button\n type=\"button\"\n class=\"btn-ghost px-1.5 py-1 hover:text-danger\"\n :disabled=\"disabled || atMin\"\n @click=\"remove(index)\"\n >\n <Trash2 :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Remove \"{{ summaryFor(row, index) }}\"</span>\n </button>\n </div>\n </div>\n\n <div v-show=\"!collapsed.has(index)\" class=\"border-t border-line p-4\">\n <FieldList\n :fields=\"field.fields\"\n :model-value=\"row\"\n :path=\"`${path}.${index}`\"\n :issues=\"issues\"\n :disabled=\"disabled || field.readonly\"\n :collection=\"collection\"\n :entry-slug=\"entrySlug\"\n @update:model-value=\"updateRow(index, $event)\"\n />\n </div>\n </li>\n </ol>\n\n <p v-else class=\"rounded-lg border border-dashed border-line px-4 py-5 text-center text-sm text-muted\">\n Nothing added yet.\n </p>\n\n <button\n type=\"button\"\n class=\"btn-secondary mt-2\"\n :disabled=\"disabled || field.readonly || atMax\"\n @click=\"add\"\n >\n <Plus :size=\"16\" aria-hidden=\"true\" />\n {{ field.addLabel ?? \"Add item\" }}\n </button>\n\n <span v-if=\"atMax\" class=\"ml-2 text-xs text-muted\">\n Maximum of {{ field.max }} reached.\n </span>\n\n <p v-if=\"error\" class=\"field-error\">{{ error }}</p>\n </fieldset>\n</template>\n"],"names":["__iconNode","Copy","createLucideIcon","Trash2","props","__props","emit","__emit","field","computed","label","error","useField","collapsed","ref","rows","atMax","atMin","summaryFor","row","index","key","value","humanise","_a","rowHasIssue","issue","emitRows","next","add","defaultValues","duplicate","source","remove","_","position","item","move","direction","target","updateRow","toggle","_createElementBlock","_createElementVNode","_hoisted_1","_createTextVNode","_toDisplayString","_unref","_hoisted_2","_openBlock","_hoisted_3","_hoisted_4","_Fragment","_renderList","_hoisted_5","$event","_createVNode","ChevronDown","_normalizeClass","_hoisted_7","_hoisted_8","_hoisted_9","ArrowUp","_hoisted_11","ArrowDown","_hoisted_13","_hoisted_15","_hoisted_17","_withDirectives","_hoisted_18","FieldList","_hoisted_19","Plus","_hoisted_21","_hoisted_22"],"mappings":"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","x_google_ignoreList":[0,1]}
1
+ {"version":3,"file":"RepeaterField-obOWml_P.js","sources":["../../../../common/temp/node_modules/.pnpm/@lucide+vue@1.37.0_vue@3.5.42_typescript@5.9.3_/node_modules/@lucide/vue/dist/esm/icons/copy.mjs","../../../../common/temp/node_modules/.pnpm/@lucide+vue@1.37.0_vue@3.5.42_typescript@5.9.3_/node_modules/@lucide/vue/dist/esm/icons/trash-2.mjs","../../src/components/fields/RepeaterField.vue"],"sourcesContent":["/**\n * @license @lucide/vue v1.37.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"rect\", { width: \"14\", height: \"14\", x: \"8\", y: \"8\", rx: \"2\", ry: \"2\", key: \"17jyea\" }],\n [\"path\", { d: \"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\", key: \"zix9uf\" }]\n];\nconst Copy = createLucideIcon(\"copy\", __iconNode);\n\nexport { __iconNode, Copy as default };\n//# sourceMappingURL=copy.mjs.map\n","/**\n * @license @lucide/vue v1.37.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M10 11v6\", key: \"nco0om\" }],\n [\"path\", { d: \"M14 11v6\", key: \"outv1u\" }],\n [\"path\", { d: \"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\", key: \"miytrc\" }],\n [\"path\", { d: \"M3 6h18\", key: \"d0wm0j\" }],\n [\"path\", { d: \"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\", key: \"e791ji\" }]\n];\nconst Trash2 = createLucideIcon(\"trash-2\", __iconNode);\n\nexport { __iconNode, Trash2 as default };\n//# sourceMappingURL=trash-2.mjs.map\n","<script setup lang=\"ts\">\n/**\n * Repeating group of fields.\n *\n * Reordering is done with buttons rather than drag-and-drop: it works with a\n * keyboard, on a tablet, and with a screen reader, none of which are true of a\n * drag handle without a great deal more code.\n */\n\nimport { computed, ref } from \"vue\";\nimport { ArrowDown, ArrowUp, ChevronDown, Copy, Plus, Trash2 } from \"@lucide/vue\";\n\nimport { defaultValues, type RepeaterField } from \"@micacms/core\";\n\nimport FieldList from \"./FieldList.vue\";\nimport { humanise, useField, type FieldProps } from \"@/composables/useField\";\n\nconst props = defineProps<\n FieldProps & { modelValue: unknown; collection?: string; entrySlug?: string }\n>();\nconst emit = defineEmits<{ \"update:modelValue\": [Record<string, unknown>[]] }>();\n\nconst field = computed(() => props.field as RepeaterField);\nconst { label, error } = useField(props);\n\nconst collapsed = ref(new Set<number>());\n\nconst rows = computed<Record<string, unknown>[]>(() =>\n Array.isArray(props.modelValue) ? (props.modelValue as Record<string, unknown>[]) : [],\n);\n\nconst atMax = computed(() => field.value.max !== undefined && rows.value.length >= field.value.max);\nconst atMin = computed(() => field.value.min !== undefined && rows.value.length <= field.value.min);\n\n/** Row summary: the declared `itemLabel` child, else a positional fallback. */\nfunction summaryFor(row: Record<string, unknown>, index: number): string {\n const key = field.value.itemLabel;\n const value = key ? row[key] : undefined;\n if (typeof value === \"string\" && value.trim()) return value.trim();\n return `${humanise(field.value.addLabel?.replace(/^Add\\s+/i, \"\") ?? \"Item\")} ${index + 1}`;\n}\n\nfunction rowHasIssue(index: number): boolean {\n return props.issues?.some((issue) => issue.path.startsWith(`${props.path}.${index}.`)) ?? false;\n}\n\nfunction emitRows(next: Record<string, unknown>[]): void {\n emit(\"update:modelValue\", next);\n}\n\nfunction add(): void {\n // A new row starts as a complete skeleton so every input is bound.\n emitRows([...rows.value, defaultValues(field.value.fields)]);\n}\n\nfunction duplicate(index: number): void {\n const source = rows.value[index];\n if (!source) return;\n const next = [...rows.value];\n next.splice(index + 1, 0, structuredClone(source));\n emitRows(next);\n}\n\nfunction remove(index: number): void {\n emitRows(rows.value.filter((_, position) => position !== index));\n collapsed.value = new Set(\n [...collapsed.value].filter((item) => item !== index).map((item) => (item > index ? item - 1 : item)),\n );\n}\n\nfunction move(index: number, direction: -1 | 1): void {\n const target = index + direction;\n if (target < 0 || target >= rows.value.length) return;\n\n const next = [...rows.value];\n [next[index], next[target]] = [next[target]!, next[index]!];\n emitRows(next);\n}\n\nfunction updateRow(index: number, value: Record<string, unknown>): void {\n emitRows(rows.value.map((row, position) => (position === index ? value : row)));\n}\n\nfunction toggle(index: number): void {\n const next = new Set(collapsed.value);\n if (next.has(index)) next.delete(index);\n else next.add(index);\n collapsed.value = next;\n}\n</script>\n\n<template>\n <fieldset>\n <legend class=\"field-label mb-1.5\">\n {{ label }}\n <span v-if=\"field.required\" class=\"text-danger\" aria-hidden=\"true\">*</span>\n </legend>\n\n <p v-if=\"field.description\" class=\"field-hint -mt-1 mb-2\">{{ field.description }}</p>\n\n <ol v-if=\"rows.length\" class=\"space-y-2\">\n <li v-for=\"(row, index) in rows\" :key=\"index\" class=\"card overflow-hidden\">\n <div class=\"flex items-center gap-1 bg-canvas/60 px-3 py-2\">\n <button\n type=\"button\"\n class=\"flex min-w-0 flex-1 items-center gap-2 text-left\"\n :aria-expanded=\"!collapsed.has(index)\"\n @click=\"toggle(index)\"\n >\n <ChevronDown\n :size=\"16\"\n :class=\"[\n 'h-4 w-4 shrink-0 text-muted transition',\n !collapsed.has(index) && 'rotate-180',\n ]\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate text-sm font-medium\">{{ summaryFor(row, index) }}</span>\n <span\n v-if=\"rowHasIssue(index)\"\n class=\"badge shrink-0 bg-danger-soft text-danger\"\n >\n Needs attention\n </span>\n </button>\n\n <div class=\"flex shrink-0 items-center gap-0.5\">\n <button\n type=\"button\"\n class=\"btn-ghost px-1.5 py-1\"\n :disabled=\"disabled || index === 0\"\n @click=\"move(index, -1)\"\n >\n <ArrowUp :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Move \"{{ summaryFor(row, index) }}\" up</span>\n </button>\n\n <button\n type=\"button\"\n class=\"btn-ghost px-1.5 py-1\"\n :disabled=\"disabled || index === rows.length - 1\"\n @click=\"move(index, 1)\"\n >\n <ArrowDown :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Move \"{{ summaryFor(row, index) }}\" down</span>\n </button>\n\n <button\n type=\"button\"\n class=\"btn-ghost px-1.5 py-1\"\n :disabled=\"disabled || atMax\"\n @click=\"duplicate(index)\"\n >\n <Copy :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Duplicate \"{{ summaryFor(row, index) }}\"</span>\n </button>\n\n <button\n type=\"button\"\n class=\"btn-ghost px-1.5 py-1 hover:text-danger\"\n :disabled=\"disabled || atMin\"\n @click=\"remove(index)\"\n >\n <Trash2 :size=\"16\" aria-hidden=\"true\" />\n <span class=\"sr-only\">Remove \"{{ summaryFor(row, index) }}\"</span>\n </button>\n </div>\n </div>\n\n <div v-show=\"!collapsed.has(index)\" class=\"border-t border-line p-4\">\n <FieldList\n :fields=\"field.fields\"\n :model-value=\"row\"\n :path=\"`${path}.${index}`\"\n :issues=\"issues\"\n :disabled=\"disabled || field.readonly\"\n :collection=\"collection\"\n :entry-slug=\"entrySlug\"\n @update:model-value=\"updateRow(index, $event)\"\n />\n </div>\n </li>\n </ol>\n\n <p v-else class=\"rounded-lg border border-dashed border-line px-4 py-5 text-center text-sm text-muted\">\n Nothing added yet.\n </p>\n\n <button\n type=\"button\"\n class=\"btn-secondary mt-2\"\n :disabled=\"disabled || field.readonly || atMax\"\n @click=\"add\"\n >\n <Plus :size=\"16\" aria-hidden=\"true\" />\n {{ field.addLabel ?? \"Add item\" }}\n </button>\n\n <span v-if=\"atMax\" class=\"ml-2 text-xs text-muted\">\n Maximum of {{ field.max }} reached.\n </span>\n\n <p v-if=\"error\" class=\"field-error\">{{ error }}</p>\n </fieldset>\n</template>\n"],"names":["__iconNode","Copy","createLucideIcon","Trash2","props","__props","emit","__emit","field","computed","label","error","useField","collapsed","ref","rows","atMax","atMin","summaryFor","row","index","key","value","humanise","_a","rowHasIssue","issue","emitRows","next","add","defaultValues","duplicate","source","remove","_","position","item","move","direction","target","updateRow","toggle","_createElementBlock","_createElementVNode","_hoisted_1","_createTextVNode","_toDisplayString","_unref","_hoisted_2","_openBlock","_hoisted_3","_hoisted_4","_Fragment","_renderList","_hoisted_5","$event","_createVNode","ChevronDown","_normalizeClass","_hoisted_7","_hoisted_8","_hoisted_9","ArrowUp","_hoisted_11","ArrowDown","_hoisted_13","_hoisted_15","_hoisted_17","_withDirectives","_hoisted_18","FieldList","_hoisted_19","Plus","_hoisted_21","_hoisted_22"],"mappings":"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","x_google_ignoreList":[0,1]}
@@ -1,2 +1,2 @@
1
- import{d as D,o as U,q as $,c as a,j as N,w as z,a as s,F as _,t as l,b as w,k as A,z as E,g as F,r as i,i as T,A as p,n as L,h as n,e as M,l as h,C as j,m as q,y as G}from"./index-9WVbsgdk.js";import{u as H,_ as I,w as J}from"./useUnsavedChanges-bO5Ha9y6.js";import{_ as K}from"./PageHeader.vue_vue_type_script_setup_true_lang-IPw0xrLa.js";import"./useField-CQv2TS4g.js";import"./arrow-right-s8aI3aOk.js";import"./plus-D6xxK_qW.js";const O=["disabled"],P={class:"mx-auto max-w-3xl px-4 py-6 sm:px-6"},Q={key:0,class:"text-sm text-muted"},R={key:0,class:"mb-5 text-sm text-muted"},W={key:1,role:"alert",class:"mb-5 rounded-md border border-warning/15 bg-warning-soft p-4"},X={key:2,role:"alert",class:"mb-5 rounded-md border border-danger/15 bg-danger-soft p-4"},Y={class:"text-sm font-medium text-danger"},Z={class:"mt-1.5 space-y-0.5 text-xs text-danger"},ee={key:1,class:"p-8 text-center text-sm text-muted"},ie=D({__name:"SingletonView",props:{name:{}},setup(k){const m=k,B=G(),u=L(),r=F(()=>B.singleton(m.name)),d=i({}),o=i([]),b=i(null),y=i(!0),c=i(!1),v=i(!1),C=i(""),{dirty:g,markClean:S}=H(d);async function x(){var t;y.value=!0;try{const e=await T.singletons.get(m.name);d.value=structuredClone(J(((t=r.value)==null?void 0:t.fields)??{},e.data)),b.value=e.revision,C.value=e.path,o.value=[],v.value=!1,S()}catch(e){u.error(e instanceof p?e.message:"Could not load these settings.")}finally{y.value=!1}}U(x),$(()=>m.name,x);async function V(){c.value=!0,o.value=[];try{const t=await T.singletons.save(m.name,d.value,b.value);b.value=t.revision,v.value=!1,S(),u.success("Settings saved."),q().refresh()}catch(t){t instanceof p&&t.status===409?(v.value=!0,u.error("Someone else saved these settings while you were editing them.")):t instanceof p&&t.issues.length>0?(o.value=t.issues,u.error(t.message,`${t.issues.length} field(s) need attention.`)):u.error(t instanceof p?t.message:"Could not save these settings.")}finally{c.value=!1}}return(t,e)=>r.value?(n(),a(_,{key:0},[N(K,{title:r.value.label,subtitle:C.value},{default:z(()=>[s("span",{class:j(["badge",h(g)?"border-accent bg-accent-soft text-accent":"text-success"])},[e[1]||(e[1]=s("span",{class:"h-1.5 w-1.5 rounded-full bg-current","aria-hidden":"true"},null,-1)),M(" "+l(h(g)?"Unsaved":"Saved"),1)],2),s("button",{type:"button",class:"btn-primary",disabled:c.value||!h(g),onClick:V},l(c.value?"Saving…":h(g)?"Save changes":"Saved"),9,O)]),_:1},8,["title","subtitle"]),s("div",P,[y.value?(n(),a("p",Q,"Loading…")):(n(),a(_,{key:1},[r.value.description?(n(),a("p",R,l(r.value.description),1)):w("",!0),v.value?(n(),a("div",W,[e[2]||(e[2]=s("p",{class:"text-sm font-medium text-warning"},"These settings changed while you were editing",-1)),e[3]||(e[3]=s("p",{class:"mt-1 text-xs text-muted"}," Nothing has been saved. Copy anything you need, then load the current version. ",-1)),s("button",{type:"button",class:"btn-secondary mt-2.5",onClick:x}," Discard mine and reload ")])):w("",!0),o.value.length?(n(),a("div",X,[s("p",Y,l(o.value.length)+" field"+l(o.value.length===1?"":"s")+" need attention ",1),s("ul",Z,[(n(!0),a(_,null,A(o.value,f=>(n(),a("li",{key:f.path},l(f.message),1))),128))])])):w("",!0),s("form",{novalidate:"",onSubmit:E(V,["prevent"])},[N(I,{modelValue:d.value,"onUpdate:modelValue":e[0]||(e[0]=f=>d.value=f),fields:r.value.fields,issues:o.value,disabled:c.value},null,8,["modelValue","fields","issues","disabled"]),e[4]||(e[4]=s("button",{type:"submit",class:"sr-only"},"Save",-1))],32)],64))])],64)):(n(),a("div",ee,' There are no settings called "'+l(k.name)+'". ',1))}});export{ie as default};
2
- //# sourceMappingURL=SingletonView-C3AqQe6a.js.map
1
+ import{d as D,o as U,q as $,c as a,j as N,w as z,a as s,F as _,t as l,b as w,k as A,z as E,g as F,r as i,i as T,A as p,n as L,h as n,e as M,l as h,C as j,m as q,y as G}from"./index-Colfqb-9.js";import{u as H,_ as I,w as J}from"./useUnsavedChanges-B0WVqgI4.js";import{_ as K}from"./PageHeader.vue_vue_type_script_setup_true_lang-B-fBJAjn.js";import"./useField-BDSV5ZPo.js";import"./arrow-right-DJTAYrz5.js";import"./plus-D_aoCFbi.js";const O=["disabled"],P={class:"mx-auto max-w-3xl px-4 py-6 sm:px-6"},Q={key:0,class:"text-sm text-muted"},R={key:0,class:"mb-5 text-sm text-muted"},W={key:1,role:"alert",class:"mb-5 rounded-md border border-warning/15 bg-warning-soft p-4"},X={key:2,role:"alert",class:"mb-5 rounded-md border border-danger/15 bg-danger-soft p-4"},Y={class:"text-sm font-medium text-danger"},Z={class:"mt-1.5 space-y-0.5 text-xs text-danger"},ee={key:1,class:"p-8 text-center text-sm text-muted"},ie=D({__name:"SingletonView",props:{name:{}},setup(k){const m=k,B=G(),u=L(),r=F(()=>B.singleton(m.name)),d=i({}),o=i([]),b=i(null),y=i(!0),c=i(!1),v=i(!1),C=i(""),{dirty:g,markClean:S}=H(d);async function x(){var t;y.value=!0;try{const e=await T.singletons.get(m.name);d.value=structuredClone(J(((t=r.value)==null?void 0:t.fields)??{},e.data)),b.value=e.revision,C.value=e.path,o.value=[],v.value=!1,S()}catch(e){u.error(e instanceof p?e.message:"Could not load these settings.")}finally{y.value=!1}}U(x),$(()=>m.name,x);async function V(){c.value=!0,o.value=[];try{const t=await T.singletons.save(m.name,d.value,b.value);b.value=t.revision,v.value=!1,S(),u.success("Settings saved."),q().refresh()}catch(t){t instanceof p&&t.status===409?(v.value=!0,u.error("Someone else saved these settings while you were editing them.")):t instanceof p&&t.issues.length>0?(o.value=t.issues,u.error(t.message,`${t.issues.length} field(s) need attention.`)):u.error(t instanceof p?t.message:"Could not save these settings.")}finally{c.value=!1}}return(t,e)=>r.value?(n(),a(_,{key:0},[N(K,{title:r.value.label,subtitle:C.value},{default:z(()=>[s("span",{class:j(["badge",h(g)?"border-accent bg-accent-soft text-accent":"text-success"])},[e[1]||(e[1]=s("span",{class:"h-1.5 w-1.5 rounded-full bg-current","aria-hidden":"true"},null,-1)),M(" "+l(h(g)?"Unsaved":"Saved"),1)],2),s("button",{type:"button",class:"btn-primary",disabled:c.value||!h(g),onClick:V},l(c.value?"Saving…":h(g)?"Save changes":"Saved"),9,O)]),_:1},8,["title","subtitle"]),s("div",P,[y.value?(n(),a("p",Q,"Loading…")):(n(),a(_,{key:1},[r.value.description?(n(),a("p",R,l(r.value.description),1)):w("",!0),v.value?(n(),a("div",W,[e[2]||(e[2]=s("p",{class:"text-sm font-medium text-warning"},"These settings changed while you were editing",-1)),e[3]||(e[3]=s("p",{class:"mt-1 text-xs text-muted"}," Nothing has been saved. Copy anything you need, then load the current version. ",-1)),s("button",{type:"button",class:"btn-secondary mt-2.5",onClick:x}," Discard mine and reload ")])):w("",!0),o.value.length?(n(),a("div",X,[s("p",Y,l(o.value.length)+" field"+l(o.value.length===1?"":"s")+" need attention ",1),s("ul",Z,[(n(!0),a(_,null,A(o.value,f=>(n(),a("li",{key:f.path},l(f.message),1))),128))])])):w("",!0),s("form",{novalidate:"",onSubmit:E(V,["prevent"])},[N(I,{modelValue:d.value,"onUpdate:modelValue":e[0]||(e[0]=f=>d.value=f),fields:r.value.fields,issues:o.value,disabled:c.value},null,8,["modelValue","fields","issues","disabled"]),e[4]||(e[4]=s("button",{type:"submit",class:"sr-only"},"Save",-1))],32)],64))])],64)):(n(),a("div",ee,' There are no settings called "'+l(k.name)+'". ',1))}});export{ie as default};
2
+ //# sourceMappingURL=SingletonView-BNJCcl7V.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SingletonView-C3AqQe6a.js","sources":["../../src/views/SingletonView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Singleton editor — site configuration, navigation, theme.\n *\n * A singleton is one file in the repository, so this view is the entry editor\n * without the parts that need a slug: no rename, no delete, no list to go back\n * to. What it shares is `revision` — the version the form was opened against,\n * sent back with the save so a second editor cannot be overwritten in silence.\n */\n\nimport { computed, onMounted, ref, watch } from \"vue\";\n\nimport { withDefaults as fillDefaults, type FieldIssue } from \"@micacms/core\";\n\nimport { api, ApiError } from \"@/api\";\nimport FieldList from \"@/components/fields/FieldList.vue\";\nimport PageHeader from \"@/components/PageHeader.vue\";\nimport { useUnsavedChanges } from \"@/composables/useUnsavedChanges\";\nimport { useSchema } from \"@/stores/schema\";\nimport { useStaging } from \"@/stores/staging\";\nimport { useToast } from \"@/stores/toast\";\n\nconst props = defineProps<{ name: string }>();\n\nconst schema = useSchema();\nconst toast = useToast();\n\nconst definition = computed(() => schema.singleton(props.name));\n\nconst values = ref<Record<string, unknown>>({});\nconst issues = ref<FieldIssue[]>([]);\nconst revision = ref<string | null>(null);\nconst loading = ref(true);\nconst saving = ref(false);\nconst conflict = ref(false);\n\n/** Where these settings live, as the server reports it. */\nconst location = ref(\"\");\n\nconst { dirty, markClean } = useUnsavedChanges(values);\n\nasync function load(): Promise<void> {\n loading.value = true;\n try {\n const detail = await api.singletons.get(props.name);\n // Defaults fill in fields added to the model since the file was written.\n values.value = structuredClone(fillDefaults(definition.value?.fields ?? {}, detail.data));\n revision.value = detail.revision;\n location.value = detail.path;\n issues.value = [];\n conflict.value = false;\n markClean();\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not load these settings.\");\n } finally {\n loading.value = false;\n }\n}\n\nonMounted(load);\nwatch(() => props.name, load);\n\nasync function save(): Promise<void> {\n saving.value = true;\n issues.value = [];\n\n try {\n const saved = await api.singletons.save(props.name, values.value, revision.value);\n revision.value = saved.revision;\n conflict.value = false;\n markClean();\n toast.success(\"Settings saved.\");\n void useStaging().refresh();\n } catch (error) {\n if (error instanceof ApiError && error.status === 409) {\n conflict.value = true;\n toast.error(\"Someone else saved these settings while you were editing them.\");\n } else if (error instanceof ApiError && error.issues.length > 0) {\n issues.value = error.issues;\n toast.error(error.message, `${error.issues.length} field(s) need attention.`);\n } else {\n toast.error(error instanceof ApiError ? error.message : \"Could not save these settings.\");\n }\n } finally {\n saving.value = false;\n }\n}\n</script>\n\n<template>\n <template v-if=\"definition\">\n <PageHeader :title=\"definition.label\" :subtitle=\"location\">\n <span :class=\"['badge', dirty ? 'border-accent bg-accent-soft text-accent' : 'text-success']\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />\n {{ dirty ? \"Unsaved\" : \"Saved\" }}\n </span>\n <button type=\"button\" class=\"btn-primary\" :disabled=\"saving || !dirty\" @click=\"save\">\n {{ saving ? \"Saving…\" : dirty ? \"Save changes\" : \"Saved\" }}\n </button>\n </PageHeader>\n\n <div class=\"mx-auto max-w-3xl px-4 py-6 sm:px-6\">\n <p v-if=\"loading\" class=\"text-sm text-muted\">Loading…</p>\n\n <template v-else>\n <p v-if=\"definition.description\" class=\"mb-5 text-sm text-muted\">\n {{ definition.description }}\n </p>\n\n <div\n v-if=\"conflict\"\n role=\"alert\"\n class=\"mb-5 rounded-md border border-warning/15 bg-warning-soft p-4\"\n >\n <p class=\"text-sm font-medium text-warning\">These settings changed while you were editing</p>\n <p class=\"mt-1 text-xs text-muted\">\n Nothing has been saved. Copy anything you need, then load the current version.\n </p>\n <button type=\"button\" class=\"btn-secondary mt-2.5\" @click=\"load\">\n Discard mine and reload\n </button>\n </div>\n\n <div\n v-if=\"issues.length\"\n role=\"alert\"\n class=\"mb-5 rounded-md border border-danger/15 bg-danger-soft p-4\"\n >\n <p class=\"text-sm font-medium text-danger\">\n {{ issues.length }} field{{ issues.length === 1 ? \"\" : \"s\" }} need attention\n </p>\n <ul class=\"mt-1.5 space-y-0.5 text-xs text-danger\">\n <li v-for=\"issue in issues\" :key=\"issue.path\">{{ issue.message }}</li>\n </ul>\n </div>\n\n <form novalidate @submit.prevent=\"save\">\n <FieldList\n v-model=\"values\"\n :fields=\"definition.fields\"\n :issues=\"issues\"\n :disabled=\"saving\"\n />\n <button type=\"submit\" class=\"sr-only\">Save</button>\n </form>\n </template>\n </div>\n </template>\n\n <div v-else class=\"p-8 text-center text-sm text-muted\">\n There are no settings called \"{{ name }}\".\n </div>\n</template>\n"],"names":["props","__props","schema","useSchema","toast","useToast","definition","computed","values","ref","issues","revision","loading","saving","conflict","location","dirty","markClean","useUnsavedChanges","load","detail","api","fillDefaults","_a","error","ApiError","onMounted","watch","save","saved","useStaging","_createElementBlock","_Fragment","_createVNode","PageHeader","_createElementVNode","_unref","_createTextVNode","_toDisplayString","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_hoisted_4","_hoisted_5","_cache","_hoisted_6","_hoisted_7","_hoisted_8","_renderList","issue","FieldList","$event","_hoisted_9"],"mappings":"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"}
1
+ {"version":3,"file":"SingletonView-BNJCcl7V.js","sources":["../../src/views/SingletonView.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Singleton editor — site configuration, navigation, theme.\n *\n * A singleton is one file in the repository, so this view is the entry editor\n * without the parts that need a slug: no rename, no delete, no list to go back\n * to. What it shares is `revision` — the version the form was opened against,\n * sent back with the save so a second editor cannot be overwritten in silence.\n */\n\nimport { computed, onMounted, ref, watch } from \"vue\";\n\nimport { withDefaults as fillDefaults, type FieldIssue } from \"@micacms/core\";\n\nimport { api, ApiError } from \"@/api\";\nimport FieldList from \"@/components/fields/FieldList.vue\";\nimport PageHeader from \"@/components/PageHeader.vue\";\nimport { useUnsavedChanges } from \"@/composables/useUnsavedChanges\";\nimport { useSchema } from \"@/stores/schema\";\nimport { useStaging } from \"@/stores/staging\";\nimport { useToast } from \"@/stores/toast\";\n\nconst props = defineProps<{ name: string }>();\n\nconst schema = useSchema();\nconst toast = useToast();\n\nconst definition = computed(() => schema.singleton(props.name));\n\nconst values = ref<Record<string, unknown>>({});\nconst issues = ref<FieldIssue[]>([]);\nconst revision = ref<string | null>(null);\nconst loading = ref(true);\nconst saving = ref(false);\nconst conflict = ref(false);\n\n/** Where these settings live, as the server reports it. */\nconst location = ref(\"\");\n\nconst { dirty, markClean } = useUnsavedChanges(values);\n\nasync function load(): Promise<void> {\n loading.value = true;\n try {\n const detail = await api.singletons.get(props.name);\n // Defaults fill in fields added to the model since the file was written.\n values.value = structuredClone(fillDefaults(definition.value?.fields ?? {}, detail.data));\n revision.value = detail.revision;\n location.value = detail.path;\n issues.value = [];\n conflict.value = false;\n markClean();\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : \"Could not load these settings.\");\n } finally {\n loading.value = false;\n }\n}\n\nonMounted(load);\nwatch(() => props.name, load);\n\nasync function save(): Promise<void> {\n saving.value = true;\n issues.value = [];\n\n try {\n const saved = await api.singletons.save(props.name, values.value, revision.value);\n revision.value = saved.revision;\n conflict.value = false;\n markClean();\n toast.success(\"Settings saved.\");\n void useStaging().refresh();\n } catch (error) {\n if (error instanceof ApiError && error.status === 409) {\n conflict.value = true;\n toast.error(\"Someone else saved these settings while you were editing them.\");\n } else if (error instanceof ApiError && error.issues.length > 0) {\n issues.value = error.issues;\n toast.error(error.message, `${error.issues.length} field(s) need attention.`);\n } else {\n toast.error(error instanceof ApiError ? error.message : \"Could not save these settings.\");\n }\n } finally {\n saving.value = false;\n }\n}\n</script>\n\n<template>\n <template v-if=\"definition\">\n <PageHeader :title=\"definition.label\" :subtitle=\"location\">\n <span :class=\"['badge', dirty ? 'border-accent bg-accent-soft text-accent' : 'text-success']\">\n <span class=\"h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />\n {{ dirty ? \"Unsaved\" : \"Saved\" }}\n </span>\n <button type=\"button\" class=\"btn-primary\" :disabled=\"saving || !dirty\" @click=\"save\">\n {{ saving ? \"Saving…\" : dirty ? \"Save changes\" : \"Saved\" }}\n </button>\n </PageHeader>\n\n <div class=\"mx-auto max-w-3xl px-4 py-6 sm:px-6\">\n <p v-if=\"loading\" class=\"text-sm text-muted\">Loading…</p>\n\n <template v-else>\n <p v-if=\"definition.description\" class=\"mb-5 text-sm text-muted\">\n {{ definition.description }}\n </p>\n\n <div\n v-if=\"conflict\"\n role=\"alert\"\n class=\"mb-5 rounded-md border border-warning/15 bg-warning-soft p-4\"\n >\n <p class=\"text-sm font-medium text-warning\">These settings changed while you were editing</p>\n <p class=\"mt-1 text-xs text-muted\">\n Nothing has been saved. Copy anything you need, then load the current version.\n </p>\n <button type=\"button\" class=\"btn-secondary mt-2.5\" @click=\"load\">\n Discard mine and reload\n </button>\n </div>\n\n <div\n v-if=\"issues.length\"\n role=\"alert\"\n class=\"mb-5 rounded-md border border-danger/15 bg-danger-soft p-4\"\n >\n <p class=\"text-sm font-medium text-danger\">\n {{ issues.length }} field{{ issues.length === 1 ? \"\" : \"s\" }} need attention\n </p>\n <ul class=\"mt-1.5 space-y-0.5 text-xs text-danger\">\n <li v-for=\"issue in issues\" :key=\"issue.path\">{{ issue.message }}</li>\n </ul>\n </div>\n\n <form novalidate @submit.prevent=\"save\">\n <FieldList\n v-model=\"values\"\n :fields=\"definition.fields\"\n :issues=\"issues\"\n :disabled=\"saving\"\n />\n <button type=\"submit\" class=\"sr-only\">Save</button>\n </form>\n </template>\n </div>\n </template>\n\n <div v-else class=\"p-8 text-center text-sm text-muted\">\n There are no settings called \"{{ name }}\".\n </div>\n</template>\n"],"names":["props","__props","schema","useSchema","toast","useToast","definition","computed","values","ref","issues","revision","loading","saving","conflict","location","dirty","markClean","useUnsavedChanges","load","detail","api","fillDefaults","_a","error","ApiError","onMounted","watch","save","saved","useStaging","_createElementBlock","_Fragment","_createVNode","PageHeader","_createElementVNode","_unref","_createTextVNode","_toDisplayString","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_hoisted_4","_hoisted_5","_cache","_hoisted_6","_hoisted_7","_hoisted_8","_renderList","issue","FieldList","$event","_hoisted_9"],"mappings":"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"}