From 1fca970b3b04955b3c91c2f5f6cb1c6794bb2dc4 Mon Sep 17 00:00:00 2001 From: MoritzWeber Date: Fri, 25 Sep 2026 20:54:18 +0200 Subject: [PATCH] build: Package Sveltia CMS using Vite This allows us to self-host all dependencies and avoid unnecessary calls to a CDN. --- .github/workflows/deploy.yaml | 2 + .github/workflows/pullrequest.yaml | 2 + .gitignore | 1 + CONTRIBUTING.md | 6 + assets/admin/fields.js | 113 +++ assets/admin/index.html | 12 + assets/admin/main.js | 6 + assets/admin/shortcodes.js | 1082 ++++++++++++++++++++ {static => assets}/admin/style.css | 0 hugo.yaml | 2 - netlify.toml | 2 +- package-lock.json | 1518 +++++++++++++++++++++++++++- package.json | 9 +- static/admin/fields.js | 116 --- static/admin/index.html | 6 +- static/admin/shortcodes.js | 1086 -------------------- vite.config.js | 11 + 17 files changed, 2730 insertions(+), 1244 deletions(-) create mode 100644 assets/admin/fields.js create mode 100644 assets/admin/index.html create mode 100644 assets/admin/main.js create mode 100644 assets/admin/shortcodes.js rename {static => assets}/admin/style.css (100%) delete mode 100644 static/admin/fields.js delete mode 100644 static/admin/shortcodes.js create mode 100644 vite.config.js diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 65dbfe7e1..cb6ec6d84 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -50,6 +50,8 @@ jobs: uses: actions/configure-pages@v5 - name: Install Node.js dependencies run: "npm ci" + - name: Build admin CMS + run: "npm run build:admin" - name: Cache Hugo Resources uses: actions/cache@v4 with: diff --git a/.github/workflows/pullrequest.yaml b/.github/workflows/pullrequest.yaml index dd6e5fd63..fa11391d2 100644 --- a/.github/workflows/pullrequest.yaml +++ b/.github/workflows/pullrequest.yaml @@ -44,6 +44,8 @@ jobs: uses: actions/configure-pages@v5 - name: Install Node.js dependencies run: "npm ci" + - name: Build admin CMS + run: "npm run build:admin" - name: Cache Hugo Resources uses: actions/cache@v4 with: diff --git a/.gitignore b/.gitignore index b0228f713..5594abba1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ node_modules/ public/ resources/ +static/admin/ hugo.exe .hugo_build.lock .DS_Store diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49723a8ca..7b8da0a14 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,6 +33,12 @@ First of all, we have to the install the node packages by running: npm i ``` +The CMS admin interface (`/admin`) is bundled separately with Vite and is not rebuilt automatically by Hugo. Run the following command whenever you change files in `assets/admin/` (or after installing/updating packages) to (re)generate it: + +```zsh +npm run build:admin +``` + Then, we can start the integrated hugo server with: ```zsh diff --git a/assets/admin/fields.js b/assets/admin/fields.js new file mode 100644 index 000000000..ccf326809 --- /dev/null +++ b/assets/admin/fields.js @@ -0,0 +1,113 @@ +import { createElement as h } from "react"; +import { getFieldType, registerFieldType } from "@sveltia/cms"; + +var SelectControl = getFieldType("select").control; + +function FipValiditySelectControl(props) { + var groups = props.entry ? props.entry.getIn(["data", "groups"]) : null; + groups = groups && typeof groups.toJS === "function" ? groups.toJS() : []; + var groupType = props.field ? props.field.get("group_type") : null; + if (groupType) { + groups = groups.filter(function (group) { + return !group.type || group.type === groupType; + }); + } + var options = groups + .filter(function (group) { + return !!group.name; + }) + .map(function (group) { + return { label: group.name, value: group.name }; + }); + + return h(SelectControl, { + field: { name: props.field.get("name"), options }, + value: props.value, + forID: props.forID, + onChange: props.onChange, + }); +} + +registerFieldType( + "fip-validity-select", + FipValiditySelectControl, + FipValiditySelectControl, +); + +function CmsEditLinkControl(props) { + var field = props.field; + var getValue = field.get("get_value"); + var hrefTemplate = field.get("href"); + var labelTemplate = field.get("label_template"); + var emptyHint = field.get("empty_hint"); + var isStatic = field.get("static"); + + var value = isStatic ? "" : String(getValue(props) || ""); + + if (!isStatic && !value) { + return h("p", { className: "o-cms-edit-link__info" }, emptyHint); + } + + var href = hrefTemplate.replace(/\{value\}/g, value); + var label = labelTemplate.replace(/\{value\}/g, value); + return h( + "p", + { className: "o-cms-edit-link__info" }, + h( + "a", + { + href: href, + onClick: function (event) { + event.preventDefault(); + if ( + event.metaKey || + event.ctrlKey || + event.shiftKey || + event.button === 1 + ) { + window.open(href, "_blank"); + return; + } + window.location.hash = href; + }, + onAuxClick: function (event) { + event.preventDefault(); + if (event.button === 1) { + window.open(href, "_blank"); + } + }, + }, + label, + ), + ); +} + +registerFieldType("cms-edit-link", CmsEditLinkControl, CmsEditLinkControl); + +var NumberControl = getFieldType("number").control; + +function CmsNumberWithUsageControl(props) { + var field = props.field; + var template = field.get("template"); + var value = props.value; + + var text = template.replace(/\{value\}/g, String(value ?? "")); + + return h( + "div", + {}, + h(NumberControl, { + field: { name: field.get("name") }, + value: value, + forID: props.forID, + onChange: props.onChange, + }), + h("p", { className: "o-cms-display__info" }, text), + ); +} + +registerFieldType( + "cms-number-with-usage", + CmsNumberWithUsageControl, + CmsNumberWithUsageControl, +); diff --git a/assets/admin/index.html b/assets/admin/index.html new file mode 100644 index 000000000..e4060cf4b --- /dev/null +++ b/assets/admin/index.html @@ -0,0 +1,12 @@ + + + + + + FIP Guide Content Manager + + + + + + diff --git a/assets/admin/main.js b/assets/admin/main.js new file mode 100644 index 000000000..9eafcb913 --- /dev/null +++ b/assets/admin/main.js @@ -0,0 +1,6 @@ +import CMS from "@sveltia/cms"; + +import "./fields.js"; +import "./shortcodes.js"; + +CMS.init(); diff --git a/assets/admin/shortcodes.js b/assets/admin/shortcodes.js new file mode 100644 index 000000000..fcc0bdf36 --- /dev/null +++ b/assets/admin/shortcodes.js @@ -0,0 +1,1082 @@ +import { registerEditorComponent } from "@sveltia/cms"; + +function parseHugoParams(str) { + var result = {}; + var re = /([\w.]+)=(?:"([^"]*)"|(\S+))/g; + var m; + while ((m = re.exec(str)) !== null) { + var val = m[2] !== undefined ? m[2] : m[3]; + if (val === "true") val = true; + else if (val === "false") val = false; + result[m[1]] = val; + } + return result; +} + +function shortcodePattern(name, bracket) { + var open = bracket || "[%<]"; + var close = bracket === "%" ? "%" : bracket === "<" ? ">" : "[%>]"; + return new RegExp( + "\\{\\{" + + open + + " " + + name + + "(?![\\w-])([\\s\\S]*?)" + + close + + "\\}\\}([\\s\\S]*?)\\{\\{" + + open + + " \\/" + + name + + " " + + close + + "\\}\\}", + ); +} + +function selfClosingPattern(name) { + return new RegExp("\\{\\{[%<] " + name + "(?![\\w-])([\\s\\S]*?)[%>/]\\}\\}"); +} + +function dualFormPattern(name) { + return new RegExp( + "\\{\\{[%<] " + + name + + "(?![\\w-])([\\s\\S]*?)(?:\\/[%>]\\}\\}|[%>]\\}\\}([\\s\\S]*?)\\{\\{[%<] \\/" + + name + + " [%>]\\}\\})", + ); +} + +function renderParamList(fields, data) { + var out = []; + (fields || []).forEach(function (field) { + var spec = field.param; + if (!spec) return; + var value = data[field.name]; + if (spec.transform) value = spec.transform(value, data); + if (!spec.required && !value) return; + if (spec.positional) { + out.push(spec.quote ? '"' + value + '"' : String(value)); + return; + } + var paramName = spec.name || field.name; + out.push(paramName + "=" + (spec.bare ? String(value) : '"' + value + '"')); + }); + return out; +} + +function renderInlineOpen(bracket, name, params, closer) { + var paramsStr = params.join(" "); + var close = + closer === "slash" + ? bracket === "%" + ? "/%}}" + : "/>}}" + : bracket === "%" + ? "%}}" + : ">}}"; + return ( + "{{" + + bracket + + " " + + name + + (paramsStr ? " " + paramsStr : "") + + " " + + close + ); +} + +function renderMultilineOpen(bracket, name, params, opts) { + opts = opts || {}; + var pad = " "; + var headParams = opts.firstInline ? params.slice(0, 1) : []; + var restParams = opts.firstInline ? params.slice(1) : params; + var close = + opts.closer === "slash" + ? bracket === "%" + ? "/%}}" + : "/>}}" + : bracket === "%" + ? "%}}" + : ">}}"; + var header = + "{{" + + bracket + + " " + + name + + (headParams.length ? " " + headParams.join(" ") : ""); + var lines = [header].concat( + restParams.map(function (p) { + return pad + p; + }), + ); + return lines.join("\n") + "\n" + close; +} + +function makeToBlock(name, cfg) { + var bracket = cfg.bracket || "%"; + var bodyMode = cfg.bodyMode || "none"; + var bodySep = cfg.bodySeparator || "\n"; + var closeTag = + "{{" + bracket + " /" + name + " " + (bracket === "%" ? "%}}" : ">}}"); + + function openTag(params, closer) { + var wrap = cfg.multiline && params.length > (cfg.multilineThreshold || 0); + if (wrap) { + return renderMultilineOpen(bracket, name, params, { + firstInline: cfg.firstInline, + closer: closer, + }); + } + return renderInlineOpen(bracket, name, params, closer); + } + + return function (data) { + var params = renderParamList(cfg.fields, data); + + if (bodyMode === "none") { + return openTag(params, "plain"); + } + + var body = String(data[cfg.bodyField || "body"] || ""); + + if (bodyMode === "dual") { + if (!body.trim()) return openTag(params, "slash"); + return openTag(params, "plain") + bodySep + body + bodySep + closeTag; + } + + var open = openTag(params, "plain"); + + if (bodyMode === "optionalPaired") { + return open + "\n" + (body ? body + "\n" : "") + closeTag; + } + + return open + bodySep + body + bodySep + closeTag; + }; +} + +var highlightFields = [ + { + name: "type", + label: "Type", + widget: "select", + options: ["important", "tip", "confusion", "inofficial"], + param: { positional: true, required: true }, + }, + { + name: "body", + label: "Content", + widget: "markdown", + editor_components: ["button", "float-image", "highlight", "image"], + }, +]; + +registerEditorComponent({ + id: "highlight", + label: "Highlight", + fields: highlightFields, + pattern: shortcodePattern("highlight", "%"), + fromBlock: function (match) { + return { type: match[1].trim(), body: match[2].trim() }; + }, + toBlock: makeToBlock("highlight", { + fields: highlightFields, + bodyMode: "required", + bodySeparator: "\n", + }), + toPreview: function () { + return ""; + }, +}); + +registerEditorComponent({ + id: "highlight-raw", + label: "Highlight", + fields: highlightFields, + pattern: shortcodePattern("highlight", "<"), + fromBlock: function (match) { + return { type: match[1].trim(), body: match[2].trim() }; + }, + toBlock: makeToBlock("highlight", { + bracket: "<", + fields: highlightFields, + bodyMode: "required", + bodySeparator: "\n", + }), + toPreview: function () { + return ""; + }, +}); + +var expanderFields = [ + { + name: "title", + label: "Title", + widget: "string", + param: { positional: true, quote: true, required: true }, + }, + { + name: "variant", + label: "Variant", + widget: "select", + options: [{ label: "None", value: "" }, "border", "info"], + required: false, + param: { positional: true }, + }, + { + name: "body", + label: "Content", + widget: "markdown", + editor_components: ["button", "float-image", "highlight-raw", "image"], + }, +]; + +registerEditorComponent({ + id: "expander", + label: "Expander", + fields: expanderFields, + pattern: shortcodePattern("expander"), + fromBlock: function (match) { + var titleMatch = match[1].match(/"([^"]*)"/); + var rest = match[1].replace(/"[^"]*"/, "").trim(); + var variantMatch = rest.match(/^(\w+)/); + return { + title: titleMatch ? titleMatch[1] : "", + variant: variantMatch ? variantMatch[1] : "", + body: match[2].trim(), + }; + }, + toBlock: makeToBlock("expander", { + fields: expanderFields, + bodyMode: "required", + bodySeparator: "\n\n", + }), + toPreview: function () { + return ""; + }, +}); + +var fipValidityFields = [ + { + name: "type", + label: "Type", + widget: "select", + options: [ + "fip-coupon", + "fip-reduced-ticket", + "fip-global-fare", + "additional", + ], + param: { required: true }, + }, + { + name: "status", + label: "Status", + widget: "select", + options: ["valid", "invalid", "unknown"], + param: { required: true }, + }, + { + name: "subtitle", + label: "Subtitle", + widget: "string", + required: false, + param: {}, + }, + { + name: "text", + label: "Additional Text", + widget: "string", + required: false, + param: {}, + }, + { + name: "disable_dialog", + label: "Disable info dialog", + widget: "boolean", + default: false, + required: false, + param: { bare: true }, + }, +]; + +registerEditorComponent({ + id: "fip-validity", + label: "FIP Validity Badge", + fields: fipValidityFields, + pattern: selfClosingPattern("fip-validity"), + fromBlock: function (match) { + var p = parseHugoParams(match[1]); + return { + type: p.type || "fip-coupon", + status: p.status || "valid", + subtitle: p.subtitle || "", + text: p.text || "", + disable_dialog: p.disable_dialog === true, + }; + }, + toBlock: makeToBlock("fip-validity", { + bracket: "<", + fields: fipValidityFields, + bodyMode: "none", + }), + toPreview: function () { + return ""; + }, +}); + +var trainCategoryFields = [ + { + name: "id", + label: "ID (anchor)", + widget: "string", + param: { required: true }, + }, + { + name: "title", + label: "Title", + widget: "string", + param: { required: true }, + }, + { + name: "type", + label: "Type", + widget: "select", + options: [ + "highspeed", + "regional", + "subway", + "sleeper", + "funicular", + "bus", + "ship", + "tram", + ], + param: { required: true }, + }, + { + name: "fip_accepted", + label: "FIP accepted", + widget: "select", + options: [ + { label: "No", value: "false" }, + { label: "Yes", value: "true" }, + { label: "Partially", value: "partially" }, + { label: "Unknown", value: "unknown" }, + ], + default: "true", + param: { bare: true, required: true }, + }, + { + name: "reservation_required", + label: "Reservation required", + widget: "select", + options: [ + { label: "No", value: "false" }, + { label: "Yes", value: "true" }, + { label: "Partially", value: "partially" }, + ], + default: "false", + param: { bare: true, required: true }, + }, + { + name: "reservation_possible", + label: "Reservation possible", + widget: "select", + options: [ + { label: "No", value: "false" }, + { label: "Yes", value: "true" }, + { label: "Partially", value: "partially" }, + ], + default: "false", + param: { bare: true, required: true }, + }, + { + name: "route_overview_url", + label: "Route overview URL", + widget: "string", + required: false, + param: {}, + }, + { + name: "additional_information_url", + label: "Additional information URL", + widget: "string", + required: false, + param: {}, + }, + { + name: "body", + label: "Description", + widget: "markdown", + editor_components: ["button", "float-image", "highlight-raw", "image"], + }, +]; + +registerEditorComponent({ + id: "train-category", + label: "Train Category", + fields: trainCategoryFields, + pattern: shortcodePattern("train-category"), + fromBlock: function (match) { + var p = parseHugoParams(match[1]); + return { + id: String(p.id || ""), + title: String(p.title || ""), + type: String(p.type || "regional"), + fip_accepted: String(p.fip_accepted ?? true), + reservation_required: String(p.reservation_required || "false"), + reservation_possible: String(p.reservation_possible || "false"), + route_overview_url: String(p.route_overview_url || ""), + additional_information_url: String(p.additional_information_url || ""), + body: match[2].trim(), + }; + }, + toBlock: makeToBlock("train-category", { + fields: trainCategoryFields, + bodyMode: "required", + bodySeparator: "\n\n", + multiline: true, + }), + toPreview: function () { + return ""; + }, +}); + +var bookingFields = [ + { + name: "id", + label: "Booking Platform", + widget: "relation", + collection: "booking", + search_fields: ["title"], + value_field: "{{slug}}", + display_fields: ["title"], + param: { + required: true, + transform: function (v) { + return String(v || "").replace(/\/index$/, ""); + }, + }, + }, + { + name: "booking_data_link", + label: "Booking Platform Link", + widget: "cms-edit-link", + static: true, + href: "#/collections/booking", + label_template: "Manage Booking Platforms \u2192", + }, + { + name: "subtitle", + label: "Subtitle", + widget: "string", + required: false, + param: {}, + }, + { + name: "classes_first", + label: "1st class reservation costs (overwrite)", + widget: "string", + required: false, + param: { name: "classes.first" }, + }, + { + name: "classes_second", + label: "2nd class reservation costs (overwrite)", + widget: "string", + required: false, + param: { name: "classes.second" }, + }, + { + name: "fip_50", + label: "FIP 50 (override)", + widget: "select", + required: false, + options: [ + { label: "Default", value: "" }, + { label: "Hide", value: "nil" }, + { label: "Yes", value: "true" }, + { label: "No", value: "false" }, + ], + default: "", + param: { bare: true }, + }, + { + name: "fip_75", + label: "FIP 75 (override)", + widget: "select", + required: false, + options: [ + { label: "Default", value: "" }, + { label: "Hide", value: "nil" }, + { label: "Yes", value: "true" }, + { label: "No", value: "false" }, + ], + default: "", + param: { bare: true }, + }, + { + name: "fip_global_fare", + label: "FIP Global Fare (override)", + widget: "select", + required: false, + options: [ + { label: "Default", value: "" }, + { label: "Hide", value: "nil" }, + { label: "Yes", value: "true" }, + { label: "No", value: "false" }, + ], + default: "", + param: { bare: true }, + }, + { + name: "reservations", + label: "Reservations (override)", + widget: "select", + required: false, + options: [ + { label: "Default", value: "" }, + { label: "Hide", value: "nil" }, + { label: "Yes", value: "true" }, + { label: "No", value: "false" }, + ], + default: "", + param: { bare: true }, + }, + { + name: "fee", + label: "Fee (override)", + widget: "string", + required: false, + hint: 'Overwrites the booking fee text, e.g. "5 %". Leave empty to use the booking platform default.', + param: {}, + }, + { + name: "body", + label: "Additional info", + widget: "markdown", + required: false, + editor_components: ["button", "float-image", "image"], + }, +]; + +registerEditorComponent({ + id: "booking", + label: "Booking", + fields: bookingFields, + pattern: dualFormPattern("booking"), + fromBlock: function (match) { + var p = parseHugoParams(match[1]); + var rawId = p.id ? String(p.id) : ""; + return { + id: rawId, + booking_data_link: rawId, + subtitle: String(p.subtitle || ""), + classes_first: String(p["classes.first"] || ""), + classes_second: String(p["classes.second"] || ""), + fip_50: String(p.fip_50 ?? ""), + fip_75: String(p.fip_75 ?? ""), + fip_global_fare: String(p.fip_global_fare ?? ""), + reservations: String(p.reservations ?? ""), + fee: String(p.fee || ""), + body: match[2] ? match[2].trim() : "", + }; + }, + toBlock: makeToBlock("booking", { + fields: bookingFields, + bodyMode: "dual", + bodySeparator: "\n", + multiline: true, + firstInline: true, + multilineThreshold: 1, + }), + toPreview: function () { + return ""; + }, +}); + +var bookingSectionFields = [ + { + name: "section", + label: "Section", + widget: "select", + options: [ + { label: "FIP 50", value: "fip_50" }, + { label: "FIP 75", value: "fip_75" }, + { label: "FIP Global Fare", value: "fip_global_fare" }, + { label: "Reservations", value: "reservations" }, + ], + param: { positional: true, quote: true, required: true }, + }, + { + name: "body", + label: "Content", + widget: "markdown", + editor_components: ["button", "float-image", "highlight-raw", "image"], + }, +]; + +registerEditorComponent({ + id: "booking-section", + label: "Booking Section", + fields: bookingSectionFields, + pattern: shortcodePattern("booking-section"), + fromBlock: function (match) { + var sectionMatch = match[1].match(/"(\w+)"/); + return { + section: sectionMatch ? sectionMatch[1] : "", + body: match[2].trim(), + }; + }, + toBlock: makeToBlock("booking-section", { + fields: bookingSectionFields, + bodyMode: "required", + bodySeparator: "\n", + }), + toPreview: function () { + return ""; + }, +}); + +var buttonFields = [ + { + name: "destination", + label: "URL", + widget: "string", + param: { required: true }, + }, + { + name: "text", + label: "Button text", + widget: "string", + param: { required: true }, + }, +]; + +registerEditorComponent({ + id: "button", + label: "Button", + fields: buttonFields, + pattern: selfClosingPattern("button"), + fromBlock: function (match) { + var p = parseHugoParams(match[1]); + return { + destination: String(p.destination || ""), + text: String(p.text || ""), + }; + }, + toBlock: makeToBlock("button", { + bracket: "<", + fields: buttonFields, + bodyMode: "none", + }), + toPreview: function () { + return ""; + }, +}); + +var identifyOperatorFields = [ + { + name: "sources", + label: "Sources", + widget: "relation", + collection: "identify-operator", + search_fields: ["title"], + value_field: "{{slug}}", + display_fields: ["title"], + multiple: true, + required: false, + param: { + transform: function (value) { + var arr = Array.isArray(value) ? value : value ? [value] : []; + return arr + .map(function (s) { + return String(s).replace(/\/index$/, ""); + }) + .join(","); + }, + }, + }, + { + name: "body", + label: "Additional info", + widget: "markdown", + required: false, + editor_components: ["button", "float-image", "highlight", "image"], + }, +]; + +registerEditorComponent({ + id: "identify-operator", + label: "Identify Operator", + fields: identifyOperatorFields, + pattern: dualFormPattern("identify-operator"), + fromBlock: function (match) { + var p = parseHugoParams(match[1] || ""); + return { + sources: p.sources + ? p.sources.split(",").map(function (s) { + return s.trim(); + }) + : [], + body: match[2] ? match[2].trim() : "", + }; + }, + toBlock: makeToBlock("identify-operator", { + bracket: "<", + fields: identifyOperatorFields, + bodyMode: "dual", + bodySeparator: "\n", + }), + toPreview: function () { + return ""; + }, +}); + +var satelliteFields = [ + { + name: "body", + label: "Additional info", + widget: "markdown", + required: false, + editor_components: ["button", "float-image", "image"], + }, +]; + +registerEditorComponent({ + id: "satellite", + label: "Satellite Notice", + fields: satelliteFields, + pattern: dualFormPattern("satellite"), + fromBlock: function (match) { + return { body: match[2] ? match[2].trim() : "" }; + }, + toBlock: makeToBlock("satellite", { + fields: satelliteFields, + bodyMode: "dual", + bodySeparator: "\n", + }), + toPreview: function () { + return ""; + }, +}); + +var dialogFields = [ + { + name: "id", + label: "Dialog ID (anchor)", + widget: "string", + param: { required: true }, + }, + { + name: "title", + label: "Title", + widget: "string", + param: { required: true }, + }, + { + name: "body", + label: "Content", + widget: "markdown", + editor_components: ["button", "float-image", "highlight", "image"], + }, +]; + +registerEditorComponent({ + id: "dialog", + label: "Dialog", + fields: dialogFields, + pattern: shortcodePattern("dialog"), + fromBlock: function (match) { + var p = parseHugoParams(match[1]); + return { + id: String(p.id || ""), + title: String(p.title || ""), + body: match[2].trim(), + }; + }, + toBlock: makeToBlock("dialog", { + fields: dialogFields, + bodyMode: "required", + bodySeparator: "\n", + multiline: true, + }), + toPreview: function () { + return ""; + }, +}); + +var floatImageFields = [ + { name: "src", label: "Image", widget: "image", param: { required: true } }, + { + name: "alt", + label: "Alt text", + widget: "string", + param: { required: true }, + }, + { + name: "caption", + label: "Caption", + widget: "string", + required: false, + param: {}, + }, + { + name: "width", + label: "Width (CSS)", + widget: "string", + default: "50%", + required: false, + param: {}, + }, + { + name: "position", + label: "Position", + widget: "select", + options: ["right", "left"], + default: "right", + param: {}, + }, + { + name: "body", + label: "Surrounding text", + widget: "markdown", + required: false, + editor_components: ["button", "image"], + }, +]; + +registerEditorComponent({ + id: "float-image", + label: "Float Image", + fields: floatImageFields, + pattern: shortcodePattern("float-image"), + fromBlock: function (match) { + var p = parseHugoParams(match[1]); + return { + src: String(p.src || ""), + alt: String(p.alt || ""), + caption: String(p.caption || ""), + width: String(p.width || "50%"), + position: String(p.position || "right"), + body: match[2].trim(), + }; + }, + toBlock: makeToBlock("float-image", { + fields: floatImageFields, + bodyMode: "optionalPaired", + multiline: true, + }), + toPreview: function () { + return ""; + }, +}); + +registerEditorComponent({ + id: "wip", + label: "Work in Progress", + fields: [], + pattern: selfClosingPattern("wip"), + fromBlock: function () { + return {}; + }, + toBlock: makeToBlock("wip", { bracket: "<", fields: [], bodyMode: "none" }), + toPreview: function () { + return ""; + }, +}); + +var updateFields = [ + { + name: "date", + label: "Date", + widget: "datetime", + date_format: "YYYY-MM-DD", + time_format: false, + param: { required: true }, + }, + { + name: "body", + label: "Content", + widget: "markdown", + editor_components: ["button", "float-image", "highlight", "image"], + }, +]; + +registerEditorComponent({ + id: "update", + label: "Update", + fields: updateFields, + pattern: shortcodePattern("update"), + fromBlock: function (match) { + var p = parseHugoParams(match[1]); + return { date: String(p.date || ""), body: match[2].trim() }; + }, + toBlock: makeToBlock("update", { + fields: updateFields, + bodyMode: "required", + bodySeparator: "\n", + }), + toPreview: function () { + return ""; + }, +}); + +var columnFields = [ + { + name: "width", + label: "Width (CSS)", + widget: "string", + param: { required: true }, + }, + { + name: "body", + label: "Content", + widget: "markdown", + editor_components: ["button", "float-image", "highlight", "image"], + }, +]; + +registerEditorComponent({ + id: "column", + label: "Column", + fields: columnFields, + pattern: shortcodePattern("column"), + fromBlock: function (match) { + var p = parseHugoParams(match[1]); + return { width: String(p.width || ""), body: match[2].trim() }; + }, + toBlock: makeToBlock("column", { + fields: columnFields, + bodyMode: "required", + bodySeparator: "\n", + }), + toPreview: function () { + return ""; + }, +}); + +var columnsFields = [ + { + name: "body", + label: "Content", + widget: "markdown", + editor_components: ["column"], + }, +]; + +registerEditorComponent({ + id: "columns", + label: "Columns", + fields: columnsFields, + pattern: shortcodePattern("columns"), + fromBlock: function (match) { + return { body: match[2].trim() }; + }, + toBlock: makeToBlock("columns", { + fields: columnsFields, + bodyMode: "required", + bodySeparator: "\n", + }), + toPreview: function () { + return ""; + }, +}); + +var fipValidityTableFields = [ + { + name: "type", + label: "Type", + widget: "select", + options: ["fip-coupon", "fip-reduced-ticket"], + param: { required: true }, + }, + { + name: "validity_data_link", + label: "FIP Validity Table", + widget: "cms-edit-link", + get_value: function () { + var match = window.location.hash.match( + /#\/collections\/application\/entries\/([^/]+)/, + ); + return match ? match[1] : ""; + }, + href: "#/collections/fip-validity/entries/{value}/validity", + label_template: + "Edit the FIP Validity Table for \u201c{value}\u201d \u2192", + empty_hint: "Save this page first to link its FIP Validity Table.", + }, +]; + +var footnoteFields = [ + { + name: "id", + label: "Number", + widget: "cms-number-with-usage", + template: + "You can use this footnote by writing out [^{value}] in the text editor.", + param: { required: true }, + }, + { + name: "name", + label: "Source Name", + widget: "string", + param: { required: true }, + }, + { + name: "link", + label: "Link", + widget: "string", + param: { required: true }, + }, +]; + +registerEditorComponent({ + id: "footnote", + label: "Footnote", + fields: footnoteFields, + pattern: /^\[\^([^\]]+)\]: \[([^\]]*)\]\(([^)]*)\)$/, + fromBlock: function (match) { + var num = Number(match[1]); + var id = Number.isNaN(num) ? match[1] : num; + return { + id: id, + name: String(match[2] || ""), + link: String(match[3] || ""), + }; + }, + toBlock: function (data) { + return ( + "[^" + + (data.id ?? "") + + "]: [" + + (data.name || "") + + "](" + + (data.link || "") + + ")" + ); + }, + toPreview: function () { + return ""; + }, +}); + +registerEditorComponent({ + id: "fip-validity-table", + label: "FIP Validity Table", + fields: fipValidityTableFields, + pattern: selfClosingPattern("fip-validity-table"), + fromBlock: function (match) { + var p = parseHugoParams(match[1]); + return { type: p.type || "fip-coupon" }; + }, + toBlock: makeToBlock("fip-validity-table", { + bracket: "<", + fields: fipValidityTableFields, + bodyMode: "none", + }), + toPreview: function () { + return ""; + }, +}); diff --git a/static/admin/style.css b/assets/admin/style.css similarity index 100% rename from static/admin/style.css rename to assets/admin/style.css diff --git a/hugo.yaml b/hugo.yaml index 02f3860d8..ae6ce0ea7 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -35,8 +35,6 @@ module: target: "static/css/@fontsource" - source: "node_modules/@material-symbols/svg-700/rounded" target: "static/icons/material-symbols-rounded" - - source: "node_modules/@sveltia/cms/dist" - target: "static/admin/sveltia-cms" hugoVersion: min: "0.164.0" diff --git a/netlify.toml b/netlify.toml index 40eef66bc..8fab1904c 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,5 +1,5 @@ [build] - command = "echo 'resourceDir: /opt/build/cache/resources_cache' >> hugo.yaml && FIPGUIDE_BRANCH=\"$HEAD\" hugo --gc --minify -b $DEPLOY_PRIME_URL && npx -y pagefind --site public" + command = "npm run build:admin && echo 'resourceDir: /opt/build/cache/resources_cache' >> hugo.yaml && FIPGUIDE_BRANCH=\"$HEAD\" hugo --gc --minify -b $DEPLOY_PRIME_URL && npx -y pagefind --site public" publish = "public" [build.environment] diff --git a/package-lock.json b/package-lock.json index accab1920..544d37993 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,12 +13,456 @@ "@fontsource/sansita": "^5.3.0", "@material-symbols/svg-700": "^0.47.5", "@panzoom/panzoom": "^4.6.2", - "@sveltia/cms": "^0.220.0", - "pagefind": "^1.5.2" + "@sveltia/cms": "^0.221.1", + "pagefind": "^1.5.2", + "react": "19.3.0" }, "devDependencies": { "prettier": "^3.9.9", - "prettier-plugin-go-template": "^0.0.15" + "prettier-plugin-go-template": "^0.0.15", + "vite": "7.3.6" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@fontsource/roboto": { @@ -45,6 +489,29 @@ "integrity": "sha512-3qo9xw/W36BP5G7UAvxOlBsJf83jFuOc++qm9fMkphW4HXmvSDLh0CE3MEK+ciXxKAPQrFddVj3T5rbDmRfnMw==", "license": "Apache-2.0" }, + "node_modules/@mixmark-io/domino": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", + "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==", + "license": "BSD-2-Clause" + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, "node_modules/@pagefind/darwin-arm64": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.5.2.tgz", @@ -76,80 +543,800 @@ "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.5.2.tgz", "integrity": "sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA==", "cpu": [ - "x64" + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@pagefind/linux-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.5.2.tgz", + "integrity": "sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/linux-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.5.2.tgz", + "integrity": "sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/windows-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.5.2.tgz", + "integrity": "sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pagefind/windows-x64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.5.2.tgz", + "integrity": "sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@panzoom/panzoom": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@panzoom/panzoom/-/panzoom-4.6.2.tgz", + "integrity": "sha512-Zn3B5/hwa6eYIPRSKX0xf2clv8nviTX8AnAU5kU/EugiTDhG41ya2wlBqYrZJYCWQROr/5XkWObZhIkepi89qw==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.5.tgz", + "integrity": "sha512-J25QJU+B78T4FhhBsNpLJyVWOi31mwtpcMwywHmOKH65Q9IWGA81gPj+dnwlhU8wktVriYE+tFAaQgrnJRzAZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.5.tgz", + "integrity": "sha512-LDopB3zuZM5Ux9TT2luNEBJW/tYbGU2g1d+VpKk6I+gSKDb+/7sYE6M225gRQt4RbMX6MSwMsVR/phdjVUgRLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.5.tgz", + "integrity": "sha512-wlJEERGfeuHeBavCL2qVnNacOK43NDoZM4sjkeRPymd04OAE9T1zBqDJgmZ+CIsPTYKwdzpUC8vmOw84dwY4Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.5.tgz", + "integrity": "sha512-4nJJGg5jbo2wwPP4JP+LfEBA3bvP8rU9CLuhp7jWvq9sxEyhjQFTFdrqi+/dHEin/pd8jpT0vcehIpnZtmEdcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.5.tgz", + "integrity": "sha512-DrZbyCDF1hneuO6jRbvZ2D7+PIBM6yIwYnJpg2vIk58T+wuFpiaGZrfUr59lDWw45bg+IrpTGLPiNi/Fk4w3Cg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.5.tgz", + "integrity": "sha512-gqfUVMJMB3mehqywxp6hTBFfgtMQykZY19+cfiaYP0toIJLb/1DZRJHVkQQGP13W4TAwfZDWeg1qBcheTRioXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.5.tgz", + "integrity": "sha512-CFmhpvAwzSaWMlN3VN7UtmoTihlZNzoP0juQib5TQRnYUyDV8dXeWOp29sobWAT6gXl/hQgAClLlEiYozQG3OQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.5.tgz", + "integrity": "sha512-Uc9H8eXCOayV6JLTH5bXKMId6qbhNHa818/BgYjm4jrlq3vZquC9cqyvHBw17xy5Mnj5f+I3gFK5JcEf3hSqrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.5.tgz", + "integrity": "sha512-VcPr/szv/1BFw112Kt//fxulXt/JPqzzidU84iW68L2DdjnOO8QFUv2zTSYBEPHD6movBD4z+bbr5y60GYM7Jw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.5.tgz", + "integrity": "sha512-BnxtJ5/91BrIHYIkGrmjz/lbMhqEHt1dPFqIxIFR+jPn0xVc/oUSCtIT089zfp5ufwGDlYz2UC+Fe1SRBpYFbQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.5.tgz", + "integrity": "sha512-LrYcHZwF+fAMNKHYTOQ5osWM4AZF7YF6D+XtsjDyEvljtt11twc+zHVXBLNEjxVSUnKYsOhvVz4Z213eW02COQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.5.tgz", + "integrity": "sha512-nj7QKQePAAUpCpJHtg0pR0W/b92A9NO17JS3BAQmHDn/yhmkir2p8llrKY9TOhleKIaSzy1JhxS3T9FVld6coA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.5.tgz", + "integrity": "sha512-5ylkX6dWMeBKge9nTU+Rxfb+ZfaCIJ9lRqIFaK0eAMcWp7OJbYnLveLgXmm0VrvuLKb8qIK+mHyH0qu88RM+iA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.5.tgz", + "integrity": "sha512-oHK4ZHYFDKjZviK34I+NwgfbGxgI7ztrNxj2hPTSSNFgeq1a/lEd7dHV2fdGAuTH4Iym3RHJg+vAbWaWG4B7Zg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.5.tgz", + "integrity": "sha512-UcetmHZ6XOXuUByiKZyQmb55ZPr0LABr3Ec/HB9wKZn6CEAFWZkE+hsJErJ9hbPBC7nI0dKuELx7CoV6IM7TMg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.5.tgz", + "integrity": "sha512-C5CmDPQBtvjVo8cgQsBs+w6WB0JLkiixhgi6hVLV11hERWdn/p0XcPU2OUcZzac9BPOFq7SbaHFa8r3SWEysCQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.5.tgz", + "integrity": "sha512-lHVQHJFKsuuxLMi3MQO9XVL8Tje3JR82CzB+QDKC5NWBcsIWuwsn9uIM5e3lBhI+fF1/s63qnyYqsg65+8rV/w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.5.tgz", + "integrity": "sha512-3W9bTFcQNJn71cSJVM9RKIiZOy8DO/XLDii8Uv/Pm6WKqDRj7JV3ZfuXIEfyuy5LXpIzAbB/1M4Ukp9GKNa7nA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.5.tgz", + "integrity": "sha512-VDC7rRJlee/scpki96GZ27Omf6yU87s1YXwVTpjE5841faVlDYYT565rgfmoR1U0sqL7z5ivQSDjcsF6VRXyBA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.5.tgz", + "integrity": "sha512-z86Ok2p4pTdv5xqCKZsTooO7yBEiaJR/HzU3Wx8RmWsPoLppnMKROhJusQob8B3IE1ghC343kUW9rC2r+Wf3ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.5.tgz", + "integrity": "sha512-IzQmj+xXwQFGhMAMKMQVXkMwMZN3TqkJgAE0nSsqvVwWWciP4AIPMmWRqOQ2GfX7TUDZr+xqGFcBS36CRPGw0g==", + "cpu": [ + "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "freebsd" + "openharmony" ] }, - "node_modules/@pagefind/linux-arm64": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.5.2.tgz", - "integrity": "sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw==", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.5.tgz", + "integrity": "sha512-F6qpTaPc9bwBH85kjy0/BLmLSW1uv7AoOXCoRIkg2arlgCYlWYcAbiMkvZuAcaWk9TpCRG//okznLAqLGshkMw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ] }, - "node_modules/@pagefind/linux-x64": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.5.2.tgz", - "integrity": "sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA==", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.5.tgz", + "integrity": "sha512-igoDsTFhhwECBeGbUuLeIk7t8Y1apa+cs6mDWpx2EZ0ch7oEQgzHbFUXN9euoHekCAQzXdXApAGkV6jznS7tWw==", "cpu": [ - "x64" + "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ] }, - "node_modules/@pagefind/windows-arm64": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.5.2.tgz", - "integrity": "sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g==", + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.5.tgz", + "integrity": "sha512-U3teMeMbXFmaM5D+OTJpsOXd+wV/qftIeYF9kBKL4v73641qyJmoXFtA28DQLsnmlyayEsTe72xpLHrArq6vHw==", "cpu": [ - "arm64" + "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ] }, - "node_modules/@pagefind/windows-x64": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.5.2.tgz", - "integrity": "sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.5.tgz", + "integrity": "sha512-ypfC34F3RKXvCXBglGqGMsUSMKlgwd1HX9AOAlx9RoZZ6GaI42YHVeKpzg3JG+wpBUJYTG+NNZhqbDWL8tBZkw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ] }, - "node_modules/@panzoom/panzoom": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@panzoom/panzoom/-/panzoom-4.6.2.tgz", - "integrity": "sha512-Zn3B5/hwa6eYIPRSKX0xf2clv8nviTX8AnAU5kU/EugiTDhG41ya2wlBqYrZJYCWQROr/5XkWObZhIkepi89qw==", + "node_modules/@shikijs/langs": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.4.3.tgz", + "integrity": "sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.4.3.tgz", + "integrity": "sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.4.3.tgz", + "integrity": "sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "license": "MIT" }, "node_modules/@sveltia/cms": { - "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@sveltia/cms/-/cms-0.220.0.tgz", - "integrity": "sha512-MVIIfnw8ptT5GIPf0TtJDEVeJ/nQkJnC3ODPoo4tTtWfcdtulE0d66GQ7QunhhTBvdjmfg8Sv3xPMlZrPd4zlw==", + "version": "0.221.1", + "resolved": "https://registry.npmjs.org/@sveltia/cms/-/cms-0.221.1.tgz", + "integrity": "sha512-J+Qoik9kGMk2L7vNpOQrnDmFRi+NB6vwJ0f657YtsxWH0P8WYSYRRSRC2pQ/jGGbVlM//Pg/WIgmkpswqoo3gQ==", "license": "MIT", + "dependencies": { + "@shikijs/langs": "4.4.3", + "@shikijs/themes": "4.4.3", + "exifr": "^7.1.3", + "immutable": "^5.1.9", + "leaflet": "^1.9.4", + "svgo": "^4.1.0", + "terra-draw": "^1.35.0", + "terra-draw-leaflet-adapter": "1.0.3", + "turndown": "^7.2.4" + }, "funding": { "type": "github", "url": "https://github.com/sponsors/kyoshino" + }, + "peerDependencies": { + "@types/react": "^19.3.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/css-select": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", + "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^7.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "nth-check": "^2.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz", + "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/exifr": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/exifr/-/exifr-7.1.3.tgz", + "integrity": "sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "license": "MIT" + }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, "node_modules/pagefind": { @@ -170,6 +1357,54 @@ "@pagefind/windows-x64": "1.5.2" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/prettier": { "version": "3.9.9", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.9.tgz", @@ -202,6 +1437,150 @@ "prettier": "^3.0.0" } }, + "node_modules/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.5.tgz", + "integrity": "sha512-KRWwmNLlPw5M7HcdYfm15oBv9n9LPtjzpzCIxS/phwqvPyxHSoKX6Y2YU3pxSPfy0CLquVgsx/j/hBi6OvH1Nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.5", + "@rollup/rollup-android-arm64": "4.63.5", + "@rollup/rollup-darwin-arm64": "4.63.5", + "@rollup/rollup-darwin-x64": "4.63.5", + "@rollup/rollup-freebsd-arm64": "4.63.5", + "@rollup/rollup-freebsd-x64": "4.63.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.5", + "@rollup/rollup-linux-arm-musleabihf": "4.63.5", + "@rollup/rollup-linux-arm64-gnu": "4.63.5", + "@rollup/rollup-linux-arm64-musl": "4.63.5", + "@rollup/rollup-linux-loong64-gnu": "4.63.5", + "@rollup/rollup-linux-loong64-musl": "4.63.5", + "@rollup/rollup-linux-ppc64-gnu": "4.63.5", + "@rollup/rollup-linux-ppc64-musl": "4.63.5", + "@rollup/rollup-linux-riscv64-gnu": "4.63.5", + "@rollup/rollup-linux-riscv64-musl": "4.63.5", + "@rollup/rollup-linux-s390x-gnu": "4.63.5", + "@rollup/rollup-linux-x64-gnu": "4.63.5", + "@rollup/rollup-linux-x64-musl": "4.63.5", + "@rollup/rollup-openbsd-x64": "4.63.5", + "@rollup/rollup-openharmony-arm64": "4.63.5", + "@rollup/rollup-win32-arm64-msvc": "4.63.5", + "@rollup/rollup-win32-ia32-msvc": "4.63.5", + "@rollup/rollup-win32-x64-gnu": "4.63.5", + "@rollup/rollup-win32-x64-msvc": "4.63.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svgo": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.1.0.tgz", + "integrity": "sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^6.0.0", + "css-tree": "^3.0.1", + "css-what": "^7.0.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "1.6.1" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/terra-draw": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/terra-draw/-/terra-draw-1.35.0.tgz", + "integrity": "sha512-uQI8lcPiUMqwVV29CexTfvT/AM4lGHtA+xWraW4njVExiM1+eGvRetYOBXmXEmgy5pL4X2dmjwMRH8JEYVtbgw==", + "license": "MIT" + }, + "node_modules/terra-draw-leaflet-adapter": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/terra-draw-leaflet-adapter/-/terra-draw-leaflet-adapter-1.0.3.tgz", + "integrity": "sha512-yYUDwyKxGj69pUzd4L2YclXwAxNhuXI01DCm8KbOtGxnf4I+PmFsAQrwA/sU5EbdaOj1AaPrPqadihgFvrSOng==", + "license": "MIT", + "peerDependencies": { + "leaflet": "^1.9.4", + "terra-draw": "^1.0.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/turndown": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.4.tgz", + "integrity": "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ==", + "license": "MIT", + "dependencies": { + "@mixmark-io/domino": "^2.2.0" + }, + "engines": { + "node": ">=18", + "npm": ">=9" + } + }, "node_modules/ulid": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.4.0.tgz", @@ -211,6 +1590,81 @@ "bin": { "ulid": "bin/cli.js" } + }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index ba65e9954..6a35e5a42 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "main": "index.html", "scripts": { + "build:admin": "vite build", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -21,11 +22,13 @@ "@fontsource/sansita": "^5.3.0", "@material-symbols/svg-700": "^0.47.5", "@panzoom/panzoom": "^4.6.2", - "@sveltia/cms": "^0.220.0", - "pagefind": "^1.5.2" + "@sveltia/cms": "^0.221.1", + "pagefind": "^1.5.2", + "react": "19.3.0" }, "devDependencies": { "prettier": "^3.9.9", - "prettier-plugin-go-template": "^0.0.15" + "prettier-plugin-go-template": "^0.0.15", + "vite": "7.3.6" } } diff --git a/static/admin/fields.js b/static/admin/fields.js deleted file mode 100644 index df880e5ae..000000000 --- a/static/admin/fields.js +++ /dev/null @@ -1,116 +0,0 @@ -(function () { - var SelectControl = CMS.getFieldType("select").control; - - function FipValiditySelectControl(props) { - var groups = props.entry ? props.entry.getIn(["data", "groups"]) : null; - groups = groups && typeof groups.toJS === "function" ? groups.toJS() : []; - var groupType = props.field ? props.field.get("group_type") : null; - if (groupType) { - groups = groups.filter(function (group) { - return !group.type || group.type === groupType; - }); - } - var options = groups - .filter(function (group) { - return !!group.name; - }) - .map(function (group) { - return { label: group.name, value: group.name }; - }); - - return h(SelectControl, { - field: { name: props.field.get("name"), options }, - value: props.value, - forID: props.forID, - onChange: props.onChange, - }); - } - - CMS.registerFieldType( - "fip-validity-select", - FipValiditySelectControl, - FipValiditySelectControl, - ); - - function CmsEditLinkControl(props) { - var field = props.field; - var getValue = field.get("get_value"); - var hrefTemplate = field.get("href"); - var labelTemplate = field.get("label_template"); - var emptyHint = field.get("empty_hint"); - var isStatic = field.get("static"); - - var value = isStatic ? "" : String(getValue(props) || ""); - - if (!isStatic && !value) { - return h("p", { className: "o-cms-edit-link__info" }, emptyHint); - } - - var href = hrefTemplate.replace(/\{value\}/g, value); - var label = labelTemplate.replace(/\{value\}/g, value); - return h( - "p", - { className: "o-cms-edit-link__info" }, - h( - "a", - { - href: href, - onClick: function (event) { - event.preventDefault(); - if ( - event.metaKey || - event.ctrlKey || - event.shiftKey || - event.button === 1 - ) { - window.open(href, "_blank"); - return; - } - window.location.hash = href; - }, - onAuxClick: function (event) { - event.preventDefault(); - if (event.button === 1) { - window.open(href, "_blank"); - } - }, - }, - label, - ), - ); - } - - CMS.registerFieldType( - "cms-edit-link", - CmsEditLinkControl, - CmsEditLinkControl, - ); - - var NumberControl = CMS.getFieldType("number").control; - - function CmsNumberWithUsageControl(props) { - var field = props.field; - var template = field.get("template"); - var value = props.value; - - var text = template.replace(/\{value\}/g, String(value ?? "")); - - return h( - "div", - {}, - h(NumberControl, { - field: { name: field.get("name") }, - value: value, - forID: props.forID, - onChange: props.onChange, - }), - h("p", { className: "o-cms-display__info" }, text), - ); - } - - CMS.registerFieldType( - "cms-number-with-usage", - CmsNumberWithUsageControl, - CmsNumberWithUsageControl, - ); -})(); diff --git a/static/admin/index.html b/static/admin/index.html index 7bf579281..263995a54 100644 --- a/static/admin/index.html +++ b/static/admin/index.html @@ -4,11 +4,9 @@ FIP Guide Content Manager - + + - - - diff --git a/static/admin/shortcodes.js b/static/admin/shortcodes.js deleted file mode 100644 index 77939f7c9..000000000 --- a/static/admin/shortcodes.js +++ /dev/null @@ -1,1086 +0,0 @@ -(function () { - function parseHugoParams(str) { - var result = {}; - var re = /([\w.]+)=(?:"([^"]*)"|(\S+))/g; - var m; - while ((m = re.exec(str)) !== null) { - var val = m[2] !== undefined ? m[2] : m[3]; - if (val === "true") val = true; - else if (val === "false") val = false; - result[m[1]] = val; - } - return result; - } - - function shortcodePattern(name, bracket) { - var open = bracket || "[%<]"; - var close = bracket === "%" ? "%" : bracket === "<" ? ">" : "[%>]"; - return new RegExp( - "\\{\\{" + - open + - " " + - name + - "(?![\\w-])([\\s\\S]*?)" + - close + - "\\}\\}([\\s\\S]*?)\\{\\{" + - open + - " \\/" + - name + - " " + - close + - "\\}\\}", - ); - } - - function selfClosingPattern(name) { - return new RegExp( - "\\{\\{[%<] " + name + "(?![\\w-])([\\s\\S]*?)[%>/]\\}\\}", - ); - } - - function dualFormPattern(name) { - return new RegExp( - "\\{\\{[%<] " + - name + - "(?![\\w-])([\\s\\S]*?)(?:\\/[%>]\\}\\}|[%>]\\}\\}([\\s\\S]*?)\\{\\{[%<] \\/" + - name + - " [%>]\\}\\})", - ); - } - - function renderParamList(fields, data) { - var out = []; - (fields || []).forEach(function (field) { - var spec = field.param; - if (!spec) return; - var value = data[field.name]; - if (spec.transform) value = spec.transform(value, data); - if (!spec.required && !value) return; - if (spec.positional) { - out.push(spec.quote ? '"' + value + '"' : String(value)); - return; - } - var paramName = spec.name || field.name; - out.push( - paramName + "=" + (spec.bare ? String(value) : '"' + value + '"'), - ); - }); - return out; - } - - function renderInlineOpen(bracket, name, params, closer) { - var paramsStr = params.join(" "); - var close = - closer === "slash" - ? bracket === "%" - ? "/%}}" - : "/>}}" - : bracket === "%" - ? "%}}" - : ">}}"; - return ( - "{{" + - bracket + - " " + - name + - (paramsStr ? " " + paramsStr : "") + - " " + - close - ); - } - - function renderMultilineOpen(bracket, name, params, opts) { - opts = opts || {}; - var pad = " "; - var headParams = opts.firstInline ? params.slice(0, 1) : []; - var restParams = opts.firstInline ? params.slice(1) : params; - var close = - opts.closer === "slash" - ? bracket === "%" - ? "/%}}" - : "/>}}" - : bracket === "%" - ? "%}}" - : ">}}"; - var header = - "{{" + - bracket + - " " + - name + - (headParams.length ? " " + headParams.join(" ") : ""); - var lines = [header].concat( - restParams.map(function (p) { - return pad + p; - }), - ); - return lines.join("\n") + "\n" + close; - } - - function makeToBlock(name, cfg) { - var bracket = cfg.bracket || "%"; - var bodyMode = cfg.bodyMode || "none"; - var bodySep = cfg.bodySeparator || "\n"; - var closeTag = - "{{" + bracket + " /" + name + " " + (bracket === "%" ? "%}}" : ">}}"); - - function openTag(params, closer) { - var wrap = cfg.multiline && params.length > (cfg.multilineThreshold || 0); - if (wrap) { - return renderMultilineOpen(bracket, name, params, { - firstInline: cfg.firstInline, - closer: closer, - }); - } - return renderInlineOpen(bracket, name, params, closer); - } - - return function (data) { - var params = renderParamList(cfg.fields, data); - - if (bodyMode === "none") { - return openTag(params, "plain"); - } - - var body = String(data[cfg.bodyField || "body"] || ""); - - if (bodyMode === "dual") { - if (!body.trim()) return openTag(params, "slash"); - return openTag(params, "plain") + bodySep + body + bodySep + closeTag; - } - - var open = openTag(params, "plain"); - - if (bodyMode === "optionalPaired") { - return open + "\n" + (body ? body + "\n" : "") + closeTag; - } - - return open + bodySep + body + bodySep + closeTag; - }; - } - - var highlightFields = [ - { - name: "type", - label: "Type", - widget: "select", - options: ["important", "tip", "confusion", "inofficial"], - param: { positional: true, required: true }, - }, - { - name: "body", - label: "Content", - widget: "markdown", - editor_components: ["button", "float-image", "highlight", "image"], - }, - ]; - - CMS.registerEditorComponent({ - id: "highlight", - label: "Highlight", - fields: highlightFields, - pattern: shortcodePattern("highlight", "%"), - fromBlock: function (match) { - return { type: match[1].trim(), body: match[2].trim() }; - }, - toBlock: makeToBlock("highlight", { - fields: highlightFields, - bodyMode: "required", - bodySeparator: "\n", - }), - toPreview: function () { - return ""; - }, - }); - - CMS.registerEditorComponent({ - id: "highlight-raw", - label: "Highlight", - fields: highlightFields, - pattern: shortcodePattern("highlight", "<"), - fromBlock: function (match) { - return { type: match[1].trim(), body: match[2].trim() }; - }, - toBlock: makeToBlock("highlight", { - bracket: "<", - fields: highlightFields, - bodyMode: "required", - bodySeparator: "\n", - }), - toPreview: function () { - return ""; - }, - }); - - var expanderFields = [ - { - name: "title", - label: "Title", - widget: "string", - param: { positional: true, quote: true, required: true }, - }, - { - name: "variant", - label: "Variant", - widget: "select", - options: [{ label: "None", value: "" }, "border", "info"], - required: false, - param: { positional: true }, - }, - { - name: "body", - label: "Content", - widget: "markdown", - editor_components: ["button", "float-image", "highlight-raw", "image"], - }, - ]; - - CMS.registerEditorComponent({ - id: "expander", - label: "Expander", - fields: expanderFields, - pattern: shortcodePattern("expander"), - fromBlock: function (match) { - var titleMatch = match[1].match(/"([^"]*)"/); - var rest = match[1].replace(/"[^"]*"/, "").trim(); - var variantMatch = rest.match(/^(\w+)/); - return { - title: titleMatch ? titleMatch[1] : "", - variant: variantMatch ? variantMatch[1] : "", - body: match[2].trim(), - }; - }, - toBlock: makeToBlock("expander", { - fields: expanderFields, - bodyMode: "required", - bodySeparator: "\n\n", - }), - toPreview: function () { - return ""; - }, - }); - - var fipValidityFields = [ - { - name: "type", - label: "Type", - widget: "select", - options: [ - "fip-coupon", - "fip-reduced-ticket", - "fip-global-fare", - "additional", - ], - param: { required: true }, - }, - { - name: "status", - label: "Status", - widget: "select", - options: ["valid", "invalid", "unknown"], - param: { required: true }, - }, - { - name: "subtitle", - label: "Subtitle", - widget: "string", - required: false, - param: {}, - }, - { - name: "text", - label: "Additional Text", - widget: "string", - required: false, - param: {}, - }, - { - name: "disable_dialog", - label: "Disable info dialog", - widget: "boolean", - default: false, - required: false, - param: { bare: true }, - }, - ]; - - CMS.registerEditorComponent({ - id: "fip-validity", - label: "FIP Validity Badge", - fields: fipValidityFields, - pattern: selfClosingPattern("fip-validity"), - fromBlock: function (match) { - var p = parseHugoParams(match[1]); - return { - type: p.type || "fip-coupon", - status: p.status || "valid", - subtitle: p.subtitle || "", - text: p.text || "", - disable_dialog: p.disable_dialog === true, - }; - }, - toBlock: makeToBlock("fip-validity", { - bracket: "<", - fields: fipValidityFields, - bodyMode: "none", - }), - toPreview: function () { - return ""; - }, - }); - - var trainCategoryFields = [ - { - name: "id", - label: "ID (anchor)", - widget: "string", - param: { required: true }, - }, - { - name: "title", - label: "Title", - widget: "string", - param: { required: true }, - }, - { - name: "type", - label: "Type", - widget: "select", - options: [ - "highspeed", - "regional", - "subway", - "sleeper", - "funicular", - "bus", - "ship", - "tram", - ], - param: { required: true }, - }, - { - name: "fip_accepted", - label: "FIP accepted", - widget: "select", - options: [ - { label: "No", value: "false" }, - { label: "Yes", value: "true" }, - { label: "Partially", value: "partially" }, - { label: "Unknown", value: "unknown" }, - ], - default: "true", - param: { bare: true, required: true }, - }, - { - name: "reservation_required", - label: "Reservation required", - widget: "select", - options: [ - { label: "No", value: "false" }, - { label: "Yes", value: "true" }, - { label: "Partially", value: "partially" }, - ], - default: "false", - param: { bare: true, required: true }, - }, - { - name: "reservation_possible", - label: "Reservation possible", - widget: "select", - options: [ - { label: "No", value: "false" }, - { label: "Yes", value: "true" }, - { label: "Partially", value: "partially" }, - ], - default: "false", - param: { bare: true, required: true }, - }, - { - name: "route_overview_url", - label: "Route overview URL", - widget: "string", - required: false, - param: {}, - }, - { - name: "additional_information_url", - label: "Additional information URL", - widget: "string", - required: false, - param: {}, - }, - { - name: "body", - label: "Description", - widget: "markdown", - editor_components: ["button", "float-image", "highlight-raw", "image"], - }, - ]; - - CMS.registerEditorComponent({ - id: "train-category", - label: "Train Category", - fields: trainCategoryFields, - pattern: shortcodePattern("train-category"), - fromBlock: function (match) { - var p = parseHugoParams(match[1]); - return { - id: String(p.id || ""), - title: String(p.title || ""), - type: String(p.type || "regional"), - fip_accepted: String(p.fip_accepted ?? true), - reservation_required: String(p.reservation_required || "false"), - reservation_possible: String(p.reservation_possible || "false"), - route_overview_url: String(p.route_overview_url || ""), - additional_information_url: String(p.additional_information_url || ""), - body: match[2].trim(), - }; - }, - toBlock: makeToBlock("train-category", { - fields: trainCategoryFields, - bodyMode: "required", - bodySeparator: "\n\n", - multiline: true, - }), - toPreview: function () { - return ""; - }, - }); - - var bookingFields = [ - { - name: "id", - label: "Booking Platform", - widget: "relation", - collection: "booking", - search_fields: ["title"], - value_field: "{{slug}}", - display_fields: ["title"], - param: { - required: true, - transform: function (v) { - return String(v || "").replace(/\/index$/, ""); - }, - }, - }, - { - name: "booking_data_link", - label: "Booking Platform Link", - widget: "cms-edit-link", - static: true, - href: "#/collections/booking", - label_template: "Manage Booking Platforms \u2192", - }, - { - name: "subtitle", - label: "Subtitle", - widget: "string", - required: false, - param: {}, - }, - { - name: "classes_first", - label: "1st class reservation costs (overwrite)", - widget: "string", - required: false, - param: { name: "classes.first" }, - }, - { - name: "classes_second", - label: "2nd class reservation costs (overwrite)", - widget: "string", - required: false, - param: { name: "classes.second" }, - }, - { - name: "fip_50", - label: "FIP 50 (override)", - widget: "select", - required: false, - options: [ - { label: "Default", value: "" }, - { label: "Hide", value: "nil" }, - { label: "Yes", value: "true" }, - { label: "No", value: "false" }, - ], - default: "", - param: { bare: true }, - }, - { - name: "fip_75", - label: "FIP 75 (override)", - widget: "select", - required: false, - options: [ - { label: "Default", value: "" }, - { label: "Hide", value: "nil" }, - { label: "Yes", value: "true" }, - { label: "No", value: "false" }, - ], - default: "", - param: { bare: true }, - }, - { - name: "fip_global_fare", - label: "FIP Global Fare (override)", - widget: "select", - required: false, - options: [ - { label: "Default", value: "" }, - { label: "Hide", value: "nil" }, - { label: "Yes", value: "true" }, - { label: "No", value: "false" }, - ], - default: "", - param: { bare: true }, - }, - { - name: "reservations", - label: "Reservations (override)", - widget: "select", - required: false, - options: [ - { label: "Default", value: "" }, - { label: "Hide", value: "nil" }, - { label: "Yes", value: "true" }, - { label: "No", value: "false" }, - ], - default: "", - param: { bare: true }, - }, - { - name: "fee", - label: "Fee (override)", - widget: "string", - required: false, - hint: 'Overwrites the booking fee text, e.g. "5 %". Leave empty to use the booking platform default.', - param: {}, - }, - { - name: "body", - label: "Additional info", - widget: "markdown", - required: false, - editor_components: ["button", "float-image", "image"], - }, - ]; - - CMS.registerEditorComponent({ - id: "booking", - label: "Booking", - fields: bookingFields, - pattern: dualFormPattern("booking"), - fromBlock: function (match) { - var p = parseHugoParams(match[1]); - var rawId = p.id ? String(p.id) : ""; - return { - id: rawId, - booking_data_link: rawId, - subtitle: String(p.subtitle || ""), - classes_first: String(p["classes.first"] || ""), - classes_second: String(p["classes.second"] || ""), - fip_50: String(p.fip_50 ?? ""), - fip_75: String(p.fip_75 ?? ""), - fip_global_fare: String(p.fip_global_fare ?? ""), - reservations: String(p.reservations ?? ""), - fee: String(p.fee || ""), - body: match[2] ? match[2].trim() : "", - }; - }, - toBlock: makeToBlock("booking", { - fields: bookingFields, - bodyMode: "dual", - bodySeparator: "\n", - multiline: true, - firstInline: true, - multilineThreshold: 1, - }), - toPreview: function () { - return ""; - }, - }); - - var bookingSectionFields = [ - { - name: "section", - label: "Section", - widget: "select", - options: [ - { label: "FIP 50", value: "fip_50" }, - { label: "FIP 75", value: "fip_75" }, - { label: "FIP Global Fare", value: "fip_global_fare" }, - { label: "Reservations", value: "reservations" }, - ], - param: { positional: true, quote: true, required: true }, - }, - { - name: "body", - label: "Content", - widget: "markdown", - editor_components: ["button", "float-image", "highlight-raw", "image"], - }, - ]; - - CMS.registerEditorComponent({ - id: "booking-section", - label: "Booking Section", - fields: bookingSectionFields, - pattern: shortcodePattern("booking-section"), - fromBlock: function (match) { - var sectionMatch = match[1].match(/"(\w+)"/); - return { - section: sectionMatch ? sectionMatch[1] : "", - body: match[2].trim(), - }; - }, - toBlock: makeToBlock("booking-section", { - fields: bookingSectionFields, - bodyMode: "required", - bodySeparator: "\n", - }), - toPreview: function () { - return ""; - }, - }); - - var buttonFields = [ - { - name: "destination", - label: "URL", - widget: "string", - param: { required: true }, - }, - { - name: "text", - label: "Button text", - widget: "string", - param: { required: true }, - }, - ]; - - CMS.registerEditorComponent({ - id: "button", - label: "Button", - fields: buttonFields, - pattern: selfClosingPattern("button"), - fromBlock: function (match) { - var p = parseHugoParams(match[1]); - return { - destination: String(p.destination || ""), - text: String(p.text || ""), - }; - }, - toBlock: makeToBlock("button", { - bracket: "<", - fields: buttonFields, - bodyMode: "none", - }), - toPreview: function () { - return ""; - }, - }); - - var identifyOperatorFields = [ - { - name: "sources", - label: "Sources", - widget: "relation", - collection: "identify-operator", - search_fields: ["title"], - value_field: "{{slug}}", - display_fields: ["title"], - multiple: true, - required: false, - param: { - transform: function (value) { - var arr = Array.isArray(value) ? value : value ? [value] : []; - return arr - .map(function (s) { - return String(s).replace(/\/index$/, ""); - }) - .join(","); - }, - }, - }, - { - name: "body", - label: "Additional info", - widget: "markdown", - required: false, - editor_components: ["button", "float-image", "highlight", "image"], - }, - ]; - - CMS.registerEditorComponent({ - id: "identify-operator", - label: "Identify Operator", - fields: identifyOperatorFields, - pattern: dualFormPattern("identify-operator"), - fromBlock: function (match) { - var p = parseHugoParams(match[1] || ""); - return { - sources: p.sources - ? p.sources.split(",").map(function (s) { - return s.trim(); - }) - : [], - body: match[2] ? match[2].trim() : "", - }; - }, - toBlock: makeToBlock("identify-operator", { - bracket: "<", - fields: identifyOperatorFields, - bodyMode: "dual", - bodySeparator: "\n", - }), - toPreview: function () { - return ""; - }, - }); - - var satelliteFields = [ - { - name: "body", - label: "Additional info", - widget: "markdown", - required: false, - editor_components: ["button", "float-image", "image"], - }, - ]; - - CMS.registerEditorComponent({ - id: "satellite", - label: "Satellite Notice", - fields: satelliteFields, - pattern: dualFormPattern("satellite"), - fromBlock: function (match) { - return { body: match[2] ? match[2].trim() : "" }; - }, - toBlock: makeToBlock("satellite", { - fields: satelliteFields, - bodyMode: "dual", - bodySeparator: "\n", - }), - toPreview: function () { - return ""; - }, - }); - - var dialogFields = [ - { - name: "id", - label: "Dialog ID (anchor)", - widget: "string", - param: { required: true }, - }, - { - name: "title", - label: "Title", - widget: "string", - param: { required: true }, - }, - { - name: "body", - label: "Content", - widget: "markdown", - editor_components: ["button", "float-image", "highlight", "image"], - }, - ]; - - CMS.registerEditorComponent({ - id: "dialog", - label: "Dialog", - fields: dialogFields, - pattern: shortcodePattern("dialog"), - fromBlock: function (match) { - var p = parseHugoParams(match[1]); - return { - id: String(p.id || ""), - title: String(p.title || ""), - body: match[2].trim(), - }; - }, - toBlock: makeToBlock("dialog", { - fields: dialogFields, - bodyMode: "required", - bodySeparator: "\n", - multiline: true, - }), - toPreview: function () { - return ""; - }, - }); - - var floatImageFields = [ - { name: "src", label: "Image", widget: "image", param: { required: true } }, - { - name: "alt", - label: "Alt text", - widget: "string", - param: { required: true }, - }, - { - name: "caption", - label: "Caption", - widget: "string", - required: false, - param: {}, - }, - { - name: "width", - label: "Width (CSS)", - widget: "string", - default: "50%", - required: false, - param: {}, - }, - { - name: "position", - label: "Position", - widget: "select", - options: ["right", "left"], - default: "right", - param: {}, - }, - { - name: "body", - label: "Surrounding text", - widget: "markdown", - required: false, - editor_components: ["button", "image"], - }, - ]; - - CMS.registerEditorComponent({ - id: "float-image", - label: "Float Image", - fields: floatImageFields, - pattern: shortcodePattern("float-image"), - fromBlock: function (match) { - var p = parseHugoParams(match[1]); - return { - src: String(p.src || ""), - alt: String(p.alt || ""), - caption: String(p.caption || ""), - width: String(p.width || "50%"), - position: String(p.position || "right"), - body: match[2].trim(), - }; - }, - toBlock: makeToBlock("float-image", { - fields: floatImageFields, - bodyMode: "optionalPaired", - multiline: true, - }), - toPreview: function () { - return ""; - }, - }); - - CMS.registerEditorComponent({ - id: "wip", - label: "Work in Progress", - fields: [], - pattern: selfClosingPattern("wip"), - fromBlock: function () { - return {}; - }, - toBlock: makeToBlock("wip", { bracket: "<", fields: [], bodyMode: "none" }), - toPreview: function () { - return ""; - }, - }); - - var updateFields = [ - { - name: "date", - label: "Date", - widget: "datetime", - date_format: "YYYY-MM-DD", - time_format: false, - param: { required: true }, - }, - { - name: "body", - label: "Content", - widget: "markdown", - editor_components: ["button", "float-image", "highlight", "image"], - }, - ]; - - CMS.registerEditorComponent({ - id: "update", - label: "Update", - fields: updateFields, - pattern: shortcodePattern("update"), - fromBlock: function (match) { - var p = parseHugoParams(match[1]); - return { date: String(p.date || ""), body: match[2].trim() }; - }, - toBlock: makeToBlock("update", { - fields: updateFields, - bodyMode: "required", - bodySeparator: "\n", - }), - toPreview: function () { - return ""; - }, - }); - - var columnFields = [ - { - name: "width", - label: "Width (CSS)", - widget: "string", - param: { required: true }, - }, - { - name: "body", - label: "Content", - widget: "markdown", - editor_components: ["button", "float-image", "highlight", "image"], - }, - ]; - - CMS.registerEditorComponent({ - id: "column", - label: "Column", - fields: columnFields, - pattern: shortcodePattern("column"), - fromBlock: function (match) { - var p = parseHugoParams(match[1]); - return { width: String(p.width || ""), body: match[2].trim() }; - }, - toBlock: makeToBlock("column", { - fields: columnFields, - bodyMode: "required", - bodySeparator: "\n", - }), - toPreview: function () { - return ""; - }, - }); - - var columnsFields = [ - { - name: "body", - label: "Content", - widget: "markdown", - editor_components: ["column"], - }, - ]; - - CMS.registerEditorComponent({ - id: "columns", - label: "Columns", - fields: columnsFields, - pattern: shortcodePattern("columns"), - fromBlock: function (match) { - return { body: match[2].trim() }; - }, - toBlock: makeToBlock("columns", { - fields: columnsFields, - bodyMode: "required", - bodySeparator: "\n", - }), - toPreview: function () { - return ""; - }, - }); - - var fipValidityTableFields = [ - { - name: "type", - label: "Type", - widget: "select", - options: ["fip-coupon", "fip-reduced-ticket"], - param: { required: true }, - }, - { - name: "validity_data_link", - label: "FIP Validity Table", - widget: "cms-edit-link", - get_value: function () { - var match = window.location.hash.match( - /#\/collections\/application\/entries\/([^/]+)/, - ); - return match ? match[1] : ""; - }, - href: "#/collections/fip-validity/entries/{value}/validity", - label_template: - "Edit the FIP Validity Table for \u201c{value}\u201d \u2192", - empty_hint: "Save this page first to link its FIP Validity Table.", - }, - ]; - - var footnoteFields = [ - { - name: "id", - label: "Number", - widget: "cms-number-with-usage", - template: - "You can use this footnote by writing out [^{value}] in the text editor.", - param: { required: true }, - }, - { - name: "name", - label: "Source Name", - widget: "string", - param: { required: true }, - }, - { - name: "link", - label: "Link", - widget: "string", - param: { required: true }, - }, - ]; - - CMS.registerEditorComponent({ - id: "footnote", - label: "Footnote", - fields: footnoteFields, - pattern: /^\[\^([^\]]+)\]: \[([^\]]*)\]\(([^)]*)\)$/, - fromBlock: function (match) { - var num = Number(match[1]); - var id = Number.isNaN(num) ? match[1] : num; - return { - id: id, - name: String(match[2] || ""), - link: String(match[3] || ""), - }; - }, - toBlock: function (data) { - return ( - "[^" + - (data.id ?? "") + - "]: [" + - (data.name || "") + - "](" + - (data.link || "") + - ")" - ); - }, - toPreview: function () { - return ""; - }, - }); - - CMS.registerEditorComponent({ - id: "fip-validity-table", - label: "FIP Validity Table", - fields: fipValidityTableFields, - pattern: selfClosingPattern("fip-validity-table"), - fromBlock: function (match) { - var p = parseHugoParams(match[1]); - return { type: p.type || "fip-coupon" }; - }, - toBlock: makeToBlock("fip-validity-table", { - bracket: "<", - fields: fipValidityTableFields, - bodyMode: "none", - }), - toPreview: function () { - return ""; - }, - }); -})(); diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 000000000..c1c956cc6 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from "vite"; +import { resolve } from "path"; + +export default defineConfig({ + root: "assets/admin", + base: "./", + build: { + outDir: resolve(__dirname, "static/admin"), + emptyOutDir: true, + }, +});