@pinegrow/vite-plugin
Advanced tools
| --- | ||
| name: assets | ||
| description: "Organize Vue Designer project content and assets. Use when the user asks about images, copy, media inventory, content sources, references, or asset usage." | ||
| --- | ||
| # assets | ||
| When used with a Vue Designer specs-to-code project, read `../../../../specs-to-code/AGENTS.md`, `project-docs/assets/02-content-and-assets.md`, `project-docs/assets/01-pages.md`, `project-docs/assets/03-design-system.md`, and `project-docs/assets/04-wireframes.md` as needed. | ||
| Update content inventory, image references, source/licensing notes, production asset locations, and page usage. Use live Markdown image links for documented images so they render as images. Never wrap image-link syntax in backticks or code fences. Ask before deleting, moving, renaming, or overwriting user-provided assets. |
| --- | ||
| name: wireframes | ||
| description: "Create, attach, or organize Vue Designer wireframes. Use when the user asks for wireframes, sketches, Figma/screenshots, page layouts, or visual planning images." | ||
| --- | ||
| # wireframes | ||
| When used with a Vue Designer specs-to-code project, read `../../../../specs-to-code/AGENTS.md`, brief, requirements, pages, content/assets, and design-system docs. | ||
| Ask whether the user has sketches/Figma/screenshots to attach or wants draft visual wireframe images generated from the text spec, unless already clear. Default generated wireframes to SVG unless the user advises otherwise. | ||
| Store images under `project-docs/assets/images/`. Use names like `wireframe-pages-home.svg` and `wireframe-components-card.svg`. Add live Markdown image links in `assets/04-wireframes.md` and the relevant page/layout/component sections in `assets/01-pages.md`. Never wrap image-link syntax in backticks or code fences. |
| # Vue Designer Specs-To-Code Workflow | ||
| This is the package-shipped manifest and durable workflow guide for Vue Designer specs-to-code projects. User projects inherit these rules from their root `AGENTS.md` through a short managed reference block. | ||
| Treat Vue Designer as the live visual review surface. Apply these rules together with the user project's own `AGENTS.md` file. Prefer `project-docs/` as the live project specification when present, but adapt to existing project docs if the project uses another structure. Start from the project `AGENTS.md`, load this workflow manifest, then read the requested skill and relevant project docs before editing code. | ||
| ## Read First | ||
| 1. The user project's root `AGENTS.md` or `AGENTS.MD`. | ||
| 2. This inherited workflow manifest: `node_modules/@pinegrow/vite-plugin/agent-workflows/specs-to-code/AGENTS.md`. | ||
| 3. The requested specs-to-code skill: | ||
| - `kickstart`: `node_modules/@pinegrow/vite-plugin/agent-workflows/specs-to-code/skills/specs/kickstart/SKILL.md` | ||
| - `plan`: `node_modules/@pinegrow/vite-plugin/agent-workflows/specs-to-code/skills/specs/plan/SKILL.md` | ||
| - `next-resume`: `node_modules/@pinegrow/vite-plugin/agent-workflows/specs-to-code/skills/implementation/next-resume/SKILL.md` | ||
| - `review`: `node_modules/@pinegrow/vite-plugin/agent-workflows/specs-to-code/skills/implementation/review/SKILL.md` | ||
| - `close-handoff`: `node_modules/@pinegrow/vite-plugin/agent-workflows/specs-to-code/skills/implementation/close-handoff/SKILL.md` | ||
| 4. `project-docs/README.md` when present. | ||
| 5. `project-docs/01-brief.md`. | ||
| 6. `project-docs/02-requirements.md`. | ||
| 7. `project-docs/03-architecture.md`. | ||
| 8. `project-docs/04-plan.md`. | ||
| 9. `project-docs/05-acceptance-criteria.md`. | ||
| 10. `project-docs/06-verification.md`. | ||
| 11. `project-docs/07-decisions.md`. | ||
| 12. `project-docs/08-handoff.md`. | ||
| 13. `project-docs/09-session-log.md`. | ||
| 14. Files under `project-docs/assets/` when work touches pages, components, content, images, wireframes, or visual design. | ||
| If this workflow manifest or requested skill is missing, ask the user to install or update dependencies so `@pinegrow/vite-plugin` is present. Do not invent a separate workflow from memory. | ||
| If this project already has its own docs instead of `project-docs/`, map the workflow roles onto those docs: brief/product context, requirements or user stories, architecture, plan, acceptance, verification, decisions, handoff/session log, and assets/design notes. | ||
| ## Skills | ||
| Use these package skills as the source of truth for the specs-to-code workflow: | ||
| - `kickstart`: guided intake, docs creation/update, implementation mode selection, visible completion boundary, and mode-aware commit handling. | ||
| - `plan`: status/readiness review, missing-decision handling, dirty checkpoint gate, milestone planning, visible planning boundary, and mode-aware commit handling. | ||
| - `next-resume`: read handoff/session log/plan, report current state, then run the next approved implementation milestone according to the selected mode. | ||
| - `review`: compare current work against specs, acceptance criteria, Vue Designer visual smoke evidence, visual/content authoring rules, component reuse rules, and recorded testing expectations. | ||
| - `close-handoff`: close an accepted milestone, update verification/acceptance/decisions/log/audit/handoff docs, handle the commit gate, and leave continuation notes. | ||
| Do not use `status`, `continue`, `next`, `resume`, `close`, or `handoff` as specs-to-code commands. Their responsibilities are intentionally folded into the five skills above. | ||
| ## Vue Designer Live Channel | ||
| When `_pginfo/live-designer.json` exists, read it before broad context questions. If it contains `ai_assistant_url`, use that as `AI_ASSISTANT_URL` and keep Vue Designer updated silently while working: | ||
| - `GET AI_ASSISTANT_URL?action=context` before kickstart, planning, implementation, review, or closing work. | ||
| - `POST AI_ASSISTANT_URL?action=phase` when a workflow phase starts or changes. | ||
| - `POST AI_ASSISTANT_URL?action=question` with one question at a time. Include `suggestions` for quick reply buttons when useful. | ||
| - `POST AI_ASSISTANT_URL?action=action-required` when review, approval, commit approval, or a decision is needed. Include suggested quick actions when useful. | ||
| - `POST AI_ASSISTANT_URL?action=file-changed` after saved source/doc edits. | ||
| - `POST AI_ASSISTANT_URL?action=component-updated` after component work. | ||
| - `POST AI_ASSISTANT_URL?action=page-section` after adding or changing a page section. | ||
| - `POST AI_ASSISTANT_URL?action=milestones` when milestone status changes. | ||
| - `POST AI_ASSISTANT_URL?action=complete` or `failed` immediately before ending a workflow response. | ||
| - `GET AI_ASSISTANT_URL?action=logs` when you need the project-local JSONL log paths for testing or workflow tuning. | ||
| - `POST AI_ASSISTANT_URL?action=workflow-log` with useful end-to-end workflow trace entries that should be retained under `.pinegrow/ai-assistant/logs/workflow-session.jsonl`. | ||
| - `POST AI_ASSISTANT_URL?action=conversation-log` with user/agent conversation trace entries that should be retained under `.pinegrow/ai-assistant/logs/conversation.jsonl`. | ||
| Keep terminal output concise. The Vue Designer Agents panel is the clean user-facing surface for questions, suggested replies, live updates, and action-required prompts. | ||
| The `.pinegrow/ai-assistant/logs/` JSONL files are for local testing and fine tuning only; do not treat them as project requirements or as source-of-truth docs. | ||
| Before any terminal response that waits for user input, first send the matching AI Assistant `POST` when `AI_ASSISTANT_URL` is available. This is mandatory for every pause, including intake questions, missing decisions, review acceptance, scope approval, commit approval, blocked checks, and risky/destructive approval. Use `question` only for one-question intake or clarification. Use `action-required` for approvals, review decisions, commit gates, and blocked workflow choices. Include concise `suggestions` so the panel can show quick action buttons. After posting, the terminal may repeat the same prompt briefly, but it must not be the only place where the user sees the pause. | ||
| ## Execution Modes | ||
| - Co-pilot mode is the default. The user is the Pilot. Build one small milestone at a time, then stop and report the result for user review and explicit commit approval before moving to the next milestone. | ||
| - Auto-pilot mode uses the same small milestones, but the agent performs self-review, records acceptance, commits the milestone automatically when Git is available, and proceeds without waiting unless risk, ambiguity, or policy requires user input. | ||
| - In Auto-pilot mode, `next-resume` means run the milestone loop. After each successful self-review and commit, immediately move to the next planned milestone. | ||
| - Auto-pilot mode stops only when the plan is complete, no next milestone is defined, required user input is missing, self-review fails, checks fail and cannot be fixed within the milestone scope, Git commit fails, the user interrupts or changes scope, a destructive/risky action needs approval, or the project is switched to Co-pilot or no-commit mode. | ||
| - Capture the implementation mode during kickstart. If the user does not choose, record `Co-pilot` as the default. | ||
| ## Milestone Build Rhythm | ||
| Never implement the full site in one pass. Shape the plan as small reviewable milestones: global layout shell, header component, footer component, other shared layout parts, first page, first page section such as a hero section, components used by that section, the next section, the next page, and separate logic or integration chunks. | ||
| At the start of each milestone, state the scope, files likely to change, acceptance checks, the required Vue Designer visual smoke check, and any additional test approach selected by the user. At the end of each milestone, report what changed, the Vue Designer visual smoke result, any additional checks, docs updated, review result, and commit status. In Co-pilot mode, ask `Do you approve committing these updates?` with a concise update summary and proposed commit subject, then create the Git commit only after approval. In Auto-pilot mode, self-review against acceptance criteria, record acceptance, create the Git commit automatically, then proceed to the next planned milestone unless a stop condition applies. Do not start the next milestone while the current completed checkpoint is uncommitted, unless the user explicitly switches the project to no-commit mode. | ||
| Do not start the app dev server or run app build commands from this workflow. The project is being worked inside Vue Designer; milestone-level visual smoke checks in the user's running Vue Designer preview are the default verification baseline. Any user-chosen automated or manual testing is added on top of that baseline. | ||
| ## Vue Designer Visual Authoring Rules | ||
| Keep visual styling editable in Vue Designer and Pinegrow. | ||
| - Put Tailwind utility classes directly on the element they style. | ||
| - Do not move static Tailwind classes into JS constants, arrays, objects, helper functions, or computed bindings just for tidiness or reuse. | ||
| - Use `class:list`, `:class`, or computed classes only for genuinely conditional state. Keep static base classes inline. | ||
| - Prefer extracting a repeated visual pattern into a small component over hiding class strings in variables. | ||
| - Before adding native interactive controls, inspect and prefer the project's existing foundational components such as `BaseButton`, `BaseIcon`, form controls, app UI config, and installed component-library components. | ||
| - Do not bypass foundational components solely to place Tailwind classes inline. If the existing component hides needed visual styling, prefer improving/extending that component, using its documented `class`/`ui`/variant hooks, or creating a small wrapper component that keeps static styling visible while preserving the foundation. | ||
| - For installed component libraries such as Vuetify, Nuxt UI, PrimeVue, or similar, use library components for interactive controls like menus, dialogs, tabs, accordions, drawers, forms, selects, date pickers, and toasts. Do not hand-roll those with native tags and JavaScript unless the project explicitly asks for custom behavior. | ||
| - Native semantic tags are still preferred for static content, layout, document structure, and route links when no project link component owns that role. | ||
| - If bypassing an existing foundational component is necessary, record the reason in the review notes or `project-docs/07-decisions.md` before accepting the milestone. | ||
| ## Vue Designer Content Authoring Rules | ||
| Keep page-visible content close to the visual surface that renders it. | ||
| - Put one-off page and section copy directly in the `.astro`, `.vue`, or component template that renders it, so Vue Designer/Pinegrow selection and text editing happen where the content appears. | ||
| - Do not move simple visible content into `site.ts`, exported objects, arrays, helper modules, or frontmatter constants just for tidiness. Examples include one-page visit details, section eyebrow/headline/body copy, button labels, small contact blocks, and provisional notes. | ||
| - Use `site.ts` for global site metadata, navigation data, SEO defaults, brand-level settings, and values reused across many routes. | ||
| - Use content collections, Markdown/MDX, JSON/data files, or local arrays only when the content is genuinely repeated, sorted, filtered, user-managed, or shared across multiple views. | ||
| - If a milestone stores page-visible content outside the rendering file, record why in `project-docs/03-architecture.md` or `project-docs/07-decisions.md` before accepting it. | ||
| ## External File Write Rules | ||
| - When working from a CLI or agent outside Vue Designer, every source/doc edit must be literally saved to disk before reporting it as ready for VD review. | ||
| - Treat save as part of the edit, not a follow-up. Use a real filesystem write such as `apply_patch`, an editor Save command, or an atomic replace that writes the final file contents. Do not leave changes only in an unsaved editor buffer, generated patch text, staged diff, tool memory, or unfinished temporary file. | ||
| - For every file expected to refresh in Vue Designer, make sure the saved file's final contents are on disk and the file modification time advances. If the editing tool may skip the write because the content appears unchanged, explicitly save or rewrite the final file contents again. | ||
| - After edits, reread the changed file or inspect `git diff -- <changed-files>` so the agent verifies the filesystem contains the final content and gives Vue Designer's watcher a completed write to observe. | ||
| - If Vue Designer does not pick up an external source/doc edit, perform one more literal save of the affected file, then allow the filesystem event and Vite HMR cycle to settle before asking for Vue Designer visual review. | ||
| - Do not work around stale VD state by starting the app dev server or app build from this workflow. | ||
| ## Intake Before Build | ||
| When the user says this is a new project, uses `kickstart`, or asks to start, create, plan, scaffold, or build, first check whether the spec is clear enough to implement. | ||
| If key docs are missing, empty, marked Draft/TBD, or do not describe the product, start intake instead of coding. Ask exactly one question at a time, offer concrete suggestions, and allow rough answers, `skip`, or `not sure`. | ||
| Capture only enough to build the first useful version: project purpose, audience, main pages or flows, visual direction, content/assets, wireframe inputs, data or integrations, stack/deployment constraints, non-goals, acceptance checks, additional testing preference, and implementation mode. After each useful answer, update every affected doc immediately. | ||
| During kickstart, ask whether the user already has sketches/Figma/screenshots to attach or wants draft visual wireframe images generated from the text spec. Also state that milestone-level visual smoke checks in Vue Designer are the default for every implementation milestone, then ask what extra testing the user wants on top of that baseline, such as no automated tests yet, lightweight unit/component tests for logic, or specific manual QA steps. Ask whether implementation should run in Co-pilot mode or Auto-pilot mode; recommend Co-pilot unless the user wants the agent to self-review, auto-commit, and proceed through milestones. | ||
| Kickstart must finish as a docs-only intake milestone. Before editing app code, produce a visible `Kickstart Complete` boundary with docs updated, open questions that can wait, testing strategy, implementation mode, recommended next command after commit, and Git commit status. In Co-pilot mode, ask for explicit approval before committing the kickstart docs checkpoint. In Auto-pilot mode, self-review, record acceptance, commit automatically, then move to `plan`, not implementation. | ||
| For a new project, use this command order: | ||
| 1. `kickstart` completes intake, records implementation mode, and commits the docs checkpoint after Co-pilot approval or automatically in Auto-pilot mode. | ||
| 2. `plan` first confirms readiness or asks for more intake, then reviews and improves the milestone sequence, closes with `Plan Complete`, commits the planning checkpoint after Co-pilot approval or automatically in Auto-pilot mode, and gives the green flag for implementation. | ||
| 3. For each planned milestone, use `next-resume` to pick up from docs and implement the next approved milestone, `review` to check it, and `close-handoff` to update docs, handle the commit gate, and refresh continuation notes. In Auto-pilot mode this repeats automatically after each successful milestone commit until the plan is complete or a stop condition applies. | ||
| ## Commit Gate | ||
| If Git is available, every completed checkpoint or milestone must be committed before the next checkpoint or milestone starts. In Co-pilot mode, never commit without asking the user first. Use this shape: | ||
| ```text | ||
| Commit Approval Needed | ||
| Updates include: | ||
| - ... | ||
| Commit includes: | ||
| - ... | ||
| Excluded from commit: | ||
| - ... | ||
| Proposed commit: <subject> | ||
| Do you approve committing these updates? | ||
| ``` | ||
| When `AI_ASSISTANT_URL` is available, send `POST AI_ASSISTANT_URL?action=action-required` with the same commit summary, proposed subject, and suggestions such as `Approve commit`, `Rework first`, and `Switch to no-commit mode` before printing or ending with the terminal commit prompt. | ||
| Do not mark a checkpoint or milestone as complete, tick it in `project-docs/04-plan.md`, or `POST AI_ASSISTANT_URL?action=complete` until the commit approval is resolved and the Git commit succeeds. While waiting for commit approval, the checkpoint status is `Commit Approval Needed` and the progress item remains not done. If the user explicitly switches to no-commit mode, record that decision first; only then may the checkpoint be treated as accepted without a commit. | ||
| After a checkpoint commit succeeds, update `project-docs/04-plan.md` and related docs, then `POST AI_ASSISTANT_URL?action=milestones` with the committed checkpoint marked done, `POST AI_ASSISTANT_URL?action=complete`, and finally `POST AI_ASSISTANT_URL?action=action-required` with the next workflow step and a suggested command button. For kickstart this next action is normally `plan`; for plan it is normally `next-resume`; for a closed implementation milestone it is the next planned milestone or project completion. Post the next-step action after `complete` so the panel keeps the prompt visible. | ||
| When the user approves, create the Git commit with the proposed subject as the first line. Use the reported update-summary bullets as the commit body, but omit the `Update summary:` heading and any blank spacer line after it. Keep the body as plain bullet points so the commit preserves the same summary that was reviewed. | ||
| In Auto-pilot mode, do not ask for commit approval. Self-review against acceptance criteria, record the review result in the relevant docs, then create the Git commit automatically using the same subject/body rules before proceeding. | ||
| If the user approves, commit before proceeding. If the user says no, asks for adjustments, or unrelated/generated junk is present, resolve that first. If the user explicitly chooses no-commit mode for the whole project, record the decision in `project-docs/07-decisions.md`, `project-docs/04-plan.md`, and `project-docs/08-handoff.md`. | ||
| ## Keep Docs Live | ||
| - Keep `project-docs/01-brief.md` as the source of truth for the AI Assistant project brief stored in `pinegrow.json`. | ||
| - Update requirements, architecture, plan, acceptance criteria, verification, decisions, handoff, and asset specs when code changes affect them. | ||
| - Record important product or technical choices in `project-docs/07-decisions.md`. | ||
| - Record session context in `project-docs/09-session-log.md` so future agents can understand what happened without chat history. | ||
| - Record validation steps and results in `project-docs/06-verification.md`. | ||
| - Put next-session context in `project-docs/08-handoff.md`. | ||
| - Keep reference images, screenshots, and wireframes in `project-docs/assets/images/`; add live Markdown image links in the relevant pages, wireframes, or content docs. Do not wrap image links in backticks or code fences. Write them so they render as images. Example: - Image:  | ||
| - Keep project docs readable in terminals and Markdown editors: prefer dated/topic sections with short bullets over tables. Do not create wide multi-column tables for planning, session logs, requirements, pages, assets, or decisions. | ||
| ## Working Rules | ||
| - During intake, edit only `project-docs/`, root agent instructions, project brief metadata, and intake-created project-doc assets unless the user explicitly asks for implementation. | ||
| - Prefer the existing stack, folder structure, styling approach, and component conventions. | ||
| - Do not overwrite user-authored docs or assets without a clear request. | ||
| - Do not start the dev server or run app build commands. Vue Designer owns the active preview/dev runtime for this workflow. | ||
| - Include the Vue Designer visual smoke baseline and the user's additional testing preference in the plan and verification docs before implementation starts. | ||
| - Build milestone by milestone; do not implement the full site in one go. | ||
| - In Co-pilot mode, pause after each milestone for user review and commit approval before proceeding. In Auto-pilot mode, self-review against acceptance criteria, record the result, commit automatically, then proceed through the planned milestone loop until complete or blocked by a stop condition. | ||
| - Create Git commits grouped by completed milestones when Git is available. Ask first in Co-pilot mode; commit automatically in Auto-pilot mode. | ||
| - When adding or changing pages/components, update the matching files in `project-docs/assets/`. | ||
| - When work is incomplete, leave the project coherent and document the exact next step in `project-docs/08-handoff.md`. |
| --- | ||
| name: close-handoff | ||
| description: "Close an accepted Vue Designer milestone, update handoff notes, commit it, and in Auto-pilot mode proceed to the next planned milestone." | ||
| --- | ||
| # close-handoff | ||
| Read `../../../AGENTS.md`, plan, acceptance criteria, verification, decisions, handoff, session log, and closing audit. | ||
| Close only accepted work. Update verification, acceptance status, decisions if needed, `09-session-log.md`, `08-handoff.md`, and `10-closing-audit.md`. | ||
| Before closing an implementation milestone, make sure `06-verification.md` records the milestone-level Vue Designer visual smoke check plus any additional user-selected tests that were run or skipped. Do not treat optional automated tests as a replacement for the VD visual smoke baseline. | ||
| Before closing, confirm the milestone passed the Vue Designer visual and content authoring checks from `../../../AGENTS.md` and `../review/SKILL.md`. Do not close a milestone that hides static Tailwind styling in class-string constants, stores one-off page-visible content outside the rendering file without a recorded reason, bypasses existing foundational components such as `BaseButton` without a recorded reason, or hand-rolls interactive controls that should use an installed component library, unless the user explicitly accepts that exception and the reason is recorded in `07-decisions.md`. | ||
| After updating docs, ask for explicit commit approval in Co-pilot mode. When `AI_ASSISTANT_URL` is available, first `POST AI_ASSISTANT_URL?action=action-required` with the milestone close summary, changed areas/files, excluded junk/unrelated changes, proposed commit subject, and quick suggestions such as `Approve commit`, `Rework first`, and `Switch to no-commit mode`. Then ask in the terminal: | ||
| `Do you approve committing these updates?` | ||
| List the milestone updates included, changed areas/files, generated junk or unrelated changes to exclude, and a proposed commit subject. Commit only after approval in Co-pilot mode. In Auto-pilot mode, self-review the milestone, record acceptance, and commit automatically. Use the proposed subject as the commit subject and the reported update-summary bullets as the commit body, without the `Update summary:` heading. Do not start the next milestone until this checkpoint is committed, unless the user explicitly switches the project to no-commit mode. | ||
| Do not mark the implementation milestone done, tick it in `04-plan.md`, or `POST ...?action=complete` until the milestone commit succeeds or no-commit mode is explicitly recorded. While waiting for Co-pilot commit approval, keep status as `Commit Approval Needed` / not committed and milestone `done` false. After the commit succeeds, update milestones, `POST ...?action=milestones`, `POST ...?action=complete`, then `POST ...?action=action-required` with the next planned workflow step or project completion state. | ||
| Before ending, update `project-docs/08-handoff.md` with current state, completed milestones, in-progress milestone if any, next block-sized milestone, open questions, known risks, verification state, commit status, and files to read next. Do not duplicate full specs or logs already stored elsewhere; reference paths. | ||
| In Auto-pilot mode, do not stop merely because the accepted milestone was committed. After the commit succeeds, proceed directly to the next planned milestone unless the plan is complete, no next milestone is defined, required user input is missing, self-review fails, checks fail and cannot be fixed within the milestone scope, Git commit fails, the user interrupts or changes scope, a destructive/risky action needs approval, or the project is switched to Co-pilot or no-commit mode. | ||
| Do not start the dev server or run app build commands. Record any skipped checks honestly. |
| --- | ||
| name: next-resume | ||
| description: "Resume a Vue Designer specs-to-code project and implement the next approved milestone. In Auto-pilot mode, proceed through the planned milestone loop until complete or blocked." | ||
| --- | ||
| # next-resume | ||
| Read `../../../AGENTS.md`, project-root `AGENTS.md`, `project-docs/08-handoff.md`, `project-docs/09-session-log.md`, `project-docs/04-plan.md`, acceptance criteria, verification, pages/design docs, and the active VD route/selection when available. | ||
| First report where work stopped, what was accepted, what remains open, verification gaps, and the next block-sized milestone selected from the plan or handoff. | ||
| Before editing app code, check whether the previous checkpoint or milestone is committed. If Git is available and the worktree is dirty from a completed checkpoint, stop and ask for commit approval in Co-pilot mode instead of starting implementation. When `AI_ASSISTANT_URL` is available, first `POST AI_ASSISTANT_URL?action=action-required` with the commit approval summary and quick suggestions. In Auto-pilot mode, self-review the completed checkpoint, record acceptance, commit automatically, then proceed. | ||
| Before editing app code, state the next milestone scope, files likely to change, acceptance checks, the required Vue Designer visual smoke check, any additional tests selected by the user, implementation mode, existing foundational components that should be reused, and where page-visible content should live. In Co-pilot mode, proceed only when the user has approved that milestone. When `AI_ASSISTANT_URL` is available, first `POST AI_ASSISTANT_URL?action=action-required` with the scope summary and quick suggestions such as `Approve milestone`, `Adjust scope`, and `Pause`. In Auto-pilot mode, proceed after recording the planned scope until the milestone is ready for self-review and commit. | ||
| In Co-pilot mode, implement only the approved milestone and stop with a review summary, including the Vue Designer visual smoke result and any additional checks, then await approval. In Auto-pilot mode, a milestone boundary is a checkpoint, not a pause: self-review against acceptance criteria, update verification/acceptance docs, commit automatically, choose the next planned milestone, and proceed through the same loop. | ||
| Follow the Vue Designer visual and content authoring rules from `../../../AGENTS.md`: keep static Tailwind classes inline on their tags, reserve class bindings for real state, reuse or extend foundational components such as `BaseButton`/form controls before native interactive tags, keep one-off page-visible content in the page/component that renders it, and use installed component-library controls for interactive UI. Keep docs live. Do not start the dev server or run app build commands. | ||
| In Auto-pilot mode, stop only when the plan is complete, no next milestone is defined, required user input is missing, self-review fails, checks fail and cannot be fixed within the milestone scope, Git commit fails, the user interrupts or changes scope, a destructive/risky action needs approval, or the project is switched to Co-pilot or no-commit mode. |
| --- | ||
| name: review | ||
| description: "Review a Vue Designer milestone. Use when the user asks to review current work, check a page/section/component, compare against the spec, or decide whether a milestone is acceptable." | ||
| --- | ||
| # review | ||
| Read `../../../AGENTS.md`, acceptance criteria, plan, verification, relevant page/design docs, and inspect the VD-visible result when available. | ||
| Review in user-visible terms: route/page, section, component, layout, responsive behavior, props/actions, content/assets, wireframes, and console/runtime issues when known. Compare actual work against acceptance criteria and docs. | ||
| Every implementation milestone gets a milestone-level visual smoke check in Vue Designer by default. Confirm or report the VD-visible route/selection reviewed, the main viewport states checked, and whether any user-selected additional tests were run or intentionally skipped. | ||
| Check Vue Designer visual authoring quality before accepting a milestone: | ||
| - Static Tailwind utility classes should stay inline on the tag they style. | ||
| - `class:list`, `:class`, and computed classes should be reserved for genuinely conditional styling, with static base classes still inline. | ||
| - Repeated visual patterns should become components or wrappers, not hidden class-string variables. | ||
| - Interactive controls should use existing foundational components such as `BaseButton`, form controls, app UI config, or installed library components when available instead of hand-rolled native markup plus JavaScript. | ||
| - The inline Tailwind rule is not a valid reason by itself to bypass a foundational component. If native markup replaces a known component, the review must include the reason and whether the component should instead be extended, wrapped, or given a variant. | ||
| - One-off page-visible content should live in the `.astro`, `.vue`, or component template that renders it. `site.ts` and data modules should be reserved for global metadata, navigation, shared settings, and real content models. | ||
| Run or mentally apply this source scan to changed files when source access is available: | ||
| ```bash | ||
| rg -n "(const|let) .*Classes\\s*=|class=\\{.*Classes\\}|:class=.*Classes" src | ||
| rg -n "<button|<input|<select|<textarea|role=\"button\"|@click=" src | ||
| ``` | ||
| Treat hidden class-bundle matches as findings unless the class bundle is genuinely state-driven and the static base classes remain inline. Treat native interactive-control matches as findings when the project already has a suitable foundational/component-library control and no exception is recorded. | ||
| Also inspect `site.ts`, data modules, and frontmatter constants changed by the milestone. Treat one-off visible copy such as visit details, hours, emails, addresses, CTA labels, notes, and section text stored outside the rendering file as findings unless the external storage is genuinely reused, modeled, or documented. | ||
| Return findings first, then acceptance state: accepted, accepted with follow-up, or not accepted. Update verification and acceptance docs when asked or when closing the review loop. | ||
| When review needs user input, approval, or a rework decision and `AI_ASSISTANT_URL` is available, first `POST AI_ASSISTANT_URL?action=action-required` with a concise block/section/component-level summary, acceptance state, findings, and quick suggestions such as `Accept`, `Rework`, and `Present changes`. Then repeat the same decision prompt briefly in the terminal. |
| --- | ||
| name: kickstart | ||
| description: "Start or refresh a Vue Designer project spec. Use when the user says this is a new project, asks to start/kickstart, wants project-docs created, or needs a guided intake before building." | ||
| --- | ||
| # kickstart | ||
| Read `../../../AGENTS.md` and use it as the canonical workflow manifest. | ||
| Create or refresh project-docs if missing. Ask one question at a time; never dump a questionnaire. When `AI_ASSISTANT_URL` is available, `POST AI_ASSISTANT_URL?action=question` before each intake question so the Vue Designer Agents panel shows the prompt. Use concrete suggestions and allow `skip`, `not sure`, or rough answers. | ||
| Use this discovery order internally, skipping anything already clear from docs, VD project metadata, or the live preview: project purpose, audience, main pages/flows, first-version outcome, visual direction, animation expectations, wireframe inputs, content/data/integration needs, framework/meta-framework-specific needs, additional testing preference, implementation mode, deployment/stack constraints, and first-version non-goals. Milestone-level visual smoke checks in Vue Designer are the default for every implementation milestone; ask what additional testing the user wants on top of that baseline. Recommend Co-pilot mode by default; Auto-pilot mode means the agent self-reviews, records acceptance, commits automatically, and proceeds through milestones. | ||
| Update every affected doc after each useful answer. Use flat `##`/`###` sections and bullets instead of wide tables. Page answers update `02-requirements.md`, `assets/01-pages.md`, and `05-acceptance-criteria.md` when scope changes. Visual answers update design/wireframe docs. Testing answers update acceptance and verification with the Vue Designer visual smoke baseline plus any user-selected extra checks. Implementation mode answers update `04-plan.md`, `05-acceptance-criteria.md`, `08-handoff.md`, and `09-session-log.md`. Do not turn kickstart into the planning pass; leave detailed milestone sequencing for the `plan` skill. | ||
| Treat kickstart as its own docs-only intake milestone. Its job is to make the project understandable enough for `plan` to confirm readiness and create the milestone sequence. | ||
| End kickstart with a visible `Kickstart Complete` boundary. Include: | ||
| - docs updated, | ||
| - open questions that can safely wait, | ||
| - testing strategy: Vue Designer visual smoke baseline plus any user-selected extra checks, | ||
| - implementation mode, | ||
| - recommended next command after commit, normally `plan`, | ||
| - Git commit status. | ||
| After the kickstart docs are updated, ask for explicit commit approval in Co-pilot mode. When `AI_ASSISTANT_URL` is available, first `POST AI_ASSISTANT_URL?action=action-required` with the `Kickstart Complete` summary, changed areas/files, excluded junk/unrelated changes, proposed commit subject, and quick suggestions such as `Approve commit`, `Rework first`, and `Switch to no-commit mode`. Then print the same approval prompt in the terminal: | ||
| `Do you approve committing these updates?` | ||
| List the intake updates included, changed areas/files, generated junk or unrelated changes to exclude, and a proposed commit subject such as `docs: kickstart project intake`. Commit only after approval in Co-pilot mode. Do not mark the Kickstart intake checkpoint as done, tick it in `04-plan.md`, or `POST ...?action=complete` until the commit succeeds or the user explicitly records no-commit mode. While waiting, keep the status as `Commit Approval Needed` / not committed and keep any milestone `done` value false. In Auto-pilot mode, self-review the kickstart checkpoint, record acceptance, and commit automatically. Use the proposed subject as the commit subject and the reported update-summary bullets as the commit body, without the `Update summary:` heading. Keep that commit limited to `AGENTS.md`, `pinegrow.json` brief metadata, `project-docs/`, and project-doc assets created during intake. If Git is unavailable or the commit cannot be made, report why in the boundary. | ||
| After the kickstart commit succeeds, update `04-plan.md` so the Kickstart intake checkpoint is ticked and commit status is committed, `POST ...?action=milestones` with Kickstart done, `POST ...?action=complete`, then `POST ...?action=action-required` with `Next workflow step is plan` and a `plan` suggestion. Post the next-step action after `complete` so the panel keeps the prompt visible. | ||
| Do not start app-code implementation as part of kickstart. Do not move to `plan` until the kickstart checkpoint is committed, unless the user explicitly switches the project to no-commit mode. |
| --- | ||
| name: plan | ||
| description: "Confirm Vue Designer specs-to-code readiness and plan milestones. Use when the user asks where things stand, what is missing, to plan, break down work, create milestones, sequence pages/sections/components, or decide the next implementation order." | ||
| --- | ||
| # plan | ||
| Read `../../../AGENTS.md`, project-root `AGENTS.md`, and all available `project-docs/` markdown files. Include requirements, pages, acceptance criteria, verification, decisions, handoff/session log, and design/wireframe docs when present. | ||
| Start with a concise readiness pass. Check Git status when Git is available. The primary outcome is one of: `Commit Approval Needed`, `Ready for planning`, or `Needs more intake`. | ||
| Report: | ||
| - whether `project-docs/` exists and has the required spec files, | ||
| - current brief/product clarity, | ||
| - implementation mode, | ||
| - testing baseline and any extra selected checks, | ||
| - current or next milestone if one already exists, | ||
| - known blockers or decisions needed, | ||
| - wireframe/assets state when relevant, | ||
| - dirty worktree or uncommitted completed checkpoint state, | ||
| - whether planning can start or intake should proceed. | ||
| If the worktree is dirty after a completed checkpoint or milestone, do not create or revise the implementation plan yet. In Co-pilot mode, end with a visible `Commit Approval Needed` boundary. When `AI_ASSISTANT_URL` is available, first `POST AI_ASSISTANT_URL?action=action-required` with the changed areas/files, excluded junk/unrelated changes, proposed commit subject, and quick suggestions such as `Approve commit`, `Rework first`, and `Switch to no-commit mode`. Then ask in the terminal: | ||
| `Do you approve committing these updates?` | ||
| List changed areas/files, generated junk or unrelated changes to exclude, and a proposed commit subject. Commit only after approval in Co-pilot mode. If the user approves the commit from this boundary, use the proposed subject as the commit subject and the reported update-summary bullets as the commit body, without the `Update summary:` heading. | ||
| Do not mark the prior checkpoint done, tick it in `04-plan.md`, or `POST ...?action=complete` while this commit approval is still pending. After the commit succeeds, `POST ...?action=milestones`, then `POST ...?action=complete`, then `POST ...?action=action-required` with the next workflow step and suggested command. | ||
| In Auto-pilot mode, do not ask for commit approval from the readiness boundary. Self-review the completed checkpoint, record acceptance, create the Git commit automatically using the same subject/body rules, then proceed to planning when the docs are ready. | ||
| If required intake is missing, ask the next single intake question or recommend returning to `kickstart`. When `AI_ASSISTANT_URL` is available, first `POST AI_ASSISTANT_URL?action=question` for the intake question or `action-required` for the return-to-kickstart recommendation. Do not create a detailed implementation plan until the spec is clear enough to build the first useful version. | ||
| Break work into visual, reviewable milestones. Do not plan a whole-site pass. Prefer this order when applicable: layout shell, header, footer, shared layout parts, first page, first page section, section components, next section, next page, logic/integration chunks. | ||
| Each milestone needs scope, likely files, existing foundational components to reuse or extend, page-visible content placement, acceptance checks, VD visual smoke notes, any additional verification approach, implementation mode, review owner, and commit checkpoint expectation. Record the plan in `project-docs/04-plan.md`. | ||
| Use the plan pass to review and improve the milestone sequence with the user. End with a visible `Plan Complete` boundary when the plan is ready. Include the approved milestone order, the first milestone to run, acceptance/verification expectations, the default Vue Designer visual smoke baseline plus any additional tests, implementation mode, and Git commit status for the docs-only planning checkpoint. | ||
| After `Plan Complete`, ask for explicit commit approval in Co-pilot mode. When `AI_ASSISTANT_URL` is available, first `POST AI_ASSISTANT_URL?action=action-required` with the planning summary, changed areas/files, excluded junk/unrelated changes, proposed commit subject, and quick suggestions such as `Approve commit`, `Rework first`, and `Switch to no-commit mode`. Then ask in the terminal: | ||
| `Do you approve committing these updates?` | ||
| List the planning updates included, changed areas/files, generated junk or unrelated changes to exclude, and a proposed commit subject such as `docs: plan project milestones`. Commit only after approval in Co-pilot mode. In Auto-pilot mode, self-review the planning checkpoint, record acceptance, and commit automatically. Use the proposed subject as the commit subject and the reported update-summary bullets as the commit body, without the `Update summary:` heading. Keep the commit limited to `project-docs/`, project-doc assets, root agent instructions, and project brief metadata changed by planning. | ||
| Do not mark the Planning checkpoint done, tick it in `04-plan.md`, or `POST ...?action=complete` until the planning commit succeeds or no-commit mode is explicitly recorded. While waiting, keep status as `Commit Approval Needed` / not committed and milestone `done` false. After the commit succeeds, update milestones, `POST ...?action=milestones`, `POST ...?action=complete`, then `POST ...?action=action-required` with `Next workflow step is next-resume` and a `next-resume` suggestion. | ||
| Give the green flag for the repeated milestone cycle only after the planning checkpoint is committed. In Co-pilot mode, `next-resume` implements one approved milestone, `review` checks it, and `close-handoff` updates docs and commits it after user approval. In Auto-pilot mode, the same `next-resume` / `review` / `close-handoff` cycle proceeds across planned milestones after each successful commit until the plan is complete or a stop condition from `../../../AGENTS.md` applies. |
| { | ||
| "version": 1, | ||
| "projectDocs": [ | ||
| { "template": "project-docs/README.md", "output": "project-docs/README.md" }, | ||
| { "template": "project-docs/01_brief.md", "output": "project-docs/01-brief.md", "brief": true }, | ||
| { "template": "project-docs/02_requirements.md", "output": "project-docs/02-requirements.md" }, | ||
| { "template": "project-docs/03_architecture.md", "output": "project-docs/03-architecture.md" }, | ||
| { "template": "project-docs/04_plan.md", "output": "project-docs/04-plan.md" }, | ||
| { "template": "project-docs/05_acceptance-criteria.md", "output": "project-docs/05-acceptance-criteria.md" }, | ||
| { "template": "project-docs/06_verification.md", "output": "project-docs/06-verification.md" }, | ||
| { "template": "project-docs/07_decisions.md", "output": "project-docs/07-decisions.md" }, | ||
| { "template": "project-docs/08_handoff.md", "output": "project-docs/08-handoff.md" }, | ||
| { "template": "project-docs/09_session-log.md", "output": "project-docs/09-session-log.md" }, | ||
| { "template": "project-docs/10_closing-audit.md", "output": "project-docs/10-closing-audit.md" }, | ||
| { "template": "project-docs/assets/README.md", "output": "project-docs/assets/README.md" }, | ||
| { "template": "project-docs/assets/01_pages.md", "output": "project-docs/assets/01-pages.md" }, | ||
| { "template": "project-docs/assets/02_content-and-assets.md", "output": "project-docs/assets/02-content-and-assets.md" }, | ||
| { "template": "project-docs/assets/03_design-system.md", "output": "project-docs/assets/03-design-system.md" }, | ||
| { "template": "project-docs/assets/04_wireframes.md", "output": "project-docs/assets/04-wireframes.md" }, | ||
| { "template": "project-docs/assets/images/.gitkeep", "output": "project-docs/assets/images/.gitkeep" } | ||
| ] | ||
| } |
| {{BRIEF_MARKER}} | ||
| {{BRIEF_BODY}} |
| # Requirements | ||
| {{PROJECT_DOCS_MARKER}} | ||
| ## Product Goals | ||
| - TBD | ||
| ## Functional Requirements | ||
| ### R1: TBD | ||
| - Priority: Must | ||
| - Status: Draft | ||
| - User value: TBD | ||
| - Details: TBD | ||
| - Acceptance link: `05-acceptance-criteria.md#AC1` | ||
| ## Content Requirements | ||
| - Pages/routes: | ||
| - Components: | ||
| - Data/content sources: | ||
| - Media/assets: | ||
| ## Visual Requirements | ||
| - Brand signals: | ||
| - Layout rules: | ||
| - Accessibility expectations: | ||
| - Responsive expectations: | ||
| ## Non-Goals | ||
| - TBD | ||
| ## Open Questions | ||
| - TBD |
| # Architecture | ||
| {{PROJECT_DOCS_MARKER}} | ||
| ## Stack | ||
| - Framework: | ||
| - Styling: | ||
| - Data/content: | ||
| - Integrations: | ||
| - Deployment: | ||
| ## Source Map | ||
| ### Pages And Routes | ||
| - Path: TBD | ||
| - Notes: TBD | ||
| ### Components | ||
| - Path: TBD | ||
| - Notes: TBD | ||
| ### Styles | ||
| - Path: TBD | ||
| - Notes: TBD | ||
| ### Data And Content | ||
| - Path: TBD | ||
| - Notes: TBD | ||
| ### Assets | ||
| - Path: TBD | ||
| - Notes: TBD | ||
| ## Runtime Flow | ||
| 1. TBD | ||
| ## Editing And Agent Notes | ||
| - Where visual edits should happen: | ||
| - Where data edits should happen: | ||
| - Page-visible content placement: keep one-off page and section copy in the `.astro`, `.vue`, or component template that renders it; use `site.ts` only for global/shared metadata, nav, and settings. | ||
| - Styling convention: keep static Tailwind classes inline on their tags for Vue Designer/Pinegrow editing; use class bindings only for real state. | ||
| - Foundational components: document project-owned controls such as `BaseButton`, `BaseIcon`, form fields, app UI config, and wrappers that agents should reuse before native interactive tags. | ||
| - Component library convention: when a UI library is installed, use its controls for interactive UI and document any exceptions here. | ||
| - Files that should not be hand-edited: | ||
| ## Risks | ||
| - TBD |
| # Plan | ||
| {{PROJECT_DOCS_MARKER}} | ||
| ## Execution Mode | ||
| - Implementation mode: Co-pilot by default. The user is the Pilot. Capture the selected mode during kickstart. | ||
| - Co-pilot mode: the user reviews each checkpoint/milestone and approves the Git commit before the next milestone starts. | ||
| - Auto-pilot mode: the agent self-reviews each checkpoint/milestone, records acceptance, commits automatically, and proceeds through the planned milestone loop until the plan is complete or a stop condition requires user input. | ||
| - Auto-pilot stop conditions: plan complete; no next milestone defined; required user input missing; self-review failed; checks failed and cannot be fixed within scope; Git commit failed; user interrupted or changed scope; destructive/risky action needs approval; project switched to Co-pilot or no-commit mode. | ||
| ## Milestones | ||
| 1. [ ] Kickstart intake checkpoint. | ||
| Scope: Confirm brief, requirements, first pages, wireframe inputs, default Vue Designer visual smoke baseline, any additional testing preference, implementation mode, and open questions that can wait. End with `Commit Approval Needed` in Co-pilot mode until the docs checkpoint commit succeeds; tick only after commit or explicitly recorded no-commit mode. | ||
| 2. [ ] Status readiness checkpoint. | ||
| Scope: Confirm whether docs are `Ready for planning`, need more intake, or are blocked by uncommitted checkpoint changes. End with `Status Complete`, Co-pilot `Commit Approval Needed`, or Auto-pilot self-review plus automatic commit. | ||
| 3. [ ] Planning checkpoint. | ||
| Scope: Review and improve the milestone sequence, acceptance checks, default Vue Designer visual smoke checks, any additional verification approach, implementation mode, and first milestone. End with `Commit Approval Needed` in Co-pilot mode until the planning checkpoint commit succeeds; tick only after commit or explicitly recorded no-commit mode. | ||
| 4. [ ] Layout shell. | ||
| Scope: App shell, shared layout structure, and page container rules. | ||
| 5. [ ] Header. | ||
| Scope: Header/navigation component and responsive behavior. | ||
| 6. [ ] Footer. | ||
| Scope: Footer component and shared footer content. | ||
| 7. [ ] First page: first section. | ||
| Scope: First page section, for example hero, plus its local components. | ||
| 8. [ ] Continue page sections. | ||
| Scope: Next section and its components, one section at a time. | ||
| 9. [ ] Next page or logic chunk. | ||
| Scope: Next page, integration, data, or interaction milestone. | ||
| 10. [ ] Milestone verification. | ||
| Scope: Acceptance checks, user review or self-review, docs update, and commit. | ||
| ## Current Milestone | ||
| - Name: Kickstart intake checkpoint | ||
| - Goal: Make the project understandable enough for `plan` to confirm readiness and define reviewable milestones. | ||
| - Status: Draft | ||
| - Review/acceptance owner: User in Co-pilot mode; agent self-review and automatic commit in Auto-pilot mode. | ||
| - Commit status: Not committed | ||
| ## Milestone Rules | ||
| - Never implement the full site in one go. | ||
| - New projects run `kickstart`, then `plan` before app-code implementation starts. | ||
| - Build in small chunks: layout shell, header, footer, shared layout parts, first page, first page section, section components, next section, next page, and separate logic/integration chunks. | ||
| - End each milestone with updated docs, verification notes, review result, and explicit commit approval in Co-pilot mode or automatic commit in Auto-pilot mode. | ||
| - Every implementation milestone includes a milestone-level visual smoke check in Vue Designer. User-selected tests are additional and do not replace that baseline. | ||
| - Do not tick a checkpoint/milestone as done or start the next milestone while the current checkpoint is waiting for commit approval or remains uncommitted. | ||
| - After `Plan Complete` is committed, repeat `next-resume`, `review`, and `close-handoff` for every planned milestone; in Auto-pilot mode this proceeds automatically after each successful milestone commit until complete or blocked. | ||
| - Do not start the dev server or run app build commands from this workflow; Vue Designer owns the running preview. | ||
| ## Next Up | ||
| - Complete `kickstart` one question at a time, then run `plan` to confirm readiness and define the implementation milestones before app-code work starts. |
| # Acceptance Criteria | ||
| {{PROJECT_DOCS_MARKER}} | ||
| ## User-Facing Criteria | ||
| ### AC1: TBD | ||
| - Status: Draft | ||
| - Verification: TBD | ||
| - Requirement link: `02-requirements.md#R1` | ||
| - Notes: TBD | ||
| ## Technical Criteria | ||
| - Main routes render in Vue Designer preview: | ||
| - Key interactions work: | ||
| - Content authoring quality: one-off visible page/section copy is colocated in the rendering file; shared metadata, navigation, and real content models are the only content moved to `site.ts` or data modules. | ||
| - Vue Designer visual authoring quality: static Tailwind classes remain inline on the elements they style, class bindings are used only for real state, existing foundational components are reused or extended for interactive controls, and installed component-library controls are used where appropriate. | ||
| - Milestone-level visual smoke checks in Vue Designer are recorded for every implementation milestone: | ||
| - Additional tests match the preference captured during kickstart: | ||
| - No known regressions: | ||
| ## Milestone Acceptance | ||
| - Each milestone has a clear scope, acceptance criteria, verification notes, and review result. | ||
| - Co-pilot mode: the user reviews, accepts, and approves the commit before the next milestone starts. | ||
| - Auto-pilot mode: the agent self-reviews against this file, records acceptance, commits automatically, and continues. | ||
| - Each accepted milestone is committed as its own Git checkpoint before the next milestone starts, unless the user explicitly switches the project to no-commit mode. | ||
| ## Done Means | ||
| - Requirements are implemented or explicitly deferred. | ||
| - Visual and content changes are reflected in `project-docs/assets/`. | ||
| - Verification notes are current. | ||
| - Commit approval is resolved in Co-pilot mode, or Auto-pilot self-review has recorded acceptance and committed automatically. No-commit mode must be explicitly recorded. | ||
| - Handoff notes explain what changed and what remains. |
| # Verification | ||
| {{PROJECT_DOCS_MARKER}} | ||
| Record checks here whenever code, content, routes, assets, or configuration changes. Do not start the dev server or run app build commands from this workflow; use the Vue Designer preview/runtime. Every implementation milestone gets a milestone-level visual smoke check in Vue Designer by default, and any additional testing preference agreed during kickstart is added on top of that baseline. | ||
| ## Testing Strategy | ||
| - Default baseline: milestone-level visual smoke checks in Vue Designer for every implementation milestone | ||
| - Additional automated test level: TBD | ||
| - Additional manual review expectations: TBD | ||
| - Checks the agent should avoid: dev-server start and app build commands | ||
| ## Current Verification Plan | ||
| Milestone focus: | ||
| - Confirm the current milestone works in Vue Designer preview. | ||
| - Check the user-facing path affected by the milestone. | ||
| - Record the Vue Designer route/selection, viewport states, and visible result reviewed. | ||
| - Run any additional tests or manual checks selected by the user. | ||
| - Keep docs and handoff current before closing the milestone. | ||
| Initial verification expectations: | ||
| - Review the changed page, component, or flow in Vue Designer. | ||
| - Run the milestone-level Vue Designer visual smoke check plus any tests/checks agreed during kickstart. | ||
| - Do not start the dev server or run app build commands from this workflow. | ||
| Completed verification: | ||
| - Not run yet. | ||
| Residual verification: | ||
| - Manual visual review remains pending until the first milestone is implemented. | ||
| ## {{CURRENT_DATE}} Checks | ||
| ### {{CURRENT_DATETIME}} - Project Docs Initialized | ||
| - Check: Created or refreshed spec-to-code docs. | ||
| - Result: Completed. | ||
| - Notes: Implementation has not started yet. | ||
| ## Manual QA Checklist | ||
| - [ ] Key pages load. | ||
| - [ ] Primary user flow works. | ||
| - [ ] Mobile layout is usable. | ||
| - [ ] Desktop layout is usable. | ||
| - [ ] Visual assets render as expected. | ||
| - [ ] No console errors in the main flow. | ||
| ## Follow-Up Checks | ||
| - Complete intake, then replace this section with milestone-specific checks. |
| # Decisions | ||
| {{PROJECT_DOCS_MARKER}} | ||
| Use this file for decisions that future agents should not have to rediscover. Add the newest decision as a `## Decision: ...` section with the date, reason, and impact in bullets. | ||
| ## Decision: TBD | ||
| - Date: YYYY-MM-DD | ||
| - Status: Draft | ||
| - Decision: TBD | ||
| - Reason: TBD | ||
| - Impact: TBD |
| # Handoff | ||
| {{PROJECT_DOCS_MARKER}} | ||
| ## Current Milestone | ||
| - Intake and testing strategy. | ||
| ## Current State | ||
| - Project docs are initialized as draft placeholders. | ||
| - Implementation should not start until the brief, first requirements, first milestone, wireframe direction, Vue Designer visual smoke baseline, and any additional testing preference are clear enough. | ||
| ## Completed | ||
| - Root agent instructions created or refreshed. | ||
| - Numbered project-docs scaffold created or refreshed. | ||
| - Asset docs and images folder created or refreshed. | ||
| ## In Progress | ||
| - Guided kickstart intake. | ||
| ## Next Task | ||
| Proceed with intake one question at a time. Update the affected docs after each useful answer, then propose the first block-sized milestone for review. | ||
| ## Changed Files In Latest Work | ||
| - `AGENTS.md` | ||
| - `project-docs/` | ||
| ## Checks Run | ||
| - Project docs scaffold created or refreshed. | ||
| ## Open Tasks And Known Failures | ||
| - Product brief is still draft until intake is complete. | ||
| - Requirements, plan, acceptance criteria, verification, pages, assets, and wireframes need project-specific content. | ||
| ## Files To Read Next | ||
| - `AGENTS.md` | ||
| - `node_modules/@pinegrow/vite-plugin/agent-workflows/specs-to-code/AGENTS.md` | ||
| - `node_modules/@pinegrow/vite-plugin/agent-workflows/specs-to-code/skills/specs/kickstart/SKILL.md` | ||
| - `project-docs/README.md` | ||
| - `project-docs/01-brief.md` | ||
| - `project-docs/02-requirements.md` | ||
| - `project-docs/04-plan.md` | ||
| - `project-docs/06-verification.md` | ||
| - `project-docs/09-session-log.md` |
| # Session Log | ||
| {{PROJECT_DOCS_MARKER}} | ||
| Use this file for concise dated session context that future agents need but should not have to rediscover from chat history. Follow the flat dated style used by Vue Designer internal project logs: one `## YYYY-MM-DD` section per day, with short bullets instead of tables. | ||
| ## {{CURRENT_DATE}} | ||
| - {{CURRENT_DATETIME}} project docs initialized: draft placeholders created, root agent instructions refreshed when template-managed, and implementation left pending until kickstart intake is complete. | ||
| - Verification: docs scaffold created or refreshed; no app checks run yet. | ||
| - Next notes: complete intake one question at a time before implementation. |
| # Closing Audit | ||
| {{PROJECT_DOCS_MARKER}} | ||
| Use this when a milestone or project slice is ready to close. | ||
| ## Status | ||
| - Open | ||
| ## Scope Reviewed | ||
| - Requirements: | ||
| - Acceptance criteria: | ||
| - Implementation: | ||
| - Docs: | ||
| - Assets: | ||
| ## Verification Summary | ||
| - Vue Designer visual smoke: | ||
| - Commands/checks run: | ||
| - Additional manual QA: | ||
| - Known skipped checks: | ||
| ## Remaining Work | ||
| - TBD | ||
| ## Closeout Decision | ||
| - TBD | ||
| ## Commit Approval | ||
| - Updates included: | ||
| - Excluded from commit: | ||
| - Proposed commit: | ||
| - Approval status: | ||
| - Commit hash: |
| # Pages | ||
| {{PROJECT_DOCS_MARKER}} | ||
| ## Routes | ||
| ### Route: `/` | ||
| - Source file: TBD | ||
| - Layout: Default | ||
| - Purpose: Homepage or main entry | ||
| - Status: Draft | ||
| - Notes: TBD | ||
| ## Shared Layouts | ||
| ### Layout: Default | ||
| - Used by: | ||
| - Header/nav: | ||
| - Footer: | ||
| - Main layout container: | ||
| - Metadata/SEO: | ||
| - Responsive rules: | ||
| - Shared components: | ||
| - Wireframe/reference: | ||
| ## Shared Components | ||
| Use this section for components reused across several pages or layouts. | ||
| ### Component: TBD | ||
| - Path: TBD | ||
| - Used by: TBD | ||
| - Purpose: TBD | ||
| - Status: Draft | ||
| - Props/state/data: TBD | ||
| - Notes: TBD | ||
| ## Page Specs | ||
| ### Page: TBD | ||
| - Route: | ||
| - Source file: | ||
| - Layout: | ||
| - Purpose: | ||
| - Composition: | ||
| - Primary content: | ||
| - Shared components: | ||
| - Page-specific components: | ||
| - Component states/props/data: | ||
| - Wireframe/reference: | ||
| - Responsive notes: | ||
| - Acceptance notes: | ||
| ## Page, Layout, And Component Wireframes | ||
| Store sketches, screenshots, and generated wireframes in `project-docs/assets/images/`, then add live image links here, not bare file paths or code-formatted links. Prefer page/layout image names like `wireframe-pages-<name>.svg` and component image names like `wireframe-components-<name>.svg`. | ||
| ### Wireframe: TBD Page | ||
| - Type: Page | ||
| - Image:  | ||
| - Status: Draft | ||
| - Notes: Default generated format is SVG unless user advises otherwise. | ||
| ### Wireframe: TBD Component | ||
| - Type: Component | ||
| - Image:  | ||
| - Status: Draft | ||
| - Notes: Default generated format is SVG unless user advises otherwise. |
| # Content And Assets | ||
| {{PROJECT_DOCS_MARKER}} | ||
| ## Content Sources | ||
| - Local content: | ||
| - CMS/API: | ||
| - Generated content: | ||
| ## Copy Inventory | ||
| ### Area: TBD | ||
| - Source: TBD | ||
| - Notes: TBD | ||
| - Owner/status: Draft | ||
| ## Media Inventory | ||
| ### Asset: TBD | ||
| - Location: `project-docs/assets/images/` | ||
| - Use: TBD | ||
| - Status: Draft | ||
| - Source/license: TBD | ||
| - Notes: TBD | ||
| ## Asset Rules | ||
| - Store planning/reference images in `project-docs/assets/images/`. | ||
| - Store production app assets in the app's normal public/static asset folders. | ||
| - Record source, license, generation prompt, and intended use when relevant. | ||
| - When documenting images in Markdown, add live image links so they render as images, not bare file paths and not code-formatted links. Example:  |
| # Design System | ||
| {{PROJECT_DOCS_MARKER}} | ||
| ## Brand Feel | ||
| - Keywords: | ||
| - What the UI should communicate: | ||
| - What the UI should avoid: | ||
| ## Colors | ||
| ### Primary | ||
| - Value: TBD | ||
| - Use: TBD | ||
| - Notes: TBD | ||
| ## Typography | ||
| - Heading font: | ||
| - Body font: | ||
| - Scale rules: | ||
| ## Layout | ||
| - Container widths: | ||
| - Grid rules: | ||
| - Section spacing: | ||
| - Mobile behavior: | ||
| ## Components | ||
| - Buttons: | ||
| - Cards/panels: | ||
| - Forms: | ||
| - Navigation: | ||
| ## Motion And Interaction | ||
| - Hover/focus: | ||
| - Transitions: | ||
| - Reduced motion: | ||
| ## Accessibility | ||
| - Contrast: | ||
| - Keyboard: | ||
| - Semantics: |
| # Wireframes | ||
| {{PROJECT_DOCS_MARKER}} | ||
| Use this file to plan or generate wireframes from the rest of the project docs. Default generated wireframes to SVG unless the user advises otherwise. Store generated SVG wireframes or supplied image files in `project-docs/assets/images/` and link them here, plus in the matching pages doc, using live image links instead of bare file paths or code-formatted links. | ||
| ## Source Docs To Consider | ||
| - `project-docs/01-brief.md` | ||
| - `project-docs/02-requirements.md` | ||
| - `project-docs/03-architecture.md` | ||
| - `project-docs/assets/01-pages.md` | ||
| - `project-docs/assets/02-content-and-assets.md` | ||
| - `project-docs/assets/03-design-system.md` | ||
| ## Page Wireframes | ||
| ### Page/Layout: TBD | ||
| - Image:  | ||
| - Status: Draft | ||
| - Notes: Default generated format is SVG unless user advises otherwise. | ||
| - Linked from pages doc: No | ||
| ## Component Wireframes | ||
| ### Component: TBD | ||
| - Image:  | ||
| - Status: Draft | ||
| - Notes: Default generated format is SVG unless user advises otherwise. | ||
| - Linked from pages doc: No | ||
| ## Generation Notes | ||
| - Intake question: do you already have sketches/Figma/screenshots to attach, or should the next pass create draft visual wireframe images from this text spec? | ||
| - Inputs used: | ||
| - Assumptions made: | ||
| - Open visual questions: | ||
| - Follow-up sketches/images needed: | ||
| - Image links added to page/layout/component sections in the pages doc: |
Sorry, the diff of this file is not supported yet
| # Project Assets And Specs | ||
| {{PROJECT_DOCS_MARKER}} | ||
| This folder keeps page, layout, component, content, media, wireframe, and design-system details close to the project docs. | ||
| ## Files | ||
| - `01-pages.md`: routes, layouts, page purpose, page composition, shared components, page-specific components, SEO notes, responsive notes, and page/component wireframe blocks. | ||
| - `02-content-and-assets.md`: copy, content/data sources, media inventory, asset rules, production asset locations, source/license notes, and image usage. | ||
| - `03-design-system.md`: brand feel, color roles, typography, spacing, layout rules, interaction states, motion, accessibility, and design constraints. | ||
| - `04-wireframes.md`: generated or planned wireframes derived from the rest of the project docs, with image links for sketches/images under `images/`. | ||
| - `images/`: screenshots, references, sketches, generated images, and wireframes. | ||
| ## Maintenance Rules | ||
| - Update these files whenever pages, layouts, components, copy, assets, or visual direction changes. | ||
| - Keep page/layout/component structure together in `01-pages.md` so agents can understand each page as a complete unit. | ||
| - Keep wireframe references in `04-wireframes.md` and add matching live image links from the page/layout/component sections in `01-pages.md`. Do not wrap image links in backticks or code fences. Store generated page wireframes as `images/wireframe-pages-<name>.svg` where possible. Example:  | ||
| - Ask before deleting, moving, renaming, or overwriting user-provided assets. | ||
| - Prefer `###` sections and bullets for pages, components, assets, and wireframes. Avoid wide tables; they become hard to read once page/component details grow. |
| # Project Docs | ||
| {{PROJECT_DOCS_MARKER}} | ||
| These files are the live spec-to-code source for the project. Keep them current as the product, implementation, verification, and handoff context changes. | ||
| ## Core Files | ||
| - `01-brief.md`: project purpose, audience, scope, non-goals, tech stack, and AI Assistant brief source. | ||
| - `02-requirements.md`: functional requirements, content needs, user journeys, priorities, non-goals, and open questions. | ||
| - `03-architecture.md`: stack, routes, data/content model, integrations, deployment, source map, and editing guidance. | ||
| - `04-plan.md`: block-by-block milestones, current status, review gates, commit grouping, and next implementation steps. | ||
| - `05-acceptance-criteria.md`: observable pass/fail criteria and done definitions. | ||
| - `06-verification.md`: checks run, manual QA, browser/device coverage, failures, and follow-up validation. | ||
| - `07-decisions.md`: dated product and technical decisions with reason and impact. | ||
| - `08-handoff.md`: current state, next steps, known issues, and files the next agent must read. | ||
| - `09-session-log.md`: concise session history and context for future agents. | ||
| - `10-closing-audit.md`: final review checklist for closing a milestone or project slice. | ||
| ## Asset Files | ||
| See `assets/README.md` for pages, layouts, components, content, images, wireframes, and design-system docs. | ||
| ## Maintenance Rules | ||
| - Start each agent session from root `AGENTS.md`, then this README and the relevant numbered docs. | ||
| - For a new project, use `kickstart` first, then `plan`; only after the `Plan Complete` checkpoint is committed move into the `next-resume` / `review` / `close-handoff` milestone loop. | ||
| - Capture implementation mode during kickstart. Co-pilot mode asks for commit approval before the next checkpoint starts. Auto-pilot mode self-reviews, records acceptance, commits automatically, and proceeds. | ||
| - Keep docs in sync with code changes in the same work session. | ||
| - Plan and build milestone by milestone instead of implementing the full site in one go. | ||
| - Do not start the dev server or run app build commands from this workflow; use Vue Designer's running preview. | ||
| - Treat milestone-level visual smoke checks in Vue Designer as the default verification baseline. Any user-selected automated or manual testing is additional. | ||
| - Store reference images, sketches, and wireframes in `assets/images/` and add image links in the relevant pages, wireframes, or content docs. | ||
| - Use `08-handoff.md` and `09-session-log.md` to make continuation possible without chat history. | ||
| - Follow the same readable artefact shape as Vue Designer project logs: `04-plan.md` uses a milestone checklist, `06-verification.md` uses verification-plan/completed/residual sections, `09-session-log.md` uses dated sections, and `08-handoff.md` keeps current state, next task, checks, and open items. Avoid wide tables; split anything with many fields into `###` blocks with bullets. |
| export interface AstroRuntimeBridgeOptions { | ||
| markerAttribute?: string | ||
| root?: Document | Element | ||
| win?: Window | ||
| clearPrevious?: boolean | ||
| } | ||
| export type AstroSourceDomHook = 'mounted' | 'updated' | 'unmounted' | ||
| export interface AstroElCacheEntry { | ||
| el: Element | ||
| framework: 'astro' | ||
| entryKind: 'source-node' | 'component-boundary' | 'page-root' | ||
| treeScopes: { | ||
| pageTree: boolean | ||
| appTree: boolean | ||
| } | ||
| isAstro: true | ||
| isAstroSourceDomEntry: boolean | ||
| isAstroComponent: boolean | ||
| isAppTreeComponent: boolean | ||
| isAstroPageRoot?: boolean | ||
| sourceId: string | ||
| pgId: string | null | ||
| key: string | null | ||
| localFile: string | null | ||
| sourceFile: string | null | ||
| nodeType: string | null | ||
| name: string | ||
| source: { | ||
| id: string | ||
| file: string | null | ||
| range: { | ||
| start: number | null | ||
| end: number | null | ||
| status: string | ||
| } | ||
| nodeType: string | null | ||
| name: string | ||
| } | ||
| component: { | ||
| file: string | ||
| name: string | ||
| kind: string | ||
| } | null | ||
| owners: Array<{ | ||
| kind: string | ||
| file: string | ||
| name?: string | ||
| }> | ||
| sourceRange: { | ||
| start: number | null | ||
| end: number | null | ||
| status: string | ||
| } | ||
| sourceStart: number | null | ||
| sourceEnd: number | null | ||
| sourceRangeStatus: string | ||
| marker: unknown | ||
| editability: unknown | ||
| attributes: unknown[] | ||
| isFragment: false | ||
| isRootFragment: false | ||
| isIsland: false | ||
| rootEl: null | ||
| firstEl: Element | ||
| lastEl: Element | null | ||
| instance: { | ||
| uid: string | ||
| isMounted: boolean | ||
| isUnmounted: boolean | ||
| type: { | ||
| __name?: string | ||
| __file?: string | null | ||
| } | ||
| props: Record<string, unknown> | ||
| attrs: Record<string, unknown> | ||
| slots: Record<string, unknown> | ||
| subTree: { | ||
| el: Element | ||
| } | ||
| appContext: unknown | ||
| } | ||
| vnode: null | ||
| } | ||
| export type AstroComponentEntry = AstroElCacheEntry & { | ||
| entryKind: 'component-boundary' | 'page-root' | ||
| treeScopes: { | ||
| pageTree: false | ||
| appTree: true | ||
| } | ||
| isAstroSourceDomEntry: false | ||
| isAstroComponent: true | ||
| isAppTreeComponent: true | ||
| pgId: null | ||
| key: null | ||
| localFile: string | ||
| sourceFile: string | ||
| nodeType: 'component-file' | ||
| component: { | ||
| file: string | ||
| name: string | ||
| kind: string | ||
| } | ||
| } | ||
| export function createAstroComponentEntries(input: { | ||
| entries: AstroElCacheEntry[] | ||
| manifest: unknown | ||
| root?: Document | Element | ||
| }): AstroComponentEntry[] | ||
| export function createAstroElCacheEntry(input: { | ||
| element: Element | ||
| manifest: unknown | ||
| node: unknown | ||
| }): AstroElCacheEntry | ||
| export function createAstroRuntimeBridge(manifest: unknown, options?: AstroRuntimeBridgeOptions): { | ||
| manifest: unknown | ||
| collect(root?: Document | Element): unknown | ||
| refresh(options?: AstroRuntimeBridgeOptions): unknown | ||
| } | ||
| export function ensurePinegrowAstroRuntime(win?: Window): unknown | ||
| export function refreshAstroSourceDomMapping( | ||
| hook: AstroSourceDomHook, | ||
| manifest: unknown, | ||
| options?: AstroRuntimeBridgeOptions | ||
| ): unknown | ||
| export function clearAstroSourceDomMapping(manifest: unknown, options?: AstroRuntimeBridgeOptions): unknown | ||
| export function collectAstroSourceDomMapping( | ||
| root: Document | Element, | ||
| manifest: unknown, | ||
| options?: AstroRuntimeBridgeOptions | ||
| ): unknown |
| import { | ||
| createSourceDomCacheEntry, | ||
| createSourceDomReactiveMap, | ||
| createSourceDomTreeScopes, | ||
| ensurePinegrowSourceDomRuntime, | ||
| normalizeSourceDomCacheEntries, | ||
| upsertSourceDomCacheEntry, | ||
| } from '../../../shared/mapping/runtime/runtime.js' | ||
| const defaultMarkerAttribute = 'data-pg-id' | ||
| const pinegrowMarkerAttribute = 'data-pg-id' | ||
| const astroFrameworkKey = 'astro' | ||
| const astroSourceIdPrefix = 'pgastro-' | ||
| const astroStateSnapshotAttribute = 'data-pg-astro-state' | ||
| const astroStateSnapshotJsonAttribute = 'data-pg-astro-state-json' | ||
| const astroStateSnapshotSourceAttribute = 'data-pg-astro-source' | ||
| const elementNodeType = 1 | ||
| const commentNodeType = 8 | ||
| const documentPositionPreceding = 2 | ||
| const documentPositionFollowing = 4 | ||
| const createSyntheticInstance = (node, element) => ({ | ||
| uid: `astro:${node.id}`, | ||
| isMounted: true, | ||
| isUnmounted: false, | ||
| parent: null, | ||
| type: { | ||
| __name: node.name || 'AstroElement', | ||
| __file: null, | ||
| }, | ||
| props: {}, | ||
| attrs: {}, | ||
| slots: {}, | ||
| subTree: { | ||
| el: element, | ||
| }, | ||
| appContext: null, | ||
| }) | ||
| const getDefaultWindow = () => { | ||
| if (typeof window !== 'undefined') { | ||
| return window | ||
| } | ||
| return null | ||
| } | ||
| const getMarkerAttribute = (manifest, options = {}) => | ||
| options.markerAttribute || manifest?.marker?.attribute || defaultMarkerAttribute | ||
| const getManifestSourceKey = manifest => manifest?.sourceFile || manifest?.id || null | ||
| const getEntryFirstElement = entry => entry?.firstEl || entry?.el || null | ||
| const getEntryLastElement = entry => entry?.lastEl || entry?.el || null | ||
| const sortEntriesByDocumentPosition = entries => | ||
| [...entries].sort((a, b) => { | ||
| const aEl = getEntryFirstElement(a) | ||
| const bEl = getEntryFirstElement(b) | ||
| if (!aEl || !bEl || aEl === bEl) { | ||
| return 0 | ||
| } | ||
| const position = aEl.compareDocumentPosition(bEl) | ||
| if (position & documentPositionPreceding) { | ||
| return 1 | ||
| } | ||
| if (position & documentPositionFollowing) { | ||
| return -1 | ||
| } | ||
| return 0 | ||
| }) | ||
| const normalizePathLike = value => `${value || ''}`.replace(/\\/g, '/') | ||
| const normalizeSnapshotSource = value => normalizePathLike(value).toLowerCase() | ||
| const isMatchingSnapshotSource = (snapshotSource, sourceFile) => | ||
| snapshotSource === sourceFile || | ||
| normalizeSnapshotSource(snapshotSource) === normalizeSnapshotSource(sourceFile) | ||
| const getBasename = value => { | ||
| if (!value) { | ||
| return '' | ||
| } | ||
| return normalizePathLike(value).split('/').pop() || '' | ||
| } | ||
| const isPageSourceFile = sourceFile => | ||
| /(^|\/)src\/pages\/.+\.astro$/i.test(normalizePathLike(sourceFile)) | ||
| const getRootElement = root => { | ||
| if (!root) { | ||
| return null | ||
| } | ||
| if (root.documentElement) { | ||
| return root.documentElement | ||
| } | ||
| if (root.body) { | ||
| return root.body | ||
| } | ||
| if (root.firstElementChild) { | ||
| return root.firstElementChild | ||
| } | ||
| return root.nodeType === 1 ? root : null | ||
| } | ||
| const createComponentSyntheticInstance = (manifest, componentId, element) => ({ | ||
| uid: componentId, | ||
| isMounted: true, | ||
| isUnmounted: false, | ||
| parent: null, | ||
| type: { | ||
| __name: getBasename(manifest?.sourceFile) || 'AstroComponent', | ||
| __file: manifest?.sourceFile || null, | ||
| }, | ||
| props: {}, | ||
| attrs: {}, | ||
| slots: {}, | ||
| subTree: { | ||
| el: element, | ||
| }, | ||
| appContext: null, | ||
| }) | ||
| const createNodeIndex = manifest => { | ||
| const index = new Map() | ||
| ;(manifest?.nodes || []).forEach(node => { | ||
| if (node?.id) { | ||
| index.set(node.id, node) | ||
| } | ||
| }) | ||
| return index | ||
| } | ||
| const createStateSnapshotKey = record => `${record?.type || ''}:${record?.key || ''}` | ||
| const decodeAstroSnapshotValue = value => { | ||
| if (!value || typeof value !== 'object' || Array.isArray(value)) { | ||
| return value | ||
| } | ||
| if (value.__pinegrowAstroType === 'undefined') { | ||
| return undefined | ||
| } | ||
| if (value.__pinegrowAstroType === 'bigint') { | ||
| return `${value.value || ''}n` | ||
| } | ||
| if (value.__pinegrowAstroType === 'function') { | ||
| return '[function]' | ||
| } | ||
| if (value.__pinegrowAstroType === 'symbol') { | ||
| return value.value || '[symbol]' | ||
| } | ||
| return value | ||
| } | ||
| const queryAstroStateSnapshotPayloads = (root, sourceFile) => { | ||
| if (!root || !sourceFile) { | ||
| return [] | ||
| } | ||
| return Array.from(root.querySelectorAll(`[${astroStateSnapshotAttribute}]`)) | ||
| .map(element => { | ||
| const source = element.getAttribute(astroStateSnapshotSourceAttribute) | ||
| const json = element.getAttribute(astroStateSnapshotJsonAttribute) | ||
| if (!isMatchingSnapshotSource(source, sourceFile) || !json) { | ||
| return null | ||
| } | ||
| try { | ||
| return JSON.parse(json) | ||
| } catch (_error) { | ||
| return null | ||
| } | ||
| }) | ||
| .filter(Boolean) | ||
| } | ||
| const createAstroStateSnapshotIndex = (root, sourceFile) => { | ||
| const index = new Map() | ||
| queryAstroStateSnapshotPayloads(root, sourceFile).forEach(payload => { | ||
| ;(payload.entries || []).forEach(entry => { | ||
| if (!entry || typeof entry.type !== 'string' || typeof entry.key !== 'string') { | ||
| return | ||
| } | ||
| index.set(createStateSnapshotKey(entry), { | ||
| value: decodeAstroSnapshotValue(entry.value), | ||
| source: payload.sourceFile || sourceFile, | ||
| version: payload.version || null, | ||
| }) | ||
| }) | ||
| }) | ||
| return index | ||
| } | ||
| const applyAstroStateSnapshots = (state, snapshotIndex) => { | ||
| if (!Array.isArray(state) || !snapshotIndex?.size) { | ||
| return state | ||
| } | ||
| return state.map(record => { | ||
| const snapshot = snapshotIndex.get(createStateSnapshotKey(record)) | ||
| if (!snapshot) { | ||
| return record | ||
| } | ||
| return { | ||
| ...record, | ||
| sourceValue: record.sourceValue ?? record.value, | ||
| value: snapshot.value, | ||
| valueStatus: 'server-snapshot', | ||
| snapshot: { | ||
| source: snapshot.source, | ||
| version: snapshot.version, | ||
| }, | ||
| } | ||
| }) | ||
| } | ||
| const queryMarkerElements = (root, markerAttribute) => { | ||
| if (!root) { | ||
| return [] | ||
| } | ||
| return Array.from(root.querySelectorAll(`[${markerAttribute}]`)) | ||
| } | ||
| const getChildNodes = node => Array.from(node?.childNodes || []) | ||
| const walkDomNodes = (node, visitor) => { | ||
| if (!node) { | ||
| return | ||
| } | ||
| visitor(node) | ||
| getChildNodes(node).forEach(child => walkDomNodes(child, visitor)) | ||
| } | ||
| const parseBoundaryComment = node => { | ||
| if (node?.nodeType !== commentNodeType) { | ||
| return null | ||
| } | ||
| const match = `${node.nodeValue || ''}`.trim().match(/^pgastro:(start|end):(.+)$/) | ||
| return match | ||
| ? { | ||
| boundary: match[1], | ||
| sourceId: match[2], | ||
| } | ||
| : null | ||
| } | ||
| const getBoundaryElements = (startComment, endComment) => { | ||
| if (!startComment?.parentNode || startComment.parentNode !== endComment?.parentNode) { | ||
| return { | ||
| firstEl: null, | ||
| lastEl: null, | ||
| } | ||
| } | ||
| let firstEl = null | ||
| let lastEl = null | ||
| let node = startComment.nextSibling | ||
| while (node && node !== endComment) { | ||
| if (node.nodeType === elementNodeType) { | ||
| if (!firstEl) { | ||
| firstEl = node | ||
| } | ||
| lastEl = node | ||
| } | ||
| node = node.nextSibling | ||
| } | ||
| return { | ||
| firstEl, | ||
| lastEl, | ||
| } | ||
| } | ||
| const queryBoundaryMarkerRanges = (root, nodeIndex) => { | ||
| const startsById = new Map() | ||
| const ranges = [] | ||
| walkDomNodes(root, node => { | ||
| const marker = parseBoundaryComment(node) | ||
| if (!marker || !nodeIndex.has(marker.sourceId)) { | ||
| return | ||
| } | ||
| if (marker.boundary === 'start') { | ||
| if (!startsById.has(marker.sourceId)) { | ||
| startsById.set(marker.sourceId, []) | ||
| } | ||
| startsById.get(marker.sourceId).push(node) | ||
| return | ||
| } | ||
| const starts = startsById.get(marker.sourceId) | ||
| const startComment = starts?.pop() | ||
| if (!startComment) { | ||
| return | ||
| } | ||
| const { firstEl, lastEl } = getBoundaryElements(startComment, node) | ||
| if (!firstEl) { | ||
| return | ||
| } | ||
| ranges.push({ | ||
| sourceId: marker.sourceId, | ||
| firstEl, | ||
| lastEl: lastEl || firstEl, | ||
| startComment, | ||
| endComment: node, | ||
| }) | ||
| }) | ||
| return ranges | ||
| } | ||
| const getElementMarkerId = (element, markerAttribute) => { | ||
| if (!element) { | ||
| return null | ||
| } | ||
| return element.getAttribute(markerAttribute) | ||
| } | ||
| const isIslandElement = element => { | ||
| const tagName = element?.tagName || element?.nodeName || element?.localName || '' | ||
| return `${tagName}`.toLowerCase() === 'astro-island' | ||
| } | ||
| const canOverwritePgId = currentValue => | ||
| !currentValue || currentValue.startsWith(astroSourceIdPrefix) | ||
| const mirrorSourceIdToPgId = (element, sourceId, options = {}) => { | ||
| if ( | ||
| options.mirrorPgId === false || | ||
| !sourceId || | ||
| !element | ||
| ) { | ||
| return false | ||
| } | ||
| const currentPgId = element.getAttribute(pinegrowMarkerAttribute) | ||
| if (currentPgId === sourceId) { | ||
| return true | ||
| } | ||
| if (!canOverwritePgId(currentPgId)) { | ||
| return false | ||
| } | ||
| element.setAttribute(pinegrowMarkerAttribute, sourceId) | ||
| return true | ||
| } | ||
| const createAstroElCacheEntry = ({ | ||
| boundary = null, | ||
| element, | ||
| firstElement = null, | ||
| lastElement = null, | ||
| manifest, | ||
| markerAttribute = defaultMarkerAttribute, | ||
| node, | ||
| mirroredPgId = false, | ||
| }) => { | ||
| const firstEl = firstElement || element | ||
| const lastEl = lastElement || firstEl | ||
| const isBoundaryRange = Boolean(boundary) | ||
| const isIsland = isBoundaryRange && isIslandElement(firstEl) | ||
| const sourceSnapshot = manifest.sourceSnapshot || null | ||
| const snapshotId = sourceSnapshot?.id || null | ||
| const sourceRange = { | ||
| start: node.sourceStart ?? null, | ||
| end: node.sourceEnd ?? null, | ||
| status: node.sourceRangeStatus || 'unknown', | ||
| } | ||
| return createSourceDomCacheEntry({ | ||
| el: firstEl, | ||
| framework: astroFrameworkKey, | ||
| entryKind: 'source-node', | ||
| treeScopes: createSourceDomTreeScopes('source-node'), | ||
| sourceId: node.id, | ||
| pgId: node.id, | ||
| snapshotId, | ||
| sourceSnapshot, | ||
| key: node.id, | ||
| localFile: null, | ||
| sourceFile: manifest.sourceFile || null, | ||
| nodeType: node.type || null, | ||
| name: node.name || '', | ||
| source: { | ||
| id: node.id, | ||
| file: manifest.sourceFile || null, | ||
| range: sourceRange, | ||
| nodeType: node.type || null, | ||
| name: node.name || '', | ||
| ...(sourceSnapshot ? { snapshot: sourceSnapshot } : {}), | ||
| }, | ||
| component: node.component || null, | ||
| owners: [], | ||
| sourceRange, | ||
| sourceStart: sourceRange.start, | ||
| sourceEnd: sourceRange.end, | ||
| sourceRangeStatus: sourceRange.status, | ||
| marker: { | ||
| ...(node.marker || {}), | ||
| liveAttribute: markerAttribute, | ||
| pinegrowAttribute: pinegrowMarkerAttribute, | ||
| mirroredPgId, | ||
| boundary, | ||
| }, | ||
| editability: node.editability || null, | ||
| attributes: node.attributes || [], | ||
| isFragment: isBoundaryRange, | ||
| isRootFragment: false, | ||
| isIsland, | ||
| rootEl: null, | ||
| firstEl, | ||
| lastEl, | ||
| instance: createSyntheticInstance(node, firstEl), | ||
| vnode: null, | ||
| extra: { | ||
| isAstro: true, | ||
| isAstroSourceDomEntry: true, | ||
| isAstroComponent: false, | ||
| isAppTreeComponent: false, | ||
| }, | ||
| }) | ||
| } | ||
| const createComponentEntry = ({ entries, manifest, root, stateSnapshotIndex = null }) => { | ||
| const sourceFile = manifest?.sourceFile | ||
| const sourceSnapshot = manifest?.sourceSnapshot || null | ||
| const snapshotId = sourceSnapshot?.id || null | ||
| const rootElement = getRootElement(root) | ||
| const isPageFile = isPageSourceFile(sourceFile) | ||
| const isPageRoot = isPageFile && rootElement | ||
| const isMarkerless = !entries.length | ||
| if (!sourceFile || (!entries.length && !rootElement)) { | ||
| return null | ||
| } | ||
| const orderedEntries = sortEntriesByDocumentPosition(entries) | ||
| const firstEntry = orderedEntries[0] | ||
| const lastEntry = orderedEntries[orderedEntries.length - 1] | ||
| const componentId = `astro-component:${sourceFile}` | ||
| const firstEl = isPageRoot || isMarkerless ? rootElement : getEntryFirstElement(firstEntry) | ||
| const lastEl = isPageRoot || isMarkerless ? null : getEntryLastElement(lastEntry) | ||
| const componentName = getBasename(sourceFile) | ||
| const entryKind = isPageRoot ? 'page-root' : 'component-boundary' | ||
| const sourceRangeStatus = isMarkerless ? 'markerless-component-file' : 'component-file' | ||
| const editabilityReasons = ['astro-component-file'] | ||
| if (isMarkerless) { | ||
| editabilityReasons.push('astro-markerless-app-tree-entry') | ||
| } | ||
| return createSourceDomCacheEntry({ | ||
| el: firstEl, | ||
| framework: astroFrameworkKey, | ||
| entryKind, | ||
| treeScopes: createSourceDomTreeScopes(entryKind), | ||
| sourceId: componentId, | ||
| pgId: null, | ||
| snapshotId, | ||
| sourceSnapshot, | ||
| key: null, | ||
| localFile: sourceFile, | ||
| sourceFile, | ||
| nodeType: 'component-file', | ||
| name: componentName, | ||
| source: { | ||
| id: componentId, | ||
| file: sourceFile, | ||
| range: { | ||
| start: null, | ||
| end: null, | ||
| status: sourceRangeStatus, | ||
| }, | ||
| nodeType: 'component-file', | ||
| name: componentName, | ||
| ...(sourceSnapshot ? { snapshot: sourceSnapshot } : {}), | ||
| }, | ||
| component: { | ||
| file: sourceFile, | ||
| name: componentName, | ||
| kind: isPageFile ? 'page' : 'component', | ||
| }, | ||
| owners: [], | ||
| sourceRange: { | ||
| start: null, | ||
| end: null, | ||
| status: sourceRangeStatus, | ||
| }, | ||
| sourceStart: null, | ||
| sourceEnd: null, | ||
| sourceRangeStatus, | ||
| marker: null, | ||
| editability: { | ||
| mode: 'inspect', | ||
| operations: ['select', 'open-source-file'], | ||
| reasons: editabilityReasons, | ||
| }, | ||
| attributes: [], | ||
| state: applyAstroStateSnapshots( | ||
| Array.isArray(manifest.state) ? manifest.state : [], | ||
| stateSnapshotIndex | ||
| ), | ||
| isFragment: false, | ||
| isRootFragment: false, | ||
| isIsland: false, | ||
| rootEl: null, | ||
| firstEl, | ||
| lastEl, | ||
| instance: createComponentSyntheticInstance(manifest, componentId, firstEl), | ||
| vnode: null, | ||
| extra: { | ||
| isAstro: true, | ||
| isAstroSourceDomEntry: false, | ||
| isAstroComponent: true, | ||
| isAppTreeComponent: true, | ||
| isAstroPageRoot: Boolean(isPageRoot), | ||
| isAstroMarkerlessComponent: Boolean(isMarkerless), | ||
| }, | ||
| }) | ||
| } | ||
| const createAstroComponentEntries = ({ entries, manifest, root }) => { | ||
| const componentEntry = createComponentEntry({ | ||
| entries, | ||
| manifest, | ||
| root, | ||
| stateSnapshotIndex: createAstroStateSnapshotIndex(root, manifest?.sourceFile), | ||
| }) | ||
| return componentEntry ? [componentEntry] : [] | ||
| } | ||
| const collectAstroSourceDomMapping = (root, manifest, options = {}) => { | ||
| const markerAttribute = getMarkerAttribute(manifest, options) | ||
| const entries = [] | ||
| const missing = [] | ||
| if (manifest?.status !== 'ready') { | ||
| return { | ||
| status: 'unavailable', | ||
| markerAttribute, | ||
| entries, | ||
| componentEntries: [], | ||
| missing, | ||
| reason: manifest?.reason || 'Astro source manifest is unavailable.', | ||
| } | ||
| } | ||
| if (!root) { | ||
| return { | ||
| status: 'unavailable', | ||
| markerAttribute, | ||
| entries, | ||
| componentEntries: [], | ||
| missing, | ||
| reason: 'A DOM root is required.', | ||
| } | ||
| } | ||
| const nodeIndex = createNodeIndex(manifest) | ||
| queryMarkerElements(root, markerAttribute).forEach(element => { | ||
| const sourceId = getElementMarkerId(element, markerAttribute) | ||
| const node = nodeIndex.get(sourceId) | ||
| if (!node) { | ||
| if (options.reportMissingMarkers) { | ||
| missing.push({ element, sourceId }) | ||
| } | ||
| return | ||
| } | ||
| const mirroredPgId = markerAttribute === pinegrowMarkerAttribute | ||
| ? false | ||
| : mirrorSourceIdToPgId(element, sourceId, options) | ||
| entries.push(createAstroElCacheEntry({ | ||
| element, | ||
| manifest, | ||
| markerAttribute, | ||
| node, | ||
| mirroredPgId, | ||
| })) | ||
| }) | ||
| queryBoundaryMarkerRanges(root, nodeIndex).forEach(boundary => { | ||
| const node = nodeIndex.get(boundary.sourceId) | ||
| entries.push(createAstroElCacheEntry({ | ||
| boundary: { | ||
| strategy: 'boundary-comment', | ||
| startComment: boundary.startComment, | ||
| endComment: boundary.endComment, | ||
| }, | ||
| element: boundary.firstEl, | ||
| firstElement: boundary.firstEl, | ||
| lastElement: boundary.lastEl, | ||
| manifest, | ||
| markerAttribute, | ||
| node, | ||
| mirroredPgId: false, | ||
| })) | ||
| }) | ||
| return { | ||
| status: 'ready', | ||
| markerAttribute, | ||
| entries, | ||
| componentEntries: createAstroComponentEntries({ entries, manifest, root }), | ||
| missing, | ||
| } | ||
| } | ||
| const ensurePinegrowAstroRuntime = (win = getDefaultWindow()) => { | ||
| const pinegrow = ensurePinegrowSourceDomRuntime(win) | ||
| if (!pinegrow) { | ||
| return null | ||
| } | ||
| if (!pinegrow.astro) { | ||
| pinegrow.astro = {} | ||
| } | ||
| pinegrow.astro.sourceManifests = createSourceDomReactiveMap(pinegrow, pinegrow.astro.sourceManifests) | ||
| return pinegrow | ||
| } | ||
| const isEntryForSource = (entry, sourceKey) => | ||
| entry?.framework === astroFrameworkKey && | ||
| (entry.localFile === sourceKey || entry.sourceFile === sourceKey) | ||
| const removeEntriesForManifest = (elCache, manifest) => { | ||
| const sourceKey = getManifestSourceKey(manifest) | ||
| let removed = 0 | ||
| const elements = [] | ||
| if (!sourceKey) { | ||
| return { elements, removed } | ||
| } | ||
| Array.from(elCache.entries()).forEach(([element, entries]) => { | ||
| const currentEntries = normalizeSourceDomCacheEntries(entries) | ||
| const nextEntries = currentEntries.filter(entry => !isEntryForSource(entry, sourceKey)) | ||
| const removedForElement = currentEntries.length - nextEntries.length | ||
| removed += removedForElement | ||
| if (removedForElement) { | ||
| elements.push(element) | ||
| } | ||
| if (nextEntries.length) { | ||
| elCache.set(element, nextEntries) | ||
| } else { | ||
| elCache.delete(element) | ||
| } | ||
| }) | ||
| return { elements, removed } | ||
| } | ||
| const notifyUpdatedElements = (pinegrow, elements) => { | ||
| elements.forEach(element => { | ||
| if (pinegrow.elUpdateHanderFn) { | ||
| pinegrow.elUpdateHanderFn(element) | ||
| } | ||
| }) | ||
| } | ||
| const clearAstroSourceDomMapping = (manifest, options = {}) => { | ||
| const win = options.win || getDefaultWindow() | ||
| const pinegrow = ensurePinegrowAstroRuntime(win) | ||
| if (!pinegrow) { | ||
| return { | ||
| status: 'unavailable', | ||
| hook: 'unmounted', | ||
| cached: 0, | ||
| removed: 0, | ||
| entries: [], | ||
| componentEntries: [], | ||
| missing: [], | ||
| reason: 'A browser window is required.', | ||
| } | ||
| } | ||
| const removal = removeEntriesForManifest(pinegrow.elCache, manifest) | ||
| notifyUpdatedElements(pinegrow, removal.elements) | ||
| const sourceKey = getManifestSourceKey(manifest) | ||
| if (sourceKey) { | ||
| pinegrow.astro.sourceManifests.delete(sourceKey) | ||
| } | ||
| return { | ||
| status: 'ready', | ||
| hook: 'unmounted', | ||
| cached: 0, | ||
| removed: removal.removed, | ||
| entries: [], | ||
| componentEntries: [], | ||
| missing: [], | ||
| } | ||
| } | ||
| const upsertEntry = (elCache, entry) => { | ||
| upsertSourceDomCacheEntry(elCache, entry, (existingEntry, nextEntry) => | ||
| existingEntry.framework === astroFrameworkKey && | ||
| existingEntry.sourceId === nextEntry.sourceId && | ||
| existingEntry.localFile === nextEntry.localFile | ||
| ) | ||
| } | ||
| const refreshAstroSourceDomMapping = (hook, manifest, options = {}) => { | ||
| if (hook === 'unmounted') { | ||
| return clearAstroSourceDomMapping(manifest, options) | ||
| } | ||
| const win = options.win || getDefaultWindow() | ||
| const root = options.root || win?.document || null | ||
| const pinegrow = ensurePinegrowAstroRuntime(win) | ||
| const collection = collectAstroSourceDomMapping(root, manifest, options) | ||
| if (!pinegrow) { | ||
| return { | ||
| ...collection, | ||
| hook, | ||
| cached: 0, | ||
| removed: 0, | ||
| reason: collection.reason || 'A browser window is required.', | ||
| } | ||
| } | ||
| if (collection.status !== 'ready') { | ||
| return { | ||
| ...collection, | ||
| hook, | ||
| cached: 0, | ||
| removed: 0, | ||
| } | ||
| } | ||
| const removal = options.clearPrevious === false | ||
| ? { elements: [], removed: 0 } | ||
| : removeEntriesForManifest(pinegrow.elCache, manifest) | ||
| const updatedElements = new Set(removal.elements) | ||
| const cacheEntries = [...collection.entries, ...(collection.componentEntries || [])] | ||
| cacheEntries.forEach(entry => { | ||
| upsertEntry(pinegrow.elCache, entry) | ||
| updatedElements.add(entry.el) | ||
| }) | ||
| notifyUpdatedElements(pinegrow, updatedElements) | ||
| const sourceKey = getManifestSourceKey(manifest) | ||
| if (sourceKey) { | ||
| pinegrow.astro.sourceManifests.set(sourceKey, manifest) | ||
| } | ||
| return { | ||
| ...collection, | ||
| hook, | ||
| cached: cacheEntries.length, | ||
| removed: removal.removed, | ||
| } | ||
| } | ||
| const createAstroRuntimeBridge = (manifest, options = {}) => ({ | ||
| manifest, | ||
| collect: root => collectAstroSourceDomMapping( | ||
| root || options.root || options.win?.document || getDefaultWindow()?.document, | ||
| manifest, | ||
| options | ||
| ), | ||
| refresh: refreshOptions => refreshAstroSourceDomMapping('updated', manifest, { ...options, ...refreshOptions }), | ||
| }) | ||
| export { | ||
| createAstroComponentEntries, | ||
| createAstroElCacheEntry, | ||
| createAstroRuntimeBridge, | ||
| ensurePinegrowAstroRuntime, | ||
| refreshAstroSourceDomMapping, | ||
| clearAstroSourceDomMapping, | ||
| collectAstroSourceDomMapping, | ||
| } |
| export { | ||
| createAstroComponentEntries, | ||
| createAstroElCacheEntry, | ||
| createAstroRuntimeBridge, | ||
| ensurePinegrowAstroRuntime, | ||
| refreshAstroSourceDomMapping, | ||
| collectAstroSourceDomMapping, | ||
| } from './bridge.js' |
| export { | ||
| createAstroComponentEntries, | ||
| createAstroElCacheEntry, | ||
| createAstroRuntimeBridge, | ||
| ensurePinegrowAstroRuntime, | ||
| refreshAstroSourceDomMapping, | ||
| collectAstroSourceDomMapping, | ||
| } from './bridge.js' |
| import MagicString from 'magic-string' | ||
| const defaultRuntimeImport = '@pinegrow/vite-plugin/astro/runtime' | ||
| const astroRuntimeModuleUrl = '/@pinegrow-vite-plugin/astro-runtime.js' | ||
| const scriptSentinel = '__pinegrowAstroSourceManifest' | ||
| const serializeForScript = value => | ||
| JSON.stringify(value) | ||
| .replace(/</g, '\\u003c') | ||
| .replace(/\u2028/g, '\\u2028') | ||
| .replace(/\u2029/g, '\\u2029') | ||
| const isBrowserResolvableModuleSpecifier = value => | ||
| value.startsWith('/') || | ||
| value.startsWith('./') || | ||
| value.startsWith('../') || | ||
| /^[a-z][a-z0-9+.-]*:/i.test(value) | ||
| const createAstroRuntimeModule = (options = {}) => { | ||
| const runtimeImport = options.runtimeImport || defaultRuntimeImport | ||
| return `export { refreshAstroSourceDomMapping } from ${JSON.stringify(runtimeImport)}\n` | ||
| } | ||
| const cleanAstroRuntimeModuleRequest = id => | ||
| String(id || '').split(/[?#]/)[0] | ||
| const isAstroRuntimeModuleRequest = id => | ||
| cleanAstroRuntimeModuleRequest(id) === astroRuntimeModuleUrl | ||
| const getAstroRuntimeScriptImport = options => { | ||
| if (options.runtimeModuleUrl) { | ||
| return options.runtimeModuleUrl | ||
| } | ||
| if (options.runtimeImport && isBrowserResolvableModuleSpecifier(options.runtimeImport)) { | ||
| return options.runtimeImport | ||
| } | ||
| return astroRuntimeModuleUrl | ||
| } | ||
| const createAstroRuntimeScript = (manifest, options = {}) => { | ||
| const runtimeImport = getAstroRuntimeScriptImport(options) | ||
| const manifestJson = serializeForScript(manifest) | ||
| return `<script type="module"> | ||
| import { refreshAstroSourceDomMapping } from ${JSON.stringify(runtimeImport)} | ||
| const ${scriptSentinel} = ${manifestJson} | ||
| let ${scriptSentinel}Mounted = false | ||
| const refreshAstroSourceDomMappingSafely = reason => { | ||
| try { | ||
| const hook = ${scriptSentinel}Mounted ? 'updated' : 'mounted' | ||
| ${scriptSentinel}Mounted = true | ||
| // Initiate Astro source-DOM mapping for the current source manifest. | ||
| refreshAstroSourceDomMapping(hook, ${scriptSentinel}, { reason }) | ||
| } catch (error) { | ||
| console.warn('[pinegrow] Astro source-DOM refresh failed', error) | ||
| } | ||
| } | ||
| const clearAstroSourceDomMappingSafely = reason => { | ||
| try { | ||
| ${scriptSentinel}Mounted = false | ||
| refreshAstroSourceDomMapping('unmounted', ${scriptSentinel}, { reason }) | ||
| } catch (error) { | ||
| console.warn('[pinegrow] Astro source-DOM cleanup failed', error) | ||
| } | ||
| } | ||
| const queueAstroSourceDomMappingRefresh = () => { | ||
| if (document.readyState === 'loading') { | ||
| document.addEventListener('DOMContentLoaded', () => refreshAstroSourceDomMappingSafely('DOMContentLoaded'), { once: true }) | ||
| } else { | ||
| Promise.resolve().then(() => refreshAstroSourceDomMappingSafely('initial')) | ||
| } | ||
| } | ||
| const refreshAstroSourceDomMappingOnPageLoad = () => refreshAstroSourceDomMappingSafely('astro:page-load') | ||
| const clearAstroSourceDomMappingBeforeSwap = () => clearAstroSourceDomMappingSafely('astro:before-swap') | ||
| // Keep Astro source-DOM mapping current after the first page load, Astro navigation, and Vite HMR. | ||
| queueAstroSourceDomMappingRefresh() | ||
| document.addEventListener('astro:before-swap', clearAstroSourceDomMappingBeforeSwap) | ||
| document.addEventListener('astro:page-load', refreshAstroSourceDomMappingOnPageLoad) | ||
| if (import.meta.hot) { | ||
| import.meta.hot.accept(() => refreshAstroSourceDomMappingSafely('hmr-accept')) | ||
| import.meta.hot.on('vite:afterUpdate', () => refreshAstroSourceDomMappingSafely('vite:afterUpdate')) | ||
| import.meta.hot.dispose(() => { | ||
| clearAstroSourceDomMappingSafely('hmr-dispose') | ||
| document.removeEventListener('astro:before-swap', clearAstroSourceDomMappingBeforeSwap) | ||
| document.removeEventListener('astro:page-load', refreshAstroSourceDomMappingOnPageLoad) | ||
| }) | ||
| } | ||
| </script>` | ||
| } | ||
| const hasRuntimeScript = source => | ||
| source.includes(scriptSentinel) | ||
| function injectAstroRuntimeScript(source, manifest, options = {}) { | ||
| const diagnostics = [] | ||
| if (options.runtimeTransport === false) { | ||
| return { | ||
| code: source, | ||
| map: null, | ||
| diagnostics, | ||
| injected: false, | ||
| skippedReason: 'runtime-transport-disabled', | ||
| } | ||
| } | ||
| if (manifest?.status !== 'ready') { | ||
| return { | ||
| code: source, | ||
| map: null, | ||
| diagnostics: [ | ||
| { | ||
| level: 'warning', | ||
| message: 'Astro source manifest is unavailable; runtime source-DOM transport was not injected.', | ||
| }, | ||
| ], | ||
| injected: false, | ||
| skippedReason: 'manifest-unavailable', | ||
| } | ||
| } | ||
| if (hasRuntimeScript(source)) { | ||
| return { | ||
| code: source, | ||
| map: null, | ||
| diagnostics, | ||
| injected: false, | ||
| skippedReason: 'existing-runtime-script', | ||
| } | ||
| } | ||
| const str = new MagicString(source) | ||
| str.append(`\n${createAstroRuntimeScript(manifest, options)}\n`) | ||
| const map = str.generateMap() | ||
| map.file = manifest.sourceFile | ||
| return { | ||
| code: str.toString(), | ||
| map, | ||
| diagnostics, | ||
| injected: true, | ||
| skippedReason: null, | ||
| } | ||
| } | ||
| export { | ||
| astroRuntimeModuleUrl, | ||
| createAstroRuntimeModule, | ||
| createAstroRuntimeScript, | ||
| injectAstroRuntimeScript, | ||
| isAstroRuntimeModuleRequest, | ||
| } |
| const documentPositionPreceding = 2 | ||
| const documentPositionFollowing = 4 | ||
| const createFallbackComputed = getter => ({ | ||
| get value() { | ||
| return getter() | ||
| }, | ||
| }) | ||
| const createFallbackRef = value => ({ value }) | ||
| const readWatchSource = source => { | ||
| if (typeof source === 'function') { | ||
| return source() | ||
| } | ||
| return source?.value | ||
| } | ||
| const createFallbackWatch = (source, callback, options = {}) => { | ||
| if (options.immediate && typeof callback === 'function') { | ||
| callback(readWatchSource(source), undefined) | ||
| } | ||
| return () => {} | ||
| } | ||
| const isMapLike = value => | ||
| value && | ||
| typeof value.get === 'function' && | ||
| typeof value.set === 'function' && | ||
| typeof value.delete === 'function' && | ||
| typeof value.entries === 'function' | ||
| const createSourceDomTreeScopes = entryKind => ({ | ||
| pageTree: entryKind === 'source-node', | ||
| appTree: entryKind === 'component-boundary' || entryKind === 'page-root', | ||
| }) | ||
| const normalizeSourceDomCacheEntries = entries => Array.isArray(entries) ? entries : [] | ||
| const getSourceDomBoundaryElement = (elCacheObj, fallbackEl = null) => | ||
| elCacheObj.firstEl || elCacheObj.el || fallbackEl || null | ||
| const getSourceDomBoundaryLastElement = (elCacheObj, fallbackEl = null) => | ||
| elCacheObj.lastEl || elCacheObj.firstEl || elCacheObj.el || fallbackEl || null | ||
| const getOptionalElementAttribute = (el, name) => | ||
| el && typeof el.getAttribute === 'function' ? el.getAttribute(name) : null | ||
| const getSourceDomSourceInspector = (elCacheObj, fallbackEl = null) => { | ||
| const props = elCacheObj.vnode?.props | ||
| const firstEl = getSourceDomBoundaryElement(elCacheObj, fallbackEl) | ||
| return ( | ||
| elCacheObj.sourceInspector || | ||
| props?.['data-v-inspector'] || | ||
| getOptionalElementAttribute(firstEl, 'data-v-inspector') || | ||
| getOptionalElementAttribute(fallbackEl, 'data-v-inspector') || | ||
| null | ||
| ) | ||
| } | ||
| const isBoundaryConnected = (elCacheObj, fallbackEl = null) => { | ||
| const firstEl = getSourceDomBoundaryElement(elCacheObj, fallbackEl) | ||
| const lastEl = getSourceDomBoundaryLastElement(elCacheObj, fallbackEl) | ||
| return Boolean(firstEl?.isConnected || lastEl?.isConnected || fallbackEl?.isConnected) | ||
| } | ||
| const isBoundaryMounted = elCacheObj => { | ||
| return elCacheObj.instance.isMounted !== false && elCacheObj.instance.isUnmounted !== true | ||
| } | ||
| const getBoundaryId = elCacheObj => { | ||
| const file = elCacheObj.localFile || '' | ||
| const uid = elCacheObj.instance.uid ?? '' | ||
| const key = elCacheObj.key ?? '' | ||
| const pgId = elCacheObj.sourcePgId ?? elCacheObj.pgId ?? elCacheObj.sourceId ?? elCacheObj.source?.id ?? '' | ||
| return [file, uid, key, pgId].join('::') | ||
| } | ||
| const createAppTreeBoundariesComputed = (elCache, computedFromContext = createFallbackComputed) => | ||
| computedFromContext(() => { | ||
| const boundariesByRuntimeKey = new Map() | ||
| for (let [el, elCacheNodes] of elCache.entries()) { | ||
| normalizeSourceDomCacheEntries(elCacheNodes).forEach(elCacheObj => { | ||
| if ( | ||
| !isBoundaryMounted(elCacheObj) || | ||
| !isBoundaryConnected(elCacheObj, el) || | ||
| elCacheObj.treeScopes.appTree !== true || | ||
| !elCacheObj.localFile | ||
| ) { | ||
| return | ||
| } | ||
| const localFile = elCacheObj.localFile | ||
| const firstEl = getSourceDomBoundaryElement(elCacheObj, el) | ||
| const lastEl = getSourceDomBoundaryLastElement(elCacheObj, el) | ||
| if (!firstEl) { | ||
| return | ||
| } | ||
| if (elCacheObj.isIsland) { | ||
| const childEl = firstEl.firstElementChild | ||
| if (childEl && elCache.has(childEl)) { | ||
| return | ||
| } | ||
| } | ||
| const sourcePgId = elCacheObj.sourcePgId ?? null | ||
| const renderPgId = !sourcePgId | ||
| ? elCacheObj.renderPgId ?? elCacheObj.pgId ?? null | ||
| : elCacheObj.renderPgId ?? null | ||
| const candidate = { | ||
| ...elCacheObj, | ||
| sourceElCacheObj: elCacheObj, | ||
| appTreeBoundary: true, | ||
| appTreeBoundaryId: null, | ||
| localFile, | ||
| firstEl, | ||
| lastEl, | ||
| sourcePgId, | ||
| renderPgId, | ||
| sourceInspector: getSourceDomSourceInspector(elCacheObj, el), | ||
| } | ||
| // Start App Tree runtime-boundary dedupe. | ||
| const runtimeKey = [localFile || '', candidate.instance.uid, candidate.key ?? ''].join('::') | ||
| const existingBoundary = boundariesByRuntimeKey.get(runtimeKey) | ||
| let boundary = candidate | ||
| if (existingBoundary) { | ||
| const preferred = | ||
| candidate.sourcePgId && !existingBoundary.sourcePgId ? candidate : existingBoundary | ||
| const fallback = preferred === existingBoundary ? candidate : existingBoundary | ||
| const sourceElCacheObj = preferred.sourcePgId | ||
| ? preferred.sourceElCacheObj || preferred | ||
| : fallback.sourcePgId | ||
| ? fallback.sourceElCacheObj || fallback | ||
| : preferred.sourceElCacheObj || fallback.sourceElCacheObj || preferred | ||
| const sourcePgId = preferred.sourcePgId ?? fallback.sourcePgId ?? null | ||
| const renderPgId = preferred.renderPgId ?? fallback.renderPgId ?? null | ||
| const sourceInspector = preferred.sourceInspector ?? fallback.sourceInspector ?? null | ||
| boundary = { | ||
| ...fallback, | ||
| ...preferred, | ||
| sourceElCacheObj, | ||
| sourcePgId, | ||
| renderPgId, | ||
| sourceInspector, | ||
| pgId: sourcePgId ?? preferred.pgId ?? fallback.pgId ?? null, | ||
| firstEl: preferred.firstEl || fallback.firstEl, | ||
| lastEl: preferred.lastEl || fallback.lastEl, | ||
| el: preferred.el || fallback.el, | ||
| localFile: preferred.localFile || fallback.localFile, | ||
| } | ||
| } | ||
| boundariesByRuntimeKey.set(runtimeKey, boundary) | ||
| // End App Tree runtime-boundary dedupe. | ||
| }) | ||
| } | ||
| const boundaries = Array.from(boundariesByRuntimeKey.values()).map(boundary => { | ||
| const boundaryId = getBoundaryId(boundary) | ||
| return { | ||
| ...boundary, | ||
| appTreeBoundaryId: boundaryId, | ||
| } | ||
| }) | ||
| return boundaries.sort((a, b) => { | ||
| const aEl = getSourceDomBoundaryElement(a) | ||
| const bEl = getSourceDomBoundaryElement(b) | ||
| if (!aEl?.compareDocumentPosition || !bEl) { | ||
| return 0 | ||
| } | ||
| const position = aEl.compareDocumentPosition(bEl) | ||
| if (position & documentPositionPreceding) { | ||
| return 1 | ||
| } | ||
| if (position & documentPositionFollowing) { | ||
| return -1 | ||
| } | ||
| return 0 | ||
| }) | ||
| }) | ||
| const createSourceDomReactiveMap = (pinegrow, value) => { | ||
| const map = isMapLike(value) ? value : new Map() | ||
| const reactiveFromContext = pinegrow?.reactiveFromContext || (value => value) | ||
| return reactiveFromContext(map) | ||
| } | ||
| const ensurePinegrowSourceDomRuntime = (win, context = {}) => { | ||
| if (!win) { | ||
| return null | ||
| } | ||
| if (!win.pinegrow) { | ||
| win.pinegrow = {} | ||
| } | ||
| const pinegrow = win.pinegrow | ||
| const reactiveFromContext = context.reactiveFromContext || pinegrow.reactiveFromContext || (value => value) | ||
| const refFromContext = context.refFromContext || pinegrow.refFromContext || createFallbackRef | ||
| const computedFromContext = context.computedFromContext || pinegrow.computedFromContext || createFallbackComputed | ||
| const watchFromContext = context.watchFromContext || pinegrow.watchFromContext || createFallbackWatch | ||
| const existingElCache = isMapLike(pinegrow.elCache) ? pinegrow.elCache : new Map() | ||
| const elCache = reactiveFromContext(existingElCache) | ||
| if ( | ||
| pinegrow.elCache !== elCache || | ||
| !pinegrow.appTreeBoundariesComputed || | ||
| pinegrow.reactiveFromContext !== reactiveFromContext || | ||
| pinegrow.refFromContext !== refFromContext || | ||
| pinegrow.computedFromContext !== computedFromContext || | ||
| pinegrow.watchFromContext !== watchFromContext | ||
| ) { | ||
| Object.assign(pinegrow, { | ||
| elCache, | ||
| appTreeBoundariesComputed: createAppTreeBoundariesComputed(elCache, computedFromContext), | ||
| reactiveFromContext, | ||
| refFromContext, | ||
| computedFromContext, | ||
| watchFromContext, | ||
| }) | ||
| } | ||
| return pinegrow | ||
| } | ||
| const createSourceDomCacheEntry = fields => { | ||
| const sourceRange = fields.sourceRange ?? null | ||
| const entryKind = fields.entryKind | ||
| return { | ||
| el: fields.el, | ||
| framework: fields.framework, | ||
| entryKind, | ||
| treeScopes: fields.treeScopes || createSourceDomTreeScopes(entryKind), | ||
| isRootFragment: Boolean(fields.isRootFragment), | ||
| rootEl: fields.rootEl ?? null, | ||
| vnode: fields.vnode ?? null, | ||
| instance: fields.instance, | ||
| isFragment: Boolean(fields.isFragment), | ||
| firstEl: fields.firstEl ?? null, | ||
| lastEl: fields.lastEl ?? null, | ||
| pgId: fields.pgId ?? null, | ||
| sourceId: fields.sourceId ?? null, | ||
| sourcePgId: fields.sourcePgId ?? null, | ||
| renderPgId: fields.renderPgId ?? null, | ||
| snapshotId: fields.snapshotId ?? null, | ||
| sourceSnapshot: fields.sourceSnapshot ?? null, | ||
| sourceInspector: fields.sourceInspector ?? null, | ||
| sourceFile: fields.sourceFile ?? null, | ||
| key: fields.key ?? null, | ||
| localFile: fields.localFile ?? null, | ||
| name: fields.name ?? '', | ||
| source: fields.source ?? null, | ||
| component: fields.component ?? null, | ||
| owners: fields.owners ?? [], | ||
| sourceRange, | ||
| sourceStart: fields.sourceStart ?? sourceRange?.start ?? null, | ||
| sourceEnd: fields.sourceEnd ?? sourceRange?.end ?? null, | ||
| sourceRangeStatus: fields.sourceRangeStatus ?? sourceRange?.status ?? null, | ||
| marker: fields.marker ?? null, | ||
| editability: fields.editability ?? null, | ||
| attributes: fields.attributes ?? [], | ||
| state: fields.state, | ||
| isIsland: Boolean(fields.isIsland), | ||
| ...(fields.extra || {}), | ||
| } | ||
| } | ||
| const upsertSourceDomCacheEntry = (elCache, entry, isSameEntry) => { | ||
| const existingEntries = normalizeSourceDomCacheEntries(elCache.get(entry.el)) | ||
| const matchesEntry = isSameEntry || ((existingEntry, nextEntry) => | ||
| existingEntry.framework === nextEntry.framework && | ||
| existingEntry.sourceId === nextEntry.sourceId && | ||
| existingEntry.localFile === nextEntry.localFile) | ||
| const nextEntries = existingEntries.filter(existingEntry => !matchesEntry(existingEntry, entry)) | ||
| nextEntries.push(entry) | ||
| elCache.set(entry.el, nextEntries) | ||
| } | ||
| export { | ||
| createAppTreeBoundariesComputed, | ||
| createFallbackComputed, | ||
| createFallbackRef, | ||
| createFallbackWatch, | ||
| createSourceDomCacheEntry, | ||
| createSourceDomReactiveMap, | ||
| createSourceDomTreeScopes, | ||
| ensurePinegrowSourceDomRuntime, | ||
| getSourceDomBoundaryElement, | ||
| getSourceDomBoundaryLastElement, | ||
| getSourceDomSourceInspector, | ||
| isMapLike, | ||
| normalizeSourceDomCacheEntries, | ||
| upsertSourceDomCacheEntry, | ||
| } |
| import { | ||
| onBeforeMount, | ||
| onMounted, | ||
| onBeforeUnmount, | ||
| getCurrentInstance, | ||
| ref, | ||
| reactive, | ||
| onUpdated, | ||
| computed, | ||
| watch, | ||
| } from 'vue' | ||
| import { | ||
| createSourceDomCacheEntry, | ||
| createSourceDomTreeScopes, | ||
| ensurePinegrowSourceDomRuntime, | ||
| getSourceDomSourceInspector as getSourceInspector, | ||
| } from '../../../shared/mapping/runtime/runtime.js' | ||
| const getVueCacheEntryKind = (isSourceNode, localFile) => { | ||
| if (isSourceNode) { | ||
| return 'source-node' | ||
| } | ||
| return localFile ? 'component-boundary' : 'render-node' | ||
| } | ||
| export function createVueRuntimeBridge() { | ||
| const ensurePinegrowVueRuntime = () => { | ||
| // conditional | ||
| const winObj = window | ||
| if (!(winObj?.process?.client && winObj.process.client !== true)) { | ||
| ensurePinegrowSourceDomRuntime(winObj, { | ||
| reactiveFromContext: reactive, | ||
| refFromContext: ref, | ||
| computedFromContext: computed, | ||
| watchFromContext: watch, | ||
| }) | ||
| } | ||
| } | ||
| // pgId & key can be optional | ||
| const refreshVueSourceDomMapping = (hook, pgId, rootEl, key, localFile, sourceFile) => async vnode => { | ||
| if (!vnode) return | ||
| if (window?.process?.client && process?.client !== true) return | ||
| if (!window.pinegrow) ensurePinegrowVueRuntime() | ||
| let el = vnode.el | ||
| const instance = vnode.component || vnode.ctx || el.__vueParentComponent | ||
| if (!el || !instance) return | ||
| try { | ||
| if ((key !== null && key !== undefined) || (vnode.key !== null && vnode.key !== undefined)) { | ||
| key = vnode.key || key | ||
| } | ||
| if (!sourceFile) { | ||
| localFile = localFile || vnode.type.__file | ||
| } | ||
| let isRootFragment = false | ||
| let isFragment = el.nodeType !== 1 | ||
| let firstEl, | ||
| lastEl, | ||
| isIsland = false, | ||
| childVNodes = [] | ||
| // May be an iles Island that wraps components with client directives | ||
| if (localFile) { | ||
| if (localFile.includes('node_modules/iles') && localFile.includes('Island.vue')) { | ||
| // Retain localFiles of iles island to apply to child elements | ||
| isIsland = true | ||
| } | ||
| if (localFile.includes('node_modules')) { | ||
| // Ignore SFCs from node_modules | ||
| localFile = null | ||
| } | ||
| } | ||
| const localFileFromInstance = instance.type.__file | ||
| if ( | ||
| localFileFromInstance && | ||
| localFileFromInstance.includes('node_modules/iles') && | ||
| localFileFromInstance.includes('Island.vue') | ||
| ) { | ||
| // Retain localFiles of iles island to apply to child elements | ||
| isIsland = true | ||
| localFile = instance.props.component?.__file || instance.props.importFrom | ||
| } | ||
| // // Computed props anyway filters out unmounted ones, and we use the local components unmounted hooks to cleanup unmounted nodes. Using the unmounted hook seems to be out of sync when el is reused but instance is remounted. | ||
| // if (hook === 'unmounted') { | ||
| // if (pinegrow.elCache.has(el)) { | ||
| // pinegrow.elCache.delete(el) | ||
| // } | ||
| // for (let [key, value] of pinegrow.elCache.entries()) { | ||
| // if (value.rootEl === rootEl) { | ||
| // pinegrow.elCache.delete(key) | ||
| // } | ||
| // } | ||
| // return | ||
| // } | ||
| // Text/comment node | ||
| if (isFragment) { | ||
| if (!rootEl) { | ||
| // root Fragment | ||
| isRootFragment = true | ||
| } | ||
| function isFragmentEl(instance) { | ||
| // return appRecord.options.types.Fragment === instance.subTree?.type | ||
| return instance.subTree.el.nodeType !== 1 | ||
| } | ||
| function getRootVNodesFromComponentInstance(instance) { | ||
| if (isFragmentEl(instance)) { | ||
| return getFragmentRootVNodes(instance.subTree) | ||
| } | ||
| if (!instance.subTree) return [] | ||
| return [instance.subTree] | ||
| } | ||
| function getFragmentRootVNodes(vnode) { | ||
| if (!vnode.children) return [] | ||
| // children is v-if when the vnode has a condition | ||
| if (!Array.isArray(vnode.children)) return [] | ||
| const list = [] | ||
| for (let i = 0, l = vnode.children.length; i < l; i++) { | ||
| const childVnode = vnode.children[i] | ||
| if (childVnode.component) { | ||
| list.push(...getRootVNodesFromComponentInstance(childVnode.component)) | ||
| } else if (childVnode) { | ||
| list.push(childVnode) | ||
| } | ||
| } | ||
| return list | ||
| } | ||
| childVNodes = getRootVNodesFromComponentInstance(instance) | ||
| if (!childVNodes.length) { | ||
| // For NuxtLayout, no childVNodes are returned, the subTree.children is in fact an object {ctx: {}, default: f} | ||
| if (el.nextElementSibling) { | ||
| const childEl = el.nextElementSibling | ||
| firstEl = lastEl = childEl | ||
| const childInstance = childEl.$ || childEl.__vueParentComponent | ||
| const childVNode = childInstance?.vnode // or subTree? | ||
| if (childVNode) { | ||
| childVNodes.push(childVNode) | ||
| } | ||
| } | ||
| } | ||
| if (childVNodes.length) { | ||
| // Filter out recursive vnodes text1 -> div, text1 (happens when text1 is the closest to a fragment with div & slot) | ||
| childVNodes = childVNodes.filter(childVNode => childVNode.el !== el) | ||
| if (childVNodes.length) { | ||
| if (childVNodes.length === 1) { | ||
| firstEl = lastEl = childVNodes[0].el | ||
| } else { | ||
| firstEl = childVNodes[0].el | ||
| lastEl = childVNodes[childVNodes.length - 1].el | ||
| } | ||
| if (firstEl && firstEl.nodeType !== 1) { | ||
| firstEl = firstEl.nextElementSibling | ||
| } | ||
| if (lastEl && lastEl.nodeType !== 1) { | ||
| lastEl = lastEl.nextElementSibling | ||
| } | ||
| } | ||
| } | ||
| } | ||
| if (isIsland && !isRootFragment) { | ||
| return | ||
| } | ||
| const isComponentVNode = Boolean(vnode.component) | ||
| const sourceInspector = getSourceInspector({ vnode, el, firstEl, lastEl }, el) | ||
| const isSourceNode = Boolean(sourceFile && pgId) | ||
| let prevElCacheNodes = pinegrow.elCache.get(el) | ||
| let prevElCacheObj = null | ||
| let index = -1 | ||
| if (prevElCacheNodes) { | ||
| index = isSourceNode | ||
| ? prevElCacheNodes.findIndex( | ||
| prevElCacheObj => | ||
| prevElCacheObj.instance.uid === instance.uid && | ||
| prevElCacheObj.sourceFile === sourceFile && | ||
| prevElCacheObj.pgId === pgId | ||
| ) | ||
| : prevElCacheNodes.findIndex(elCacheObj => elCacheObj.instance.uid === instance.uid) | ||
| if (index > -1) { | ||
| prevElCacheObj = prevElCacheNodes[index] | ||
| if (!sourceFile) { | ||
| localFile = localFile || prevElCacheObj.localFile | ||
| } | ||
| } | ||
| } | ||
| localFile = localFile ?? null | ||
| const entryKind = getVueCacheEntryKind(isSourceNode, localFile) | ||
| let elCacheObj = createSourceDomCacheEntry({ | ||
| el, | ||
| isRootFragment, | ||
| rootEl, | ||
| vnode, | ||
| instance, | ||
| isFragment, | ||
| firstEl, | ||
| lastEl, | ||
| pgId, | ||
| sourcePgId: !sourceFile && isComponentVNode && pgId ? pgId : null, | ||
| renderPgId: !sourceFile && !isComponentVNode && pgId ? pgId : null, | ||
| sourceInspector, | ||
| sourceFile: isSourceNode ? sourceFile : null, | ||
| framework: 'vue', | ||
| entryKind, | ||
| treeScopes: createSourceDomTreeScopes(entryKind), | ||
| key, | ||
| localFile, | ||
| isIsland, | ||
| }) | ||
| if (prevElCacheNodes) { | ||
| if (index > -1) { | ||
| elCacheObj.sourcePgId = elCacheObj.sourcePgId || prevElCacheObj.sourcePgId | ||
| elCacheObj.renderPgId = elCacheObj.renderPgId || prevElCacheObj.renderPgId | ||
| elCacheObj.sourceInspector = elCacheObj.sourceInspector || prevElCacheObj.sourceInspector | ||
| if (elCacheObj.pgId && prevElCacheObj.pgId && elCacheObj.pgId !== prevElCacheObj.pgId) { | ||
| if (!prevElCacheNodes.map(prevElCacheNode => prevElCacheNode.pgId).includes(elCacheObj.pgId)) { | ||
| prevElCacheNodes.push(elCacheObj) | ||
| } | ||
| } else { | ||
| elCacheObj.pgId = elCacheObj.pgId || prevElCacheObj.pgId | ||
| prevElCacheNodes[index] = elCacheObj | ||
| } | ||
| } else { | ||
| prevElCacheNodes.push(elCacheObj) | ||
| } | ||
| pinegrow.elCache.set(el, prevElCacheNodes) | ||
| } else { | ||
| pinegrow.elCache.set(el, [elCacheObj]) | ||
| childVNodes.forEach(childVNode => { | ||
| if (sourceFile) { | ||
| refreshVueSourceDomMapping( | ||
| hook, | ||
| pgId, | ||
| isRootFragment ? el : rootEl, | ||
| key, | ||
| localFile, | ||
| sourceFile | ||
| )(childVNode) | ||
| } else { | ||
| refreshVueSourceDomMapping(hook, pgId, isRootFragment ? el : rootEl, key)(childVNode) | ||
| } | ||
| }) | ||
| } | ||
| if (pinegrow.elUpdateHanderFn) { | ||
| pinegrow.elUpdateHanderFn(el) | ||
| } | ||
| } catch (err) { | ||
| console.log(err) | ||
| if (pinegrow.elCacheErrHandlerFn) { | ||
| pinegrow.elCacheErrHandlerFn(vnode, hook, rootEl, pgId, key, el, instance, err.message) | ||
| } | ||
| } | ||
| } | ||
| const elUpdateHanderFn = el => { | ||
| // if (pinegrow.elCache.has(el)) { | ||
| // const { pgId } = pinegrow.elCache.get(el) | ||
| // if (pgId) { | ||
| // // console.log(`Reselect ${pgId}`) | ||
| // } | ||
| // } | ||
| } | ||
| const elCacheErrHandlerFn = () => { | ||
| if (message) { | ||
| // console.log(message) | ||
| } | ||
| } | ||
| const cleanupCache = () => { | ||
| // for (let [key, value] of pinegrow.elCache.entries()) { | ||
| // if (value.isFragment && !value.firstEl) { | ||
| // console.log(value) | ||
| // } | ||
| // } | ||
| for (let [el, elCacheNodes] of pinegrow.elCache.entries()) { | ||
| // if (!el.isConnected) { | ||
| // pinegrow.elCache.delete(el) | ||
| // continue | ||
| // } | ||
| const cleanedUpElCacheNodes = elCacheNodes.filter( | ||
| elCacheObj => !elCacheObj.instance.isUnmounted // || elCacheObj.localFile | ||
| ) | ||
| if (cleanedUpElCacheNodes.length) { | ||
| if (cleanedUpElCacheNodes.length !== elCacheNodes.length) { | ||
| pinegrow.elCache.set(el, cleanedUpElCacheNodes) | ||
| } | ||
| } else { | ||
| pinegrow.elCache.delete(el) | ||
| } | ||
| if (pinegrow.elUpdateHanderFn) { | ||
| pinegrow.elUpdateHanderFn(el) | ||
| } | ||
| } | ||
| } | ||
| // Local Component | ||
| const rootVNode = ref(null) | ||
| const mountLocalComponent = () => { | ||
| const instance = getCurrentInstance() | ||
| const vnode = instance?.vnode | ||
| const el = vnode?.el | ||
| const localFile = instance.type.__file && !instance.type.__file.includes('node_modules') && instance.type.__file | ||
| if (instance && vnode && el) { | ||
| rootVNode.value = vnode | ||
| refreshVueSourceDomMapping('mounted', null, null, null, localFile)(vnode) | ||
| } | ||
| } | ||
| const unmountLocalComponent = () => { | ||
| if (rootVNode.value) { | ||
| refreshVueSourceDomMapping('unmounted')(rootVNode.value) | ||
| } | ||
| cleanupCache() | ||
| } | ||
| onBeforeMount(() => ensurePinegrowVueRuntime()) | ||
| onMounted(() => { | ||
| mountLocalComponent() | ||
| }) | ||
| onBeforeUnmount(() => unmountLocalComponent()) | ||
| onUpdated(() => { | ||
| mountLocalComponent() | ||
| }) | ||
| return { | ||
| refreshVueSourceDomMapping, | ||
| pgUpdateElCache: refreshVueSourceDomMapping, | ||
| } | ||
| } | ||
| export function usePinegrow() { | ||
| return createVueRuntimeBridge() | ||
| } |
| export { createVueRuntimeBridge, usePinegrow } from './runtime.js' |
| (()=>{var e={748:e=>{e.exports=function(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n},e.exports.__esModule=!0,e.exports.default=e.exports},314:e=>{e.exports=function(e){if(Array.isArray(e))return e},e.exports.__esModule=!0,e.exports.default=e.exports},850:(e,t,r)=>{var n=r(748);e.exports=function(e){if(Array.isArray(e))return n(e)},e.exports.__esModule=!0,e.exports.default=e.exports},290:(e,t,r)=>{var n=r(739);e.exports=function(e,t,r){return(t=n(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e},e.exports.__esModule=!0,e.exports.default=e.exports},912:e=>{e.exports=function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)},e.exports.__esModule=!0,e.exports.default=e.exports},193:e=>{e.exports=function(e,t){var r=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,o,l,u,i=[],s=!0,a=!1;try{if(l=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;s=!1}else for(;!(s=(n=l.call(r)).done)&&(i.push(n.value),i.length!==t);s=!0);}catch(e){a=!0,o=e}finally{try{if(!s&&null!=r.return&&(u=r.return(),Object(u)!==u))return}finally{if(a)throw o}}return i}},e.exports.__esModule=!0,e.exports.default=e.exports},849:e=>{e.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")},e.exports.__esModule=!0,e.exports.default=e.exports},96:e=>{e.exports=function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")},e.exports.__esModule=!0,e.exports.default=e.exports},681:(e,t,r)=>{var n=r(314),o=r(193),l=r(121),u=r(849);e.exports=function(e,t){return n(e)||o(e,t)||l(e,t)||u()},e.exports.__esModule=!0,e.exports.default=e.exports},408:(e,t,r)=>{var n=r(850),o=r(912),l=r(121),u=r(96);e.exports=function(e){return n(e)||o(e)||l(e)||u()},e.exports.__esModule=!0,e.exports.default=e.exports},64:(e,t,r)=>{var n=r(425).default;e.exports=function(e,t){if("object"!==n(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var o=r.call(e,t||"default");if("object"!==n(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)},e.exports.__esModule=!0,e.exports.default=e.exports},739:(e,t,r)=>{var n=r(425).default,o=r(64);e.exports=function(e){var t=o(e,"string");return"symbol"===n(t)?t:String(t)},e.exports.__esModule=!0,e.exports.default=e.exports},425:e=>{function t(r){return e.exports=t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.__esModule=!0,e.exports.default=e.exports,t(r)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},121:(e,t,r)=>{var n=r(748);e.exports=function(e,t){if(e){if("string"==typeof e)return n(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(e,t):void 0}},e.exports.__esModule=!0,e.exports.default=e.exports}},t={};function r(n){var o=t[n];if(void 0!==o)return o.exports;var l=t[n]={exports:{}};return e[n](l,l.exports,r),l.exports}r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var n={};(()=>{"use strict";r.r(n),r.d(n,{collectAstroSourceDomMapping:()=>U,createAstroComponentEntries:()=>N,createAstroElCacheEntry:()=>B,createAstroRuntimeBridge:()=>L,ensurePinegrowAstroRuntime:()=>q,refreshAstroSourceDomMapping:()=>H});var e=r(681),t=r(290),o=r(425),l=r(408);function u(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var r=1;r<arguments.length;r++){var n=null!=arguments[r]?arguments[r]:{};r%2?u(Object(n),!0).forEach((function(r){t(e,r,n[r])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):u(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function s(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r<t;r++)n[r]=e[r];return n}var a=function(e){return{get value(){return e()}}},c=function(e){return{value:e}},d=function(e,t){return(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).immediate&&"function"==typeof t&&t(function(e){return"function"==typeof e?e():null==e?void 0:e.value}(e),void 0),function(){}},f=function(e){return e&&"function"==typeof e.get&&"function"==typeof e.set&&"function"==typeof e.delete&&"function"==typeof e.entries},p=function(e){return{pageTree:"source-node"===e,appTree:"component-boundary"===e||"page-root"===e}},v=function(e){return Array.isArray(e)?e:[]},m=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return e.firstEl||e.el||t||null},y=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return e.lastEl||e.firstEl||e.el||t||null},g=function(e,t){return e&&"function"==typeof e.getAttribute?e.getAttribute(t):null},b=function(e){var t,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=null===(t=e.vnode)||void 0===t?void 0:t.props,o=m(e,r);return e.sourceInspector||(null==n?void 0:n["data-v-inspector"])||g(o,"data-v-inspector")||g(r,"data-v-inspector")||null},h=function(t){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:a)((function(){var r,n=new Map,o=function(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(e){if("string"==typeof e)return s(e,t);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?s(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var l,u=!0,i=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return u=e.done,e},e:function(e){i=!0,l=e},f:function(){try{u||null==r.return||r.return()}finally{if(i)throw l}}}}(t.entries());try{var l=function(){var o=e(r.value,2),l=o[0],u=o[1];v(u).forEach((function(e){var r,o,u,s,a;if(function(e){return!1!==e.instance.isMounted&&!0!==e.instance.isUnmounted}(e)&&function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,r=m(e,t),n=y(e,t);return Boolean((null==r?void 0:r.isConnected)||(null==n?void 0:n.isConnected)||(null==t?void 0:t.isConnected))}(e,l)&&!0===e.treeScopes.appTree&&e.localFile){var c=e.localFile,d=m(e,l),f=y(e,l);if(d){if(e.isIsland){var p=d.firstElementChild;if(p&&t.has(p))return}var v=null!==(r=e.sourcePgId)&&void 0!==r?r:null,g=v?null!==(s=e.renderPgId)&&void 0!==s?s:null:null!==(o=null!==(u=e.renderPgId)&&void 0!==u?u:e.pgId)&&void 0!==o?o:null,h=i(i({},e),{},{sourceElCacheObj:e,appTreeBoundary:!0,appTreeBoundaryId:null,localFile:c,firstEl:d,lastEl:f,sourcePgId:v,renderPgId:g,sourceInspector:b(e,l)}),x=[c||"",h.instance.uid,null!==(a=h.key)&&void 0!==a?a:""].join("::"),E=n.get(x),I=h;if(E){var w,A,S,C,F,O,P,j,_=h.sourcePgId&&!E.sourcePgId?h:E,k=_===E?h:E,M=_.sourcePgId?_.sourceElCacheObj||_:k.sourcePgId?k.sourceElCacheObj||k:_.sourceElCacheObj||k.sourceElCacheObj||_,T=null!==(w=null!==(A=_.sourcePgId)&&void 0!==A?A:k.sourcePgId)&&void 0!==w?w:null,D=null!==(S=null!==(C=_.renderPgId)&&void 0!==C?C:k.renderPgId)&&void 0!==S?S:null,R=null!==(F=null!==(O=_.sourceInspector)&&void 0!==O?O:k.sourceInspector)&&void 0!==F?F:null;I=i(i(i({},k),_),{},{sourceElCacheObj:M,sourcePgId:T,renderPgId:D,sourceInspector:R,pgId:null!==(P=null!==(j=null!=T?T:_.pgId)&&void 0!==j?j:k.pgId)&&void 0!==P?P:null,firstEl:_.firstEl||k.firstEl,lastEl:_.lastEl||k.lastEl,el:_.el||k.el,localFile:_.localFile||k.localFile})}n.set(x,I)}}}))};for(o.s();!(r=o.n()).done;)l()}catch(e){o.e(e)}finally{o.f()}return Array.from(n.values()).map((function(e){var t,r,n,o,l,u,s,a,c=[(t=e).localFile||"",null!==(r=t.instance.uid)&&void 0!==r?r:"",null!==(n=t.key)&&void 0!==n?n:"",null!==(o=null!==(l=null!==(u=null!==(s=t.sourcePgId)&&void 0!==s?s:t.pgId)&&void 0!==u?u:t.sourceId)&&void 0!==l?l:null===(a=t.source)||void 0===a?void 0:a.id)&&void 0!==o?o:""].join("::");return i(i({},e),{},{appTreeBoundaryId:c})})).sort((function(e,t){var r=m(e),n=m(t);if(null==r||!r.compareDocumentPosition||!n)return 0;var o=r.compareDocumentPosition(n);return 2&o?1:4&o?-1:0}))}))},x=function(e){var t,r,n,o,l,u,s,a,c,d,f,v,m,y,g,b,h,x,E,I,w,A,S,C,F,O,P,j,_=null!==(t=e.sourceRange)&&void 0!==t?t:null,k=e.entryKind;return i({el:e.el,framework:e.framework,entryKind:k,treeScopes:e.treeScopes||p(k),isRootFragment:Boolean(e.isRootFragment),rootEl:null!==(r=e.rootEl)&&void 0!==r?r:null,vnode:null!==(n=e.vnode)&&void 0!==n?n:null,instance:e.instance,isFragment:Boolean(e.isFragment),firstEl:null!==(o=e.firstEl)&&void 0!==o?o:null,lastEl:null!==(l=e.lastEl)&&void 0!==l?l:null,pgId:null!==(u=e.pgId)&&void 0!==u?u:null,sourceId:null!==(s=e.sourceId)&&void 0!==s?s:null,sourcePgId:null!==(a=e.sourcePgId)&&void 0!==a?a:null,renderPgId:null!==(c=e.renderPgId)&&void 0!==c?c:null,snapshotId:null!==(d=e.snapshotId)&&void 0!==d?d:null,sourceSnapshot:null!==(f=e.sourceSnapshot)&&void 0!==f?f:null,sourceInspector:null!==(v=e.sourceInspector)&&void 0!==v?v:null,sourceFile:null!==(m=e.sourceFile)&&void 0!==m?m:null,key:null!==(y=e.key)&&void 0!==y?y:null,localFile:null!==(g=e.localFile)&&void 0!==g?g:null,name:null!==(b=e.name)&&void 0!==b?b:"",source:null!==(h=e.source)&&void 0!==h?h:null,component:null!==(x=e.component)&&void 0!==x?x:null,owners:null!==(E=e.owners)&&void 0!==E?E:[],sourceRange:_,sourceStart:null!==(I=null!==(w=e.sourceStart)&&void 0!==w?w:null==_?void 0:_.start)&&void 0!==I?I:null,sourceEnd:null!==(A=null!==(S=e.sourceEnd)&&void 0!==S?S:null==_?void 0:_.end)&&void 0!==A?A:null,sourceRangeStatus:null!==(C=null!==(F=e.sourceRangeStatus)&&void 0!==F?F:null==_?void 0:_.status)&&void 0!==C?C:null,marker:null!==(O=e.marker)&&void 0!==O?O:null,editability:null!==(P=e.editability)&&void 0!==P?P:null,attributes:null!==(j=e.attributes)&&void 0!==j?j:[],state:e.state,isIsland:Boolean(e.isIsland)},e.extra||{})};function E(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function I(e){for(var r=1;r<arguments.length;r++){var n=null!=arguments[r]?arguments[r]:{};r%2?E(Object(n),!0).forEach((function(r){t(e,r,n[r])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):E(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var w="data-pg-id",A="data-pg-id",S="astro",C=function(e,t){return{uid:"astro:".concat(e.id),isMounted:!0,isUnmounted:!1,parent:null,type:{__name:e.name||"AstroElement",__file:null},props:{},attrs:{},slots:{},subTree:{el:t},appContext:null}},F=function(){return"undefined"!=typeof window?window:null},O=function(e){return(null==e?void 0:e.sourceFile)||(null==e?void 0:e.id)||null},P=function(e){return(null==e?void 0:e.firstEl)||(null==e?void 0:e.el)||null},j=function(e){return"".concat(e||"").replace(/\\/g,"/")},_=function(e){return j(e).toLowerCase()},k=function(e){return e&&j(e).split("/").pop()||""},M=function(e,t,r){return{uid:t,isMounted:!0,isUnmounted:!1,parent:null,type:{__name:k(null==e?void 0:e.sourceFile)||"AstroComponent",__file:(null==e?void 0:e.sourceFile)||null},props:{},attrs:{},slots:{},subTree:{el:r},appContext:null}},T=function(e){return"".concat((null==e?void 0:e.type)||"",":").concat((null==e?void 0:e.key)||"")},D=function(e,t){var r=new Map;return function(e,t){return e&&t?Array.from(e.querySelectorAll("[".concat("data-pg-astro-state","]"))).map((function(e){var r=e.getAttribute("data-pg-astro-source"),n=e.getAttribute("data-pg-astro-state-json");if(!function(e,t){return e===t||_(e)===_(t)}(r,t)||!n)return null;try{return JSON.parse(n)}catch(e){return null}})).filter(Boolean):[]}(e,t).forEach((function(e){(e.entries||[]).forEach((function(n){var l;n&&"string"==typeof n.type&&"string"==typeof n.key&&r.set(T(n),{value:(l=n.value,!l||"object"!==o(l)||Array.isArray(l)?l:"undefined"!==l.__pinegrowAstroType?"bigint"===l.__pinegrowAstroType?"".concat(l.value||"","n"):"function"===l.__pinegrowAstroType?"[function]":"symbol"===l.__pinegrowAstroType?l.value||"[symbol]":l:void 0),source:e.sourceFile||t,version:e.version||null})}))})),r},R=function e(t,r){t&&(r(t),function(e){return Array.from((null==e?void 0:e.childNodes)||[])}(t).forEach((function(t){return e(t,r)})))},B=function(e){var t,r,n=e.boundary,o=void 0===n?null:n,l=e.element,u=e.firstElement,i=void 0===u?null:u,s=e.lastElement,a=void 0===s?null:s,c=e.manifest,d=e.markerAttribute,f=void 0===d?w:d,v=e.node,m=e.mirroredPgId,y=void 0!==m&&m,g=i||l,b=a||g,h=Boolean(o),E=h&&function(e){var t=(null==e?void 0:e.tagName)||(null==e?void 0:e.nodeName)||(null==e?void 0:e.localName)||"";return"astro-island"==="".concat(t).toLowerCase()}(g),F=c.sourceSnapshot||null,O=(null==F?void 0:F.id)||null,P={start:null!==(t=v.sourceStart)&&void 0!==t?t:null,end:null!==(r=v.sourceEnd)&&void 0!==r?r:null,status:v.sourceRangeStatus||"unknown"};return x({el:g,framework:S,entryKind:"source-node",treeScopes:p("source-node"),sourceId:v.id,pgId:v.id,snapshotId:O,sourceSnapshot:F,key:v.id,localFile:null,sourceFile:c.sourceFile||null,nodeType:v.type||null,name:v.name||"",source:I({id:v.id,file:c.sourceFile||null,range:P,nodeType:v.type||null,name:v.name||""},F?{snapshot:F}:{}),component:v.component||null,owners:[],sourceRange:P,sourceStart:P.start,sourceEnd:P.end,sourceRangeStatus:P.status,marker:I(I({},v.marker||{}),{},{liveAttribute:f,pinegrowAttribute:A,mirroredPgId:y,boundary:o}),editability:v.editability||null,attributes:v.attributes||[],isFragment:h,isRootFragment:!1,isIsland:E,rootEl:null,firstEl:g,lastEl:b,instance:C(v,g),vnode:null,extra:{isAstro:!0,isAstroSourceDomEntry:!0,isAstroComponent:!1,isAppTreeComponent:!1}})},N=function(e){var t=e.entries,r=e.manifest,n=e.root,o=function(e){var t=e.entries,r=e.manifest,n=e.root,o=e.stateSnapshotIndex,u=void 0===o?null:o,i=null==r?void 0:r.sourceFile,s=(null==r?void 0:r.sourceSnapshot)||null,a=(null==s?void 0:s.id)||null,c=function(e){return e?e.documentElement?e.documentElement:e.body?e.body:e.firstElementChild?e.firstElementChild:1===e.nodeType?e:null:null}(n),d=function(e){return/(^|\/)src\/pages\/.+\.astro$/i.test(j(e))}(i),f=d&&c,v=!t.length;if(!i||!t.length&&!c)return null;var m,y,g,b=function(e){return l(e).sort((function(e,t){var r=P(e),n=P(t);if(!r||!n||r===n)return 0;var o=r.compareDocumentPosition(n);return 2&o?1:4&o?-1:0}))}(t),h=b[0],E=b[b.length-1],w="astro-component:".concat(i),A=f||v?c:P(h),C=f||v?null:(null==(m=E)?void 0:m.lastEl)||(null==m?void 0:m.el)||null,F=k(i),O=f?"page-root":"component-boundary",_=v?"markerless-component-file":"component-file",D=["astro-component-file"];return v&&D.push("astro-markerless-app-tree-entry"),x({el:A,framework:S,entryKind:O,treeScopes:p(O),sourceId:w,pgId:null,snapshotId:a,sourceSnapshot:s,key:null,localFile:i,sourceFile:i,nodeType:"component-file",name:F,source:I({id:w,file:i,range:{start:null,end:null,status:_},nodeType:"component-file",name:F},s?{snapshot:s}:{}),component:{file:i,name:F,kind:d?"page":"component"},owners:[],sourceRange:{start:null,end:null,status:_},sourceStart:null,sourceEnd:null,sourceRangeStatus:_,marker:null,editability:{mode:"inspect",operations:["select","open-source-file"],reasons:D},attributes:[],state:(y=Array.isArray(r.state)?r.state:[],g=u,Array.isArray(y)&&null!=g&&g.size?y.map((function(e){var t,r=g.get(T(e));return r?I(I({},e),{},{sourceValue:null!==(t=e.sourceValue)&&void 0!==t?t:e.value,value:r.value,valueStatus:"server-snapshot",snapshot:{source:r.source,version:r.version}}):e})):y),isFragment:!1,isRootFragment:!1,isIsland:!1,rootEl:null,firstEl:A,lastEl:C,instance:M(r,w,A),vnode:null,extra:{isAstro:!0,isAstroSourceDomEntry:!1,isAstroComponent:!0,isAppTreeComponent:!0,isAstroPageRoot:Boolean(f),isAstroMarkerlessComponent:Boolean(v)}})}({entries:t,manifest:r,root:n,stateSnapshotIndex:D(n,null==r?void 0:r.sourceFile)});return o?[o]:[]},U=function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},n=function(e){var t;return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).markerAttribute||(null==e||null===(t=e.marker)||void 0===t?void 0:t.attribute)||w}(t,r),o=[],l=[];if("ready"!==(null==t?void 0:t.status))return{status:"unavailable",markerAttribute:n,entries:o,componentEntries:[],missing:l,reason:(null==t?void 0:t.reason)||"Astro source manifest is unavailable."};if(!e)return{status:"unavailable",markerAttribute:n,entries:o,componentEntries:[],missing:l,reason:"A DOM root is required."};var u=function(e){var t=new Map;return((null==e?void 0:e.nodes)||[]).forEach((function(e){null!=e&&e.id&&t.set(e.id,e)})),t}(t);return function(e,t){return e?Array.from(e.querySelectorAll("[".concat(t,"]"))):[]}(e,n).forEach((function(e){var i=function(e,t){return e?e.getAttribute(t):null}(e,n),s=u.get(i);if(s){var a=n!==A&&function(e,t){if(!1===(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).mirrorPgId||!t||!e)return!1;var r,n=e.getAttribute(A);return n===t||!((r=n)&&!r.startsWith("pgastro-"))&&(e.setAttribute(A,t),!0)}(e,i,r);o.push(B({element:e,manifest:t,markerAttribute:n,node:s,mirroredPgId:a}))}else r.reportMissingMarkers&&l.push({element:e,sourceId:i})})),function(e,t){var r=new Map,n=[];return R(e,(function(e){var o=function(e){if(8!==(null==e?void 0:e.nodeType))return null;var t="".concat(e.nodeValue||"").trim().match(/^pgastro:(start|end):(.+)$/);return t?{boundary:t[1],sourceId:t[2]}:null}(e);if(o&&t.has(o.sourceId)){if("start"===o.boundary)return r.has(o.sourceId)||r.set(o.sourceId,[]),void r.get(o.sourceId).push(e);var l=r.get(o.sourceId),u=null==l?void 0:l.pop();if(u){var i=function(e,t){if(null==e||!e.parentNode||e.parentNode!==(null==t?void 0:t.parentNode))return{firstEl:null,lastEl:null};for(var r=null,n=null,o=e.nextSibling;o&&o!==t;)1===o.nodeType&&(r||(r=o),n=o),o=o.nextSibling;return{firstEl:r,lastEl:n}}(u,e),s=i.firstEl,a=i.lastEl;s&&n.push({sourceId:o.sourceId,firstEl:s,lastEl:a||s,startComment:u,endComment:e})}}})),n}(e,u).forEach((function(e){var r=u.get(e.sourceId);o.push(B({boundary:{strategy:"boundary-comment",startComment:e.startComment,endComment:e.endComment},element:e.firstEl,firstElement:e.firstEl,lastElement:e.lastEl,manifest:t,markerAttribute:n,node:r,mirroredPgId:!1}))})),{status:"ready",markerAttribute:n,entries:o,componentEntries:N({entries:o,manifest:t,root:e}),missing:l}},q=function(){var e=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!e)return null;e.pinegrow||(e.pinegrow={});var r=e.pinegrow,n=t.reactiveFromContext||r.reactiveFromContext||function(e){return e},o=t.refFromContext||r.refFromContext||c,l=t.computedFromContext||r.computedFromContext||a,u=t.watchFromContext||r.watchFromContext||d,i=n(f(r.elCache)?r.elCache:new Map);return r.elCache===i&&r.appTreeBoundariesComputed&&r.reactiveFromContext===n&&r.refFromContext===o&&r.computedFromContext===l&&r.watchFromContext===u||Object.assign(r,{elCache:i,appTreeBoundariesComputed:h(i,l),reactiveFromContext:n,refFromContext:o,computedFromContext:l,watchFromContext:u}),r}(arguments.length>0&&void 0!==arguments[0]?arguments[0]:F());return e?(e.astro||(e.astro={}),e.astro.sourceManifests=function(e,t){var r=f(t)?t:new Map,n=(null==e?void 0:e.reactiveFromContext)||function(e){return e};return n(r)}(e,e.astro.sourceManifests),e):null},K=function(t,r){var n=O(r),o=0,l=[];return n?(Array.from(t.entries()).forEach((function(r){var u=e(r,2),i=u[0],s=u[1],a=v(s),c=a.filter((function(e){return!function(e,t){return(null==e?void 0:e.framework)===S&&(e.localFile===t||e.sourceFile===t)}(e,n)})),d=a.length-c.length;o+=d,d&&l.push(i),c.length?t.set(i,c):t.delete(i)})),{elements:l,removed:o}):{elements:l,removed:o}},$=function(e,t){t.forEach((function(t){e.elUpdateHanderFn&&e.elUpdateHanderFn(t)}))},V=function(e,t){!function(e,t,r){var n=v(e.get(t.el)),o=function(e,t){return e.framework===S&&e.sourceId===t.sourceId&&e.localFile===t.localFile}||function(e,t){return e.framework===t.framework&&e.sourceId===t.sourceId&&e.localFile===t.localFile},l=n.filter((function(e){return!o(e,t)}));l.push(t),e.set(t.el,l)}(e,t)},H=function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if("unmounted"===e)return function(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).win||F(),r=q(t);if(!r)return{status:"unavailable",hook:"unmounted",cached:0,removed:0,entries:[],componentEntries:[],missing:[],reason:"A browser window is required."};var n=K(r.elCache,e);$(r,n.elements);var o=O(e);return o&&r.astro.sourceManifests.delete(o),{status:"ready",hook:"unmounted",cached:0,removed:n.removed,entries:[],componentEntries:[],missing:[]}}(t,r);var n=r.win||F(),o=r.root||(null==n?void 0:n.document)||null,u=q(n),i=U(o,t,r);if(!u)return I(I({},i),{},{hook:e,cached:0,removed:0,reason:i.reason||"A browser window is required."});if("ready"!==i.status)return I(I({},i),{},{hook:e,cached:0,removed:0});var s=!1===r.clearPrevious?{elements:[],removed:0}:K(u.elCache,t),a=new Set(s.elements),c=[].concat(l(i.entries),l(i.componentEntries||[]));c.forEach((function(e){V(u.elCache,e),a.add(e.el)})),$(u,a);var d=O(t);return d&&u.astro.sourceManifests.set(d,t),I(I({},i),{},{hook:e,cached:c.length,removed:s.removed})},L=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return{manifest:e,collect:function(r){var n,o;return U(r||t.root||(null===(n=t.win)||void 0===n?void 0:n.document)||(null===(o=F())||void 0===o?void 0:o.document),e,t)},refresh:function(r){return H("updated",e,I(I({},t),r))}}}})(),module.exports=n})(); |
| /*! For license information please see runtime.cjs.LICENSE.txt */ | ||
| (()=>{var e={748:e=>{e.exports=function(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n},e.exports.__esModule=!0,e.exports.default=e.exports},314:e=>{e.exports=function(e){if(Array.isArray(e))return e},e.exports.__esModule=!0,e.exports.default=e.exports},850:(e,t,r)=>{var n=r(748);e.exports=function(e){if(Array.isArray(e))return n(e)},e.exports.__esModule=!0,e.exports.default=e.exports},461:e=>{function t(e,t,r,n,o,l,i){try{var u=e[l](i),a=u.value}catch(e){return void r(e)}u.done?t(a):Promise.resolve(a).then(n,o)}e.exports=function(e){return function(){var r=this,n=arguments;return new Promise((function(o,l){var i=e.apply(r,n);function u(e){t(i,o,l,u,a,"next",e)}function a(e){t(i,o,l,u,a,"throw",e)}u(void 0)}))}},e.exports.__esModule=!0,e.exports.default=e.exports},290:(e,t,r)=>{var n=r(739);e.exports=function(e,t,r){return(t=n(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e},e.exports.__esModule=!0,e.exports.default=e.exports},912:e=>{e.exports=function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)},e.exports.__esModule=!0,e.exports.default=e.exports},193:e=>{e.exports=function(e,t){var r=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,o,l,i,u=[],a=!0,c=!1;try{if(l=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;a=!1}else for(;!(a=(n=l.call(r)).done)&&(u.push(n.value),u.length!==t);a=!0);}catch(e){c=!0,o=e}finally{try{if(!a&&null!=r.return&&(i=r.return(),Object(i)!==i))return}finally{if(c)throw o}}return u}},e.exports.__esModule=!0,e.exports.default=e.exports},849:e=>{e.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")},e.exports.__esModule=!0,e.exports.default=e.exports},96:e=>{e.exports=function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")},e.exports.__esModule=!0,e.exports.default=e.exports},609:(e,t,r)=>{var n=r(425).default;function o(){"use strict";e.exports=o=function(){return r},e.exports.__esModule=!0,e.exports.default=e.exports;var t,r={},l=Object.prototype,i=l.hasOwnProperty,u=Object.defineProperty||function(e,t,r){e[t]=r.value},a="function"==typeof Symbol?Symbol:{},c=a.iterator||"@@iterator",s=a.asyncIterator||"@@asyncIterator",d=a.toStringTag||"@@toStringTag";function f(e,t,r){return Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}),e[t]}try{f({},"")}catch(t){f=function(e,t,r){return e[t]=r}}function p(e,t,r,n){var o=t&&t.prototype instanceof b?t:b,l=Object.create(o.prototype),i=new M(n||[]);return u(l,"_invoke",{value:C(e,r,i)}),l}function v(e,t,r){try{return{type:"normal",arg:e.call(t,r)}}catch(e){return{type:"throw",arg:e}}}r.wrap=p;var h="suspendedStart",y="suspendedYield",g="executing",m="completed",x={};function b(){}function w(){}function I(){}var _={};f(_,c,(function(){return this}));var E=Object.getPrototypeOf,P=E&&E(E(k([])));P&&P!==l&&i.call(P,c)&&(_=P);var F=I.prototype=b.prototype=Object.create(_);function S(e){["next","throw","return"].forEach((function(t){f(e,t,(function(e){return this._invoke(t,e)}))}))}function j(e,t){function r(o,l,u,a){var c=v(e[o],e,l);if("throw"!==c.type){var s=c.arg,d=s.value;return d&&"object"==n(d)&&i.call(d,"__await")?t.resolve(d.__await).then((function(e){r("next",e,u,a)}),(function(e){r("throw",e,u,a)})):t.resolve(d).then((function(e){s.value=e,u(s)}),(function(e){return r("throw",e,u,a)}))}a(c.arg)}var o;u(this,"_invoke",{value:function(e,n){function l(){return new t((function(t,o){r(e,n,t,o)}))}return o=o?o.then(l,l):l()}})}function C(e,r,n){var o=h;return function(l,i){if(o===g)throw new Error("Generator is already running");if(o===m){if("throw"===l)throw i;return{value:t,done:!0}}for(n.method=l,n.arg=i;;){var u=n.delegate;if(u){var a=O(u,n);if(a){if(a===x)continue;return a}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(o===h)throw o=m,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);o=g;var c=v(e,r,n);if("normal"===c.type){if(o=n.done?m:y,c.arg===x)continue;return{value:c.arg,done:n.done}}"throw"===c.type&&(o=m,n.method="throw",n.arg=c.arg)}}}function O(e,r){var n=r.method,o=e.iterator[n];if(o===t)return r.delegate=null,"throw"===n&&e.iterator.return&&(r.method="return",r.arg=t,O(e,r),"throw"===r.method)||"return"!==n&&(r.method="throw",r.arg=new TypeError("The iterator does not provide a '"+n+"' method")),x;var l=v(o,e.iterator,r.arg);if("throw"===l.type)return r.method="throw",r.arg=l.arg,r.delegate=null,x;var i=l.arg;return i?i.done?(r[e.resultName]=i.value,r.next=e.nextLoc,"return"!==r.method&&(r.method="next",r.arg=t),r.delegate=null,x):i:(r.method="throw",r.arg=new TypeError("iterator result is not an object"),r.delegate=null,x)}function T(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function A(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function M(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(T,this),this.reset(!0)}function k(e){if(e||""===e){var r=e[c];if(r)return r.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var o=-1,l=function r(){for(;++o<e.length;)if(i.call(e,o))return r.value=e[o],r.done=!1,r;return r.value=t,r.done=!0,r};return l.next=l}}throw new TypeError(n(e)+" is not iterable")}return w.prototype=I,u(F,"constructor",{value:I,configurable:!0}),u(I,"constructor",{value:w,configurable:!0}),w.displayName=f(I,d,"GeneratorFunction"),r.isGeneratorFunction=function(e){var t="function"==typeof e&&e.constructor;return!!t&&(t===w||"GeneratorFunction"===(t.displayName||t.name))},r.mark=function(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,I):(e.__proto__=I,f(e,d,"GeneratorFunction")),e.prototype=Object.create(F),e},r.awrap=function(e){return{__await:e}},S(j.prototype),f(j.prototype,s,(function(){return this})),r.AsyncIterator=j,r.async=function(e,t,n,o,l){void 0===l&&(l=Promise);var i=new j(p(e,t,n,o),l);return r.isGeneratorFunction(t)?i:i.next().then((function(e){return e.done?e.value:i.next()}))},S(F),f(F,d,"Generator"),f(F,c,(function(){return this})),f(F,"toString",(function(){return"[object Generator]"})),r.keys=function(e){var t=Object(e),r=[];for(var n in t)r.push(n);return r.reverse(),function e(){for(;r.length;){var n=r.pop();if(n in t)return e.value=n,e.done=!1,e}return e.done=!0,e}},r.values=k,M.prototype={constructor:M,reset:function(e){if(this.prev=0,this.next=0,this.sent=this._sent=t,this.done=!1,this.delegate=null,this.method="next",this.arg=t,this.tryEntries.forEach(A),!e)for(var r in this)"t"===r.charAt(0)&&i.call(this,r)&&!isNaN(+r.slice(1))&&(this[r]=t)},stop:function(){this.done=!0;var e=this.tryEntries[0].completion;if("throw"===e.type)throw e.arg;return this.rval},dispatchException:function(e){if(this.done)throw e;var r=this;function n(n,o){return u.type="throw",u.arg=e,r.next=n,o&&(r.method="next",r.arg=t),!!o}for(var o=this.tryEntries.length-1;o>=0;--o){var l=this.tryEntries[o],u=l.completion;if("root"===l.tryLoc)return n("end");if(l.tryLoc<=this.prev){var a=i.call(l,"catchLoc"),c=i.call(l,"finallyLoc");if(a&&c){if(this.prev<l.catchLoc)return n(l.catchLoc,!0);if(this.prev<l.finallyLoc)return n(l.finallyLoc)}else if(a){if(this.prev<l.catchLoc)return n(l.catchLoc,!0)}else{if(!c)throw new Error("try statement without catch or finally");if(this.prev<l.finallyLoc)return n(l.finallyLoc)}}}},abrupt:function(e,t){for(var r=this.tryEntries.length-1;r>=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&i.call(n,"finallyLoc")&&this.prev<n.finallyLoc){var o=n;break}}o&&("break"===e||"continue"===e)&&o.tryLoc<=t&&t<=o.finallyLoc&&(o=null);var l=o?o.completion:{};return l.type=e,l.arg=t,o?(this.method="next",this.next=o.finallyLoc,x):this.complete(l)},complete:function(e,t){if("throw"===e.type)throw e.arg;return"break"===e.type||"continue"===e.type?this.next=e.arg:"return"===e.type?(this.rval=this.arg=e.arg,this.method="return",this.next="end"):"normal"===e.type&&t&&(this.next=t),x},finish:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),A(r),x}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var o=n.arg;A(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(e,r,n){return this.delegate={iterator:k(e),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=t),x}},r}e.exports=o,e.exports.__esModule=!0,e.exports.default=e.exports},681:(e,t,r)=>{var n=r(314),o=r(193),l=r(121),i=r(849);e.exports=function(e,t){return n(e)||o(e,t)||l(e,t)||i()},e.exports.__esModule=!0,e.exports.default=e.exports},408:(e,t,r)=>{var n=r(850),o=r(912),l=r(121),i=r(96);e.exports=function(e){return n(e)||o(e)||l(e)||i()},e.exports.__esModule=!0,e.exports.default=e.exports},64:(e,t,r)=>{var n=r(425).default;e.exports=function(e,t){if("object"!==n(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var o=r.call(e,t||"default");if("object"!==n(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)},e.exports.__esModule=!0,e.exports.default=e.exports},739:(e,t,r)=>{var n=r(425).default,o=r(64);e.exports=function(e){var t=o(e,"string");return"symbol"===n(t)?t:String(t)},e.exports.__esModule=!0,e.exports.default=e.exports},425:e=>{function t(r){return e.exports=t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.__esModule=!0,e.exports.default=e.exports,t(r)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},121:(e,t,r)=>{var n=r(748);e.exports=function(e,t){if(e){if("string"==typeof e)return n(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(e,t):void 0}},e.exports.__esModule=!0,e.exports.default=e.exports},841:(e,t,r)=>{var n=r(609)();e.exports=n;try{regeneratorRuntime=n}catch(e){"object"==typeof globalThis?globalThis.regeneratorRuntime=n:Function("r","regeneratorRuntime = r")(n)}}},t={};function r(n){var o=t[n];if(void 0!==o)return o.exports;var l=t[n]={exports:{}};return e[n](l,l.exports,r),l.exports}r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var n={};(()=>{"use strict";r.r(n),r.d(n,{createVueRuntimeBridge:()=>E,usePinegrow:()=>P});var e=r(681),t=r(408),o=r(461),l=r(841);const i=require("vue");var u=r(290);function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function c(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?a(Object(r),!0).forEach((function(t){u(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):a(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function s(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r<t;r++)n[r]=e[r];return n}var d=function(e){return{get value(){return e()}}},f=function(e){return{value:e}},p=function(e,t){return(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).immediate&&"function"==typeof t&&t(function(e){return"function"==typeof e?e():null==e?void 0:e.value}(e),void 0),function(){}},v=function(e){return{pageTree:"source-node"===e,appTree:"component-boundary"===e||"page-root"===e}},h=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return e.firstEl||e.el||t||null},y=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return e.lastEl||e.firstEl||e.el||t||null},g=function(e,t){return e&&"function"==typeof e.getAttribute?e.getAttribute(t):null},m=function(e){var t,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=null===(t=e.vnode)||void 0===t?void 0:t.props,o=h(e,r);return e.sourceInspector||(null==n?void 0:n["data-v-inspector"])||g(o,"data-v-inspector")||g(r,"data-v-inspector")||null},x=function(t){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:d)((function(){var r,n=new Map,o=function(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(e){if("string"==typeof e)return s(e,t);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?s(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var l,i=!0,u=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return i=e.done,e},e:function(e){u=!0,l=e},f:function(){try{i||null==r.return||r.return()}finally{if(u)throw l}}}}(t.entries());try{var l=function(){var o,l=e(r.value,2),i=l[0],u=l[1];(o=u,Array.isArray(o)?o:[]).forEach((function(e){var r,o,l,u,a;if(function(e){return!1!==e.instance.isMounted&&!0!==e.instance.isUnmounted}(e)&&function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,r=h(e,t),n=y(e,t);return Boolean((null==r?void 0:r.isConnected)||(null==n?void 0:n.isConnected)||(null==t?void 0:t.isConnected))}(e,i)&&!0===e.treeScopes.appTree&&e.localFile){var s=e.localFile,d=h(e,i),f=y(e,i);if(d){if(e.isIsland){var p=d.firstElementChild;if(p&&t.has(p))return}var v=null!==(r=e.sourcePgId)&&void 0!==r?r:null,g=v?null!==(u=e.renderPgId)&&void 0!==u?u:null:null!==(o=null!==(l=e.renderPgId)&&void 0!==l?l:e.pgId)&&void 0!==o?o:null,x=c(c({},e),{},{sourceElCacheObj:e,appTreeBoundary:!0,appTreeBoundaryId:null,localFile:s,firstEl:d,lastEl:f,sourcePgId:v,renderPgId:g,sourceInspector:m(e,i)}),b=[s||"",x.instance.uid,null!==(a=x.key)&&void 0!==a?a:""].join("::"),w=n.get(b),I=x;if(w){var _,E,P,F,S,j,C,O,T=x.sourcePgId&&!w.sourcePgId?x:w,A=T===w?x:w,M=T.sourcePgId?T.sourceElCacheObj||T:A.sourcePgId?A.sourceElCacheObj||A:T.sourceElCacheObj||A.sourceElCacheObj||T,k=null!==(_=null!==(E=T.sourcePgId)&&void 0!==E?E:A.sourcePgId)&&void 0!==_?_:null,L=null!==(P=null!==(F=T.renderPgId)&&void 0!==F?F:A.renderPgId)&&void 0!==P?P:null,B=null!==(S=null!==(j=T.sourceInspector)&&void 0!==j?j:A.sourceInspector)&&void 0!==S?S:null;I=c(c(c({},A),T),{},{sourceElCacheObj:M,sourcePgId:k,renderPgId:L,sourceInspector:B,pgId:null!==(C=null!==(O=null!=k?k:T.pgId)&&void 0!==O?O:A.pgId)&&void 0!==C?C:null,firstEl:T.firstEl||A.firstEl,lastEl:T.lastEl||A.lastEl,el:T.el||A.el,localFile:T.localFile||A.localFile})}n.set(b,I)}}}))};for(o.s();!(r=o.n()).done;)l()}catch(e){o.e(e)}finally{o.f()}return Array.from(n.values()).map((function(e){var t,r,n,o,l,i,u,a,s=[(t=e).localFile||"",null!==(r=t.instance.uid)&&void 0!==r?r:"",null!==(n=t.key)&&void 0!==n?n:"",null!==(o=null!==(l=null!==(i=null!==(u=t.sourcePgId)&&void 0!==u?u:t.pgId)&&void 0!==i?i:t.sourceId)&&void 0!==l?l:null===(a=t.source)||void 0===a?void 0:a.id)&&void 0!==o?o:""].join("::");return c(c({},e),{},{appTreeBoundaryId:s})})).sort((function(e,t){var r=h(e),n=h(t);if(null==r||!r.compareDocumentPosition||!n)return 0;var o=r.compareDocumentPosition(n);return 2&o?1:4&o?-1:0}))}))},b=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!e)return null;e.pinegrow||(e.pinegrow={});var r,n=e.pinegrow,o=t.reactiveFromContext||n.reactiveFromContext||function(e){return e},l=t.refFromContext||n.refFromContext||f,i=t.computedFromContext||n.computedFromContext||d,u=t.watchFromContext||n.watchFromContext||p,a=o((r=n.elCache)&&"function"==typeof r.get&&"function"==typeof r.set&&"function"==typeof r.delete&&"function"==typeof r.entries?n.elCache:new Map);return n.elCache===a&&n.appTreeBoundariesComputed&&n.reactiveFromContext===o&&n.refFromContext===l&&n.computedFromContext===i&&n.watchFromContext===u||Object.assign(n,{elCache:a,appTreeBoundariesComputed:x(a,i),reactiveFromContext:o,refFromContext:l,computedFromContext:i,watchFromContext:u}),n},w=function(e){var t,r,n,o,l,i,u,a,s,d,f,p,h,y,g,m,x,b,w,I,_,E,P,F,S,j,C,O,T=null!==(t=e.sourceRange)&&void 0!==t?t:null,A=e.entryKind;return c({el:e.el,framework:e.framework,entryKind:A,treeScopes:e.treeScopes||v(A),isRootFragment:Boolean(e.isRootFragment),rootEl:null!==(r=e.rootEl)&&void 0!==r?r:null,vnode:null!==(n=e.vnode)&&void 0!==n?n:null,instance:e.instance,isFragment:Boolean(e.isFragment),firstEl:null!==(o=e.firstEl)&&void 0!==o?o:null,lastEl:null!==(l=e.lastEl)&&void 0!==l?l:null,pgId:null!==(i=e.pgId)&&void 0!==i?i:null,sourceId:null!==(u=e.sourceId)&&void 0!==u?u:null,sourcePgId:null!==(a=e.sourcePgId)&&void 0!==a?a:null,renderPgId:null!==(s=e.renderPgId)&&void 0!==s?s:null,snapshotId:null!==(d=e.snapshotId)&&void 0!==d?d:null,sourceSnapshot:null!==(f=e.sourceSnapshot)&&void 0!==f?f:null,sourceInspector:null!==(p=e.sourceInspector)&&void 0!==p?p:null,sourceFile:null!==(h=e.sourceFile)&&void 0!==h?h:null,key:null!==(y=e.key)&&void 0!==y?y:null,localFile:null!==(g=e.localFile)&&void 0!==g?g:null,name:null!==(m=e.name)&&void 0!==m?m:"",source:null!==(x=e.source)&&void 0!==x?x:null,component:null!==(b=e.component)&&void 0!==b?b:null,owners:null!==(w=e.owners)&&void 0!==w?w:[],sourceRange:T,sourceStart:null!==(I=null!==(_=e.sourceStart)&&void 0!==_?_:null==T?void 0:T.start)&&void 0!==I?I:null,sourceEnd:null!==(E=null!==(P=e.sourceEnd)&&void 0!==P?P:null==T?void 0:T.end)&&void 0!==E?E:null,sourceRangeStatus:null!==(F=null!==(S=e.sourceRangeStatus)&&void 0!==S?S:null==T?void 0:T.status)&&void 0!==F?F:null,marker:null!==(j=e.marker)&&void 0!==j?j:null,editability:null!==(C=e.editability)&&void 0!==C?C:null,attributes:null!==(O=e.attributes)&&void 0!==O?O:[],state:e.state,isIsland:Boolean(e.isIsland)},e.extra||{})};function I(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r<t;r++)n[r]=e[r];return n}var _=function(e,t){return e?"source-node":t?"component-boundary":"render-node"};function E(){var r=function(){var e,t=window;null!=t&&null!==(e=t.process)&&void 0!==e&&e.client&&!0!==t.process.client||b(t,{reactiveFromContext:i.reactive,refFromContext:i.ref,computedFromContext:i.computed,watchFromContext:i.watch})},n=function e(n,i,u,a,c,s){var d;return function(f){return(d=d||o(l.mark((function o(d){var f,p,h,y,g,x,b,I,E,P,F,S,j,C,O,T,A,M,k,L,B,U,R,N,G,D,H;return l.wrap((function(o){for(;;)switch(o.prev=o.next){case 0:if(d){o.next=2;break}return o.abrupt("return");case 2:if(null===(f=window)||void 0===f||null===(f=f.process)||void 0===f||!f.client||!0===(null===(p=process)||void 0===p?void 0:p.client)){o.next=4;break}return o.abrupt("return");case 4:if(window.pinegrow||r(),h=d.el,y=d.component||d.ctx||h.__vueParentComponent,h&&y){o.next=9;break}return o.abrupt("return");case 9:if(o.prev=9,(null!=a||null!==d.key&&void 0!==d.key)&&(a=d.key||a),s||(c=c||d.type.__file),x=!1,b=1!==h.nodeType,P=!1,F=[],c&&(c.includes("node_modules/iles")&&c.includes("Island.vue")&&(P=!0),c.includes("node_modules")&&(c=null)),(S=y.type.__file)&&S.includes("node_modules/iles")&&S.includes("Island.vue")&&(P=!0,c=(null===(j=y.props.component)||void 0===j?void 0:j.__file)||y.props.importFrom),b&&(C=function(e){return 1!==e.subTree.el.nodeType},O=function(e){return C(e)?T(e.subTree):e.subTree?[e.subTree]:[]},T=function(e){if(!e.children)return[];if(!Array.isArray(e.children))return[];for(var r=[],n=0,o=e.children.length;n<o;n++){var l=e.children[n];l.component?r.push.apply(r,t(O(l.component))):l&&r.push(l)}return r},u||(x=!0),(F=O(y)).length||h.nextElementSibling&&(A=h.nextElementSibling,I=E=A,M=A.$||A.__vueParentComponent,(k=null==M?void 0:M.vnode)&&F.push(k)),F.length&&(F=F.filter((function(e){return e.el!==h})),F.length&&(1===F.length?I=E=F[0].el:(I=F[0].el,E=F[F.length-1].el),I&&1!==I.nodeType&&(I=I.nextElementSibling),E&&1!==E.nodeType&&(E=E.nextElementSibling)))),!P||x){o.next=21;break}return o.abrupt("return");case 21:L=Boolean(d.component),B=m({vnode:d,el:h,firstEl:I,lastEl:E},h),U=Boolean(s&&i),R=pinegrow.elCache.get(h),N=null,G=-1,R&&(G=U?R.findIndex((function(e){return e.instance.uid===y.uid&&e.sourceFile===s&&e.pgId===i})):R.findIndex((function(e){return e.instance.uid===y.uid})),G>-1&&(N=R[G],s||(c=c||N.localFile))),c=null!==(g=c)&&void 0!==g?g:null,D=_(U,c),H=w({el:h,isRootFragment:x,rootEl:u,vnode:d,instance:y,isFragment:b,firstEl:I,lastEl:E,pgId:i,sourcePgId:!s&&L&&i?i:null,renderPgId:s||L||!i?null:i,sourceInspector:B,sourceFile:U?s:null,framework:"vue",entryKind:D,treeScopes:v(D),key:a,localFile:c,isIsland:P}),R?(G>-1?(H.sourcePgId=H.sourcePgId||N.sourcePgId,H.renderPgId=H.renderPgId||N.renderPgId,H.sourceInspector=H.sourceInspector||N.sourceInspector,H.pgId&&N.pgId&&H.pgId!==N.pgId?R.map((function(e){return e.pgId})).includes(H.pgId)||R.push(H):(H.pgId=H.pgId||N.pgId,R[G]=H)):R.push(H),pinegrow.elCache.set(h,R)):(pinegrow.elCache.set(h,[H]),F.forEach((function(t){s?e(n,i,x?h:u,a,c,s)(t):e(n,i,x?h:u,a)(t)}))),pinegrow.elUpdateHanderFn&&pinegrow.elUpdateHanderFn(h),o.next=39;break;case 35:o.prev=35,o.t0=o.catch(9),console.log(o.t0),pinegrow.elCacheErrHandlerFn&&pinegrow.elCacheErrHandlerFn(d,n,u,i,a,h,y,o.t0.message);case 39:case"end":return o.stop()}}),o,null,[[9,35]])})))).apply(this,arguments)}},u=(0,i.ref)(null),a=function(){var e=(0,i.getCurrentInstance)(),t=null==e?void 0:e.vnode,r=null==t?void 0:t.el,o=e.type.__file&&!e.type.__file.includes("node_modules")&&e.type.__file;e&&t&&r&&(u.value=t,n("mounted",null,null,null,o)(t))},c=function(){u.value&&n("unmounted")(u.value),function(){var t,r=function(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(e){if("string"==typeof e)return I(e,t);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?I(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var l,i=!0,u=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return i=e.done,e},e:function(e){u=!0,l=e},f:function(){try{i||null==r.return||r.return()}finally{if(u)throw l}}}}(pinegrow.elCache.entries());try{for(r.s();!(t=r.n()).done;){var n=e(t.value,2),o=n[0],l=n[1],i=l.filter((function(e){return!e.instance.isUnmounted}));i.length?i.length!==l.length&&pinegrow.elCache.set(o,i):pinegrow.elCache.delete(o),pinegrow.elUpdateHanderFn&&pinegrow.elUpdateHanderFn(o)}}catch(e){r.e(e)}finally{r.f()}}()};return(0,i.onBeforeMount)((function(){return r()})),(0,i.onMounted)((function(){a()})),(0,i.onBeforeUnmount)((function(){return c()})),(0,i.onUpdated)((function(){a()})),{refreshVueSourceDomMapping:n,pgUpdateElCache:n}}function P(){return E()}})(),module.exports=n})(); |
| /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ |
| export type VueSourceDomMappingRefresher = ( | ||
| hook?: string, | ||
| pgId?: string | number | null, | ||
| rootEl?: Element | null, | ||
| key?: string | number | null, | ||
| localFile?: string | null, | ||
| sourceFile?: string | null | ||
| ) => (vnode: unknown) => Promise<void> | ||
| export function createVueRuntimeBridge(): { | ||
| refreshVueSourceDomMapping: VueSourceDomMappingRefresher | ||
| pgUpdateElCache: VueSourceDomMappingRefresher | ||
| } | ||
| export function usePinegrow(): { | ||
| refreshVueSourceDomMapping: VueSourceDomMappingRefresher | ||
| pgUpdateElCache: VueSourceDomMappingRefresher | ||
| } |
+41
-3
| { | ||
| "name": "@pinegrow/vite-plugin", | ||
| "version": "3.0.77-alpha.0", | ||
| "version": "3.0.77-alpha.1", | ||
| "description": "Pinegrow Vite Plugin", | ||
@@ -8,3 +8,6 @@ "type": "module", | ||
| "dist", | ||
| "./types.d.ts" | ||
| "agent-workflows", | ||
| "./types.d.ts", | ||
| "./vue-runtime.d.ts", | ||
| "./astro-runtime.d.ts" | ||
| ], | ||
@@ -24,4 +27,28 @@ "main": "./dist/index.cjs", | ||
| }, | ||
| "./dev/vue/runtime": { | ||
| "import": "./src/adapters/vue-sfc/mapping/runtime/runtime.js", | ||
| "require": "./src/adapters/vue-sfc/mapping/runtime/runtime.js" | ||
| }, | ||
| "./dev/vue": { | ||
| "import": "./src/adapters/vue-sfc/mapping/runtime/runtime.js", | ||
| "require": "./src/adapters/vue-sfc/mapping/runtime/runtime.js" | ||
| }, | ||
| "./dev/astro/runtime": { | ||
| "import": "./src/adapters/astro-sfc/mapping/runtime/runtime.js", | ||
| "require": "./src/adapters/astro-sfc/mapping/runtime/runtime.js" | ||
| }, | ||
| "./vue/runtime": { | ||
| "types": "./vue-runtime.d.ts", | ||
| "import": "./dist/adapters/vue-sfc/mapping/runtime/runtime.mjs", | ||
| "require": "./dist/vue/runtime.cjs" | ||
| }, | ||
| "./vue": { | ||
| "import": "./dist/runtime/vue/runtime-adapter.js" | ||
| "types": "./vue-runtime.d.ts", | ||
| "import": "./dist/adapters/vue-sfc/mapping/runtime/runtime.mjs", | ||
| "require": "./dist/vue/runtime.cjs" | ||
| }, | ||
| "./astro/runtime": { | ||
| "types": "./astro-runtime.d.ts", | ||
| "import": "./dist/adapters/astro-sfc/mapping/runtime/runtime.mjs", | ||
| "require": "./dist/astro/runtime.cjs" | ||
| } | ||
@@ -52,3 +79,14 @@ }, | ||
| "magic-string": "^0.27.0" | ||
| }, | ||
| "peerDependencies": { | ||
| "@astrojs/compiler": "^2.2.1" | ||
| }, | ||
| "peerDependenciesMeta": { | ||
| "@astrojs/compiler": { | ||
| "optional": true | ||
| } | ||
| }, | ||
| "devDependencies": { | ||
| "@astrojs/compiler": "^2.2.1" | ||
| } | ||
| } |
+10
-1
@@ -116,2 +116,12 @@ // https://vuedesigner.com | ||
| /** | ||
| * Experimental integration hints consumed by Vue Designer. | ||
| */ | ||
| experimental?: { | ||
| designableFileTypes?: string[] | ||
| disabledFeatures?: string[] | ||
| sourceFrameworks?: string[] | ||
| [key: string]: any | ||
| } | ||
| /** | ||
| * Array of Pinegrow plugins (npm package names) | ||
@@ -142,2 +152,1 @@ * To use file-based Pinegrow Plugins, provide the absolute path of the plugin file, for eg, [path.resolve(__dirname, './pg-plugin/index.js')] as pluginPath, along with a unique key | ||
| export { PinegrowVitePlugin, LiveDesignerOptions, export_default as default, export_default as liveDesigner } | ||
| import { | ||
| onBeforeMount, | ||
| onMounted, | ||
| onBeforeUnmount, | ||
| getCurrentInstance, | ||
| ref, | ||
| reactive, | ||
| onUpdated, | ||
| computed, | ||
| watch, | ||
| } from 'vue' | ||
| const getVueCacheEntryKind = (isSourceNode, localFile) => { | ||
| if (isSourceNode) { | ||
| return 'source-node' | ||
| } | ||
| return localFile ? 'component-boundary' : 'render-node' | ||
| } | ||
| const getVueCacheTreeScopes = entryKind => ({ | ||
| pageTree: entryKind === 'source-node', | ||
| appTree: entryKind === 'component-boundary', | ||
| }) | ||
| const getBoundaryElement = (elCacheObj, fallbackEl = null) => | ||
| elCacheObj.firstEl || elCacheObj.el || fallbackEl || null | ||
| const getBoundaryLastElement = (elCacheObj, fallbackEl = null) => | ||
| elCacheObj.lastEl || elCacheObj.firstEl || elCacheObj.el || fallbackEl || null | ||
| const getSourceInspector = (elCacheObj, fallbackEl = null) => { | ||
| const props = elCacheObj.vnode?.props | ||
| const firstEl = getBoundaryElement(elCacheObj, fallbackEl) | ||
| return ( | ||
| elCacheObj.sourceInspector || | ||
| props?.['data-v-inspector'] || | ||
| firstEl?.getAttribute?.('data-v-inspector') || | ||
| fallbackEl?.getAttribute?.('data-v-inspector') || | ||
| null | ||
| ) | ||
| } | ||
| const isBoundaryConnected = (elCacheObj, fallbackEl = null) => { | ||
| const firstEl = getBoundaryElement(elCacheObj, fallbackEl) | ||
| const lastEl = getBoundaryLastElement(elCacheObj, fallbackEl) | ||
| return Boolean(firstEl?.isConnected || lastEl?.isConnected || fallbackEl?.isConnected) | ||
| } | ||
| const isBoundaryMounted = elCacheObj => { | ||
| return elCacheObj.instance.isMounted !== false && elCacheObj.instance.isUnmounted !== true | ||
| } | ||
| const getBoundaryId = elCacheObj => { | ||
| const file = elCacheObj.localFile || '' | ||
| const uid = elCacheObj.instance.uid ?? '' | ||
| const key = elCacheObj.key ?? '' | ||
| const pgId = elCacheObj.sourcePgId ?? elCacheObj.pgId ?? elCacheObj.sourceId ?? elCacheObj.source?.id ?? '' | ||
| return [file, uid, key, pgId].join('::') | ||
| } | ||
| const createAppTreeBoundariesComputed = elCache => | ||
| computed(() => { | ||
| const boundariesByRuntimeKey = new Map() | ||
| for (let [el, elCacheNodes] of elCache.entries()) { | ||
| elCacheNodes.forEach(elCacheObj => { | ||
| if ( | ||
| !isBoundaryMounted(elCacheObj) || | ||
| !isBoundaryConnected(elCacheObj, el) || | ||
| elCacheObj.treeScopes.appTree !== true || | ||
| !elCacheObj.localFile | ||
| ) { | ||
| return | ||
| } | ||
| const localFile = elCacheObj.localFile | ||
| const firstEl = getBoundaryElement(elCacheObj, el) | ||
| const lastEl = getBoundaryLastElement(elCacheObj, el) | ||
| if (!firstEl) { | ||
| return | ||
| } | ||
| if (elCacheObj.isIsland) { | ||
| const childEl = firstEl.firstElementChild | ||
| if (childEl && elCache.has(childEl)) { | ||
| return | ||
| } | ||
| } | ||
| const sourcePgId = elCacheObj.sourcePgId ?? null | ||
| const renderPgId = !sourcePgId | ||
| ? elCacheObj.renderPgId ?? elCacheObj.pgId ?? null | ||
| : elCacheObj.renderPgId ?? null | ||
| const candidate = { | ||
| ...elCacheObj, | ||
| sourceElCacheObj: elCacheObj, | ||
| appTreeBoundary: true, | ||
| appTreeBoundaryId: null, | ||
| localFile, | ||
| firstEl, | ||
| lastEl, | ||
| sourcePgId, | ||
| renderPgId, | ||
| sourceInspector: getSourceInspector(elCacheObj, el), | ||
| } | ||
| // Start App Tree runtime-boundary dedupe. | ||
| const runtimeKey = [localFile || '', candidate.instance.uid, candidate.key ?? ''].join('::') | ||
| const existingBoundary = boundariesByRuntimeKey.get(runtimeKey) | ||
| let boundary = candidate | ||
| if (existingBoundary) { | ||
| const preferred = | ||
| candidate.sourcePgId && !existingBoundary.sourcePgId ? candidate : existingBoundary | ||
| const fallback = preferred === existingBoundary ? candidate : existingBoundary | ||
| const sourceElCacheObj = preferred.sourcePgId | ||
| ? preferred.sourceElCacheObj || preferred | ||
| : fallback.sourcePgId | ||
| ? fallback.sourceElCacheObj || fallback | ||
| : preferred.sourceElCacheObj || fallback.sourceElCacheObj || preferred | ||
| const sourcePgId = preferred.sourcePgId ?? fallback.sourcePgId ?? null | ||
| const renderPgId = preferred.renderPgId ?? fallback.renderPgId ?? null | ||
| const sourceInspector = preferred.sourceInspector ?? fallback.sourceInspector ?? null | ||
| boundary = { | ||
| ...fallback, | ||
| ...preferred, | ||
| sourceElCacheObj, | ||
| sourcePgId, | ||
| renderPgId, | ||
| sourceInspector, | ||
| pgId: sourcePgId ?? preferred.pgId ?? fallback.pgId ?? null, | ||
| firstEl: preferred.firstEl || fallback.firstEl, | ||
| lastEl: preferred.lastEl || fallback.lastEl, | ||
| el: preferred.el || fallback.el, | ||
| localFile: preferred.localFile || fallback.localFile, | ||
| } | ||
| } | ||
| boundariesByRuntimeKey.set(runtimeKey, boundary) | ||
| // End App Tree runtime-boundary dedupe. | ||
| }) | ||
| } | ||
| const boundaries = Array.from(boundariesByRuntimeKey.values()).map(boundary => { | ||
| const boundaryId = getBoundaryId(boundary) | ||
| return { | ||
| ...boundary, | ||
| appTreeBoundaryId: boundaryId, | ||
| } | ||
| }) | ||
| return boundaries.sort((a, b) => { | ||
| const aEl = getBoundaryElement(a) | ||
| const bEl = getBoundaryElement(b) | ||
| if (!aEl?.compareDocumentPosition || !bEl) { | ||
| return 0 | ||
| } | ||
| const position = aEl.compareDocumentPosition(bEl) | ||
| if (position & Node.DOCUMENT_POSITION_PRECEDING) { | ||
| return 1 | ||
| } | ||
| if (position & Node.DOCUMENT_POSITION_FOLLOWING) { | ||
| return -1 | ||
| } | ||
| return 0 | ||
| }) | ||
| }) | ||
| export function usePinegrow() { | ||
| const initCache = () => { | ||
| // conditional | ||
| const winObj = window | ||
| if (!(winObj?.process?.client && winObj.process.client !== true)) { | ||
| const isMapLike = value => | ||
| value && | ||
| typeof value.get === 'function' && | ||
| typeof value.set === 'function' && | ||
| typeof value.delete === 'function' && | ||
| typeof value.entries === 'function' | ||
| const existingPinegrow = winObj.pinegrow || {} | ||
| const existingElCache = isMapLike(existingPinegrow.elCache) ? existingPinegrow.elCache : new Map() | ||
| const elCache = reactive(existingElCache) | ||
| if ( | ||
| !winObj.pinegrow || | ||
| winObj.pinegrow.elCache !== elCache || | ||
| !winObj.pinegrow.appTreeBoundariesComputed || | ||
| winObj.pinegrow.reactiveFromContext !== reactive || | ||
| winObj.pinegrow.refFromContext !== ref || | ||
| winObj.pinegrow.computedFromContext !== computed || | ||
| winObj.pinegrow.watchFromContext !== watch | ||
| ) { | ||
| // console.log('Cache initialized by Vue Plugin!') | ||
| const enrichWithComponentName = (elCacheObj, localFile) => { | ||
| return { | ||
| name: elCacheObj.instance.type.__name || '', | ||
| localFile, | ||
| ...elCacheObj, | ||
| } | ||
| } | ||
| winObj.pinegrow = Object.assign(existingPinegrow, { | ||
| elCache, | ||
| // pgIdToElComputed, | ||
| // localComponentToElComputed, | ||
| appTreeBoundariesComputed: createAppTreeBoundariesComputed(elCache), | ||
| reactiveFromContext: reactive, | ||
| refFromContext: ref, | ||
| computedFromContext: computed, | ||
| watchFromContext: watch, | ||
| // // Uncomment these two to test locally | ||
| // elCacheErrHandlerFn, | ||
| // elUpdateHanderFn, | ||
| }) | ||
| } | ||
| } | ||
| } | ||
| // pgId & key can be optional | ||
| const pgUpdateElCache = (hook, pgId, rootEl, key, localFile, sourceFile) => async vnode => { | ||
| if (!vnode) return | ||
| if (window?.process?.client && process?.client !== true) return | ||
| if (!window.pinegrow) initCache() | ||
| let el = vnode.el | ||
| const instance = vnode.component || vnode.ctx || el.__vueParentComponent | ||
| if (!el || !instance) return | ||
| try { | ||
| if ((key !== null && key !== undefined) || (vnode.key !== null && vnode.key !== undefined)) { | ||
| key = vnode.key || key | ||
| } | ||
| if (!sourceFile) { | ||
| localFile = localFile || vnode.type.__file | ||
| } | ||
| let isRootFragment = false | ||
| let isFragment = el.nodeType !== 1 | ||
| let firstEl, | ||
| lastEl, | ||
| isIsland = false, | ||
| childVNodes = [] | ||
| // May be an iles Island that wraps components with client directives | ||
| if (localFile) { | ||
| if (localFile.includes('node_modules/iles') && localFile.includes('Island.vue')) { | ||
| // Retain localFiles of iles island to apply to child elements | ||
| isIsland = true | ||
| } | ||
| if (localFile.includes('node_modules')) { | ||
| // Ignore SFCs from node_modules | ||
| localFile = null | ||
| } | ||
| } | ||
| const localFileFromInstance = instance.type.__file | ||
| if ( | ||
| localFileFromInstance && | ||
| localFileFromInstance.includes('node_modules/iles') && | ||
| localFileFromInstance.includes('Island.vue') | ||
| ) { | ||
| // Retain localFiles of iles island to apply to child elements | ||
| isIsland = true | ||
| localFile = instance.props.component?.__file || instance.props.importFrom | ||
| } | ||
| // // Computed props anyway filters out unmounted ones, and we use the local components unmounted hooks to cleanup unmounted nodes. Using the unmounted hook seems to be out of sync when el is reused but instance is remounted. | ||
| // if (hook === 'unmounted') { | ||
| // if (pinegrow.elCache.has(el)) { | ||
| // pinegrow.elCache.delete(el) | ||
| // } | ||
| // for (let [key, value] of pinegrow.elCache.entries()) { | ||
| // if (value.rootEl === rootEl) { | ||
| // pinegrow.elCache.delete(key) | ||
| // } | ||
| // } | ||
| // return | ||
| // } | ||
| // Text/comment node | ||
| if (isFragment) { | ||
| if (!rootEl) { | ||
| // root Fragment | ||
| isRootFragment = true | ||
| } | ||
| function isFragmentEl(instance) { | ||
| // return appRecord.options.types.Fragment === instance.subTree?.type | ||
| return instance.subTree.el.nodeType !== 1 | ||
| } | ||
| function getRootVNodesFromComponentInstance(instance) { | ||
| if (isFragmentEl(instance)) { | ||
| return getFragmentRootVNodes(instance.subTree) | ||
| } | ||
| if (!instance.subTree) return [] | ||
| return [instance.subTree] | ||
| } | ||
| function getFragmentRootVNodes(vnode) { | ||
| if (!vnode.children) return [] | ||
| // children is v-if when the vnode has a condition | ||
| if (!Array.isArray(vnode.children)) return [] | ||
| const list = [] | ||
| for (let i = 0, l = vnode.children.length; i < l; i++) { | ||
| const childVnode = vnode.children[i] | ||
| if (childVnode.component) { | ||
| list.push(...getRootVNodesFromComponentInstance(childVnode.component)) | ||
| } else if (childVnode) { | ||
| list.push(childVnode) | ||
| } | ||
| } | ||
| return list | ||
| } | ||
| childVNodes = getRootVNodesFromComponentInstance(instance) | ||
| if (!childVNodes.length) { | ||
| // For NuxtLayout, no childVNodes are returned, the subTree.children is in fact an object {ctx: {}, default: f} | ||
| if (el.nextElementSibling) { | ||
| const childEl = el.nextElementSibling | ||
| firstEl = lastEl = childEl | ||
| const childInstance = childEl.$ || childEl.__vueParentComponent | ||
| const childVNode = childInstance?.vnode // or subTree? | ||
| if (childVNode) { | ||
| childVNodes.push(childVNode) | ||
| } | ||
| } | ||
| } | ||
| if (childVNodes.length) { | ||
| // Filter out recursive vnodes text1 -> div, text1 (happens when text1 is the closest to a fragment with div & slot) | ||
| childVNodes = childVNodes.filter(childVNode => childVNode.el !== el) | ||
| if (childVNodes.length) { | ||
| if (childVNodes.length === 1) { | ||
| firstEl = lastEl = childVNodes[0].el | ||
| } else { | ||
| firstEl = childVNodes[0].el | ||
| lastEl = childVNodes[childVNodes.length - 1].el | ||
| } | ||
| if (firstEl && firstEl.nodeType !== 1) { | ||
| firstEl = firstEl.nextElementSibling | ||
| } | ||
| if (lastEl && lastEl.nodeType !== 1) { | ||
| lastEl = lastEl.nextElementSibling | ||
| } | ||
| } | ||
| } | ||
| } | ||
| if (isIsland && !isRootFragment) { | ||
| return | ||
| } | ||
| const isComponentVNode = Boolean(vnode.component) | ||
| const sourceInspector = getSourceInspector({ vnode, el, firstEl, lastEl }, el) | ||
| const isSourceNode = Boolean(sourceFile && pgId) | ||
| let prevElCacheNodes = pinegrow.elCache.get(el) | ||
| let prevElCacheObj = null | ||
| let index = -1 | ||
| if (prevElCacheNodes) { | ||
| index = isSourceNode | ||
| ? prevElCacheNodes.findIndex( | ||
| prevElCacheObj => | ||
| prevElCacheObj.instance.uid === instance.uid && | ||
| prevElCacheObj.sourceFile === sourceFile && | ||
| prevElCacheObj.pgId === pgId | ||
| ) | ||
| : prevElCacheNodes.findIndex(elCacheObj => elCacheObj.instance.uid === instance.uid) | ||
| if (index > -1) { | ||
| prevElCacheObj = prevElCacheNodes[index] | ||
| if (!sourceFile) { | ||
| localFile = localFile || prevElCacheObj.localFile | ||
| } | ||
| } | ||
| } | ||
| localFile = localFile ?? null | ||
| const entryKind = getVueCacheEntryKind(isSourceNode, localFile) | ||
| let elCacheObj = { | ||
| el, | ||
| isRootFragment, | ||
| rootEl, | ||
| vnode, | ||
| instance, | ||
| isFragment, | ||
| firstEl, | ||
| lastEl, | ||
| pgId, | ||
| sourcePgId: !sourceFile && isComponentVNode && pgId ? pgId : null, | ||
| renderPgId: !sourceFile && !isComponentVNode && pgId ? pgId : null, | ||
| sourceInspector, | ||
| sourceFile: isSourceNode ? sourceFile : null, | ||
| framework: 'vue', | ||
| entryKind, | ||
| treeScopes: getVueCacheTreeScopes(entryKind), | ||
| key, | ||
| localFile, | ||
| isIsland, | ||
| } | ||
| if (prevElCacheNodes) { | ||
| if (index > -1) { | ||
| elCacheObj.sourcePgId = elCacheObj.sourcePgId || prevElCacheObj.sourcePgId | ||
| elCacheObj.renderPgId = elCacheObj.renderPgId || prevElCacheObj.renderPgId | ||
| elCacheObj.sourceInspector = elCacheObj.sourceInspector || prevElCacheObj.sourceInspector | ||
| if (elCacheObj.pgId && prevElCacheObj.pgId && elCacheObj.pgId !== prevElCacheObj.pgId) { | ||
| if (!prevElCacheNodes.map(prevElCacheNode => prevElCacheNode.pgId).includes(elCacheObj.pgId)) { | ||
| prevElCacheNodes.push(elCacheObj) | ||
| } | ||
| } else { | ||
| elCacheObj.pgId = elCacheObj.pgId || prevElCacheObj.pgId | ||
| prevElCacheNodes[index] = elCacheObj | ||
| } | ||
| } else { | ||
| prevElCacheNodes.push(elCacheObj) | ||
| } | ||
| pinegrow.elCache.set(el, prevElCacheNodes) | ||
| } else { | ||
| pinegrow.elCache.set(el, [elCacheObj]) | ||
| childVNodes.forEach(childVNode => { | ||
| if (sourceFile) { | ||
| pgUpdateElCache( | ||
| hook, | ||
| pgId, | ||
| isRootFragment ? el : rootEl, | ||
| key, | ||
| localFile, | ||
| sourceFile | ||
| )(childVNode) | ||
| } else { | ||
| pgUpdateElCache(hook, pgId, isRootFragment ? el : rootEl, key)(childVNode) | ||
| } | ||
| }) | ||
| } | ||
| if (pinegrow.elUpdateHanderFn) { | ||
| pinegrow.elUpdateHanderFn(el) | ||
| } | ||
| } catch (err) { | ||
| console.log(err) | ||
| if (pinegrow.elCacheErrHandlerFn) { | ||
| pinegrow.elCacheErrHandlerFn(vnode, hook, rootEl, pgId, key, el, instance, err.message) | ||
| } | ||
| } | ||
| } | ||
| const elUpdateHanderFn = el => { | ||
| // if (pinegrow.elCache.has(el)) { | ||
| // const { pgId } = pinegrow.elCache.get(el) | ||
| // if (pgId) { | ||
| // // console.log(`Reselect ${pgId}`) | ||
| // } | ||
| // } | ||
| } | ||
| const elCacheErrHandlerFn = () => { | ||
| if (message) { | ||
| // console.log(message) | ||
| } | ||
| } | ||
| const cleanupCache = () => { | ||
| // for (let [key, value] of pinegrow.elCache.entries()) { | ||
| // if (value.isFragment && !value.firstEl) { | ||
| // console.log(value) | ||
| // } | ||
| // } | ||
| for (let [el, elCacheNodes] of pinegrow.elCache.entries()) { | ||
| // if (!el.isConnected) { | ||
| // pinegrow.elCache.delete(el) | ||
| // continue | ||
| // } | ||
| const cleanedUpElCacheNodes = elCacheNodes.filter( | ||
| elCacheObj => !elCacheObj.instance.isUnmounted // || elCacheObj.localFile | ||
| ) | ||
| if (cleanedUpElCacheNodes.length) { | ||
| if (cleanedUpElCacheNodes.length !== elCacheNodes.length) { | ||
| pinegrow.elCache.set(el, cleanedUpElCacheNodes) | ||
| } | ||
| } else { | ||
| pinegrow.elCache.delete(el) | ||
| } | ||
| if (pinegrow.elUpdateHanderFn) { | ||
| pinegrow.elUpdateHanderFn(el) | ||
| } | ||
| } | ||
| } | ||
| // Local Component | ||
| const rootVNode = ref(null) | ||
| const mountLocalComponent = () => { | ||
| const instance = getCurrentInstance() | ||
| const vnode = instance?.vnode | ||
| const el = vnode?.el | ||
| const localFile = instance.type.__file && !instance.type.__file.includes('node_modules') && instance.type.__file | ||
| if (instance && vnode && el) { | ||
| rootVNode.value = vnode | ||
| pgUpdateElCache('mounted', null, null, null, localFile)(vnode) | ||
| } | ||
| } | ||
| const unmountLocalComponent = () => { | ||
| if (rootVNode.value) { | ||
| pgUpdateElCache('unmounted')(rootVNode.value) | ||
| } | ||
| cleanupCache() | ||
| } | ||
| onBeforeMount(() => initCache()) | ||
| onMounted(() => { | ||
| mountLocalComponent() | ||
| }) | ||
| onBeforeUnmount(() => unmountLocalComponent()) | ||
| onUpdated(() => { | ||
| mountLocalComponent() | ||
| }) | ||
| return { pgUpdateElCache } | ||
| } |
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
1163291
20.77%45
462.5%1744
188.74%3
50%1
Infinity%5
66.67%31
10.71%11
10%