@at-flux/astro-feature-flags 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -31
- package/dist/dev-toolbar-app.d.mts +0 -1
- package/dist/dev-toolbar-app.d.mts.map +1 -1
- package/dist/dev-toolbar-app.mjs +1 -1
- package/dist/dev-toolbar-app.mjs.map +1 -1
- package/dist/{dev-toolbar-flag-icon-BHCQJ53N.mjs → dev-toolbar-flag-icon-MQsIFekv.mjs} +1 -1
- package/dist/{dev-toolbar-flag-icon-BHCQJ53N.mjs.map → dev-toolbar-flag-icon-MQsIFekv.mjs.map} +1 -1
- package/dist/index.d.mts +74 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +204 -18
- package/dist/index.mjs.map +1 -1
- package/dist/runtime.d.mts +4 -11
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs.map +1 -1
- package/docs/how-to/hide-from-sitemaps.md +56 -10
- package/package.json +7 -7
- package/src/dev-head-inject.ts +2 -0
- package/src/dev-inline-runtimes.ts +77 -15
- package/src/dev-outline-css.ts +48 -7
- package/src/dev-toolbar-app.ts +2 -4
- package/src/index.ts +60 -11
- package/src/inline-script.ts +3 -1
- package/src/runtime.ts +18 -11
- package/src/sitemap-prune.ts +171 -0
package/README.md
CHANGED
|
@@ -24,10 +24,10 @@ Feature flags for Astro with a declarative config:
|
|
|
24
24
|
|
|
25
25
|
## Terminology (short)
|
|
26
26
|
|
|
27
|
-
| Term
|
|
28
|
-
|
|
|
29
|
-
| **Astro dev**
|
|
30
|
-
| **Non-dev build**
|
|
27
|
+
| Term | Meaning |
|
|
28
|
+
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
29
|
+
| **Astro dev** | Local `astro dev` — the built-in dev layer is active; all flags on at resolve time; dev toolbar only affects the browser. |
|
|
30
|
+
| **Non-dev build** | `astro build` / preview / deploy with a shipped layer (`prod`, `staging`, …): `environments.<key>.flags` drives SSR, HTML culling, and route pruning. |
|
|
31
31
|
| **`isAstroDev`** (virtual module) | `import.meta.env.DEV` — Vite’s compile-time flag for components. Layer selection still comes from `environments` / `forceEnvironment`; use `isAstroDev` when you need Astro’s literal dev detection. |
|
|
32
32
|
|
|
33
33
|
You cannot add an environment key named **`dev`** — that name is reserved and merged automatically (including its `when`; you do not set it in config).
|
|
@@ -180,18 +180,19 @@ The dev toolbar changes client-side preview state only.
|
|
|
180
180
|
|
|
181
181
|
### Top-level options
|
|
182
182
|
|
|
183
|
-
| Option | Type
|
|
184
|
-
| ------------------ |
|
|
185
|
-
| `configRoot` | `string`
|
|
186
|
-
| `jsonConfigPath` | `string`
|
|
187
|
-
| `forceEnvironment` | `string`
|
|
188
|
-
| `mode` | `string`
|
|
189
|
-
| `env` | `Record<string, string \| undefined>` | `process.env`
|
|
190
|
-
| `tokenNamespace` | `string`
|
|
191
|
-
| `flags` | `Record<string, FlagConfig>`
|
|
192
|
-
| `environments` | `Record<string, EnvironmentConfig>`
|
|
193
|
-
| `css` | `DevOutlineCssOptions`
|
|
194
|
-
| `staticMinify` | `boolean`
|
|
183
|
+
| Option | Type | Default | Notes |
|
|
184
|
+
| ------------------ | ------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
185
|
+
| `configRoot` | `string` | `process.cwd()` | Resolves relative `jsonConfigPath` values (root + per-environment). |
|
|
186
|
+
| `jsonConfigPath` | `string` | unset | Optional **root** JSON file merged after inline config (see merge order for per-environment files). |
|
|
187
|
+
| `forceEnvironment` | `string` | unset | Pin the active layer (skips `when` / `AFF_ENVIRONMENT` validation). |
|
|
188
|
+
| `mode` | `string` | `NODE_ENV` | Optional advanced override for runtime resolution (mainly tests/tooling). Most apps should omit this and rely on `NODE_ENV` + `environments.when`. |
|
|
189
|
+
| `env` | `Record<string, string \| undefined>` | `process.env` | `AFF_FEATURE_*` / `ASTRO_FEATURE_FLAGS` (not applied in `dev` layer). |
|
|
190
|
+
| `tokenNamespace` | `string` | `'ff'` | CSS var namespace (`--ff-c-*`). |
|
|
191
|
+
| `flags` | `Record<string, FlagConfig>` | `{}` | Flag declarations. |
|
|
192
|
+
| `environments` | `Record<string, EnvironmentConfig>` | _(see below)_ | Declare non-`dev` layers only; reserved `dev` is injected. At least one other key; exactly one `when: true` unless forced. |
|
|
193
|
+
| `css` | `DevOutlineCssOptions` | defaults | Global badge/outline layout and styling. |
|
|
194
|
+
| `staticMinify` | `boolean` | `true` | For static builds: route-prune disabled prefixes + cull gated HTML in `dist/`. Set `false` to keep emitted files untouched. |
|
|
195
|
+
| `pruneSitemap` | `boolean` | `true` | Drop pruned routes from generated sitemaps in `dist/`. Needs this integration **after** `sitemap()` in `integrations`. See [Hide flagged routes from sitemaps](docs/how-to/hide-from-sitemaps.md). |
|
|
195
196
|
|
|
196
197
|
If you omit `environments`, the integration injects a minimal reserved `dev` plus **`prod`** tied to `NODE_ENV` (or `mode` only if you explicitly override it) so `astroFeatureFlags()` still runs in small demos.
|
|
197
198
|
|
|
@@ -210,10 +211,10 @@ The key **`dev`** is reserved: do not list it under `environments`. The integrat
|
|
|
210
211
|
|
|
211
212
|
### `EnvironmentConfig`
|
|
212
213
|
|
|
213
|
-
| Field | Type | Default | Notes
|
|
214
|
-
| ---------------- | ------------------------- | ------- |
|
|
215
|
-
| `when` | `boolean` | unset | Exactly one environment must have `when: true` (unless forced).
|
|
216
|
-
| `flags` | `Record<string, boolean>` | `{}` | For non-`dev` layers: booleans per flag. Ignored for reserved `dev`.
|
|
214
|
+
| Field | Type | Default | Notes |
|
|
215
|
+
| ---------------- | ------------------------- | ------- | -------------------------------------------------------------------------------------------- |
|
|
216
|
+
| `when` | `boolean` | unset | Exactly one environment must have `when: true` (unless forced). |
|
|
217
|
+
| `flags` | `Record<string, boolean>` | `{}` | For non-`dev` layers: booleans per flag. Ignored for reserved `dev`. |
|
|
217
218
|
| `jsonConfigPath` | `string` | unset | Optional JSON merged when this environment is the active layer (not used on reserved `dev`). |
|
|
218
219
|
|
|
219
220
|
Merge order:
|
|
@@ -227,12 +228,12 @@ Layer select override: `AFF_ENVIRONMENT=prod`. **`forceEnvironment`** on the int
|
|
|
227
228
|
|
|
228
229
|
### DevOutlineCssOptions
|
|
229
230
|
|
|
230
|
-
| Field
|
|
231
|
-
|
|
|
232
|
-
| `elementBadgeHorizontalAlign`
|
|
233
|
-
| `elementBadgeHorizontalPercent`
|
|
234
|
-
| `elementBadgeVerticalShiftPercent`
|
|
235
|
-
| `elementBadgeVerticalAnchor`
|
|
231
|
+
| Field | Default | Meaning |
|
|
232
|
+
| ---------------------------------- | --------- | ----------------------------------------------------------------------------------------------- |
|
|
233
|
+
| `elementBadgeHorizontalAlign` | `'end'` | `'start'` \| `'center'` \| `'end'` — LTR: **`end`** = top-right. |
|
|
234
|
+
| `elementBadgeHorizontalPercent` | _(unset)_ | 0–100: horizontal anchor with pill centred (`translateX(-50%)`); overrides align. |
|
|
235
|
+
| `elementBadgeVerticalShiftPercent` | `80` | Vertical shift as **% of the pill height** (default keeps most of the label above the element). |
|
|
236
|
+
| `elementBadgeVerticalAnchor` | `'top'` | `'top'` or `'bottom'`. |
|
|
236
237
|
|
|
237
238
|
Per-element badge overrides (markup):
|
|
238
239
|
|
|
@@ -250,12 +251,12 @@ If your editor misses virtual module types, add one reference in `src/env.d.ts`.
|
|
|
250
251
|
|
|
251
252
|
## Dev toolbar
|
|
252
253
|
|
|
253
|
-
| Control | Effect
|
|
254
|
-
| ----------- |
|
|
254
|
+
| Control | Effect |
|
|
255
|
+
| ----------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
255
256
|
| **Enabled** | Off → hide nodes carrying that token in `data-ff` (including combined hosts/pages when any member token is Off). |
|
|
256
|
-
| **Outline** | Toggle outlines only (element and route frame). For combined hosts this is a non-layout-shifting overlay ring.
|
|
257
|
-
| **Badges** | Toggle badges only (element pills + route pill).
|
|
258
|
-
| **Colour** | `--<namespace>-c-<token>` (persisted).
|
|
257
|
+
| **Outline** | Toggle outlines only (element and route frame). For combined hosts this is a non-layout-shifting overlay ring. |
|
|
258
|
+
| **Badges** | Toggle badges only (element pills + route pill). |
|
|
259
|
+
| **Colour** | `--<namespace>-c-<token>` (persisted). |
|
|
259
260
|
|
|
260
261
|
## Virtual module
|
|
261
262
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-toolbar-app.d.mts","names":[],"sources":["../src/dev-toolbar-app.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev-toolbar-app.d.mts","names":[],"sources":["../src/dev-toolbar-app.ts"],"mappings":";;QASQ;YACI;IACR;MACE;MACA,SAAS;MACT;;;;cAmHA,kBAAkB"}
|
package/dist/dev-toolbar-app.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as DEV_TOOLBAR_FLAG_ICON_SVG } from "./dev-toolbar-flag-icon-
|
|
1
|
+
import { t as DEV_TOOLBAR_FLAG_ICON_SVG } from "./dev-toolbar-flag-icon-MQsIFekv.mjs";
|
|
2
2
|
import { defineToolbarApp } from "astro/toolbar";
|
|
3
3
|
import { closeOnOutsideClick, createWindowElement, synchronizePlacementOnUpdate } from "astro/runtime/client/dev-toolbar/apps/utils/window.js";
|
|
4
4
|
//#region src/dev-toolbar-app.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-toolbar-app.mjs","names":[],"sources":["../src/dev-toolbar-app.ts"],"sourcesContent":["import type { DevToolbarApp } from \"astro\";\nimport { defineToolbarApp } from \"astro/toolbar\";\nimport {\n closeOnOutsideClick,\n createWindowElement,\n synchronizePlacementOnUpdate,\n} from \"astro/runtime/client/dev-toolbar/apps/utils/window.js\";\nimport { DEV_TOOLBAR_FLAG_ICON_SVG } from \"./dev-toolbar-flag-icon\";\n\ndeclare global {\n interface Window {\n __AFF__?: {\n tokens: string[];\n colors?: Record<string, string>;\n namespace?: string;\n };\n }\n}\n\ntype AffState = {\n enabled: Record<string, boolean>;\n outline: Record<string, boolean>;\n badge: Record<string, boolean>;\n colors: Record<string, string>;\n};\n\nfunction toHexColor(raw: string | undefined, fallback: string): string {\n if (!raw) return fallback;\n const s = raw.trim();\n const hex7 = s.match(/^#([0-9a-fA-F]{6})$/);\n if (hex7) return `#${hex7[1]!.toLowerCase()}`;\n const rgb = s.match(/rgba?\\(\\s*([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)/);\n if (rgb) {\n const r = Math.min(255, Math.round(Number(rgb[1])));\n const g = Math.min(255, Math.round(Number(rgb[2])));\n const b = Math.min(255, Math.round(Number(rgb[3])));\n return `#${[r, g, b].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\")}`;\n }\n return fallback;\n}\n\nfunction readMeta(): {\n tokens: string[];\n colors: Record<string, string>;\n namespace: string;\n} {\n try {\n const raw = window.__AFF__;\n const tokens = raw && Array.isArray(raw.tokens) ? raw.tokens : [];\n const colors =\n raw?.colors && typeof raw.colors === \"object\" ? raw.colors : {};\n const namespace = raw?.namespace?.trim() ? raw.namespace.trim() : \"ff\";\n return { tokens, colors, namespace };\n } catch {\n return { tokens: [], colors: {}, namespace: \"ff\" };\n }\n}\n\nfunction storageKey(namespace: string): string {\n return `${namespace}.dev.v1`;\n}\n\nfunction loadState(tokens: string[], namespace: string): AffState {\n const next: AffState = {\n enabled: {},\n outline: {},\n badge: {},\n colors: {},\n };\n for (const t of tokens) {\n next.enabled[t] = true;\n next.outline[t] = true;\n next.badge[t] = true;\n }\n try {\n const raw =\n localStorage.getItem(storageKey(namespace)) ||\n localStorage.getItem(\"aff.dev.v1\");\n if (!raw) return next;\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const outline = (parsed.outline ?? parsed.chrome) as\n | Record<string, boolean>\n | undefined;\n const enabled = (parsed.enabled ?? parsed.render) as\n | Record<string, boolean>\n | undefined;\n const badge = parsed.badge as Record<string, boolean> | undefined;\n const colors = parsed.colors as Record<string, string> | undefined;\n if (outline) Object.assign(next.outline, outline);\n if (enabled) Object.assign(next.enabled, enabled);\n if (badge) Object.assign(next.badge, badge);\n if (colors) Object.assign(next.colors, colors);\n } catch {\n /* ignore */\n }\n return next;\n}\n\nfunction persist(state: AffState, namespace: string) {\n try {\n localStorage.setItem(storageKey(namespace), JSON.stringify(state));\n } catch {\n /* ignore */\n }\n}\n\nfunction applyDom(\n tokens: string[],\n state: AffState,\n defaults: Record<string, string>,\n namespace: string,\n) {\n const root = document.documentElement;\n for (const t of tokens) {\n if (state.outline[t] !== false)\n root.removeAttribute(`data-ff-outline-${t}`);\n else root.setAttribute(`data-ff-outline-${t}`, \"off\");\n\n if (state.enabled[t] !== false)\n root.removeAttribute(`data-ff-enabled-${t}`);\n else root.setAttribute(`data-ff-enabled-${t}`, \"off\");\n\n if (state.badge[t] !== false) root.removeAttribute(`data-ff-badge-${t}`);\n else root.setAttribute(`data-ff-badge-${t}`, \"off\");\n\n const col = state.colors[t] ?? defaults[t];\n if (col) root.style.setProperty(`--${namespace}-c-${t}`, col);\n else root.style.removeProperty(`--${namespace}-c-${t}`);\n }\n}\n\nconst affDevToolbarApp: DevToolbarApp = defineToolbarApp({\n init(canvas, eventTarget) {\n let state = loadState([], \"ff\");\n let tokens: string[] = [];\n let namespace = \"ff\";\n\n function mount() {\n canvas.querySelector(\"astro-dev-toolbar-window\")?.remove();\n const {\n tokens: nextTokens,\n colors: defaultColors,\n namespace: ns,\n } = readMeta();\n tokens = nextTokens;\n namespace = ns;\n state = loadState(tokens, namespace);\n applyDom(tokens, state, defaultColors, namespace);\n\n const windowElement = createWindowElement(`\n\t\t\t\t<style>\n\t\t\t\t\t:host astro-dev-toolbar-window {\n\t\t\t\t\t\theight: min(520px, 78vh);\n\t\t\t\t\t\toverflow-y: auto;\n\t\t\t\t\t\tcolor-scheme: dark;\n\t\t\t\t\t\t--color-muted: rgba(191, 193, 201, 1);\n\t\t\t\t\t\t--color-purple: rgba(224, 204, 250, 1);\n\t\t\t\t\t}\n\t\t\t\t\theader {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tjustify-content: space-between;\n\t\t\t\t\t\tgap: 12px;\n\t\t\t\t\t}\n\t\t\t\t\th1 {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tgap: 8px;\n\t\t\t\t\t\tfont-weight: 600;\n\t\t\t\t\t\tcolor: #fff;\n\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t\tfont-size: 22px;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag-toolbar-icon {\n\t\t\t\t\t\tdisplay: inline-flex;\n\t\t\t\t\t\twidth: 1em;\n\t\t\t\t\t\theight: 1em;\n\t\t\t\t\t\tflex-shrink: 0;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tjustify-content: center;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag-toolbar-icon svg {\n\t\t\t\t\t\twidth: 100%;\n\t\t\t\t\t\theight: 100%;\n\t\t\t\t\t\tdisplay: block;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-intro {\n\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t\tfont-size: 14px;\n\t\t\t\t\t\tline-height: 1.5rem;\n\t\t\t\t\t\tcolor: var(--color-muted);\n margin-bottom: 1rem;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-top {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\talign-items: flex-start;\n\t\t\t\t\t\tjustify-content: space-between;\n\t\t\t\t\t\tgap: 12px;\n\t\t\t\t\t\tmargin: 1rem 0;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-top-left {\n\t\t\t\t\t\tflex: 1;\n\t\t\t\t\t\tmin-width: 0;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-help {\n\t\t\t\t\t\tdisplay: grid;\n\t\t\t\t\t\tgrid-template-columns: minmax(7.5rem, auto) 1fr;\n\t\t\t\t\t\tgap: 0.45rem 0.75rem;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-help code,\n\t\t\t\t\t.aff-help span {\n\t\t\t\t\t\tfont-size: 12px;\n\t\t\t\t\t\tline-height: 1.35;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-help code {\n\t\t\t\t\t\tjustify-self: start;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-actions {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\tjustify-content: flex-end;\n\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t\tflex-shrink: 0;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-reset {\n\t\t\t\t\t\tborder: 1px solid #343841;\n\t\t\t\t\t\tborder-radius: 0.5rem;\n\t\t\t\t\t\tbackground: #24262d;\n\t\t\t\t\t\tcolor: #fff;\n\t\t\t\t\t\tpadding: 0.3rem 0.55rem;\n\t\t\t\t\t\tfont-size: 12px;\n\t\t\t\t\t\tcursor: pointer;\n\t\t\t\t\t}\n\t\t\t\t\thr {\n\t\t\t\t\t\tborder: 1px solid rgba(27, 30, 36, 1);\n\t\t\t\t\t\tmargin: 1em 0;\n\t\t\t\t\t}\n\t\t\t\t\t.setting-row {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\tjustify-content: space-between;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tgap: 12px;\n\t\t\t\t\t\tmargin: 0 0 8px;\n\t\t\t\t\t}\n\t\t\t\t\t.setting-row section { flex: 1; min-width: 0; max-width: 54%; }\n\t\t\t\t\th3.aff-row-title {\n\t\t\t\t\t\tfont-size: 12px;\n\t\t\t\t\t\tfont-weight: 600;\n\t\t\t\t\t\tcolor: #fff;\n\t\t\t\t\t\tletter-spacing: 0.05em;\n\t\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-radios {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\tgap: 8px;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tflex-shrink: 0;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-radios label {\n\t\t\t\t\t\tdisplay: inline-flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tgap: 4px;\n\t\t\t\t\t\tfont-size: 11px;\n\t\t\t\t\t\tcursor: pointer;\n\t\t\t\t\t\tcolor: var(--color-muted);\n\t\t\t\t\t\tletter-spacing: 0.04em;\n\t\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-radios input { accent-color: var(--color-purple); cursor: pointer; }\n\t\t\t\t\t.aff-flag {\n\t\t\t\t\t\tmargin: 0 0 12px;\n\t\t\t\t\t\tborder: 1px solid rgba(52, 56, 65, 1);\n\t\t\t\t\t\tborder-radius: 10px;\n\t\t\t\t\t\tpadding: 0 10px 10px;\n\t\t\t\t\t\tbackground: rgba(19, 21, 26, 0.55);\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag summary {\n\t\t\t\t\t\tlist-style: none;\n\t\t\t\t\t\tcursor: pointer;\n\t\t\t\t\t\tuser-select: none;\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tgap: 8px;\n\t\t\t\t\t\tpadding: 10px 0;\n\t\t\t\t\t\tfont-weight: 600;\n\t\t\t\t\t\tfont-size: 11px;\n\t\t\t\t\t\tletter-spacing: 0.06em;\n\t\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\t\tcolor: #fff;\n\t\t\t\t\t\tjustify-content: space-between;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag summary::-webkit-details-marker { display: none; }\n\t\t\t\t\t.aff-flag summary::after {\n\t\t\t\t\t\tcontent: '▸';\n\t\t\t\t\t\tdisplay: inline-flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tjustify-content: center;\n\t\t\t\t\t\tmargin-left: auto;\n\t\t\t\t\t\tcolor: var(--color-muted);\n\t\t\t\t\t\tfont-size: 12px;\n\t\t\t\t\t\ttransform: rotate(0deg);\n\t\t\t\t\t\ttransition: transform 0.12s ease;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag[open] summary::after {\n\t\t\t\t\t\ttransform: rotate(90deg);\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag-summary-left {\n\t\t\t\t\t\tdisplay: inline-flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tgap: 8px;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-swatch {\n\t\t\t\t\t\twidth: 10px;\n\t\t\t\t\t\theight: 10px;\n\t\t\t\t\t\tborder-radius: 9999px;\n\t\t\t\t\t\tflex-shrink: 0;\n\t\t\t\t\t\tbox-shadow: inset 0 0 0 1px rgba(255,255,255,0.12);\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag-body { padding-top: 2px; }\n\t\t\t\t\t.aff-empty {\n\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t\tfont-size: 14px;\n\t\t\t\t\t\tline-height: 1.5rem;\n\t\t\t\t\t\tcolor: var(--color-muted);\n\t\t\t\t\t}\n\t\t\t\t\tcode {\n\t\t\t\t\t\tcolor: var(--color-purple);\n\t\t\t\t\t\tborder: 1px solid #343841;\n\t\t\t\t\t\tborder-radius: 0.35em;\n\t\t\t\t\t\tbackground-color: #24262d;\n\t\t\t\t\t\tpadding: 0.15em 0.35em;\n\t\t\t\t\t\tfont-size: 0.9em;\n\t\t\t\t\t}\n\t\t\t\t\tinput[type=\"color\"].aff-color {\n\t\t\t\t\t\twidth: 2.25rem;\n\t\t\t\t\t\theight: 1.75rem;\n\t\t\t\t\t\tpadding: 0;\n\t\t\t\t\t\tborder: 1px solid #343841;\n\t\t\t\t\t\tborder-radius: 6px;\n\t\t\t\t\t\tcursor: pointer;\n\t\t\t\t\t\tbackground: transparent;\n\t\t\t\t\t}\n\t\t\t\t</style>\n\t\t\t\t<header>\n\t\t\t\t\t<h1><span class=\"aff-flag-toolbar-icon\">${DEV_TOOLBAR_FLAG_ICON_SVG}</span> Feature flags</h1>\n\t\t\t\t\t<div class=\"aff-actions\"><button id=\"aff-reset\" class=\"aff-reset\" type=\"button\" title=\"Reset all flags to defaults\">Reset all</button></div>\n\t\t\t\t</header>\n\t\t\t\t<div class=\"aff-top\">\n\t\t\t\t\t<div class=\"aff-top-left\">\n\t\t\t\t\t\t<p class=\"aff-intro\">Per-flag preview for <code>data-${namespace}</code> regions.</p>\n\t\t\t\t<div class=\"aff-help\">\n\t\t\t\t\t<code>Enabled</code><span>Show/hide flagged regions in preview.</span>\n\t\t\t\t\t<code>Outline</code><span>Toggle flag outlines without changing layout.</span>\n\t\t\t\t\t<code>Badges</code><span>Toggle element and route labels.</span>\n\t\t\t\t\t<code>Colour</code><span>Set <code>--${namespace}-c-<token></code> on <code><html></code>.</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<hr />\n\t\t\t\t<div id=\"aff-body\"></div>\n\t\t\t`);\n\n const body = windowElement.querySelector(\"#aff-body\");\n const resetBtn = windowElement.querySelector(\n \"#aff-reset\",\n ) as HTMLButtonElement | null;\n\n if (body) {\n if (tokens.length === 0) {\n const p = document.createElement(\"p\");\n p.className = \"aff-empty\";\n p.textContent = \"No flags in ff.json.\";\n body.append(p);\n } else {\n for (const token of tokens) {\n const details = document.createElement(\"details\");\n details.className = \"aff-flag\";\n details.open = tokens.length <= 8;\n\n const summary = document.createElement(\"summary\");\n const summaryLeft = document.createElement(\"span\");\n summaryLeft.className = \"aff-flag-summary-left\";\n const swatch = document.createElement(\"span\");\n swatch.className = \"aff-swatch\";\n const name = document.createElement(\"span\");\n name.textContent = token;\n summaryLeft.append(swatch, name);\n summary.append(summaryLeft);\n\n const syncSwatch = () => {\n const c =\n state.colors[token] ?? defaultColors[token] ?? \"#888888\";\n swatch.style.background = c;\n };\n syncSwatch();\n\n const inner = document.createElement(\"div\");\n inner.className = \"aff-flag-body\";\n\n const row = (\n title: string,\n tooltip: string,\n key: keyof Pick<AffState, \"enabled\" | \"outline\" | \"badge\">,\n onLab: string,\n offLab: string,\n ) => {\n const label = document.createElement(\"label\");\n label.className = \"setting-row\";\n const section = document.createElement(\"section\");\n const h = document.createElement(\"h3\");\n h.className = \"aff-row-title\";\n h.textContent = title;\n section.append(h);\n const radios = document.createElement(\"div\");\n radios.className = \"aff-radios\";\n const mk = (on: boolean, lab: string) => {\n const wrap = document.createElement(\"label\");\n const inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.name = `aff-${token}-${key}`;\n const cur = state[key][token] !== false;\n inp.checked = on ? cur : !cur;\n inp.addEventListener(\"change\", () => {\n if (!inp.checked) return;\n state[key][token] = on;\n applyDom(tokens, state, defaultColors, namespace);\n persist(state, namespace);\n });\n wrap.append(inp, document.createTextNode(lab));\n return wrap;\n };\n radios.append(mk(true, onLab), mk(false, offLab));\n label.append(section, radios);\n return label;\n };\n\n inner.append(\n row(\n \"Enabled\",\n \"When off, nodes with this flag token are hidden in preview.\",\n \"enabled\",\n \"On\",\n \"Off\",\n ),\n row(\n \"Outline\",\n \"Toggles dashed outline around flagged elements.\",\n \"outline\",\n \"On\",\n \"Off\",\n ),\n row(\n \"Badges\",\n \"Toggles element badges and top-right route pill.\",\n \"badge\",\n \"On\",\n \"Off\",\n ),\n );\n\n const colorRow = document.createElement(\"label\");\n colorRow.className = \"setting-row\";\n const csection = document.createElement(\"section\");\n const ch = document.createElement(\"h3\");\n ch.className = \"aff-row-title\";\n ch.textContent = \"Colour\";\n ch.title = `Maps to CSS --${namespace}-c-<token> on <html>.`;\n csection.append(ch);\n const cinp = document.createElement(\"input\");\n cinp.type = \"color\";\n cinp.className = \"aff-color\";\n cinp.title = `Set --${namespace}-c-${token}`;\n cinp.value = toHexColor(\n state.colors[token] ?? defaultColors[token],\n \"#888888\",\n );\n cinp.addEventListener(\"input\", () => {\n state.colors[token] = cinp.value;\n syncSwatch();\n applyDom(tokens, state, defaultColors, namespace);\n persist(state, namespace);\n });\n colorRow.append(csection, cinp);\n inner.append(colorRow);\n\n details.append(summary, inner);\n body.append(details);\n }\n }\n }\n\n if (resetBtn) {\n resetBtn.addEventListener(\"click\", () => {\n try {\n localStorage.removeItem(storageKey(namespace));\n localStorage.removeItem(\"aff.dev.v1\");\n } catch {\n // ignore\n }\n state = loadState(tokens, namespace);\n applyDom(tokens, state, defaultColors, namespace);\n mount();\n });\n }\n\n canvas.append(windowElement);\n }\n\n mount();\n document.addEventListener(\"astro:after-swap\", mount);\n closeOnOutsideClick(eventTarget);\n synchronizePlacementOnUpdate(eventTarget, canvas);\n },\n});\n\nexport default affDevToolbarApp;\n"],"mappings":";;;;AA0BA,SAAS,WAAW,KAAyB,UAA0B;AACrE,KAAI,CAAC,IAAK,QAAO;CACjB,MAAM,IAAI,IAAI,MAAM;CACpB,MAAM,OAAO,EAAE,MAAM,sBAAsB;AAC3C,KAAI,KAAM,QAAO,IAAI,KAAK,GAAI,aAAa;CAC3C,MAAM,MAAM,EAAE,MAAM,iDAAiD;AACrE,KAAI,IAIF,QAAO,IAAI;EAHD,KAAK,IAAI,KAAK,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,CAAC;EACzC,KAAK,IAAI,KAAK,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,CAAC;EACzC,KAAK,IAAI,KAAK,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,CAAC;EAC/B,CAAC,KAAK,MAAM,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,GAAG;AAE3E,QAAO;;AAGT,SAAS,WAIP;AACA,KAAI;EACF,MAAM,MAAM,OAAO;AAKnB,SAAO;GAAE,QAJM,OAAO,MAAM,QAAQ,IAAI,OAAO,GAAG,IAAI,SAAS,EAAE;GAIhD,QAFf,KAAK,UAAU,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS,EAAE;GAExC,WADP,KAAK,WAAW,MAAM,GAAG,IAAI,UAAU,MAAM,GAAG;GAC9B;SAC9B;AACN,SAAO;GAAE,QAAQ,EAAE;GAAE,QAAQ,EAAE;GAAE,WAAW;GAAM;;;AAItD,SAAS,WAAW,WAA2B;AAC7C,QAAO,GAAG,UAAU;;AAGtB,SAAS,UAAU,QAAkB,WAA6B;CAChE,MAAM,OAAiB;EACrB,SAAS,EAAE;EACX,SAAS,EAAE;EACX,OAAO,EAAE;EACT,QAAQ,EAAE;EACX;AACD,MAAK,MAAM,KAAK,QAAQ;AACtB,OAAK,QAAQ,KAAK;AAClB,OAAK,QAAQ,KAAK;AAClB,OAAK,MAAM,KAAK;;AAElB,KAAI;EACF,MAAM,MACJ,aAAa,QAAQ,WAAW,UAAU,CAAC,IAC3C,aAAa,QAAQ,aAAa;AACpC,MAAI,CAAC,IAAK,QAAO;EACjB,MAAM,SAAS,KAAK,MAAM,IAAI;EAC9B,MAAM,UAAW,OAAO,WAAW,OAAO;EAG1C,MAAM,UAAW,OAAO,WAAW,OAAO;EAG1C,MAAM,QAAQ,OAAO;EACrB,MAAM,SAAS,OAAO;AACtB,MAAI,QAAS,QAAO,OAAO,KAAK,SAAS,QAAQ;AACjD,MAAI,QAAS,QAAO,OAAO,KAAK,SAAS,QAAQ;AACjD,MAAI,MAAO,QAAO,OAAO,KAAK,OAAO,MAAM;AAC3C,MAAI,OAAQ,QAAO,OAAO,KAAK,QAAQ,OAAO;SACxC;AAGR,QAAO;;AAGT,SAAS,QAAQ,OAAiB,WAAmB;AACnD,KAAI;AACF,eAAa,QAAQ,WAAW,UAAU,EAAE,KAAK,UAAU,MAAM,CAAC;SAC5D;;AAKV,SAAS,SACP,QACA,OACA,UACA,WACA;CACA,MAAM,OAAO,SAAS;AACtB,MAAK,MAAM,KAAK,QAAQ;AACtB,MAAI,MAAM,QAAQ,OAAO,MACvB,MAAK,gBAAgB,mBAAmB,IAAI;MACzC,MAAK,aAAa,mBAAmB,KAAK,MAAM;AAErD,MAAI,MAAM,QAAQ,OAAO,MACvB,MAAK,gBAAgB,mBAAmB,IAAI;MACzC,MAAK,aAAa,mBAAmB,KAAK,MAAM;AAErD,MAAI,MAAM,MAAM,OAAO,MAAO,MAAK,gBAAgB,iBAAiB,IAAI;MACnE,MAAK,aAAa,iBAAiB,KAAK,MAAM;EAEnD,MAAM,MAAM,MAAM,OAAO,MAAM,SAAS;AACxC,MAAI,IAAK,MAAK,MAAM,YAAY,KAAK,UAAU,KAAK,KAAK,IAAI;MACxD,MAAK,MAAM,eAAe,KAAK,UAAU,KAAK,IAAI;;;AAI3D,MAAM,mBAAkC,iBAAiB,EACvD,KAAK,QAAQ,aAAa;CACxB,IAAI,QAAQ,UAAU,EAAE,EAAE,KAAK;CAC/B,IAAI,SAAmB,EAAE;CACzB,IAAI,YAAY;CAEhB,SAAS,QAAQ;AACf,SAAO,cAAc,2BAA2B,EAAE,QAAQ;EAC1D,MAAM,EACJ,QAAQ,YACR,QAAQ,eACR,WAAW,OACT,UAAU;AACd,WAAS;AACT,cAAY;AACZ,UAAQ,UAAU,QAAQ,UAAU;AACpC,WAAS,QAAQ,OAAO,eAAe,UAAU;EAEjD,MAAM,gBAAgB,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+CAmMD,0BAA0B;;;;;6DAKZ,UAAU;;;;;4CAK3B,UAAU;;;;;;KAMjD;EAEC,MAAM,OAAO,cAAc,cAAc,YAAY;EACrD,MAAM,WAAW,cAAc,cAC7B,aACD;AAED,MAAI,KACF,KAAI,OAAO,WAAW,GAAG;GACvB,MAAM,IAAI,SAAS,cAAc,IAAI;AACrC,KAAE,YAAY;AACd,KAAE,cAAc;AAChB,QAAK,OAAO,EAAE;QAEd,MAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,UAAU,SAAS,cAAc,UAAU;AACjD,WAAQ,YAAY;AACpB,WAAQ,OAAO,OAAO,UAAU;GAEhC,MAAM,UAAU,SAAS,cAAc,UAAU;GACjD,MAAM,cAAc,SAAS,cAAc,OAAO;AAClD,eAAY,YAAY;GACxB,MAAM,SAAS,SAAS,cAAc,OAAO;AAC7C,UAAO,YAAY;GACnB,MAAM,OAAO,SAAS,cAAc,OAAO;AAC3C,QAAK,cAAc;AACnB,eAAY,OAAO,QAAQ,KAAK;AAChC,WAAQ,OAAO,YAAY;GAE3B,MAAM,mBAAmB;IACvB,MAAM,IACJ,MAAM,OAAO,UAAU,cAAc,UAAU;AACjD,WAAO,MAAM,aAAa;;AAE5B,eAAY;GAEZ,MAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,SAAM,YAAY;GAElB,MAAM,OACJ,OACA,SACA,KACA,OACA,WACG;IACH,MAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,UAAM,YAAY;IAClB,MAAM,UAAU,SAAS,cAAc,UAAU;IACjD,MAAM,IAAI,SAAS,cAAc,KAAK;AACtC,MAAE,YAAY;AACd,MAAE,cAAc;AAChB,YAAQ,OAAO,EAAE;IACjB,MAAM,SAAS,SAAS,cAAc,MAAM;AAC5C,WAAO,YAAY;IACnB,MAAM,MAAM,IAAa,QAAgB;KACvC,MAAM,OAAO,SAAS,cAAc,QAAQ;KAC5C,MAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,SAAI,OAAO;AACX,SAAI,OAAO,OAAO,MAAM,GAAG;KAC3B,MAAM,MAAM,MAAM,KAAK,WAAW;AAClC,SAAI,UAAU,KAAK,MAAM,CAAC;AAC1B,SAAI,iBAAiB,gBAAgB;AACnC,UAAI,CAAC,IAAI,QAAS;AAClB,YAAM,KAAK,SAAS;AACpB,eAAS,QAAQ,OAAO,eAAe,UAAU;AACjD,cAAQ,OAAO,UAAU;OACzB;AACF,UAAK,OAAO,KAAK,SAAS,eAAe,IAAI,CAAC;AAC9C,YAAO;;AAET,WAAO,OAAO,GAAG,MAAM,MAAM,EAAE,GAAG,OAAO,OAAO,CAAC;AACjD,UAAM,OAAO,SAAS,OAAO;AAC7B,WAAO;;AAGT,SAAM,OACJ,IACE,WACA,+DACA,WACA,MACA,MACD,EACD,IACE,WACA,mDACA,WACA,MACA,MACD,EACD,IACE,UACA,oDACA,SACA,MACA,MACD,CACF;GAED,MAAM,WAAW,SAAS,cAAc,QAAQ;AAChD,YAAS,YAAY;GACrB,MAAM,WAAW,SAAS,cAAc,UAAU;GAClD,MAAM,KAAK,SAAS,cAAc,KAAK;AACvC,MAAG,YAAY;AACf,MAAG,cAAc;AACjB,MAAG,QAAQ,iBAAiB,UAAU;AACtC,YAAS,OAAO,GAAG;GACnB,MAAM,OAAO,SAAS,cAAc,QAAQ;AAC5C,QAAK,OAAO;AACZ,QAAK,YAAY;AACjB,QAAK,QAAQ,SAAS,UAAU,KAAK;AACrC,QAAK,QAAQ,WACX,MAAM,OAAO,UAAU,cAAc,QACrC,UACD;AACD,QAAK,iBAAiB,eAAe;AACnC,UAAM,OAAO,SAAS,KAAK;AAC3B,gBAAY;AACZ,aAAS,QAAQ,OAAO,eAAe,UAAU;AACjD,YAAQ,OAAO,UAAU;KACzB;AACF,YAAS,OAAO,UAAU,KAAK;AAC/B,SAAM,OAAO,SAAS;AAEtB,WAAQ,OAAO,SAAS,MAAM;AAC9B,QAAK,OAAO,QAAQ;;AAK1B,MAAI,SACF,UAAS,iBAAiB,eAAe;AACvC,OAAI;AACF,iBAAa,WAAW,WAAW,UAAU,CAAC;AAC9C,iBAAa,WAAW,aAAa;WAC/B;AAGR,WAAQ,UAAU,QAAQ,UAAU;AACpC,YAAS,QAAQ,OAAO,eAAe,UAAU;AACjD,UAAO;IACP;AAGJ,SAAO,OAAO,cAAc;;AAG9B,QAAO;AACP,UAAS,iBAAiB,oBAAoB,MAAM;AACpD,qBAAoB,YAAY;AAChC,8BAA6B,aAAa,OAAO;GAEpD,CAAC"}
|
|
1
|
+
{"version":3,"file":"dev-toolbar-app.mjs","names":[],"sources":["../src/dev-toolbar-app.ts"],"sourcesContent":["import type { DevToolbarApp } from \"astro\";\nimport { defineToolbarApp } from \"astro/toolbar\";\nimport {\n closeOnOutsideClick,\n createWindowElement,\n synchronizePlacementOnUpdate,\n} from \"astro/runtime/client/dev-toolbar/apps/utils/window.js\";\nimport { DEV_TOOLBAR_FLAG_ICON_SVG } from \"./dev-toolbar-flag-icon\";\n\ndeclare global {\n interface Window {\n __AFF__?: {\n tokens: string[];\n colors?: Record<string, string>;\n namespace?: string;\n };\n }\n}\n\ntype AffState = {\n enabled: Record<string, boolean>;\n outline: Record<string, boolean>;\n badge: Record<string, boolean>;\n colors: Record<string, string>;\n};\n\nfunction toHexColor(raw: string | undefined, fallback: string): string {\n if (!raw) return fallback;\n const s = raw.trim();\n const hex7 = s.match(/^#([0-9a-fA-F]{6})$/);\n if (hex7) return `#${hex7[1]!.toLowerCase()}`;\n const rgb = s.match(/rgba?\\(\\s*([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)/);\n if (rgb) {\n const r = Math.min(255, Math.round(Number(rgb[1])));\n const g = Math.min(255, Math.round(Number(rgb[2])));\n const b = Math.min(255, Math.round(Number(rgb[3])));\n return `#${[r, g, b].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\")}`;\n }\n return fallback;\n}\n\nfunction readMeta(): {\n tokens: string[];\n colors: Record<string, string>;\n namespace: string;\n} {\n try {\n const raw = window.__AFF__;\n const tokens = raw && Array.isArray(raw.tokens) ? raw.tokens : [];\n const colors =\n raw?.colors && typeof raw.colors === \"object\" ? raw.colors : {};\n const namespace = raw?.namespace?.trim() ? raw.namespace.trim() : \"ff\";\n return { tokens, colors, namespace };\n } catch {\n return { tokens: [], colors: {}, namespace: \"ff\" };\n }\n}\n\nfunction storageKey(namespace: string): string {\n return `${namespace}.dev.v1`;\n}\n\nfunction loadState(tokens: string[], namespace: string): AffState {\n const next: AffState = {\n enabled: {},\n outline: {},\n badge: {},\n colors: {},\n };\n for (const t of tokens) {\n next.enabled[t] = true;\n next.outline[t] = true;\n next.badge[t] = true;\n }\n try {\n const raw =\n localStorage.getItem(storageKey(namespace)) ||\n localStorage.getItem(\"aff.dev.v1\");\n if (!raw) return next;\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const outline = (parsed.outline ?? parsed.chrome) as\n Record<string, boolean> | undefined;\n const enabled = (parsed.enabled ?? parsed.render) as\n Record<string, boolean> | undefined;\n const badge = parsed.badge as Record<string, boolean> | undefined;\n const colors = parsed.colors as Record<string, string> | undefined;\n if (outline) Object.assign(next.outline, outline);\n if (enabled) Object.assign(next.enabled, enabled);\n if (badge) Object.assign(next.badge, badge);\n if (colors) Object.assign(next.colors, colors);\n } catch {\n /* ignore */\n }\n return next;\n}\n\nfunction persist(state: AffState, namespace: string) {\n try {\n localStorage.setItem(storageKey(namespace), JSON.stringify(state));\n } catch {\n /* ignore */\n }\n}\n\nfunction applyDom(\n tokens: string[],\n state: AffState,\n defaults: Record<string, string>,\n namespace: string,\n) {\n const root = document.documentElement;\n for (const t of tokens) {\n if (state.outline[t] !== false)\n root.removeAttribute(`data-ff-outline-${t}`);\n else root.setAttribute(`data-ff-outline-${t}`, \"off\");\n\n if (state.enabled[t] !== false)\n root.removeAttribute(`data-ff-enabled-${t}`);\n else root.setAttribute(`data-ff-enabled-${t}`, \"off\");\n\n if (state.badge[t] !== false) root.removeAttribute(`data-ff-badge-${t}`);\n else root.setAttribute(`data-ff-badge-${t}`, \"off\");\n\n const col = state.colors[t] ?? defaults[t];\n if (col) root.style.setProperty(`--${namespace}-c-${t}`, col);\n else root.style.removeProperty(`--${namespace}-c-${t}`);\n }\n}\n\nconst affDevToolbarApp: DevToolbarApp = defineToolbarApp({\n init(canvas, eventTarget) {\n let state = loadState([], \"ff\");\n let tokens: string[] = [];\n let namespace = \"ff\";\n\n function mount() {\n canvas.querySelector(\"astro-dev-toolbar-window\")?.remove();\n const {\n tokens: nextTokens,\n colors: defaultColors,\n namespace: ns,\n } = readMeta();\n tokens = nextTokens;\n namespace = ns;\n state = loadState(tokens, namespace);\n applyDom(tokens, state, defaultColors, namespace);\n\n const windowElement = createWindowElement(`\n\t\t\t\t<style>\n\t\t\t\t\t:host astro-dev-toolbar-window {\n\t\t\t\t\t\theight: min(520px, 78vh);\n\t\t\t\t\t\toverflow-y: auto;\n\t\t\t\t\t\tcolor-scheme: dark;\n\t\t\t\t\t\t--color-muted: rgba(191, 193, 201, 1);\n\t\t\t\t\t\t--color-purple: rgba(224, 204, 250, 1);\n\t\t\t\t\t}\n\t\t\t\t\theader {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tjustify-content: space-between;\n\t\t\t\t\t\tgap: 12px;\n\t\t\t\t\t}\n\t\t\t\t\th1 {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tgap: 8px;\n\t\t\t\t\t\tfont-weight: 600;\n\t\t\t\t\t\tcolor: #fff;\n\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t\tfont-size: 22px;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag-toolbar-icon {\n\t\t\t\t\t\tdisplay: inline-flex;\n\t\t\t\t\t\twidth: 1em;\n\t\t\t\t\t\theight: 1em;\n\t\t\t\t\t\tflex-shrink: 0;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tjustify-content: center;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag-toolbar-icon svg {\n\t\t\t\t\t\twidth: 100%;\n\t\t\t\t\t\theight: 100%;\n\t\t\t\t\t\tdisplay: block;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-intro {\n\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t\tfont-size: 14px;\n\t\t\t\t\t\tline-height: 1.5rem;\n\t\t\t\t\t\tcolor: var(--color-muted);\n margin-bottom: 1rem;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-top {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\talign-items: flex-start;\n\t\t\t\t\t\tjustify-content: space-between;\n\t\t\t\t\t\tgap: 12px;\n\t\t\t\t\t\tmargin: 1rem 0;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-top-left {\n\t\t\t\t\t\tflex: 1;\n\t\t\t\t\t\tmin-width: 0;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-help {\n\t\t\t\t\t\tdisplay: grid;\n\t\t\t\t\t\tgrid-template-columns: minmax(7.5rem, auto) 1fr;\n\t\t\t\t\t\tgap: 0.45rem 0.75rem;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-help code,\n\t\t\t\t\t.aff-help span {\n\t\t\t\t\t\tfont-size: 12px;\n\t\t\t\t\t\tline-height: 1.35;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-help code {\n\t\t\t\t\t\tjustify-self: start;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-actions {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\tjustify-content: flex-end;\n\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t\tflex-shrink: 0;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-reset {\n\t\t\t\t\t\tborder: 1px solid #343841;\n\t\t\t\t\t\tborder-radius: 0.5rem;\n\t\t\t\t\t\tbackground: #24262d;\n\t\t\t\t\t\tcolor: #fff;\n\t\t\t\t\t\tpadding: 0.3rem 0.55rem;\n\t\t\t\t\t\tfont-size: 12px;\n\t\t\t\t\t\tcursor: pointer;\n\t\t\t\t\t}\n\t\t\t\t\thr {\n\t\t\t\t\t\tborder: 1px solid rgba(27, 30, 36, 1);\n\t\t\t\t\t\tmargin: 1em 0;\n\t\t\t\t\t}\n\t\t\t\t\t.setting-row {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\tjustify-content: space-between;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tgap: 12px;\n\t\t\t\t\t\tmargin: 0 0 8px;\n\t\t\t\t\t}\n\t\t\t\t\t.setting-row section { flex: 1; min-width: 0; max-width: 54%; }\n\t\t\t\t\th3.aff-row-title {\n\t\t\t\t\t\tfont-size: 12px;\n\t\t\t\t\t\tfont-weight: 600;\n\t\t\t\t\t\tcolor: #fff;\n\t\t\t\t\t\tletter-spacing: 0.05em;\n\t\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-radios {\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\tgap: 8px;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tflex-shrink: 0;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-radios label {\n\t\t\t\t\t\tdisplay: inline-flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tgap: 4px;\n\t\t\t\t\t\tfont-size: 11px;\n\t\t\t\t\t\tcursor: pointer;\n\t\t\t\t\t\tcolor: var(--color-muted);\n\t\t\t\t\t\tletter-spacing: 0.04em;\n\t\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-radios input { accent-color: var(--color-purple); cursor: pointer; }\n\t\t\t\t\t.aff-flag {\n\t\t\t\t\t\tmargin: 0 0 12px;\n\t\t\t\t\t\tborder: 1px solid rgba(52, 56, 65, 1);\n\t\t\t\t\t\tborder-radius: 10px;\n\t\t\t\t\t\tpadding: 0 10px 10px;\n\t\t\t\t\t\tbackground: rgba(19, 21, 26, 0.55);\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag summary {\n\t\t\t\t\t\tlist-style: none;\n\t\t\t\t\t\tcursor: pointer;\n\t\t\t\t\t\tuser-select: none;\n\t\t\t\t\t\tdisplay: flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tgap: 8px;\n\t\t\t\t\t\tpadding: 10px 0;\n\t\t\t\t\t\tfont-weight: 600;\n\t\t\t\t\t\tfont-size: 11px;\n\t\t\t\t\t\tletter-spacing: 0.06em;\n\t\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\t\tcolor: #fff;\n\t\t\t\t\t\tjustify-content: space-between;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag summary::-webkit-details-marker { display: none; }\n\t\t\t\t\t.aff-flag summary::after {\n\t\t\t\t\t\tcontent: '▸';\n\t\t\t\t\t\tdisplay: inline-flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tjustify-content: center;\n\t\t\t\t\t\tmargin-left: auto;\n\t\t\t\t\t\tcolor: var(--color-muted);\n\t\t\t\t\t\tfont-size: 12px;\n\t\t\t\t\t\ttransform: rotate(0deg);\n\t\t\t\t\t\ttransition: transform 0.12s ease;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag[open] summary::after {\n\t\t\t\t\t\ttransform: rotate(90deg);\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag-summary-left {\n\t\t\t\t\t\tdisplay: inline-flex;\n\t\t\t\t\t\talign-items: center;\n\t\t\t\t\t\tgap: 8px;\n\t\t\t\t\t}\n\t\t\t\t\t.aff-swatch {\n\t\t\t\t\t\twidth: 10px;\n\t\t\t\t\t\theight: 10px;\n\t\t\t\t\t\tborder-radius: 9999px;\n\t\t\t\t\t\tflex-shrink: 0;\n\t\t\t\t\t\tbox-shadow: inset 0 0 0 1px rgba(255,255,255,0.12);\n\t\t\t\t\t}\n\t\t\t\t\t.aff-flag-body { padding-top: 2px; }\n\t\t\t\t\t.aff-empty {\n\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t\tfont-size: 14px;\n\t\t\t\t\t\tline-height: 1.5rem;\n\t\t\t\t\t\tcolor: var(--color-muted);\n\t\t\t\t\t}\n\t\t\t\t\tcode {\n\t\t\t\t\t\tcolor: var(--color-purple);\n\t\t\t\t\t\tborder: 1px solid #343841;\n\t\t\t\t\t\tborder-radius: 0.35em;\n\t\t\t\t\t\tbackground-color: #24262d;\n\t\t\t\t\t\tpadding: 0.15em 0.35em;\n\t\t\t\t\t\tfont-size: 0.9em;\n\t\t\t\t\t}\n\t\t\t\t\tinput[type=\"color\"].aff-color {\n\t\t\t\t\t\twidth: 2.25rem;\n\t\t\t\t\t\theight: 1.75rem;\n\t\t\t\t\t\tpadding: 0;\n\t\t\t\t\t\tborder: 1px solid #343841;\n\t\t\t\t\t\tborder-radius: 6px;\n\t\t\t\t\t\tcursor: pointer;\n\t\t\t\t\t\tbackground: transparent;\n\t\t\t\t\t}\n\t\t\t\t</style>\n\t\t\t\t<header>\n\t\t\t\t\t<h1><span class=\"aff-flag-toolbar-icon\">${DEV_TOOLBAR_FLAG_ICON_SVG}</span> Feature flags</h1>\n\t\t\t\t\t<div class=\"aff-actions\"><button id=\"aff-reset\" class=\"aff-reset\" type=\"button\" title=\"Reset all flags to defaults\">Reset all</button></div>\n\t\t\t\t</header>\n\t\t\t\t<div class=\"aff-top\">\n\t\t\t\t\t<div class=\"aff-top-left\">\n\t\t\t\t\t\t<p class=\"aff-intro\">Per-flag preview for <code>data-${namespace}</code> regions.</p>\n\t\t\t\t<div class=\"aff-help\">\n\t\t\t\t\t<code>Enabled</code><span>Show/hide flagged regions in preview.</span>\n\t\t\t\t\t<code>Outline</code><span>Toggle flag outlines without changing layout.</span>\n\t\t\t\t\t<code>Badges</code><span>Toggle element and route labels.</span>\n\t\t\t\t\t<code>Colour</code><span>Set <code>--${namespace}-c-<token></code> on <code><html></code>.</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<hr />\n\t\t\t\t<div id=\"aff-body\"></div>\n\t\t\t`);\n\n const body = windowElement.querySelector(\"#aff-body\");\n const resetBtn = windowElement.querySelector(\n \"#aff-reset\",\n ) as HTMLButtonElement | null;\n\n if (body) {\n if (tokens.length === 0) {\n const p = document.createElement(\"p\");\n p.className = \"aff-empty\";\n p.textContent = \"No flags in ff.json.\";\n body.append(p);\n } else {\n for (const token of tokens) {\n const details = document.createElement(\"details\");\n details.className = \"aff-flag\";\n details.open = tokens.length <= 8;\n\n const summary = document.createElement(\"summary\");\n const summaryLeft = document.createElement(\"span\");\n summaryLeft.className = \"aff-flag-summary-left\";\n const swatch = document.createElement(\"span\");\n swatch.className = \"aff-swatch\";\n const name = document.createElement(\"span\");\n name.textContent = token;\n summaryLeft.append(swatch, name);\n summary.append(summaryLeft);\n\n const syncSwatch = () => {\n const c =\n state.colors[token] ?? defaultColors[token] ?? \"#888888\";\n swatch.style.background = c;\n };\n syncSwatch();\n\n const inner = document.createElement(\"div\");\n inner.className = \"aff-flag-body\";\n\n const row = (\n title: string,\n tooltip: string,\n key: keyof Pick<AffState, \"enabled\" | \"outline\" | \"badge\">,\n onLab: string,\n offLab: string,\n ) => {\n const label = document.createElement(\"label\");\n label.className = \"setting-row\";\n const section = document.createElement(\"section\");\n const h = document.createElement(\"h3\");\n h.className = \"aff-row-title\";\n h.textContent = title;\n section.append(h);\n const radios = document.createElement(\"div\");\n radios.className = \"aff-radios\";\n const mk = (on: boolean, lab: string) => {\n const wrap = document.createElement(\"label\");\n const inp = document.createElement(\"input\");\n inp.type = \"radio\";\n inp.name = `aff-${token}-${key}`;\n const cur = state[key][token] !== false;\n inp.checked = on ? cur : !cur;\n inp.addEventListener(\"change\", () => {\n if (!inp.checked) return;\n state[key][token] = on;\n applyDom(tokens, state, defaultColors, namespace);\n persist(state, namespace);\n });\n wrap.append(inp, document.createTextNode(lab));\n return wrap;\n };\n radios.append(mk(true, onLab), mk(false, offLab));\n label.append(section, radios);\n return label;\n };\n\n inner.append(\n row(\n \"Enabled\",\n \"When off, nodes with this flag token are hidden in preview.\",\n \"enabled\",\n \"On\",\n \"Off\",\n ),\n row(\n \"Outline\",\n \"Toggles dashed outline around flagged elements.\",\n \"outline\",\n \"On\",\n \"Off\",\n ),\n row(\n \"Badges\",\n \"Toggles element badges and top-right route pill.\",\n \"badge\",\n \"On\",\n \"Off\",\n ),\n );\n\n const colorRow = document.createElement(\"label\");\n colorRow.className = \"setting-row\";\n const csection = document.createElement(\"section\");\n const ch = document.createElement(\"h3\");\n ch.className = \"aff-row-title\";\n ch.textContent = \"Colour\";\n ch.title = `Maps to CSS --${namespace}-c-<token> on <html>.`;\n csection.append(ch);\n const cinp = document.createElement(\"input\");\n cinp.type = \"color\";\n cinp.className = \"aff-color\";\n cinp.title = `Set --${namespace}-c-${token}`;\n cinp.value = toHexColor(\n state.colors[token] ?? defaultColors[token],\n \"#888888\",\n );\n cinp.addEventListener(\"input\", () => {\n state.colors[token] = cinp.value;\n syncSwatch();\n applyDom(tokens, state, defaultColors, namespace);\n persist(state, namespace);\n });\n colorRow.append(csection, cinp);\n inner.append(colorRow);\n\n details.append(summary, inner);\n body.append(details);\n }\n }\n }\n\n if (resetBtn) {\n resetBtn.addEventListener(\"click\", () => {\n try {\n localStorage.removeItem(storageKey(namespace));\n localStorage.removeItem(\"aff.dev.v1\");\n } catch {\n // ignore\n }\n state = loadState(tokens, namespace);\n applyDom(tokens, state, defaultColors, namespace);\n mount();\n });\n }\n\n canvas.append(windowElement);\n }\n\n mount();\n document.addEventListener(\"astro:after-swap\", mount);\n closeOnOutsideClick(eventTarget);\n synchronizePlacementOnUpdate(eventTarget, canvas);\n },\n});\n\nexport default affDevToolbarApp;\n"],"mappings":";;;;AA0BA,SAAS,WAAW,KAAyB,UAA0B;CACrE,IAAI,CAAC,KAAK,OAAO;CACjB,MAAM,IAAI,IAAI,KAAK;CACnB,MAAM,OAAO,EAAE,MAAM,qBAAqB;CAC1C,IAAI,MAAM,OAAO,IAAI,KAAK,EAAE,CAAE,YAAY;CAC1C,MAAM,MAAM,EAAE,MAAM,gDAAgD;CACpE,IAAI,KAIF,OAAO,IAAI;EAHD,KAAK,IAAI,KAAK,KAAK,MAAM,OAAO,IAAI,EAAE,CAAC,CAGrC;EAFF,KAAK,IAAI,KAAK,KAAK,MAAM,OAAO,IAAI,EAAE,CAAC,CAElC;EADL,KAAK,IAAI,KAAK,KAAK,MAAM,OAAO,IAAI,EAAE,CAAC,CAC/B;CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;CAE1E,OAAO;AACT;AAEA,SAAS,WAIP;CACA,IAAI;EACF,MAAM,MAAM,OAAO;EAKnB,OAAO;GAAE,QAJM,OAAO,MAAM,QAAQ,IAAI,MAAM,IAAI,IAAI,SAAS,CAAC;GAI/C,QAFf,KAAK,UAAU,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS,CAAC;GAEvC,WADP,KAAK,WAAW,KAAK,IAAI,IAAI,UAAU,KAAK,IAAI;EAC/B;CACrC,QAAQ;EACN,OAAO;GAAE,QAAQ,CAAC;GAAG,QAAQ,CAAC;GAAG,WAAW;EAAK;CACnD;AACF;AAEA,SAAS,WAAW,WAA2B;CAC7C,OAAO,GAAG,UAAU;AACtB;AAEA,SAAS,UAAU,QAAkB,WAA6B;CAChE,MAAM,OAAiB;EACrB,SAAS,CAAC;EACV,SAAS,CAAC;EACV,OAAO,CAAC;EACR,QAAQ,CAAC;CACX;CACA,KAAK,MAAM,KAAK,QAAQ;EACtB,KAAK,QAAQ,KAAK;EAClB,KAAK,QAAQ,KAAK;EAClB,KAAK,MAAM,KAAK;CAClB;CACA,IAAI;EACF,MAAM,MACJ,aAAa,QAAQ,WAAW,SAAS,CAAC,KAC1C,aAAa,QAAQ,YAAY;EACnC,IAAI,CAAC,KAAK,OAAO;EACjB,MAAM,SAAS,KAAK,MAAM,GAAG;EAC7B,MAAM,UAAW,OAAO,WAAW,OAAO;EAE1C,MAAM,UAAW,OAAO,WAAW,OAAO;EAE1C,MAAM,QAAQ,OAAO;EACrB,MAAM,SAAS,OAAO;EACtB,IAAI,SAAS,OAAO,OAAO,KAAK,SAAS,OAAO;EAChD,IAAI,SAAS,OAAO,OAAO,KAAK,SAAS,OAAO;EAChD,IAAI,OAAO,OAAO,OAAO,KAAK,OAAO,KAAK;EAC1C,IAAI,QAAQ,OAAO,OAAO,KAAK,QAAQ,MAAM;CAC/C,QAAQ,CAER;CACA,OAAO;AACT;AAEA,SAAS,QAAQ,OAAiB,WAAmB;CACnD,IAAI;EACF,aAAa,QAAQ,WAAW,SAAS,GAAG,KAAK,UAAU,KAAK,CAAC;CACnE,QAAQ,CAER;AACF;AAEA,SAAS,SACP,QACA,OACA,UACA,WACA;CACA,MAAM,OAAO,SAAS;CACtB,KAAK,MAAM,KAAK,QAAQ;EACtB,IAAI,MAAM,QAAQ,OAAO,OACvB,KAAK,gBAAgB,mBAAmB,GAAG;OACxC,KAAK,aAAa,mBAAmB,KAAK,KAAK;EAEpD,IAAI,MAAM,QAAQ,OAAO,OACvB,KAAK,gBAAgB,mBAAmB,GAAG;OACxC,KAAK,aAAa,mBAAmB,KAAK,KAAK;EAEpD,IAAI,MAAM,MAAM,OAAO,OAAO,KAAK,gBAAgB,iBAAiB,GAAG;OAClE,KAAK,aAAa,iBAAiB,KAAK,KAAK;EAElD,MAAM,MAAM,MAAM,OAAO,MAAM,SAAS;EACxC,IAAI,KAAK,KAAK,MAAM,YAAY,KAAK,UAAU,KAAK,KAAK,GAAG;OACvD,KAAK,MAAM,eAAe,KAAK,UAAU,KAAK,GAAG;CACxD;AACF;AAEA,MAAM,mBAAkC,iBAAiB,EACvD,KAAK,QAAQ,aAAa;CACxB,IAAI,QAAQ,UAAU,CAAC,GAAG,IAAI;CAC9B,IAAI,SAAmB,CAAC;CACxB,IAAI,YAAY;CAEhB,SAAS,QAAQ;EACf,OAAO,cAAc,0BAA0B,CAAC,EAAE,OAAO;EACzD,MAAM,EACJ,QAAQ,YACR,QAAQ,eACR,WAAW,OACT,SAAS;EACb,SAAS;EACT,YAAY;EACZ,QAAQ,UAAU,QAAQ,SAAS;EACnC,SAAS,QAAQ,OAAO,eAAe,SAAS;EAEhD,MAAM,gBAAgB,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+CAmMD,0BAA0B;;;;;6DAKZ,UAAU;;;;;4CAK3B,UAAU;;;;;;IAMlD;EAEE,MAAM,OAAO,cAAc,cAAc,WAAW;EACpD,MAAM,WAAW,cAAc,cAC7B,YACF;EAEA,IAAI,MACF,IAAI,OAAO,WAAW,GAAG;GACvB,MAAM,IAAI,SAAS,cAAc,GAAG;GACpC,EAAE,YAAY;GACd,EAAE,cAAc;GAChB,KAAK,OAAO,CAAC;EACf,OACE,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,UAAU,SAAS,cAAc,SAAS;GAChD,QAAQ,YAAY;GACpB,QAAQ,OAAO,OAAO,UAAU;GAEhC,MAAM,UAAU,SAAS,cAAc,SAAS;GAChD,MAAM,cAAc,SAAS,cAAc,MAAM;GACjD,YAAY,YAAY;GACxB,MAAM,SAAS,SAAS,cAAc,MAAM;GAC5C,OAAO,YAAY;GACnB,MAAM,OAAO,SAAS,cAAc,MAAM;GAC1C,KAAK,cAAc;GACnB,YAAY,OAAO,QAAQ,IAAI;GAC/B,QAAQ,OAAO,WAAW;GAE1B,MAAM,mBAAmB;IACvB,MAAM,IACJ,MAAM,OAAO,UAAU,cAAc,UAAU;IACjD,OAAO,MAAM,aAAa;GAC5B;GACA,WAAW;GAEX,MAAM,QAAQ,SAAS,cAAc,KAAK;GAC1C,MAAM,YAAY;GAElB,MAAM,OACJ,OACA,SACA,KACA,OACA,WACG;IACH,MAAM,QAAQ,SAAS,cAAc,OAAO;IAC5C,MAAM,YAAY;IAClB,MAAM,UAAU,SAAS,cAAc,SAAS;IAChD,MAAM,IAAI,SAAS,cAAc,IAAI;IACrC,EAAE,YAAY;IACd,EAAE,cAAc;IAChB,QAAQ,OAAO,CAAC;IAChB,MAAM,SAAS,SAAS,cAAc,KAAK;IAC3C,OAAO,YAAY;IACnB,MAAM,MAAM,IAAa,QAAgB;KACvC,MAAM,OAAO,SAAS,cAAc,OAAO;KAC3C,MAAM,MAAM,SAAS,cAAc,OAAO;KAC1C,IAAI,OAAO;KACX,IAAI,OAAO,OAAO,MAAM,GAAG;KAC3B,MAAM,MAAM,MAAM,IAAI,CAAC,WAAW;KAClC,IAAI,UAAU,KAAK,MAAM,CAAC;KAC1B,IAAI,iBAAiB,gBAAgB;MACnC,IAAI,CAAC,IAAI,SAAS;MAClB,MAAM,IAAI,CAAC,SAAS;MACpB,SAAS,QAAQ,OAAO,eAAe,SAAS;MAChD,QAAQ,OAAO,SAAS;KAC1B,CAAC;KACD,KAAK,OAAO,KAAK,SAAS,eAAe,GAAG,CAAC;KAC7C,OAAO;IACT;IACA,OAAO,OAAO,GAAG,MAAM,KAAK,GAAG,GAAG,OAAO,MAAM,CAAC;IAChD,MAAM,OAAO,SAAS,MAAM;IAC5B,OAAO;GACT;GAEA,MAAM,OACJ,IACE,WACA,+DACA,WACA,MACA,KACF,GACA,IACE,WACA,mDACA,WACA,MACA,KACF,GACA,IACE,UACA,oDACA,SACA,MACA,KACF,CACF;GAEA,MAAM,WAAW,SAAS,cAAc,OAAO;GAC/C,SAAS,YAAY;GACrB,MAAM,WAAW,SAAS,cAAc,SAAS;GACjD,MAAM,KAAK,SAAS,cAAc,IAAI;GACtC,GAAG,YAAY;GACf,GAAG,cAAc;GACjB,GAAG,QAAQ,iBAAiB,UAAU;GACtC,SAAS,OAAO,EAAE;GAClB,MAAM,OAAO,SAAS,cAAc,OAAO;GAC3C,KAAK,OAAO;GACZ,KAAK,YAAY;GACjB,KAAK,QAAQ,SAAS,UAAU,KAAK;GACrC,KAAK,QAAQ,WACX,MAAM,OAAO,UAAU,cAAc,QACrC,SACF;GACA,KAAK,iBAAiB,eAAe;IACnC,MAAM,OAAO,SAAS,KAAK;IAC3B,WAAW;IACX,SAAS,QAAQ,OAAO,eAAe,SAAS;IAChD,QAAQ,OAAO,SAAS;GAC1B,CAAC;GACD,SAAS,OAAO,UAAU,IAAI;GAC9B,MAAM,OAAO,QAAQ;GAErB,QAAQ,OAAO,SAAS,KAAK;GAC7B,KAAK,OAAO,OAAO;EACrB;EAIJ,IAAI,UACF,SAAS,iBAAiB,eAAe;GACvC,IAAI;IACF,aAAa,WAAW,WAAW,SAAS,CAAC;IAC7C,aAAa,WAAW,YAAY;GACtC,QAAQ,CAER;GACA,QAAQ,UAAU,QAAQ,SAAS;GACnC,SAAS,QAAQ,OAAO,eAAe,SAAS;GAChD,MAAM;EACR,CAAC;EAGH,OAAO,OAAO,aAAa;CAC7B;CAEA,MAAM;CACN,SAAS,iBAAiB,oBAAoB,KAAK;CACnD,oBAAoB,WAAW;CAC/B,6BAA6B,aAAa,MAAM;AAClD,EACF,CAAC"}
|
package/dist/{dev-toolbar-flag-icon-BHCQJ53N.mjs.map → dev-toolbar-flag-icon-MQsIFekv.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-toolbar-flag-icon-
|
|
1
|
+
{"version":3,"file":"dev-toolbar-flag-icon-MQsIFekv.mjs","names":[],"sources":["../src/dev-toolbar-flag-icon.ts"],"sourcesContent":["/**\n * Inline SVG for the dev toolbar app button and panel header (Heroicons-style flag, 20×20 in a 22×22 box).\n * `currentColor` matches built-in Astro toolbar icons.\n */\nexport const DEV_TOOLBAR_FLAG_ICON_SVG =\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 22 22\" fill=\"currentColor\" aria-hidden=\"true\">' +\n '<path d=\"M4.25 3.25v15.5a.75.75 0 1 1-1.5 0V3.25a.75.75 0 0 1 1.5 0Z\"/>' +\n '<path d=\"M6.5 4.75v9.2a1 1 0 0 0 1.5.87l7.5-4.6a1 1 0 0 0 0-1.74l-7.5-4.6a1 1 0 0 0-1.5.87Z\"/>' +\n \"</svg>\";\n"],"mappings":";;;;;AAIA,MAAa,4BACX"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { FeatureFlagMap, ResolveFeatureRuntimeOptions, ResolvedFeatureRuntime, longestMatchingRoutePrefix, mergeFlagsWithProcessEnvOverrides, resolveFeatureFlagsByEnvironment, routePatternToPrefix } from "./runtime.mjs";
|
|
2
|
-
|
|
3
2
|
//#region src/badge-layout.d.ts
|
|
4
3
|
type ElementBadgeVerticalAnchor = "top" | "bottom";
|
|
5
4
|
/** Horizontal placement of the element pill: `end` = top-right (LTR), `start` = top-left, `center` = centred. */
|
|
@@ -42,6 +41,12 @@ declare function normalizeElementBadgeLayout(input: ElementBadgeLayoutOptions |
|
|
|
42
41
|
declare function elementBadgePositionBlock(layout: NormalizedElementBadgeLayout): string;
|
|
43
42
|
//#endregion
|
|
44
43
|
//#region src/dev-outline-css.d.ts
|
|
44
|
+
/**
|
|
45
|
+
* Cascade layer for the declarations the app must be able to override without
|
|
46
|
+
* writing `!important`. Named rather than anonymous so the order statement in
|
|
47
|
+
* {@link affHeadInlineRuntime} can name it too.
|
|
48
|
+
*/
|
|
49
|
+
declare const DEV_LAYER = "aff-dev";
|
|
45
50
|
type DevOutlineHiddenStrategy = "visibility" | "display";
|
|
46
51
|
interface DevOutlineCssOptions extends ElementBadgeLayoutOptions {
|
|
47
52
|
outlineWidth?: string;
|
|
@@ -66,6 +71,28 @@ interface DevOutlineCssOptions extends ElementBadgeLayoutOptions {
|
|
|
66
71
|
* Dev-only styles: `data-ff="token"` (or space-separated tokens) gates outlines/badges.
|
|
67
72
|
* `html[data-ff-route="<token>"]` (dev) adds a fixed top-right route badge (label only).
|
|
68
73
|
* Toolbar toggles: `data-ff-enabled-*`, `data-ff-outline-*`, `data-ff-badge-*`, `--<namespace>-c-*`.
|
|
74
|
+
*
|
|
75
|
+
* The badge is an absolutely positioned `::before`, so its host has to be a
|
|
76
|
+
* containing block, and the host also wants rounding for the outline to follow.
|
|
77
|
+
* Both of those are the app's business, not ours: a flagged element that the app
|
|
78
|
+
* positions itself, or rounds itself, must keep doing so. Marking a flagged
|
|
79
|
+
* element must not move it.
|
|
80
|
+
*
|
|
81
|
+
* So `position` and `border-radius` are emitted inside `:where()` *and* inside
|
|
82
|
+
* the `aff-dev` cascade layer. `:where()` alone is not enough: an unlayered rule
|
|
83
|
+
* beats a layered one before specificity is ever consulted, so a zero-specificity
|
|
84
|
+
* unlayered rule still overrides Tailwind's `.rounded-full` and `.absolute`,
|
|
85
|
+
* which live in `@layer utilities`. Layering ours is what lets the app win.
|
|
86
|
+
*
|
|
87
|
+
* Layer priority follows the order layers are first declared, so the layer only
|
|
88
|
+
* sorts below the app's own layers if `@layer aff-dev;` appears before them in
|
|
89
|
+
* document order. The sheet opens with that statement for the case where it
|
|
90
|
+
* lands first, and {@link affHeadInlineRuntime} also prepends a style element
|
|
91
|
+
* carrying it as the first child of `<head>`, which is what actually guarantees
|
|
92
|
+
* the order at runtime.
|
|
93
|
+
*
|
|
94
|
+
* Everything else here is dev chrome the app has no opinion about, and stays at
|
|
95
|
+
* its natural specificity.
|
|
69
96
|
*/
|
|
70
97
|
declare function createFeatureFlagStyles(runtime: ResolvedFeatureRuntime, css?: DevOutlineCssOptions): string;
|
|
71
98
|
/**
|
|
@@ -86,11 +113,56 @@ declare function cullProductionHtml(html: string, runtime: ResolvedFeatureRuntim
|
|
|
86
113
|
*/
|
|
87
114
|
declare function applyProductionHtmlCullToDist(outDir: string, runtime: ResolvedFeatureRuntime): void;
|
|
88
115
|
//#endregion
|
|
116
|
+
//#region src/sitemap-prune.d.ts
|
|
117
|
+
/**
|
|
118
|
+
* Drop every `<url>` whose `<loc>` points at a route this runtime prunes.
|
|
119
|
+
*
|
|
120
|
+
* The alternative is asking every site to duplicate the flag decision in its own
|
|
121
|
+
* `sitemap({ filter })`, which is what the docs used to say and what the site this
|
|
122
|
+
* package was written for got subtly wrong: a substring test culled `/blog/about-x/`
|
|
123
|
+
* along with `/about/`. Deciding it here, from the same runtime that deletes the
|
|
124
|
+
* files, means the two answers cannot drift.
|
|
125
|
+
*
|
|
126
|
+
* String surgery rather than an XML parse, so the untouched entries come back
|
|
127
|
+
* byte-identical and the diff of a rebuild stays readable.
|
|
128
|
+
*/
|
|
129
|
+
declare function pruneSitemapXml(xml: string, runtime: ResolvedFeatureRuntime): string;
|
|
130
|
+
/** Number of `<url>` entries left in a urlset. */
|
|
131
|
+
declare function sitemapUrlCount(xml: string): number;
|
|
132
|
+
/**
|
|
133
|
+
* Remove the `<sitemap>` entries of an index that point at files which no longer exist.
|
|
134
|
+
*/
|
|
135
|
+
declare function pruneSitemapIndexXml(xml: string, removedFiles: readonly string[]): string;
|
|
136
|
+
interface SitemapPruneResult {
|
|
137
|
+
/** Sitemap files rewritten with fewer entries. */
|
|
138
|
+
rewritten: string[];
|
|
139
|
+
/** Sitemap files deleted because nothing was left in them. */
|
|
140
|
+
removed: string[];
|
|
141
|
+
/** Whether any sitemap file was found at all. */
|
|
142
|
+
found: boolean;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Walk `outDir` (Astro `dist/`) and take the pruned routes out of every sitemap.
|
|
146
|
+
*
|
|
147
|
+
* Runs in `astro:build:done`, which means this integration has to sit **after**
|
|
148
|
+
* `@astrojs/sitemap` in `integrations` — Astro runs the hook in array order, and a
|
|
149
|
+
* sitemap written after this pass would keep its dead URLs.
|
|
150
|
+
*/
|
|
151
|
+
declare function applySitemapPruneToDist(outDir: string, runtime: ResolvedFeatureRuntime): SitemapPruneResult;
|
|
152
|
+
//#endregion
|
|
89
153
|
//#region src/index.d.ts
|
|
90
154
|
interface AstroFeatureFlagsOptions extends ResolveFeatureRuntimeOptions {
|
|
91
155
|
css?: DevOutlineCssOptions;
|
|
92
156
|
/** When false, keeps static build output untouched (no route pruning / HTML cull). */
|
|
93
157
|
staticMinify?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* When false, leaves generated sitemaps alone. On by default: a pruned route that is
|
|
160
|
+
* still advertised in `sitemap-0.xml` is a 404 handed to every crawler that reads it.
|
|
161
|
+
*
|
|
162
|
+
* Needs this integration to sit **after** `@astrojs/sitemap` in `integrations`, because
|
|
163
|
+
* Astro runs `astro:build:done` in array order.
|
|
164
|
+
*/
|
|
165
|
+
pruneSitemap?: boolean;
|
|
94
166
|
}
|
|
95
167
|
/** Prefer `prod` if present; else first non-`dev` key (sorted); else `"prod"`. */
|
|
96
168
|
declare function primaryNonDevEnvironmentKey(flagsByEnvironment: Record<string, FeatureFlagMap>): string;
|
|
@@ -99,5 +171,5 @@ declare function getResolvedFeatures(options?: ResolveFeatureRuntimeOptions): Re
|
|
|
99
171
|
declare function featureRouteIncluded(pathname: string, runtime: ResolvedFeatureRuntime): boolean;
|
|
100
172
|
declare function astroFeatureFlags(options?: AstroFeatureFlagsOptions): any;
|
|
101
173
|
//#endregion
|
|
102
|
-
export { AstroFeatureFlagsOptions, type DevOutlineCssOptions, type DevOutlineHiddenStrategy, type ElementBadgeHorizontalAlign, type ElementBadgeLayoutOptions, type ElementBadgeVerticalAnchor, type FeatureFlagMap, type NormalizedElementBadgeLayout, applyProductionHtmlCullToDist, createFeatureFlagStyles, createProductionGateStyles, createVirtualModuleSource, cullProductionHtml, astroFeatureFlags as default, elementBadgePositionBlock, featureRouteIncluded, getResolvedFeatures, longestMatchingRoutePrefix, mergeFlagsWithProcessEnvOverrides, normalizeElementBadgeLayout, primaryNonDevEnvironmentKey, resolveFeatureFlagsByEnvironment, routePatternToPrefix };
|
|
174
|
+
export { AstroFeatureFlagsOptions, DEV_LAYER, type DevOutlineCssOptions, type DevOutlineHiddenStrategy, type ElementBadgeHorizontalAlign, type ElementBadgeLayoutOptions, type ElementBadgeVerticalAnchor, type FeatureFlagMap, type NormalizedElementBadgeLayout, type SitemapPruneResult, applyProductionHtmlCullToDist, applySitemapPruneToDist, createFeatureFlagStyles, createProductionGateStyles, createVirtualModuleSource, cullProductionHtml, astroFeatureFlags as default, elementBadgePositionBlock, featureRouteIncluded, getResolvedFeatures, longestMatchingRoutePrefix, mergeFlagsWithProcessEnvOverrides, normalizeElementBadgeLayout, primaryNonDevEnvironmentKey, pruneSitemapIndexXml, pruneSitemapXml, resolveFeatureFlagsByEnvironment, routePatternToPrefix, sitemapUrlCount };
|
|
103
175
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/badge-layout.ts","../src/dev-outline-css.ts","../src/production-html-cull.ts","../src/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/badge-layout.ts","../src/dev-outline-css.ts","../src/production-html-cull.ts","../src/sitemap-prune.ts","../src/index.ts"],"mappings":";;KAAY;;KAGA;;UAGK;;;;;EAKf,8BAA8B;;;;;EAK9B;;;;;EAKA;;EAEA,6BAA6B;;KAGnB;EAEN;EACA,iBAAiB;EACjB;EACA,gBAAgB;;EAGhB;EACA;EACA;EACA,gBAAgB;;iBAQN,4BACd,OAAO,wCACN;;;;iBA+Ba,0BACd,QAAQ;;;;;;;;cChEG;KAED;UAEK,6BAA6B;EAC5C;;EAEA;;EAEA,sBAAsB;EACtB;EACA;;EAGA;;EAEA;EACA,oBAAoB;EAEpB,iBAAiB;;;;EAKjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4Fc,wBACd,SAAS,wBACT,MAAM;;;;;iBAiUQ,2BACd,SAAS;;;;;;;;iBCnXK,mBACd,cACA,SAAS;;;;iBAqCK,8BACd,gBACA,SAAS;;;;;;;;;;;;;;;iBC3DK,gBACd,aACA,SAAS;;iBAgBK,gBAAgB;;;;iBAOhB,qBACd,aACA;UAWe;;EAEf;;EAEA;;EAEA;;;;;;;;;iBAUc,wBACd,gBACA,SAAS,yBACR;;;UC3Dc,iCAAiC;EAChD,MAAM;;EAEN;;;;;;;;EAQA;;;iBAIc,4BACd,oBAAoB,eAAe;iBAoCrB,0BACd,SAAS,wBACT,MAAM,sBACN,qBAAoB,eAAe,iBACnC;iBA8Kc,oBACd,UAAS,+BACR;iBAIa,qBACd,kBACA,SAAS;iBAkBa,kBACtB,UAAS"}
|