diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 879fa32..01d89a0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,6 +21,7 @@ concurrency: cancel-in-progress: false env: + EDITOR_MANIFEST_SHA256: 3e0510de27b93c5aed5018058b55bd82798ad7ed535fd1c25812aef5a0aadc03 ZOLA_VERSION: "0.22.1" CF_PROJECT: terraphim-ai OP_API_TOKEN: op://TerraphimPlatform/terraphim-md-book-cloudflare/workers-api-token @@ -42,9 +43,17 @@ jobs: sudo mv zola /usr/local/bin zola --version + - name: Verify admitted editor source assets + run: | + python3 -m unittest discover -s scripts -p 'test_*.py' -v + python3 scripts/import-editor.py --verify --destination static/editor --manifest-sha256 "$EDITOR_MANIFEST_SHA256" + - name: Build site run: zola build + - name: Verify generated editor assets before deployment + run: python3 scripts/import-editor.py --verify --destination public/editor --manifest-sha256 "$EDITOR_MANIFEST_SHA256" + - name: Load secrets from 1Password id: op-load-secrets uses: 1password/load-secrets-action@v2 @@ -56,6 +65,7 @@ jobs: CLOUDFLARE_ACCOUNT_ID: ${{ env.OP_ACCOUNT_ID }} - name: Deploy to Cloudflare Pages + if: github.ref == 'refs/heads/main' id: deploy uses: cloudflare/wrangler-action@v3 with: @@ -63,7 +73,16 @@ jobs: accountId: ${{ env.CLOUDFLARE_ACCOUNT_ID }} command: pages deploy public --project-name=${{ env.CF_PROJECT }} --branch=main + - name: Deploy qualified branch candidate to preview + if: github.ref != 'refs/heads/main' + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ env.CLOUDFLARE_API_TOKEN }} + accountId: ${{ env.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy public --project-name=${{ env.CF_PROJECT }} --branch=editor-publication-preview + - name: Verify the deployed site serves the committed version + if: github.ref == 'refs/heads/main' env: DEPLOY_URL: ${{ steps.deploy.outputs.deployment-url }} run: | diff --git a/campaigns/skills-2026/campaign.md b/campaigns/skills-2026/campaign.md new file mode 100644 index 0000000..fab253d --- /dev/null +++ b/campaigns/skills-2026/campaign.md @@ -0,0 +1,105 @@ +# Evidence-backed skills campaign + +## Objective + +Publish a phased, source-linked account of what the 54 marketplace candidate skills instruct agents to do, while leading release announcements with the four free community skills. Do not imply that instruction guarantees an outcome. Keep four evidence classes separate: + +1. documented workflow guidance; +2. source-branch release candidate; +3. independently exercised runtime behaviour; +4. currently live marketplace availability. + +The live marketplace remains authoritative for availability. The source branch is authoritative only for candidate content. + +## Stop conditions + +No platform announcement or Zola post leaves draft while any applicable condition remains: + +- candidate and live catalogue counts differ without an explicit limitation; +- a promoted skill is unmerged or unavailable through the claimed route; +- installer copy implies that `scripts/skills.sh` exposes core or premium packages, or admits a community package that requires unsigned or omitted companion files; +- package validation is presented as runtime proof; +- a claim lacks a stable evidence ID, reviewed source range, and reproduction result; +- `learning-capture` or `gitea-issue-to-pr` appears as a recommendation before its blockers close; +- source links do not resolve to an immutable public commit; +- a platform's current self-promotion, flair, tag, account, or generated-content rules have not been checked by the human publisher. + +## Phase 0: remediate and release + +1. After explicit approval, merge stacked PRs #65, #66, and #72 in order, then mirror the release commit to GitHub. +2. Verify the public shell installer exposes exactly the four signed community packages and installs each package completely. +3. Publish the four community packages, verify each live page and install route, and rerun the ledger commands at the release commit. + +If phase 0 is incomplete, publication remains paused. Drafts may be reviewed internally. + +The premium audit drafts have independent gates. `learning-capture` requires secret redaction, no payload re-emission, accurate storage discovery and host activation, adversarial tests, and security review. `gitea-issue-to-pr` requires fail-closed gates, validation-verdict enforcement, structured remote read-backs, shell-safe values, portable configuration, and executable failure-path tests. These findings do not block the community release when the affected premium packages are omitted from recommendation and installer copy. + +## Phase 1: community release + +Publish the overview first, then one community spotlight no more frequently than every three days: + +1. `terraphim-learning-memory`: present session retrieval as built-in while distinguishing the guide from an unavailable `memory` namespace. +2. `terraphim-grep`: separate the unmerged skill package from locally exercised `terraphim-grep` 1.21.13 behaviour. +3. `local-knowledge`: present the validated direct, structured, read-only retrieval workflow and its privacy boundary. +4. `kg-rlm-ingest`: present deterministic-first extraction and the authorised write plus retrieval boundary; do not imply that production knowledge was mutated during validation. + +Do not turn the sequence into near-identical launch posts. Each spotlight must lead with a distinct workflow, limitation, and verification task. The `learning-capture`, `gitea-issue-to-pr`, and `issue-to-pr-core` drafts remain internal premium catalogue audits, not community-installer announcements. + +## Phase 2: broader marketplace catalogue + +Begin only after phase 1 feedback is incorporated. Build the schedule from marketplace metadata and manifest tiers, excluding the four community packages already covered. Publish one grouped article per week, with individual spotlights only where readers ask for them. Never present a core or premium package as available through `scripts/skills.sh`. + +Suggested themes are discovery and planning, verification and review, Rust engineering, advanced knowledge and orchestration, trackers and collaboration, research and communication, and architecture and delivery. Recount the non-community set against `.claude-plugin/marketplace.json` before scheduling. + +## Proof protocol + +For every material sentence: + +1. Assign a stable ID before drafting. +2. Copy the exact public wording into `evidence.md`. +3. Record the primary source path and line range at the reviewed revision. +4. Record the exact command, exit status, and meaningful result for executable claims. +5. Mark the claim `proved`, `documented`, or `blocked`. +6. Add `` immediately after the sentence in Zola content. +7. Classify it as guidance, candidate, runtime, or live availability. +8. Re-run executable evidence after any source or release change. +9. Remove or narrow a claim when evidence weakens. Never broaden it to save copy. + +Package signatures, manifests, static validators, and installer structure belong to package evidence. Runtime claims require an exercised released binary or workflow. Availability requires a check against the live marketplace, not a branch or PR. + +## Editorial rules + +- Use British English and plain technical language. +- Use no emoji, prose em dashes, inflated adjectives, or outcome guarantees. +- Do not claim performance, adoption, production use, reliability, security, or effectiveness without dedicated evidence. +- Say what a skill instructs an agent to do. +- Link public copy to immutable GitHub paths. Use an obvious editorial placeholder until the commit is public. +- Keep blocked findings visible. A valid signature does not cancel a security or workflow defect. +- Require a human technical and platform review before changing `draft=true`. + +## Platform anti-spam controls + +### Reddit + +- Use one substantive post per community, not repeated link drops. +- For r/opensource, use Promotional flair, disclose maintainer affiliation, and recheck current self-promotion rules. +- Do not post the same copy to r/TerraphimAI and r/opensource on the same day. +- Answer questions directly and do not manufacture engagement. + +### Hacker News + +- Submit only the title and link specified in `platform-drafts.md`. +- Use a regular submission for the catalogue article; Show HN excludes blog posts and lists. +- Comments and replies must be personally written. Do not use generated comments. +- Do not repost after a weak reception unless there is a material new release. + +### Lobsters + +- Confirm account eligibility before submission. +- Use `show` and `vibecoding` only if they still fit current tag guidance. +- Avoid near-duplicate submissions and disclose maintainer context in any permitted description. +- Require the account holder to provide meaningful human authorship, not a cursory approval of generated copy. + +## Measurement and pause rule + +Track referral visits, catalogue-page visits, documentation corrections, reproduction reports, and substantive questions. Do not infer workflow effectiveness from clicks. Pause the next wave when reviewers find an unsupported claim, a broken reproduction, a package mismatch, or repeated platform complaints. Correct the source, ledger, and every affected draft before resuming. diff --git a/campaigns/skills-2026/evidence.md b/campaigns/skills-2026/evidence.md new file mode 100644 index 0000000..6eb6d30 --- /dev/null +++ b/campaigns/skills-2026/evidence.md @@ -0,0 +1,499 @@ +# Evidence ledger: skills campaign 2026 + +Review basis: `/Users/alex/projects/terraphim/terraphim-skills` at `fe4dd603451b8912e69598579b299f2fd128c071` on branch `task/71-community-only-installer`, reviewed 2026-09-07. Absolute paths are intentional in this internal ledger. Public copy must use a stable GitHub commit path or an editorial placeholder. + +## Claim states + +- **proved**: directly supported by reviewed source and an executed check. +- **documented**: the skill instructs an agent to follow the stated workflow; runtime outcomes are not implied. +- **blocked**: the claim or package must not be promoted until its gate passes. + +Package validation establishes integrity and structure. It does not establish that a workflow produces its intended runtime outcome. + +## Catalogue and release claims + +### CAT-001 + +- **Exact claim:** The source-branch marketplace candidate lists 54 skill packages. +- **Status:** proved +- **Primary source:** `/Users/alex/projects/terraphim/terraphim-skills/.claude-plugin/marketplace.json:11-1198` +- **Verification:** + + ```text + $ jq -r '.plugins[].name' .claude-plugin/marketplace.json | wc -l | tr -d ' ' + 54 + ``` + +- **Publication gate:** retain the count only while marketplace metadata still contains 54 unique packages at the publication revision. + +### CAT-002 + +- **Exact claim:** Each of those 54 marketplace candidate skills has a `SKILL.md` and a `skill.toml`. +- **Status:** proved +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/*/SKILL.md:1-10`; `/Users/alex/projects/terraphim/terraphim-skills/skills/*/skill.toml:1-24` +- **Verification:** iterating over `jq -r '.plugins[].name' .claude-plugin/marketplace.json` and checking `skills/$skill/SKILL.md` plus `skills/$skill/skill.toml` returned no missing paths and exit 0. +- **Publication gate:** rerun against the publication revision. + +### CAT-003 + +- **Exact claim:** The candidate marketplace metadata version is 1.6.1. +- **Status:** proved +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/.claude-plugin/marketplace.json:7-10`; `/Users/alex/projects/terraphim/terraphim-skills/scripts/skills.sh:79-80` +- **Verification:** `jq -r '.metadata.version' .claude-plugin/marketplace.json` returned `1.6.1`; `rg -n '^echo "Terraphim Engineering Skills v1.6.1"$' scripts/skills.sh` returned the matching version line. +- **Publication gate:** confirm the version has not changed before publication. + +### CAT-004 + +- **Exact claim:** The live marketplace currently reports 47 active skills, not 54. +- **Status:** proved +- **Primary source:** `https://terraphim-skills.md`, fetched 2026-09-07 +- **Verification:** `curl -fsSL https://terraphim-skills.md | rg -o '47 production-grade skills|View all 47 skills'` returned both `47 production-grade skills` and `View all 47 skills` with exit 0. +- **Publication gate:** replace this claim only after the live marketplace itself reports the new count. + +### CAT-005 + +- **Exact claim:** The four community packages are source-branch release candidates on open, unmerged stacked pull requests #65, #66, and #72. +- **Status:** proved +- **Primary sources:** Gitea PRs #65, #66, and #72; `/Users/alex/projects/terraphim/terraphim-skills/scripts/skills.sh:10-15` +- **Verification:** `gtr view-pull` returned `state:"open"` and `merged:false` for all three stacked PRs; #72 is based on #66 and contains the four-entry community installer plus the `local-knowledge` and `kg-rlm-ingest` community changes. +- **Publication gate:** all three PRs merged in order, mirrored to a stable public GitHub commit, packages published, and public install routes verified. + +### CAT-006 + +- **Exact claim:** The community change passed 45 Python tests, 73% aggregate branch coverage, six Bun tests, signed-manifest and installer-policy validation, TSM validation for all four community skills, and an isolated install of four skills with zero skipped. +- **Status:** proved +- **Primary source:** `/Users/alex/projects/terraphim/terraphim-skills/docs/validation/validation-report-community-knowledge-skills.md:101-116` +- **Verification:** the report records 45 passing Python tests, 73% aggregate branch coverage, six passing Bun tests, passing trust-chain checks, four passing TSM validations, and `Four installed, zero skipped` in an isolated destination. +- **Publication gate:** rerun the checks at the release commit and keep package, documentation, and runtime claims separate. + +### CAT-007 + +- **Exact claim:** The community shell installer downloads one `SKILL.md` per selected skill; every current community manifest signs only that standalone file, so the four-skill install does not omit a required community package file. +- **Status:** proved +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/scripts/skills.sh:38-75`; `/Users/alex/projects/terraphim/terraphim-skills/docs/validation/validation-report-community-knowledge-skills.md:23-35` +- **Verification:** signed-manifest validation and an isolated install confirmed that all four current community packages contain the required standalone `SKILL.md` and no omitted companion file. +- **Publication gate:** keep the shell route community-only; extend it to manifest-declared files before admitting any future multi-file community package. + +### CAT-008 + +- **Exact claim:** A valid digest can prove that downloaded bytes match reviewed bytes. It cannot prove that those bytes redact secrets, activate on every claimed host, or store data where the guide says. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/docs/signed-skill-manifests.md:3-15`; CAT-006; LRN-001; LRN-002 +- **Verification:** trust-chain documentation and blocker review. +- **Publication gate:** keep integrity and runtime wording separate. + +### CAT-009 + +- **Exact claim:** The adapter validator can show that expected files and contract text are present. It does not prove fail-closed execution, safe interpolation, or independent remote verification. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/scripts/validate-command-adapters.py:280-368`; GIT-001 to GIT-003 +- **Verification:** validator returned 23/23; workflow source review found runtime enforcement gaps. +- **Publication gate:** add executable adapter tests before making runtime conformance claims. + +### CAT-010 + +- **Exact claim:** `learning-capture` and `gitea-issue-to-pr` remain blocked in this campaign. Their spotlight drafts explain the findings and the checks required before either can be recommended. +- **Status:** blocked +- **Primary sources:** LRN-001; LRN-002; GIT-001 to GIT-004 +- **Verification:** source and threat review recorded in those entries. +- **Publication gate:** close every referenced blocker. + +### CAT-011 + +- **Exact claim:** `scripts/skills.sh` exposes exactly four free community skills: `kg-rlm-ingest`, `local-knowledge`, `terraphim-grep`, and `terraphim-learning-memory`; core and premium packages remain in the 54-entry marketplace catalogue and are not exposed through that installer. +- **Status:** proved +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/scripts/skills.sh:10-15`; `/Users/alex/projects/terraphim/terraphim-skills/README.md:8-34`; `/Users/alex/projects/terraphim/terraphim-skills/scripts/validate-optional-augmentation.py` +- **Verification:** `bash scripts/skills.sh --list` returned the four names; the validator derived expected membership from signed manifests with `tier = "community"` and rejected missing, additional, or duplicate entries. +- **Publication gate:** merge PR #72 and verify the public installer returns the same four names. + +## Community and blocked spotlight claims + +### MEM-001 + +- **Exact claim:** The installed `terraphim-agent` 1.21.13 has no `memory` namespace. +- **Status:** proved +- **Primary source:** installed CLI help on 2026-09-07 +- **Verification:** `terraphim-agent --version; terraphim-agent memory --help` returned `terraphim-agent 1.21.13`, `error: unrecognized subcommand 'memory'`, and exit 2. +- **Publication gate:** retain this limitation until a released binary's help exposes and runtime checks exercise the namespace. + +### MEM-002 + +- **Exact claim:** `terraphim-learning-memory` is orchestration guidance that starts with capability discovery and routes writes to owning Terraphim components; it is not evidence of a shipped memory runtime. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:31-62`; `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:64-80` +- **Verification:** source review; MEM-001 confirms the installed CLI limitation. +- **Publication gate:** present it as guidance until the package is published and any runtime namespace is separately released and exercised. + +### MEM-003 + +- **Exact claim:** The learning-memory guidance assigns builds and tests to implementation or verification, and makes deterministic KG extraction the default with RLM optional only for long, genuinely novel material. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:174-180`; `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:227-231` +- **Verification:** source review. +- **Publication gate:** preserve these boundaries through review and release. + +### MEM-004 + +- **Exact claim:** The practical boundary is capability discovery. An agent checks installed help before selecting a command, then uses the component that owns the information: failed-command learning, session history, local role knowledge, or a project knowledge graph. A successful route message is not treated as proof that content was stored. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:31-62`; `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:64-80` +- **Verification:** source review. +- **Publication gate:** keep command selection conditional on installed help. + +### MEM-005 + +- **Exact claim:** That means a normal “remember this” request should not trigger a broad build or recursive decomposition. Build results can become evidence, but the memory guide does not own compilation. Likewise, deterministic extraction remains the normal route for knowledge-graph material; RLM is a bounded fallback rather than the default. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:174-180`; `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:227-231` +- **Verification:** source review. +- **Publication gate:** preserve deterministic-first and build-ownership wording. + +### MEM-006 + +- **Exact claim:** Publication can call this a released skill only after PRs #65, #66, and #72 merge, the package is published, and its live catalogue and install route are checked. +- **Status:** blocked +- **Primary source:** CAT-005 +- **Verification:** current PR and live catalogue checks under CAT-004 and CAT-005. +- **Publication gate:** the claim itself states the gate. + +### MEM-007 + +- **Exact claim:** Claims about a `memory` runtime need separate evidence from a released binary whose help exposes that namespace, followed by exercised capture, read-back, correction, retirement, and failure behaviour. Until then, the accurate description is narrower: this candidate teaches capability-aware memory orchestration across existing Terraphim components. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:31-62`; installed CLI result under MEM-001 +- **Verification:** source review and `terraphim-agent memory --help` exit 2. +- **Publication gate:** runtime namespace release plus the listed behavioural checks. + +### MEM-008 + +- **Exact claim:** This makes the guide an orchestration layer over capabilities that may differ between installations. It also keeps temporary task progress out of long-lived knowledge unless there is a durable, sourced fact to preserve. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:31-80`; `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:109-123` +- **Verification:** source review. +- **Publication gate:** no additional gate beyond MEM-006. + +### MEM-009 + +- **Exact claim:** Session history is built into this community workflow. The released CLI exposes `sources`, `list`, `search`, and `stats`; live validation found matching prior work while also surfacing parser warnings that callers must not hide. +- **Status:** proved +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:83-133`; `/Users/alex/projects/terraphim/terraphim-skills/docs/validation/validation-report-community-knowledge-skills.md:37-49` +- **Verification:** `terraphim-agent sessions --help` exposed the four subcommands and `terraphim-agent sessions search "local-knowledge kg-rlm-ingest community skill validation" --limit 10` returned matching sessions with parser warnings for unsupported records. +- **Publication gate:** retain the installed version and warning boundary; do not invent `sessions import`, `sessions show`, or export commands. + +### LKG-001 + +- **Exact claim:** The community `local-knowledge` guide uses the current direct CLI to discover roles and configured paths, perform structured read-only search, cite results, and inspect response data rather than trusting an empty-search exit status. +- **Status:** proved +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/local-knowledge/SKILL.md:25-132`; `/Users/alex/projects/terraphim/terraphim-skills/docs/validation/validation-report-community-knowledge-skills.md:37-49` +- **Verification:** live checks on `terraphim-agent 1.21.13` returned 16 roles, role-specific KG and haystack configuration, and one structured local result with concept and total-match metadata. A separate empty query returned exit 0 and `success:true`, so the guide checks `.data.results` and `.data.total_matches`. +- **Publication gate:** merge PR #72 and rerun against the release binary and public package. + +### KGI-001 + +- **Exact claim:** The community `kg-rlm-ingest` guide discovers the configured role path, extracts and deduplicates concepts deterministically, requires an authorised safe edit, refreshes the role cache, and accepts success only after retrieval of the intended note; validation stopped before mutating production knowledge. +- **Status:** proved +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/kg-rlm-ingest/SKILL.md:39-197`; `/Users/alex/projects/terraphim/terraphim-skills/docs/validation/validation-report-community-knowledge-skills.md:71-85` +- **Verification:** live CLI checks established role/path discovery, deterministic extraction, structured deduplication search, and role-scoped cache refresh. The write protocol was validated up to the explicit production-authorisation boundary; no production knowledge note was changed. +- **Publication gate:** merge PR #72; every real ingestion still requires authorised path inspection, write, cache refresh, and retrieval. + +### GRP-001 + +- **Exact claim:** The locally installed `terraphim-grep` 1.21.13 independently exercised search-only mode, JSON output, directory path scoping, and rejection of conflicting LLM flags. +- **Status:** proved +- **Primary sources:** installed binary; `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-grep/SKILL.md:12-22`; `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-grep/SKILL.md:81-138`; `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-grep/SKILL.md:185-203` +- **Verification:** + - `terraphim-grep --version` returned `terraphim-grep 1.21.13`. + - `terraphim-grep 'CORE_PATH' --search-only --haystack code --json --paths scripts/ | jq '{chunk_count:(.chunks|length), answer, sufficiency}'` returned `chunk_count: 4`, `answer: null`, `sufficiency: "SearchOnly"`. + - the same query scoped to `skills/` returned 0 chunks, confirming directory scope changed the result set. + - `terraphim-grep 'CORE_PATH' --search-only --answer` returned exit 2 and `the argument '--search-only' cannot be used with '--answer'`. +- **Publication gate:** describe this only as exercised binary behaviour, not as proof that the unmerged skill package is released. + +### GRP-002 + +- **Exact claim:** The `terraphim-grep` skill package is not yet merged or published. +- **Status:** blocked +- **Primary source:** CAT-005 +- **Verification:** PR #65 returned open and unmerged; live marketplace remains at 47 skills under CAT-004. +- **Publication gate:** merge PR #65, publish the package, and verify its live catalogue page and install route. + +### GRP-003 + +- **Exact claim:** The guide begins with capability detection, recommends narrow identifier searches, and separates local retrieval from optional synthesis. It also tells an agent to preserve path scope when falling back to another search tool. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-grep/SKILL.md:24-66`; `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-grep/SKILL.md:185-203`; `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-grep/SKILL.md:242-251` +- **Verification:** source review. +- **Publication gate:** merge and publish under GRP-002. + +### GRP-004 + +- **Exact claim:** The runtime check used installed version 1.21.13. A search-only JSON query scoped to `scripts/` returned four chunks, a null answer, and `SearchOnly` sufficiency. The same query scoped to `skills/` returned no chunks. Combining `--search-only` with `--answer` exited with code 2 and reported that the arguments conflict. +- **Status:** proved +- **Primary source:** installed binary results under GRP-001 +- **Verification:** exact commands and results are recorded under GRP-001. +- **Publication gate:** retain the tested version and options in public wording. + +### LRN-001 + +- **Exact claim:** `learning-capture` is blocked because `terraphim-agent` 1.21.13 redacts captured error output but stores the parsed command and full command chain without applying the same redaction; generated and documented hook wrappers also echo the original hook payload after invoking the CLI. +- **Status:** blocked +- **Primary sources:** `terraphim-clients` tag `v1.21.13`, `crates/terraphim_agent/src/learnings/capture.rs:970-1000`; `crates/terraphim_agent/src/learnings/install.rs:51-115`; `/Users/alex/projects/terraphim/terraphim-skills/skills/learning-capture/SKILL.md:68-92` +- **Verification:** `git show v1.21.13:crates/terraphim_agent/src/learnings/capture.rs` shows `redact_secrets(error_output)` but passes `actual_command.clone()` and `chain.clone()` into `CapturedLearning` without redaction. `git show v1.21.13:crates/terraphim_agent/src/learnings/install.rs` and the skill's manual wrapper each show a final `echo "$INPUT"`. +- **Publication gate:** redact secrets before persistence, stop re-emitting the original payload, add adversarial secret tests, and obtain an independent security review. + +### LRN-002 + +- **Exact claim:** `learning-capture` must not be recommended for activation because its install-hook activation and storage documentation are not accurate across the claimed hosts. +- **Status:** blocked +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/learning-capture/SKILL.md:56-92`; `/Users/alex/projects/terraphim/terraphim-skills/skills/learning-capture/SKILL.md:179-192` +- **Verification:** source review against installed `terraphim-agent learn --help` and observed macOS capture location; documentation assumes a single Linux storage path and lists host activation without demonstrated end-to-end results. +- **Publication gate:** test installation, activation, capture, read-back, and storage discovery on every named host, then update the documentation to match. + +### LRN-003 + +- **Exact claim:** The record format contains the failed command and redacted stderr or stdout. In version 1.21.13, command and chain fields bypass the error-output redaction. The documented and generated wrappers read the full hook input, forward it to `terraphim-agent learn hook`, and then echo the original input again. +- **Status:** blocked +- **Primary sources:** `terraphim-clients` tag `v1.21.13`, `crates/terraphim_agent/src/learnings/capture.rs:970-1000`; `crates/terraphim_agent/src/learnings/install.rs:51-115`; `/Users/alex/projects/terraphim/terraphim-skills/skills/learning-capture/SKILL.md:68-92`; `/Users/alex/projects/terraphim/terraphim-skills/skills/learning-capture/SKILL.md:108-133` +- **Verification:** immutable tag source and skill source review. +- **Publication gate:** LRN-001. + +### LRN-004 + +- **Exact claim:** Commands and tool output can contain tokens, credentials, personal data, internal URLs, or other restricted values. A fail-open hook does not solve persistence or re-emission risk. The release gate therefore requires redaction before storage, removal of original-payload re-emission, adversarial secret fixtures, and an independent security review. +- **Status:** blocked +- **Primary sources:** LRN-001 and LRN-003. +- **Verification:** threat review of the versioned implementation and documented data flow. +- **Publication gate:** the claim states the required controls. + +### LRN-005 + +- **Exact claim:** The current text presents Claude Code, Codex, and OpenCode installation commands and one fixed Linux-style storage path. The publication gate is an end-to-end check on every named host: install, activate, cause a controlled non-secret failure, locate the resulting record, query it, add a correction, and verify read-back. Documentation then needs to match the observed host-specific behaviour. +- **Status:** blocked +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/learning-capture/SKILL.md:56-106`; `/Users/alex/projects/terraphim/terraphim-skills/skills/learning-capture/SKILL.md:179-192` +- **Verification:** source review; no complete cross-host evidence is present. +- **Publication gate:** the claim states the required test sequence. + +### LRN-006 + +- **Exact claim:** The intended loop is straightforward: retain a failed command with context, add a verified correction, and query that record before repeating similar work. The current package documentation, however, makes the capture boundary too broad for a recommendation. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/learning-capture/SKILL.md:94-167`; blocker LRN-001 +- **Verification:** source and threat review. +- **Publication gate:** LRN-001 and LRN-002. + +### GIT-001 + +- **Exact claim:** `gitea-issue-to-pr` is blocked because its Grok workflow logs failed tests, traceability, or review but still advances to validation and PR creation, and it does not gate PR creation on the validation verdict. +- **Status:** blocked +- **Primary source:** `/Users/alex/projects/terraphim/terraphim-skills/.grok/workflows/gitea-to-pr.rhai:543-580` +- **Verification:** source review shows the failure branch only calls `log`, then execution enters `phase("Validate")` and `phase("PR")`; the validation `passed` field is never read. +- **Publication gate:** make every required failure stop delivery and cover each failure path with executable workflow tests. + +### GIT-002 + +- **Exact claim:** The Grok workflow trusts remote postconditions reported by its PR agent instead of independently reading Gitea state. +- **Status:** blocked +- **Primary source:** `/Users/alex/projects/terraphim/terraphim-skills/.grok/workflows/gitea-to-pr.rhai:580-639` +- **Verification:** source review shows read-back instructions inside a prompt, followed by direct trust in `pr.output` fields. +- **Publication gate:** perform structured read-backs in the workflow and compare repository, SHA, branch, PR, comment, and merged state before completion. + +### GIT-003 + +- **Exact claim:** The Grok workflow interpolates issue-derived owner, repository, index, title, base, and branch values into shell command text without a strict allow-list or shell-safe encoding. +- **Status:** blocked +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/.grok/workflows/gitea-to-pr.rhai:47-80`; `/Users/alex/projects/terraphim/terraphim-skills/.grok/workflows/gitea-to-pr.rhai:193-219`; `/Users/alex/projects/terraphim/terraphim-skills/.grok/workflows/gitea-to-pr.rhai:591-605` +- **Verification:** source review shows trimming and substring parsing but no character validation before command construction. +- **Publication gate:** validate identifiers, keep titles and bodies out of shell interpolation, and add malicious-input tests. + +### GIT-004 + +- **Exact claim:** The `gitea-issue-to-pr` package includes a host-specific `http://bigbox:3456/v1` proxy configuration. +- **Status:** blocked +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/gitea-issue-to-pr/grok-proxy-models.toml:1-29`; `/Users/alex/projects/terraphim/terraphim-skills/skills/gitea-issue-to-pr/skill.toml:14-19` +- **Verification:** source review found the URL three times and confirmed the file is manifest-declared. +- **Publication gate:** remove host-specific configuration from the package and document user-supplied endpoints without a private hostname. + +### GIT-005 + +- **Exact claim:** The workflow collects booleans for tests, traceability, and review. When one is false, it logs a warning and continues. It then invokes validation, records only the validation summary, and advances to the PR phase without reading validation's `passed` field. +- **Status:** blocked +- **Primary source:** `/Users/alex/projects/terraphim/terraphim-skills/.grok/workflows/gitea-to-pr.rhai:543-580` +- **Verification:** source review. +- **Publication gate:** GIT-001. + +### GIT-006 + +- **Exact claim:** Prompting an agent to re-read a pull request is not equivalent to a structured workflow read-back. The workflow must fetch and compare owner, repository, head SHA, base, state, comment, and merged status before it reports completion. +- **Status:** blocked +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/.grok/workflows/gitea-to-pr.rhai:580-639`; `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:207-224` +- **Verification:** source review. +- **Publication gate:** GIT-002. + +### GIT-007 + +- **Exact claim:** The profile text asks for repository identity checks, issue read-back, scoped branch delivery, pull-request verification, and an explicit stop before merge. The problem is not the intended contract. The problem is that the packaged Grok workflow does not enforce all of those boundaries itself. +- **Status:** blocked +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/gitea-issue-to-pr/SKILL.md:34-85`; GIT-001 to GIT-004 +- **Verification:** profile and adapter source review. +- **Publication gate:** all four adapter blockers. + +### GIT-008 + +- **Exact claim:** The release gate is fail-closed behaviour: every required negative or missing verdict must stop commit, push, comment, and PR creation. Executable tests must cover each failure path. +- **Status:** blocked +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:159-176`; `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:195-224` +- **Verification:** required remediation derived from GIT-001. +- **Publication gate:** executable workflow tests pass for every listed path. + +### CORE-001 + +- **Exact claim:** `issue-to-pr-core` passes as a written tracker-neutral contract; this does not prove that every adapter conforms at runtime. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:17-45`; `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:159-176`; `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:249-259` +- **Verification:** `python3 scripts/validate-command-adapters.py` returned 23/23, which is a static contract and package check. +- **Publication gate:** describe it only as a contract until each adapter has independent failure-path and postcondition tests. + +### CORE-002 + +- **Exact claim:** Tracker profiles are expected to supply issue identity, forge operations, and read-backs. The core remains the written location for mode selection, acceptance evidence, review convergence, and optional delivery operations. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:17-45`; `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:249-259` +- **Verification:** source review. +- **Publication gate:** CORE-001. + +### CORE-003 + +- **Exact claim:** This is a contract description. It is not a statement that a Linear, Gitea, Grok, Claude, or OpenCode adapter executes every clause correctly. +- **Status:** documented +- **Primary source:** CORE-001 +- **Verification:** static validation only; no all-adapter runtime evidence reviewed. +- **Publication gate:** adapter-specific conformance evidence. + +### CORE-004 + +- **Exact claim:** These questions come from the written preflight, acceptance-evidence, delivery, and tracker-profile sections. Passing the static adapter validator confirms the expected contract text and package relationships are present; it does not answer the runtime questions. +- **Status:** documented +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:61-80`; `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:159-176`; `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:207-224`; `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:249-259` +- **Verification:** adapter validator returned 23/23; runtime conformance was not exercised. +- **Publication gate:** CORE-001. + +### CORE-005 + +- **Exact claim:** The current Gitea spotlight demonstrates the separation. Its profile delegates to the core, while its Grok workflow has independent blockers around failed gates, remote postconditions, shell values, and host-specific configuration. A claim that “the adapter follows the core” would therefore be broader than the evidence. +- **Status:** blocked +- **Primary sources:** GIT-001 to GIT-004; `/Users/alex/projects/terraphim/terraphim-skills/skills/gitea-issue-to-pr/SKILL.md:13-16` +- **Verification:** source review. +- **Publication gate:** GIT-001 to GIT-004. + +### CORE-006 + +- **Exact claim:** The accurate publication language stays narrow: the core is a reviewed, signed written contract, and adapter conformance requires separate failure-path and postcondition tests. +- **Status:** documented +- **Primary sources:** CORE-001; CAT-006 +- **Verification:** static validators passed; no all-adapter runtime evidence reviewed. +- **Publication gate:** preserve the limitation or add adapter-specific runtime evidence. + +## Site and platform evidence + +### SITE-001 + +- **Exact claim:** All six Zola post drafts parse and render when draft content is included. +- **Status:** proved +- **Primary sources:** `/Users/alex/projects/terraphim/terraphim.ai/content/posts/terraphim-skills-54-agent-workflows.md`; the five `skill-spotlight-*.md` files listed in `campaign.md` +- **Verification:** `zola check --drafts --skip-external-links` returned `50 pages (2 orphan), 4 sections` and exit 0. `zola build --drafts` returned `Creating 50 pages (2 orphan) and 4 sections` and exit 0. The two orphan warnings predate this campaign. +- **Publication gate:** repeat without `--drafts` only after all content and release gates pass. + +### PUB-001 + +- **Exact claim:** Reddit prohibits repeated or unsolicited mass engagement and identifies mass-posting repetitive promotional content as potential spam. +- **Status:** proved +- **Primary source:** `https://support.reddithelp.com/hc/en-us/articles/360043504051-Spam`, updated 2026-05-19 and fetched 2026-09-07 +- **Verification:** the policy text contains both statements and directs posters to check community-specific rules. +- **Publication gate:** a human must recheck each target community's current rules immediately before posting and must not post near-duplicate submissions. + +### PUB-002 + +- **Exact claim:** Hacker News excludes blog posts, lists, and other reading material from Show HN, asks submitters to retain the original title except for stated corrections, and prohibits generated or AI-edited comments. +- **Status:** proved +- **Primary sources:** `https://news.ycombinator.com/showhn.html`; `https://news.ycombinator.com/newsguidelines.html`, fetched 2026-09-07 +- **Verification:** Show HN guidance says those materials are off-topic and should be regular submissions. Submission guidance asks for the original title unless it is misleading or linkbait, with limited stated edits. Comment guidance says, “Don't post generated text or AI-edited text.” +- **Publication gate:** submit the catalogue article as a regular story, not Show HN. Every comment and reply must be written personally. + +### PUB-003 + +- **Exact claim:** Lobsters limits self-promotion, restricts new accounts from `show` and `vibecoding`, and treats promotional content or content without meaningful human authorship as potential spam. +- **Status:** proved +- **Primary sources:** `https://lobste.rs/about`; `https://lobste.rs/tags`, fetched 2026-09-07 +- **Verification:** the About page states the less-than-one-quarter self-promotion rule, the 70-day new-user restrictions, and the spam flag criteria. The tag catalogue defines `show` for projects and `vibecoding` for using AI/LLM coding tools. +- **Publication gate:** an established eligible account holder must substantially review and adopt the article and submission as their own work before deciding whether to submit. + +## Catalogue description ledger + +Each sentence below is an instruction summary, not an outcome guarantee. Sources are the named skill's frontmatter in the reviewed source branch. Recheck wording whenever frontmatter changes. + +| ID | Exact claim | Status | Primary source and line range | +|---|---|---|---| +| SKL-001 | `acceptance-testing` instructs an agent to turn requirements into acceptance criteria, test cases, test data, and a sign-off checklist. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/acceptance-testing/SKILL.md:1-10` | +| SKL-002 | `adf-orchestrate` instructs an agent to dispatch, monitor, or cancel approved asynchronous ADF work on a user-controlled server. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/adf-orchestrate/SKILL.md:1-10` | +| SKL-003 | `ai-config-management` instructs an agent to specify configuration control, semantic governance, drift detection, traceability, and change control for AI artefacts. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/ai-config-management/SKILL.md:1-10` | +| SKL-004 | `architecture` instructs an agent to design Rust and WebAssembly architecture, APIs, modules, and decision records without implementing code. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/architecture/SKILL.md:1-10` | +| SKL-005 | `code-review` instructs an agent to review Rust and WebAssembly code for correctness, security, performance, and maintainability findings. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/code-review/SKILL.md:1-10` | +| SKL-006 | `community-engagement` instructs an agent to support contributors, discussions, release notes, and open-source community practice. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/community-engagement/SKILL.md:1-10` | +| SKL-007 | `debugging` instructs an agent to reproduce and diagnose Rust failures, use logging or profiling, and remove temporary debug changes. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/debugging/SKILL.md:1-10` | +| SKL-008 | `deep-research` instructs an agent to route bounded or open research across file-only and externally enhanced modes, then reconcile evidence. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/deep-research/SKILL.md:1-10` | +| SKL-009 | `deterministic-rlm-review` instructs an agent to run capability-routed review perspectives and reconcile severity-ranked findings. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/deterministic-rlm-review/SKILL.md:1-10` | +| SKL-010 | `devops` instructs an agent to work on Rust CI/CD, containers, deployment automation, and infrastructure as code. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/devops/SKILL.md:1-10` | +| SKL-011 | `disciplined-design` instructs an agent to turn approved research into a file-level implementation plan with signatures, tests, and sequencing. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/disciplined-design/SKILL.md:1-10` | +| SKL-012 | `disciplined-implementation` instructs an agent to execute an approved plan step by step with tests and reviewable changes. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/disciplined-implementation/SKILL.md:1-10` | +| SKL-013 | `disciplined-quality-evaluation` instructs an agent to assess research or design documents with the six-dimension KLS framework before phase transitions. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/disciplined-quality-evaluation/SKILL.md:1-10` | +| SKL-014 | `disciplined-research` instructs an agent to map the existing system, constraints, risks, and exclusions before design. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/disciplined-research/SKILL.md:1-10` | +| SKL-015 | `disciplined-specification` instructs an agent to refine implementation details, edge cases, and trade-offs through a structured interview. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/disciplined-specification/SKILL.md:1-10` | +| SKL-016 | `disciplined-validation` instructs an agent to validate a system against original requirements and gather structured acceptance evidence. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/disciplined-validation/SKILL.md:1-10` | +| SKL-017 | `disciplined-verification` instructs an agent to verify implementation against design with tests, coverage evidence, and traceability. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/disciplined-verification/SKILL.md:1-10` | +| SKL-018 | `documentation` instructs an agent to write Rust API, README, architecture, and contributor documentation in the project's style. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/documentation/SKILL.md:1-10` | +| SKL-019 | `gitea-issue-to-pr` instructs an agent to apply a Gitea tracker profile to the shared issue-to-PR contract. | blocked | `/Users/alex/projects/terraphim/terraphim-skills/skills/gitea-issue-to-pr/SKILL.md:1-16`; see GIT-001 to GIT-004 | +| SKL-020 | `gitea-pagerank-workflow` instructs an agent to prioritise and manage Gitea issues and dependencies with `gtr`. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/gitea-pagerank-workflow/SKILL.md:1-10` | +| SKL-021 | `git-safety-guard` instructs an agent to configure a fail-open pre-tool guard for selected destructive Git and filesystem commands. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/git-safety-guard/SKILL.md:1-10` | +| SKL-022 | `gpui-components` instructs an agent to generate GPUI components using Zed-style rendering, state, completion, and theming patterns. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/gpui-components/SKILL.md:1-10` | +| SKL-023 | `grounded-content-writer` instructs an agent to attach citations to material claims and recheck numbers against their sources. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/grounded-content-writer/SKILL.md:1-10` | +| SKL-024 | `implementation` instructs an agent to implement an approved design with tests, documentation, and lint checks. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/implementation/SKILL.md:1-10` | +| SKL-025 | `issue-to-pr-core` instructs an agent to use a tracker-neutral issue-to-PR contract with risk routing, evidence, review, and explicit delivery gates. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/issue-to-pr-core/SKILL.md:1-21`; see CORE-001 | +| SKL-026 | `judge` instructs an agent to assess outputs with semantic, pragmatic, and syntactic dimensions and record structured verdicts. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/judge/SKILL.md:1-10` | +| SKL-027 | `kg-rlm-ingest` instructs an agent to distil sourced material into role-scoped knowledge-graph notes, using deterministic extraction before optional RLM work and requiring retrieval after every authorised write. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/kg-rlm-ingest/SKILL.md:1-35`; `/Users/alex/projects/terraphim/terraphim-skills/skills/kg-rlm-ingest/SKILL.md:142-160` | +| SKL-028 | `learning-capture` instructs an agent to capture and query failed-command records, but activation is blocked pending safety and accuracy fixes. | blocked | `/Users/alex/projects/terraphim/terraphim-skills/skills/learning-capture/SKILL.md:1-17`; see LRN-001 and LRN-002 | +| SKL-029 | `linear-issue-to-pr` instructs an agent to take a Linear issue through a phased engineering and pull-request workflow. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/linear-issue-to-pr/SKILL.md:1-10` | +| SKL-030 | `linear-ticket-writer` instructs an agent to draft structured Linear tickets with lifecycle, priority, state, and estimate fields. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/linear-ticket-writer/SKILL.md:1-10` | +| SKL-031 | `local-knowledge` instructs an agent to discover configured roles and paths, search local knowledge through structured direct commands, cite results, and remain read-only. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/local-knowledge/SKILL.md:1-23`; `/Users/alex/projects/terraphim/terraphim-skills/skills/local-knowledge/SKILL.md:41-81` | +| SKL-032 | `md-book` instructs an agent to work with the MD-Book documentation generator, including highlighting, live reload, search, and templates. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/md-book/SKILL.md:1-10` | +| SKL-033 | `open-source-contribution` instructs an agent to prepare issues and pull requests that follow a project's contribution conventions. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/open-source-contribution/SKILL.md:1-10` | +| SKL-034 | `quality-gate` instructs an agent to assemble review, security, performance-risk, traceability, acceptance, and relevant visual evidence for a change. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/quality-gate/SKILL.md:1-10` | +| SKL-035 | `quickwit-log-search` instructs an agent to investigate logs and incidents with Quickwit using one of three index-discovery modes. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/quickwit-log-search/SKILL.md:1-10` | +| SKL-036 | `repeatable-demo-video` instructs an agent to create scripted terminal or browser recordings as repeatable review evidence. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/repeatable-demo-video/SKILL.md:1-10` | +| SKL-037 | `requirements-traceability` instructs an agent to map requirements through design, implementation, tests, and evidence, then flag gaps. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/requirements-traceability/SKILL.md:1-10` | +| SKL-038 | `rust-ci-cd` instructs an agent to configure Rust CI/CD checks, dependency policy, coverage, benchmark, and release workflows. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/rust-ci-cd/SKILL.md:1-10` | +| SKL-039 | `rust-development` instructs an agent to apply idiomatic Rust patterns for async code, errors, traits, safety, and performance. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/rust-development/SKILL.md:1-10` | +| SKL-040 | `rust-fleet-standard` instructs an agent to apply the repository's shared Rust build, validation, issue-flow, and knowledge-stack conventions. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/rust-fleet-standard/SKILL.md:1-10` | +| SKL-041 | `rust-observability` instructs an agent to configure tracing, OpenTelemetry, Prometheus metrics, request context, and environment-specific logging for Rust services. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/rust-observability/SKILL.md:1-10` | +| SKL-042 | `rust-performance` instructs an agent to profile and optimise Rust code with benchmarks, memory analysis, SIMD, and zero-copy techniques. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/rust-performance/SKILL.md:1-10` | +| SKL-043 | `security-audit` instructs an agent to review Rust and WebAssembly input handling, unsafe code, defaults, and relevant OWASP risks. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/security-audit/SKILL.md:1-10` | +| SKL-044 | `session-search` instructs an agent to search imported coding-assistant session history for previous conversations and decisions. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/session-search/SKILL.md:1-10` | +| SKL-045 | `structural-pr-review` instructs an agent to produce a severity-ranked pull-request review with file overviews, confidence, and an architecture diagram. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/structural-pr-review/SKILL.md:1-10` | +| SKL-046 | `technical-book-writer` instructs an agent to structure a long technical explanation with progressive concepts, diagrams, and chapter files. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/technical-book-writer/SKILL.md:1-10` | +| SKL-047 | `terraphim-grep` instructs an agent to use local search first, scope paths, request JSON when needed, and make LLM synthesis explicit. | blocked | `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-grep/SKILL.md:1-22`; see GRP-001 and GRP-002 | +| SKL-048 | `terraphim-hooks` instructs an agent to configure knowledge-graph-based text replacement for supported command and commit hooks. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-hooks/SKILL.md:1-10` | +| SKL-049 | `terraphim-learning-memory` instructs an agent to retrieve, assess, verify, capture, and revisit durable evidence, with source session search built into the workflow. | blocked | `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:1-29`; `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-learning-memory/SKILL.md:83-133`; see MEM-001 to MEM-003 and CAT-005 | +| SKL-050 | `terraphim-linear` instructs an agent to fetch and inspect Linear issues through semantic aliases or structured output. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-linear/SKILL.md:1-10` | +| SKL-051 | `terraphim-rlm` instructs an agent to route multi-step, multi-file work through budgeted recursive decomposition and reconciliation tools. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/terraphim-rlm/SKILL.md:1-10` | +| SKL-052 | `testing` instructs an agent to write and run unit, integration, property-based, and benchmark tests and analyse failures. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/testing/SKILL.md:1-10` | +| SKL-053 | `ubs-scanner` instructs an agent to run Ultimate Bug Scanner and incorporate its findings into a quality-gate workflow. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/ubs-scanner/SKILL.md:1-10` | +| SKL-054 | `visual-testing` instructs an agent to define screenshot coverage, rendering stabilisation, baseline management, and CI checks for UI changes. | documented | `/Users/alex/projects/terraphim/terraphim-skills/skills/visual-testing/SKILL.md:1-10` | + +## Shared publication gate + +Before any announcement leaves draft: + +1. Rerun CAT-001, CAT-002, CAT-006, CAT-007, CAT-011, and all relevant runtime checks at the exact release commit. +2. Keep `scripts/skills.sh` community-only and verify that every admitted community package remains complete through that route. +3. Confirm live catalogue count and each promoted package page through the public service. +4. Merge and publish all three stacked community pull requests before describing the four skills as released. +5. Keep `learning-capture` and `gitea-issue-to-pr` out of recommendation copy until every blocker closes. +6. Run a human editorial review for claim wording, source links, platform rules, and affiliation disclosure. diff --git a/campaigns/skills-2026/platform-drafts.md b/campaigns/skills-2026/platform-drafts.md new file mode 100644 index 0000000..c1e17d9 --- /dev/null +++ b/campaigns/skills-2026/platform-drafts.md @@ -0,0 +1,85 @@ +# Platform announcement drafts + +Every section is editorial material only. + +## r/TerraphimAI + +**DO NOT PUBLISH until the campaign gates pass.** + +**Proposed title:** 54 Terraphim workflow guides, with four free community skills + +**Draft:** + +We have prepared a 54-skill marketplace candidate and audited its claims against the source. The live marketplace still reports 47 active skills, so we will not call all 54 live until publication checks pass. + +The free shell installer is deliberately narrower: it exposes only `terraphim-grep`, `terraphim-learning-memory`, `local-knowledge`, and `kg-rlm-ingest`. Their shared workflow searches current code, recovers source sessions, retrieves role-scoped notes, and persists only validated concepts. The accompanying evidence ledger separates signed package checks, exercised CLI behaviour, authorised write boundaries, and live availability. + +Catalogue: + +Source and evidence: + +If you review it, please challenge a specific claim or reproduction command. We will update the ledger rather than argue from intent. + +**Evidence:** CAT-001, CAT-004, CAT-005, CAT-006, CAT-011, MEM-001, MEM-009, GRP-001, LKG-001, KGI-001. + +**Gate:** promoted entries are live, links resolve to a public commit, blocked skills are fixed or omitted, and subreddit rules are rechecked immediately before posting. + +## r/opensource + +**DO NOT PUBLISH until the campaign gates pass. Recheck whether Promotional flair is still required.** + +**Proposed title:** We audited a 54-guide catalogue and kept the free installer to four skills + +**Draft:** + +Affiliation disclosure: I maintain Terraphim Skills. + +We have a 54-entry marketplace candidate for coding-agent workflow guidance. Rather than treating a valid manifest as proof that a workflow works, we built a claim ledger that records source lines, reproduction commands, status, and publication gates. + +The audit found useful limits. The live service still reports 47 active skills. The free shell installer exposes only four signed community skills, and each is currently a complete standalone `SKILL.md`. Core and premium catalogue packages are not included. Learning capture and one Gitea workflow remain blocked by security, fail-closed, portability, and verification issues; they will not be recommended until those findings are fixed and retested. + +Catalogue: + +Repository: + +Feedback on the evidence format and reproducibility would be useful. + +**Evidence:** CAT-001, CAT-004, CAT-006, CAT-007, CAT-011, LRN-001, LRN-002, GIT-001 to GIT-004, PUB-001. + +**Gate:** check current flair and self-promotion rules, disclose affiliation, avoid same-day duplicate posts, and publish only after release and blocker gates pass. + +## Hacker News + +**DO NOT PUBLISH until the campaign gates pass.** + +HN submission must be title and link only. The catalogue article is reading material, so submit it as a regular story rather than Show HN: + +**Title:** 54 Agent Workflow Guides, with Four Free Community Skills + +**URL:** + +Do not add generated submission text. Comments and replies must be personally written because Hacker News prohibits generated or AI-edited comments. The person posting must read the linked material, disclose relevant affiliation when appropriate, and answer from direct knowledge. + +**Evidence:** PUB-002. + +**Gate:** public catalogue reflects the announced release, source links resolve, blocked skills are fixed or excluded, and a human writes every comment or reply. + +## Lobsters + +**DO NOT PUBLISH until the campaign gates pass.** + +**Proposed title:** Terraphim Skills: evidence-traced workflow guides for coding agents + +**URL:** + +**Tags:** `show`, `vibecoding` + +**Optional description, only if the submission form permits it:** + +An overview of a workflow-guide catalogue with a public claim ledger. Package integrity, documented guidance, exercised binary behaviour, and live availability are reported separately. + +**Evidence:** CAT-006, PUB-003, and the proof protocol in `campaign.md`. + +**Account eligibility gate:** confirm the submitting account is eligible to create a story, the project and link satisfy current Lobsters rules, and `show` plus `vibecoding` remain the appropriate tags. Do not seek an invitation or create an account solely to run the campaign. + +**Gate:** all shared publication gates pass, an eligible account holder substantially reviews and adopts the article and submission as their own work, and it is not posted as a near-duplicate of another recent Lobsters story. diff --git a/config.toml b/config.toml index 32b9092..8878c85 100644 --- a/config.toml +++ b/config.toml @@ -51,11 +51,12 @@ include_content = true [extra] version = "1.21.16" -release_version = "1.21.16" +release_version = "1.21.18" navbar_items = [ { code = "en", nav_items = [ { url = "$BASE_URL/", name = "Home" }, + { url = "$BASE_URL/editor/", name = "Try the editor" }, { url = "$BASE_URL/capabilities", name = "Capabilities" }, { url = "$BASE_URL/how-tos", name = "How-tos" }, { url = "https://docs.terraphim.ai", name = "Docs" }, diff --git a/content/capabilities/terraphim-editor.md b/content/capabilities/terraphim-editor.md new file mode 100644 index 0000000..b73668a --- /dev/null +++ b/content/capabilities/terraphim-editor.md @@ -0,0 +1,40 @@ ++++ +title = "Terraphim Editor" +description = "Intelligent editing with alternative suggestions powered by knowledge graphs and roles." +date = 2026-10-11 +weight = 10 + +[taxonomies] +categories = ["Capabilities"] +tags = ["editor", "knowledge-graph", "role-based", "wasm"] + +[extra] +toc = true +waypoint = "WP-010" +icon = "fa-solid fa-pen-to-square" ++++ + +## Intelligent editing with your knowledge graph + +Terraphim Editor brings knowledge-graph alternative suggestions into your writing. Use role-specific concepts and synonyms to compare wording in context, choose an alternative, and keep other options attached to the text. Roles provide the vocabulary for your domain; the online preview starts with a bundled local knowledge graph. + +[Try the intelligent editor preview](/editor/) + +## Keep your decisions and alternatives + +Attach alternatives to words, sentences and paragraphs. Save an annotated Markdown document and reopen it with those choices preserved. Newly authored alternatives register as document-scoped synonyms using `synonyms::` syntax; they do not silently update the shared graph used by other tools. + +Ghost passages to retain them outside clean export, set notes aside in Overflow, and inspect possible cuts in the Lab. KG-protected Trim preserves matched concepts and their original spelling, including supported Unicode normalization. + +The editor runs as WebAssembly in your browser. Editing with the bundled graph does not require an LLM or sending your document to a provider. + +## See actual saved outputs + +[Watch the captioned editor and terraphim-grep walkthroughs](/how-tos/knowledge-work-editor/): Chrome, Safari, Firefox and the existing Zed extension, followed by search with the same shared KG, with and without synthesis. Each video identifies its recorded scope; the current browser build includes the later Unicode normalization repair. + +This is a preview. Complete acceptance against all 41 source-video requirements remains open, including Safari dragging and some visual/native cases. The existing Zed extension demonstrates its supported capabilities; the separate Zed fork is outside this publication. + +## Explore related capabilities + +- [Knowledge Graph Engine](/capabilities/knowledge-graph/) +- [KG-Boosted File Search](/capabilities/kg-file-search/) diff --git a/content/docs/installation.md b/content/docs/installation.md index 9e7f4fe..c740bec 100644 --- a/content/docs/installation.md +++ b/content/docs/installation.md @@ -28,7 +28,7 @@ curl -fsSL https://raw.githubusercontent.com/terraphim/terraphim-ai/main/scripts curl -fsSL https://raw.githubusercontent.com/terraphim/terraphim-ai/main/scripts/install.sh | bash --install-dir /usr/local/bin # Require a specific version (fails if the channel serves a different one) -curl -fsSL https://raw.githubusercontent.com/terraphim/terraphim-ai/main/scripts/install.sh | bash --version 1.21.16 +curl -fsSL https://raw.githubusercontent.com/terraphim/terraphim-ai/main/scripts/install.sh | bash --version 1.21.18 ``` The installer publishes these exit codes: `0` success, `1` usage error, @@ -59,11 +59,11 @@ cargo install terraphim_agent --features repl-full #### Binary Download Archives are served from `downloads.terraphim.ai`. Substitute the release you -want for `1.21.16`, or read the current version from the +want for `1.21.18`, or read the current version from the [manifest](https://downloads.terraphim.ai/terraphim-agent/stable-v2.json). ```bash -VERSION=1.21.16 +VERSION=1.21.18 # x86_64 (GNU) curl -fsSLO "https://downloads.terraphim.ai/terraphim-agent/terraphim-agent-${VERSION}-x86_64-unknown-linux-gnu.tar.gz" @@ -101,7 +101,7 @@ sudo cp target/release/terraphim-agent /usr/local/bin/ #### Binary Download ```bash -VERSION=1.21.16 +VERSION=1.21.18 # Apple Silicon (ARM64) curl -fsSLO "https://downloads.terraphim.ai/terraphim-agent/terraphim-agent-${VERSION}-aarch64-apple-darwin.tar.gz" @@ -131,14 +131,9 @@ sudo cp target/release/terraphim-agent /usr/local/bin/ ### Windows -#### Binary Download - -```powershell -$VERSION = "1.21.16" -curl.exe -fsSLO "https://downloads.terraphim.ai/terraphim-agent/terraphim-agent-$VERSION-x86_64-pc-windows-msvc.zip" -``` +#### WSL installation -Extract the zip and add the directory to your PATH. +Native Windows archives are unavailable on the current stable channel. Use the Linux instructions inside WSL, or build from source below. #### Build from Source @@ -205,7 +200,7 @@ After installation, verify that Terraphim is working: ```bash # Check version terraphim-agent --version -# terraphim-agent 1.21.16 +# terraphim-agent 1.21.18 # Confirm the updater can see the channel terraphim-agent check-update diff --git a/content/how-tos/knowledge-work-editor.md b/content/how-tos/knowledge-work-editor.md new file mode 100644 index 0000000..6a10824 --- /dev/null +++ b/content/how-tos/knowledge-work-editor.md @@ -0,0 +1,64 @@ ++++ +title = "Write and search with the same knowledge graph" +description = "Try Terraphim Editor, then inspect search results with and without synthesis." +date = 2026-10-11 ++++ + +[Try Terraphim Editor](/editor/) + +Keep alternatives attached to words, sentences and paragraphs. Ghost a passage without losing it, stash notes in Overflow, and use the Lab to inspect possible cuts. The editor runs as WebAssembly in your browser with a bundled local knowledge graph. + +## Grammarly for knowledge work + +Follow a public project brief from editing to search using the same knowledge graph. The 77-second captioned overview shows the browser editor, explicitly hands off to the clean file exported from Safari, then compares real `terraphim-grep` executions with and without `--answer`. + + + +### Recorded host walkthroughs + +- [Chrome: editing, saved annotations and reopen — 44s](/demos/knowledge-work/chrome-online-editor.mp4) +- [Safari: editing, saved annotations and reopen — 45s](/demos/knowledge-work/safari-online-editor-public.mp4) +- [Firefox: editing, saved annotations and reopen — 49s](/demos/knowledge-work/firefox-online-editor.mp4) +- [Existing Zed extension: KG completion, saved history, Ghost and Revive — 60s](/demos/knowledge-work/zed-existing-extension-public.mp4) +- [Real grep execution: evidence without synthesis, then with `--answer` — 21s](/demos/knowledge-work/grep-live-execution-public.mp4) + +The browser recordings produced identical saved annotated files. A separately checked Safari clean-export file supplies the grep corpus; the Safari walkthrough does not show that export gesture. Newly authored Assessment registers as a document-scoped synonym; it does not update the shared KG used by the CLI. Zed demonstrates existing extension capabilities; the Zed fork is separate. The brief's Trim preview produced zero cuts, so this recording does not demonstrate text reduction. The generated synthesis overstates a review policy as observed practice; compare that statement with the returned evidence rather than treating it as verified fact. + +All six videos now have explanatory on-screen captions. Idle intervals are cut, actions are accelerated by 1.5–3× where disclosed, and the long terminal end hold is removed. Captions sit outside the application image. Safari setup is cut and the Zed avatar remains masked. Chronological source ranges and speed changes are recorded in the manifest; original captures are retained. These are finite walkthroughs, not complete acceptance of all 41 requirements. [Build identities, media hashes and edit disclosures](/demos/knowledge-work/manifest.json) identify the recorded candidate. The current editor preview includes the Unicode normalization repair: canonically equivalent accents, Terraphim Unicode lowercase and Greek sigma equivalence protect exact original spellings during Trim. The videos retain their earlier recorded build identity; they do not demonstrate this later repair. + +This is a preview. The complete source-video requirements are still being qualified; Safari dragging and some visual/native cases remain unresolved. Keyboard pull from Overflow is available. The browser demo does not imply full custom visual parity in the stock Zed extension. + +## Start with a public brief + +Paste this supplied example into the editor. It is sample input, not generated output: + +```markdown +# Public research workspace + +Decision: launch a local-first research workspace for the delivery team. + +The decision owner will review the brief before the pilot begins. + +Risk: inconsistent terminology makes prior decisions hard to locate. + +Use the project brief to align the team and keep the decision record clear. +``` + +Turn on Write_On using the word counter. Explore alternatives for “decision”, add your own wording, and compare it in context. Ghost/revive a passage, stash a note in Overflow and inspect the Lab. Save the result and reopen the actual file to check the choices and annotations. + +## Save your work and use it in search + +Save keeps the Markdown, alternatives, ghosts and Overflow together. Clean Markdown export includes active text and omits ghosts, Overflow and the annotation block. Chrome can offer a file handle; Firefox and Safari use a download. Check the actual browser dialog and downloaded file. + +Download the [same bundled KG](/editor/roles/default.json) and inspect the [editor build identity](/editor/release.json). Newly authored alternatives register as document-scoped synonyms; they do not silently change this shared KG. + +Move the chosen saved or clean-exported file into a dedicated local corpus folder. Keep evidence logs and other copies outside that folder. With `terraphim-grep` installed, set paths for that public corpus and downloaded KG: + +```bash +terraphim-grep "decision" --thesaurus ./default.json --paths ./public-corpus --json +terraphim-grep "decision" --thesaurus ./default.json --paths ./public-corpus --json --answer +``` + +The first command returns search evidence without enabling synthesis. The second asks for an answer when sufficient evidence and a configured provider are available. Keep the query, corpus and KG unchanged to compare them. Add the same `--role-config` path to both commands if configuring a provider through a role. Check the generated answer against the returned source lines; a missing answer or provider error is not a successful synthesis. + +Writing and local search do not require sending your document to an LLM. Synthesis may send retrieved content to the configured provider. Use public sample content for the demo and inspect the provider configuration before enabling `--answer`. diff --git a/content/posts/skill-spotlight-gitea-issue-to-pr.md b/content/posts/skill-spotlight-gitea-issue-to-pr.md new file mode 100644 index 0000000..59e811c --- /dev/null +++ b/content/posts/skill-spotlight-gitea-issue-to-pr.md @@ -0,0 +1,70 @@ ++++ +title="Skill Spotlight Audit: Gitea Issue to PR Remains Blocked" +date=2026-09-07 +draft=true + +[taxonomies] +categories = ["Technical"] +tags = ["Terraphim", "Gitea", "pull-requests", "workflow-security", "agent-skills"] + +[extra] +toc = true +comments = true ++++ + +`gitea-issue-to-pr` is blocked because its Grok workflow logs failed tests, traceability, or review but still advances to validation and PR creation, and it does not gate PR creation on the validation verdict. + + +This draft records why the current adapter should not be recommended, even though its written profile describes stronger gates. + + + +## The intended profile + +`gitea-issue-to-pr` instructs an agent to apply a Gitea tracker profile to the shared issue-to-PR contract. + + +The profile text asks for repository identity checks, issue read-back, scoped branch delivery, pull-request verification, and an explicit stop before merge. The problem is not the intended contract. The problem is that the packaged Grok workflow does not enforce all of those boundaries itself. + + +## Delivery can continue after failed gates + +The workflow collects booleans for tests, traceability, and review. When one is false, it logs a warning and continues. It then invokes validation, records only the validation summary, and advances to the PR phase without reading validation's `passed` field. + + +The release gate is fail-closed behaviour: every required negative or missing verdict must stop commit, push, comment, and PR creation. Executable tests must cover each failure path. + + +## Remote state is delegated, not verified + +The Grok workflow trusts remote postconditions reported by its PR agent instead of independently reading Gitea state. + + +Prompting an agent to re-read a pull request is not equivalent to a structured workflow read-back. The workflow must fetch and compare owner, repository, head SHA, base, state, comment, and merged status before it reports completion. + + +## Shell and portability blockers + +The Grok workflow interpolates issue-derived owner, repository, index, title, base, and branch values into shell command text without a strict allow-list or shell-safe encoding. + + +The `gitea-issue-to-pr` package includes a host-specific `http://bigbox:3456/v1` proxy configuration. + + +The first finding requires validated identifiers, argument-safe command construction, and malicious-input tests. The second requires removing the private hostname from the package and documenting user-supplied endpoints instead. + +## Static checks do not close these findings + +The community change passed 45 Python tests, 73% aggregate branch coverage, six Bun tests, signed-manifest and installer-policy validation, TSM validation for all four community skills, and an isolated install of four skills with zero skipped. + + +Those community distribution checks do not resolve this premium adapter's runtime enforcement blockers. + +The adapter validator can show that expected files and contract text are present. It does not prove fail-closed execution, safe interpolation, or independent remote verification. + + +This post must stay in draft until GIT-001 through GIT-004 close and the corrected workflow is exercised. + +Repository: [terraphim/terraphim-skills](https://github.com/terraphim/terraphim-skills). Reviewed source: [skill revision](https://github.com/terraphim/terraphim-skills/blob/RELEASE_COMMIT/skills/gitea-issue-to-pr/SKILL.md). + +Publication rule: replace `RELEASE_COMMIT` only after the corrected workflow and its evidence are present in that revision. diff --git a/content/posts/skill-spotlight-issue-to-pr-core.md b/content/posts/skill-spotlight-issue-to-pr-core.md new file mode 100644 index 0000000..4773e16 --- /dev/null +++ b/content/posts/skill-spotlight-issue-to-pr-core.md @@ -0,0 +1,60 @@ ++++ +title="Skill Spotlight: Reading Issue to PR Core as a Contract" +date=2026-09-07 +draft=true + +[taxonomies] +categories = ["Technical"] +tags = ["Terraphim", "issue-tracking", "pull-requests", "verification", "agent-skills"] + +[extra] +toc = true +comments = true ++++ + +`issue-to-pr-core` passes as a written tracker-neutral contract; this does not prove that every adapter conforms at runtime. + + +That boundary matters because a careful contract can coexist with an adapter that fails to enforce it. + + + +## What the core is for + +`issue-to-pr-core` instructs an agent to use a tracker-neutral issue-to-PR contract with risk routing, evidence, review, and explicit delivery gates. + + +Tracker profiles are expected to supply issue identity, forge operations, and read-backs. The core remains the written location for mode selection, acceptance evidence, review convergence, and optional delivery operations. + + +This is a contract description. It is not a statement that a Linear, Gitea, Grok, Claude, or OpenCode adapter executes every clause correctly. + + +## The useful review questions + +Reviewers can apply the contract as a checklist: + +1. Was repository and issue identity resolved before mutation? +2. Does each acceptance criterion map to implementation and an executed check? +3. Did a failed required gate stop delivery? +4. Were remote mutations read back through the tracker or forge? +5. Were merge and live validation left off unless explicitly requested? + +These questions come from the written preflight, acceptance-evidence, delivery, and tracker-profile sections. Passing the static adapter validator confirms the expected contract text and package relationships are present; it does not answer the runtime questions. + + +## Why the distinction is practical + +The current Gitea spotlight demonstrates the separation. Its profile delegates to the core, while its Grok workflow has independent blockers around failed gates, remote postconditions, shell values, and host-specific configuration. A claim that “the adapter follows the core” would therefore be broader than the evidence. + + +The accurate publication language stays narrow: the core is a reviewed, signed written contract, and adapter conformance requires separate failure-path and postcondition tests. + + +## Publication gate + +Before stronger claims are made, each adapter needs independent tests for rejected inputs, required-gate failures, remote read-backs, `no_commit`, `no_pr`, and opt-in operations. Results should name the adapter revision and quote meaningful assertions rather than relying on an agent summary. + +Repository: [terraphim/terraphim-skills](https://github.com/terraphim/terraphim-skills). Reviewed source: [contract revision](https://github.com/terraphim/terraphim-skills/blob/RELEASE_COMMIT/skills/issue-to-pr-core/SKILL.md). + +Publication rule: replace `RELEASE_COMMIT` with the immutable revision covered by the final adapter evidence. diff --git a/content/posts/skill-spotlight-learning-capture.md b/content/posts/skill-spotlight-learning-capture.md new file mode 100644 index 0000000..cf1cb8d --- /dev/null +++ b/content/posts/skill-spotlight-learning-capture.md @@ -0,0 +1,60 @@ ++++ +title="Skill Spotlight Audit: Why Learning Capture Is Not Ready to Recommend" +date=2026-09-07 +draft=true + +[taxonomies] +categories = ["Technical"] +tags = ["Terraphim", "learning", "hooks", "security", "agent-skills"] + +[extra] +toc = true +comments = true ++++ + +`learning-capture` is blocked because `terraphim-agent` 1.21.13 redacts captured error output but stores the parsed command and full command chain without applying the same redaction; generated and documented hook wrappers also echo the original hook payload after invoking the CLI. + + +Do not enable the workflow from this draft. The useful idea needs a safer implementation and accurate activation guidance first. + + + +## Intended workflow + +`learning-capture` instructs an agent to capture and query failed-command records, but activation is blocked pending safety and accuracy fixes. + + +The intended loop is straightforward: retain a failed command with context, add a verified correction, and query that record before repeating similar work. The current package documentation, however, makes the capture boundary too broad for a recommendation. + + +## Security blocker + +The record format contains the failed command and redacted stderr or stdout. In version 1.21.13, command and chain fields bypass the error-output redaction. The documented and generated wrappers read the full hook input, forward it to `terraphim-agent learn hook`, and then echo the original input again. + + +Commands and tool output can contain tokens, credentials, personal data, internal URLs, or other restricted values. A fail-open hook does not solve persistence or re-emission risk. The release gate therefore requires redaction before storage, removal of original-payload re-emission, adversarial secret fixtures, and an independent security review. + + +## Accuracy blocker + +`learning-capture` must not be recommended for activation because its install-hook activation and storage documentation are not accurate across the claimed hosts. + + +The current text presents Claude Code, Codex, and OpenCode installation commands and one fixed Linux-style storage path. The publication gate is an end-to-end check on every named host: install, activate, cause a controlled non-secret failure, locate the resulting record, query it, add a correction, and verify read-back. Documentation then needs to match the observed host-specific behaviour. + + +## Why a signed package is not enough + +The community change passed 45 Python tests, 73% aggregate branch coverage, six Bun tests, signed-manifest and installer-policy validation, TSM validation for all four community skills, and an isolated install of four skills with zero skipped. + + +Those community distribution checks do not resolve this premium package's runtime and security blockers. + +A valid digest can prove that downloaded bytes match reviewed bytes. It cannot prove that those bytes redact secrets, activate on every claimed host, or store data where the guide says. + + +This spotlight stays an audit draft until those runtime and security gates pass. + +Repository: [terraphim/terraphim-skills](https://github.com/terraphim/terraphim-skills). Reviewed source: [skill revision](https://github.com/terraphim/terraphim-skills/blob/RELEASE_COMMIT/skills/learning-capture/SKILL.md). + +Publication rule: replace `RELEASE_COMMIT`, do not turn this into setup guidance, and do not include an activation command until LRN-001 and LRN-002 are closed with executable evidence. diff --git a/content/posts/skill-spotlight-terraphim-grep.md b/content/posts/skill-spotlight-terraphim-grep.md new file mode 100644 index 0000000..f6f123f --- /dev/null +++ b/content/posts/skill-spotlight-terraphim-grep.md @@ -0,0 +1,55 @@ ++++ +title="Skill Spotlight: Terraphim Grep, with Package and Runtime Evidence Kept Separate" +date=2026-09-07 +draft=true + +[taxonomies] +categories = ["Technical"] +tags = ["Terraphim", "search", "knowledge-graph", "developer-tools", "agent-skills"] + +[extra] +toc = true +comments = true ++++ + +The `terraphim-grep` skill package is not yet merged or published. + + +The locally installed `terraphim-grep` 1.21.13 independently exercised search-only mode, JSON output, directory path scoping, and rejection of conflicting LLM flags. + + +These are two different facts. This draft keeps them separate. + + + +## What the candidate guide covers + +`terraphim-grep` instructs an agent to use local search first, scope paths, request JSON when needed, and make LLM synthesis explicit. + + +The guide begins with capability detection, recommends narrow identifier searches, and separates local retrieval from optional synthesis. It also tells an agent to preserve path scope when falling back to another search tool. + + +That is documented workflow guidance. It does not show that the package is available in the live marketplace. + +## What was exercised + +The runtime check used installed version 1.21.13. A search-only JSON query scoped to `scripts/` returned four chunks, a null answer, and `SearchOnly` sufficiency. The same query scoped to `skills/` returned no chunks. Combining `--search-only` with `--answer` exited with code 2 and reported that the arguments conflict. + + +They do not support speed, reliability, adoption, or package-availability claims. + +## Publication boundary + +The four community packages are source-branch release candidates on open, unmerged stacked pull requests #65, #66, and #72. + + +`scripts/skills.sh` exposes exactly four free community skills: `kg-rlm-ingest`, `local-knowledge`, `terraphim-grep`, and `terraphim-learning-memory`; core and premium packages remain in the 54-entry marketplace catalogue and are not exposed through that installer. + + +The live marketplace currently reports 47 active skills, not 54. + + +Before changing this post from draft, merge the stacked PRs through #72, publish the skill package, verify its public catalogue page and community install route, and replace `RELEASE_COMMIT` in the source link below. Runtime examples should continue to state the binary version that produced them. + +Repository: [terraphim/terraphim-skills](https://github.com/terraphim/terraphim-skills). Candidate source: [reviewed skill revision](https://github.com/terraphim/terraphim-skills/blob/RELEASE_COMMIT/skills/terraphim-grep/SKILL.md). diff --git a/content/posts/skill-spotlight-terraphim-learning-memory.md b/content/posts/skill-spotlight-terraphim-learning-memory.md new file mode 100644 index 0000000..8d8482d --- /dev/null +++ b/content/posts/skill-spotlight-terraphim-learning-memory.md @@ -0,0 +1,67 @@ ++++ +title="Skill Spotlight: Terraphim Learning Memory as Orchestration Guidance" +date=2026-09-07 +draft=true + +[taxonomies] +categories = ["Technical"] +tags = ["Terraphim", "agent-memory", "knowledge-graph", "provenance", "agent-skills"] + +[extra] +toc = true +comments = true ++++ + +`terraphim-learning-memory` is orchestration guidance that starts with capability discovery and routes writes to owning Terraphim components; it is not evidence of a shipped memory runtime. + + +That distinction is the point of this draft: useful memory practice starts by checking what the installed tool can actually do. + + + +## Release status first + +The four community packages are source-branch release candidates on open, unmerged stacked pull requests #65, #66, and #72. + + +`scripts/skills.sh` exposes exactly four free community skills: `kg-rlm-ingest`, `local-knowledge`, `terraphim-grep`, and `terraphim-learning-memory`; core and premium packages remain in the 54-entry marketplace catalogue and are not exposed through that installer. + + +The installed `terraphim-agent` 1.21.13 has no `memory` namespace. + + +Do not read the candidate skill as an announcement of a released memory command. Before publication, replace `RELEASE_COMMIT` in the [candidate source link](https://github.com/terraphim/terraphim-skills/blob/RELEASE_COMMIT/skills/terraphim-learning-memory/SKILL.md) and confirm the package appears on the live catalogue. + +## What the guide asks an agent to do + +`terraphim-learning-memory` instructs an agent to retrieve, assess, verify, capture, and revisit durable evidence through available Terraphim components. + + +The practical boundary is capability discovery. An agent checks installed help before selecting a command, then uses the component that owns the information: failed-command learning, session history, local role knowledge, or a project knowledge graph. A successful route message is not treated as proof that content was stored. + + +Session history is built into this community workflow. The released CLI exposes `sources`, `list`, `search`, and `stats`; live validation found matching prior work while also surfacing parser warnings that callers must not hide. + + +This makes the guide an orchestration layer over capabilities that may differ between installations. It also keeps temporary task progress out of long-lived knowledge unless there is a durable, sourced fact to preserve. + + +## Deterministic before recursive + +The learning-memory guidance assigns builds and tests to implementation or verification, and makes deterministic KG extraction the default with RLM optional only for long, genuinely novel material. + + +That means a normal “remember this” request should not trigger a broad build or recursive decomposition. Build results can become evidence, but the memory guide does not own compilation. Likewise, deterministic extraction remains the normal route for knowledge-graph material; RLM is a bounded fallback rather than the default. + + +## What would justify stronger wording + +Publication can call this a released skill only after PRs #65, #66, and #72 merge, the package is published, and its live catalogue and install route are checked. + + +Claims about a `memory` runtime need separate evidence from a released binary whose help exposes that namespace, followed by exercised capture, read-back, correction, retirement, and failure behaviour. Until then, the accurate description is narrower: this candidate teaches capability-aware memory orchestration across existing Terraphim components. + + +Repository: [terraphim/terraphim-skills](https://github.com/terraphim/terraphim-skills). Candidate source: [reviewed skill revision](https://github.com/terraphim/terraphim-skills/blob/RELEASE_COMMIT/skills/terraphim-learning-memory/SKILL.md). + +Editorial source gate: replace `RELEASE_COMMIT` after all three stacked pull requests merge and the GitHub mirror contains the reviewed commit. diff --git a/content/posts/terraphim-skills-54-agent-workflows.md b/content/posts/terraphim-skills-54-agent-workflows.md new file mode 100644 index 0000000..1ab62fc --- /dev/null +++ b/content/posts/terraphim-skills-54-agent-workflows.md @@ -0,0 +1,179 @@ ++++ +title="Terraphim Skills: 54 Agent Workflow Guides in the Release Candidate" +date=2026-09-07 +draft=true + +[taxonomies] +categories = ["Announcements"] +tags = ["Terraphim", "agent-skills", "developer-tools", "evidence", "workflows"] + +[extra] +toc = true +comments = true ++++ + +The source-branch marketplace candidate lists 54 skill packages. + + +`scripts/skills.sh` exposes exactly four free community skills: `kg-rlm-ingest`, `local-knowledge`, `terraphim-grep`, and `terraphim-learning-memory`; core and premium packages remain in the 54-entry marketplace catalogue and are not exposed through that installer. + + +The live marketplace currently reports 47 active skills, not 54. + + +This draft describes the candidate catalogue without presenting every candidate as released or installable. + + + +## How to read this catalogue + +Each of those 54 marketplace candidate skills has a `SKILL.md` and a `skill.toml`. + + +The candidate marketplace metadata version is 1.6.1. + + +The community change passed 45 Python tests, 73% aggregate branch coverage, six Bun tests, signed-manifest and installer-policy validation, TSM validation for all four community skills, and an isolated install of four skills with zero skipped. + + +The descriptions below say what each guide instructs an agent to do. They do not guarantee that the agent will complete the task, produce a correct result, or satisfy a deployment environment. The [repository](https://github.com/terraphim/terraphim-skills) is the public project link. Before publication, replace `RELEASE_COMMIT` in the [reviewed source link](https://github.com/terraphim/terraphim-skills/tree/RELEASE_COMMIT) with the immutable release commit. + +## Research, specification, and phased delivery + +- `acceptance-testing` instructs an agent to turn requirements into acceptance criteria, test cases, test data, and a sign-off checklist. + +- `disciplined-research` instructs an agent to map the existing system, constraints, risks, and exclusions before design. + +- `disciplined-quality-evaluation` instructs an agent to assess research or design documents with the six-dimension KLS framework before phase transitions. + +- `disciplined-design` instructs an agent to turn approved research into a file-level implementation plan with signatures, tests, and sequencing. + +- `disciplined-specification` instructs an agent to refine implementation details, edge cases, and trade-offs through a structured interview. + +- `disciplined-implementation` instructs an agent to execute an approved plan step by step with tests and reviewable changes. + +- `disciplined-verification` instructs an agent to verify implementation against design with tests, coverage evidence, and traceability. + +- `disciplined-validation` instructs an agent to validate a system against original requirements and gather structured acceptance evidence. + + +## Review, evidence, and safety + +- `code-review` instructs an agent to review Rust and WebAssembly code for correctness, security, performance, and maintainability findings. + +- `deterministic-rlm-review` instructs an agent to run capability-routed review perspectives and reconcile severity-ranked findings. + +- `quality-gate` instructs an agent to assemble review, security, performance-risk, traceability, acceptance, and relevant visual evidence for a change. + +- `requirements-traceability` instructs an agent to map requirements through design, implementation, tests, and evidence, then flag gaps. + +- `security-audit` instructs an agent to review Rust and WebAssembly input handling, unsafe code, defaults, and relevant OWASP risks. + +- `structural-pr-review` instructs an agent to produce a severity-ranked pull-request review with file overviews, confidence, and an architecture diagram. + +- `ubs-scanner` instructs an agent to run Ultimate Bug Scanner and incorporate its findings into a quality-gate workflow. + +- `git-safety-guard` instructs an agent to configure a fail-open pre-tool guard for selected destructive Git and filesystem commands. + + +## Rust and application engineering + +- `architecture` instructs an agent to design Rust and WebAssembly architecture, APIs, modules, and decision records without implementing code. + +- `implementation` instructs an agent to implement an approved design with tests, documentation, and lint checks. + +- `testing` instructs an agent to write and run unit, integration, property-based, and benchmark tests and analyse failures. + +- `debugging` instructs an agent to reproduce and diagnose Rust failures, use logging or profiling, and remove temporary debug changes. + +- `rust-development` instructs an agent to apply idiomatic Rust patterns for async code, errors, traits, safety, and performance. + +- `rust-performance` instructs an agent to profile and optimise Rust code with benchmarks, memory analysis, SIMD, and zero-copy techniques. + +- `rust-observability` instructs an agent to configure tracing, OpenTelemetry, Prometheus metrics, request context, and environment-specific logging for Rust services. + +- `rust-ci-cd` instructs an agent to configure Rust CI/CD checks, dependency policy, coverage, benchmark, and release workflows. + +- `rust-fleet-standard` instructs an agent to apply the repository's shared Rust build, validation, issue-flow, and knowledge-stack conventions. + +- `gpui-components` instructs an agent to generate GPUI components using Zed-style rendering, state, completion, and theming patterns. + +- `visual-testing` instructs an agent to define screenshot coverage, rendering stabilisation, baseline management, and CI checks for UI changes. + + +## Knowledge, search, and agent orchestration + +- `terraphim-grep` instructs an agent to use local search first, scope paths, request JSON when needed, and make LLM synthesis explicit. + +- `terraphim-learning-memory` instructs an agent to retrieve, assess, verify, capture, and revisit durable evidence, with source session search built into the workflow. + +- `learning-capture` instructs an agent to capture and query failed-command records, but activation is blocked pending safety and accuracy fixes. + +- `local-knowledge` instructs an agent to discover configured roles and paths, search local knowledge through structured direct commands, cite results, and remain read-only. + +- `session-search` instructs an agent to search imported coding-assistant session history for previous conversations and decisions. + +- `kg-rlm-ingest` instructs an agent to distil sourced material into role-scoped knowledge-graph notes, using deterministic extraction before optional RLM work and requiring retrieval after every authorised write. + +- `terraphim-hooks` instructs an agent to configure knowledge-graph-based text replacement for supported command and commit hooks. + +- `terraphim-rlm` instructs an agent to route multi-step, multi-file work through budgeted recursive decomposition and reconciliation tools. + +- `adf-orchestrate` instructs an agent to dispatch, monitor, or cancel approved asynchronous ADF work on a user-controlled server. + + +## Issues, pull requests, and collaboration + +- `issue-to-pr-core` instructs an agent to use a tracker-neutral issue-to-PR contract with risk routing, evidence, review, and explicit delivery gates. + +- `gitea-issue-to-pr` instructs an agent to apply a Gitea tracker profile to the shared issue-to-PR contract. + +- `linear-issue-to-pr` instructs an agent to take a Linear issue through a phased engineering and pull-request workflow. + +- `gitea-pagerank-workflow` instructs an agent to prioritise and manage Gitea issues and dependencies with `gtr`. + +- `terraphim-linear` instructs an agent to fetch and inspect Linear issues through semantic aliases or structured output. + +- `linear-ticket-writer` instructs an agent to draft structured Linear tickets with lifecycle, priority, state, and estimate fields. + +- `open-source-contribution` instructs an agent to prepare issues and pull requests that follow a project's contribution conventions. + +- `community-engagement` instructs an agent to support contributors, discussions, release notes, and open-source community practice. + + +## Documentation, research, and communication + +- `documentation` instructs an agent to write Rust API, README, architecture, and contributor documentation in the project's style. + +- `md-book` instructs an agent to work with the MD-Book documentation generator, including highlighting, live reload, search, and templates. + +- `grounded-content-writer` instructs an agent to attach citations to material claims and recheck numbers against their sources. + +- `technical-book-writer` instructs an agent to structure a long technical explanation with progressive concepts, diagrams, and chapter files. + +- `deep-research` instructs an agent to route bounded or open research across file-only and externally enhanced modes, then reconcile evidence. + +- `repeatable-demo-video` instructs an agent to create scripted terminal or browser recordings as repeatable review evidence. + +- `judge` instructs an agent to assess outputs with semantic, pragmatic, and syntactic dimensions and record structured verdicts. + + +## Operations and delivery tools + +- `ai-config-management` instructs an agent to specify configuration control, semantic governance, drift detection, traceability, and change control for AI artefacts. + +- `devops` instructs an agent to work on Rust CI/CD, containers, deployment automation, and infrastructure as code. + +- `quickwit-log-search` instructs an agent to investigate logs and incidents with Quickwit using one of three index-discovery modes. + + +## Release boundaries + +The four community packages are source-branch release candidates on open, unmerged stacked pull requests #65, #66, and #72. + + +The community shell installer downloads one `SKILL.md` per selected skill; every current community manifest signs only that standalone file, so the four-skill install does not omit a required community package file. + + +`learning-capture` and `gitea-issue-to-pr` remain blocked in this campaign. Their spotlight drafts explain the findings and the checks required before either can be recommended. + diff --git a/docs/editor-publication.md b/docs/editor-publication.md new file mode 100644 index 0000000..e4520a9 --- /dev/null +++ b/docs/editor-publication.md @@ -0,0 +1,30 @@ +# Editor website candidate + +The `/editor/` bundle is a hash-admitted preview candidate, not full source-video acceptance. Its immutable source identity and qualification are recorded in `static/editor/release.json`; the independent manifest digest is pinned in the deployment workflow. + +Before deployment, verify both source assets and Zola output with that independent digest: + +```bash +python3 -m unittest discover -s scripts -p test_import_editor.py -v +python3 scripts/import-editor.py --verify --destination static/editor --manifest-sha256 3e0510de27b93c5aed5018058b55bd82798ad7ed535fd1c25812aef5a0aadc03 +zola build +python3 scripts/import-editor.py --verify --destination public/editor --manifest-sha256 3e0510de27b93c5aed5018058b55bd82798ad7ed535fd1c25812aef5a0aadc03 +``` + +Use Zola 0.22.1, matching deployment. A different installed Zola version is not the deployment build. + +Import a replacement into a fresh directory using `scripts/import-editor.py --source BUILD --manifest MANIFEST --manifest-sha256 APPROVED_DIGEST --destination NEW_DIRECTORY`. Review source identity, runtime closure and browser output evidence before changing the independent pin. Never derive the expected digest from the mutable deployed receipt during verification. + +The current bundle was staged from clean editor commit `949b5252e89814ed294c1a238cef726663873a76`, including the independently reviewed Unicode KG-protected Trim repair. Confirm that this source is admitted to the editor release chain before promoting this website candidate. Deployment and HTTPS/browser acceptance are separate gates. Safari dragging and complete 41-requirement acceptance remain unresolved. A stock Zed capability preview is not custom visual parity; the fork is outside this change. + +The how-to uses authored public input and an existing shared KG. New alternatives are document-scoped synonyms. The paired grep synthesis demonstration requires the clients candidate with synonym retrieval, semantic coverage and explicit `--answer` fixes; an older installed binary may behave differently. Configured provider synthesis may send public retrieved text outside the device. + +## Public walkthrough media + +Six reviewed public MP4 files are served under `/demos/knowledge-work/`. `manifest.json` pins their SHA-256 digests, recorded editor source and editing disclosures. Raw Safari and Zed recordings are excluded. `scripts/test_publication_media.py` verifies the actual publication bytes and required links/disclosures. The homepage links both the editor preview and the walkthrough. + +The recorded browser candidate remains `e2ee3ff9f6133cd67cd15533109c8020c20a0cdf`; the later pointer and Unicode normalization repairs are not silently relabelled as recorded. Publication is a public preview, not stable editor/extension release or complete 41-requirement qualification. All eight publication tests, Zola0.22.1 and verification of all132 source/output editor assets passed before publication. + +Unicode repair verification:110 native library tests, six original protection tests, eight Unicode regressions, strict native/WASM Clippy, real Chrome Trim reduction, Done ghosts, exact annotated save/reopen and undo/redo. Existing Terraphim normalized term and matcher types are reused; NFC and original grapheme boundary mapping are added. Greek sigma variants are equivalent; general full case folding and Straße/STRASSE transliteration are not claimed. Exact original spelling is retained. + +The paced-v2 media edition adds burned-in explanatory text outside the app image and replaces idle-heavy walkthroughs with disclosed chronological cuts and1.5–3× speed changes. Overview77s, Chrome44s, Firefox49s, Safari45s, Zed60s, grep21s. Original footage, source identity and qualification limits remain unchanged. The manifest records every retained source range and output caption interval. diff --git a/scripts/check-release-links.py b/scripts/check-release-links.py index 32652ea..9fbe0bf 100644 --- a/scripts/check-release-links.py +++ b/scripts/check-release-links.py @@ -37,7 +37,6 @@ "aarch64-unknown-linux-musl": "linux-aarch64", "x86_64-apple-darwin": "macos-x86_64", "aarch64-apple-darwin": "macos-aarch64", - "x86_64-pc-windows-msvc": "windows-x86_64", } CLI_BINARIES = ("terraphim-agent", "terraphim-cli", "terraphim-grep") @@ -119,6 +118,13 @@ def main() -> int: print(f"{binary}: checked {len(TARGETS)} advertised targets against {CHANNEL_BASE}") + # Additional homepage downloads not represented by the four common targets. + for target in ('universal-apple-darwin', 'x86_64-unknown-linux-musl'): + url = f"{CHANNEL_BASE}/terraphim-agent/terraphim-agent-{version}-{target}.tar.gz" + size = head_size(url) + if size is None or size < MIN_ASSET_BYTES: + failures.append(f"Additional homepage download unavailable: {url}") + if failures: for failure in failures: print(f"::error::{failure}") diff --git a/scripts/import-editor.py b/scripts/import-editor.py new file mode 100644 index 0000000..e876cc0 --- /dev/null +++ b/scripts/import-editor.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +"""Import hash-admitted editor assets into a new website directory; never deploy.""" +import argparse +import hashlib +import json +import os +from pathlib import Path, PurePosixPath +import re +import shutil +import tempfile + + +def sha(data): + return hashlib.sha256(data).hexdigest() + + +def safe(path): + for part in (path, *path.parents): + if part.is_symlink(): + raise ValueError(f"symlink forbidden: {part}") + return path + + +def validated_manifest(raw): + """Validate identity and every relative asset path before accessing files.""" + data = json.loads(raw) + if data.get("schema") != 1 or not re.fullmatch(r"[a-f0-9]{40}", data.get("sourceHead", "")): + raise ValueError("invalid source manifest identity") + assets = data.get("assets") + if not isinstance(assets, dict) or not {"index.html", "roles/default.json", "vendor/manifest.json"} <= assets.keys(): + raise ValueError("missing required editor assets") + for name, expected in assets.items(): + if (not isinstance(name, str) or not name or "\\" in name + or PurePosixPath(name).is_absolute() or any(p in ("", ".", "..") for p in name.split("/")) + or name in ("release.json", "assets-manifest.json")): + raise ValueError("unsafe or reserved asset path") + if not isinstance(expected, str) or not re.fullmatch(r"[a-f0-9]{64}", expected): + raise ValueError("invalid asset digest") + return data, assets + + +def stage(source, manifest, expected_sha, destination): + """Verify every byte before creating a new app tree; preserve existing trees.""" + source, manifest, destination = map(lambda p: Path(os.path.abspath(p)), (source, manifest, destination)) + safe(source) + safe(manifest) + safe(destination) + if destination.exists(): + raise ValueError("destination already exists; use a fresh staging directory") + raw = manifest.read_bytes() + if not re.fullmatch(r"[a-f0-9]{64}", expected_sha) or sha(raw) != expected_sha: + raise ValueError("manifest does not match independently admitted SHA256") + data, assets = validated_manifest(raw) + payloads = {} + for name, expected in assets.items(): + payload = safe(source / name).read_bytes() + if not isinstance(expected, str) or sha(payload) != expected: + raise ValueError(f"asset hash mismatch: {name}") + payloads[name] = payload + # Payloads are immutable snapshots of the verified reads, not later source copies. + destination.parent.mkdir(parents=True, exist_ok=True) + safe(destination.parent) + temporary = Path(tempfile.mkdtemp(prefix=".editor-stage-", dir=destination.parent)) + try: + for name, payload in payloads.items(): + path = temporary / name + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(payload) + (temporary / "assets-manifest.json").write_bytes(raw) + (temporary / "release.json").write_text(json.dumps({ + "schema": 1, "sourceHead": data["sourceHead"], "manifestSha256": expected_sha, + "assets": len(payloads), "fullParityAccepted": False, + "qualification": "HASH-ADMITTED CANDIDATE; browser/deployment acceptance required", + }, indent=2) + "\n") + safe(destination) + if destination.exists(): + raise ValueError("destination appeared during staging") + temporary.rename(destination) + finally: + if temporary.exists(): + shutil.rmtree(temporary) + + +def verify(destination, expected_sha): + """Recheck imported bytes before site build/deploy without changing them.""" + destination = safe(Path(os.path.abspath(destination))) + receipt = json.loads(safe(destination / "release.json").read_text()) + raw = safe(destination / "assets-manifest.json").read_bytes() + if (not isinstance(expected_sha, str) or not re.fullmatch(r"[a-f0-9]{64}", expected_sha) + or sha(raw) != expected_sha or receipt["manifestSha256"] != expected_sha): + raise ValueError("imported manifest identity changed") + data, assets = validated_manifest(raw) + if (receipt.get("schema") != 1 or receipt.get("sourceHead") != data["sourceHead"] + or receipt.get("assets") != len(assets) or receipt.get("fullParityAccepted") is not False): + raise ValueError("receipt does not describe the admitted candidate") + expected_paths = set(assets) | {"release.json", "assets-manifest.json"} + actual_paths = set() + for parent, directories, files in os.walk(destination, followlinks=False): + for name in directories + files: + safe(Path(parent) / name) + for name in files: + actual_paths.add((Path(parent) / name).relative_to(destination).as_posix()) + if actual_paths != expected_paths: + raise ValueError("imported tree contains missing or unadmitted files") + for name, expected in assets.items(): + if sha(safe(destination / name).read_bytes()) != expected: + raise ValueError(f"imported asset changed: {name}") + return receipt + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--source", type=Path) + parser.add_argument("--manifest", type=Path) + parser.add_argument("--manifest-sha256") + parser.add_argument("--destination", type=Path, default=Path("static/editor")) + parser.add_argument("--verify", action="store_true") + args = parser.parse_args() + if args.verify: + print(json.dumps(verify(args.destination, args.manifest_sha256), indent=2)) + else: + if not all((args.source, args.manifest, args.manifest_sha256)): + parser.error("import requires --source, --manifest and --manifest-sha256") + stage(args.source, args.manifest, args.manifest_sha256, args.destination) + print(f"Imported admitted candidate into {args.destination}; no deployment performed") + + +if __name__ == "__main__": + main() diff --git a/scripts/test_import_editor.py b/scripts/test_import_editor.py new file mode 100644 index 0000000..edb6f24 --- /dev/null +++ b/scripts/test_import_editor.py @@ -0,0 +1,108 @@ +"""Real-filesystem admission tests for the website's editor bundle.""" +import hashlib +import importlib.util +import json +from pathlib import Path +import tempfile +import unittest + +SPEC = importlib.util.spec_from_file_location("import_editor", Path(__file__).with_name("import-editor.py")) +MODULE = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(MODULE) + + +class ImportEditorTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory(dir=Path(tempfile.gettempdir()).resolve()) + self.addCleanup(self.temp.cleanup) + self.root = Path(self.temp.name) + self.source = self.root / "source" + self.source.mkdir() + files = {"index.html": b"Editor", + "roles/default.json": b'{"name":"test","data":{}}', + "vendor/manifest.json": b'{"files":[]}'} + for name, data in files.items(): + path = self.source / name + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(data) + self.manifest = self.root / "assets.json" + self.manifest.write_text(json.dumps({"schema": 1, "sourceHead": "a" * 40, + "assets": {name: hashlib.sha256(data).hexdigest() for name, data in files.items()}})) + self.expected = hashlib.sha256(self.manifest.read_bytes()).hexdigest() + self.destination = self.root / "website" / "static" / "editor" + + def test_copies_only_admitted_bytes_and_records_identity(self): + (self.source / "private-log.txt").write_text("not a website asset") + MODULE.stage(self.source, self.manifest, self.expected, self.destination) + self.assertFalse((self.destination / "private-log.txt").exists()) + self.assertEqual((self.destination / "index.html").read_bytes(), (self.source / "index.html").read_bytes()) + receipt = json.loads((self.destination / "release.json").read_text()) + self.assertEqual(receipt["manifestSha256"], self.expected) + self.assertEqual(receipt["sourceHead"], "a" * 40) + self.assertFalse(receipt["fullParityAccepted"]) + self.assertEqual(MODULE.verify(self.destination, self.expected), receipt) + (self.destination / "index.html").write_text("changed after import") + with self.assertRaises(ValueError): + MODULE.verify(self.destination, self.expected) + + def test_verify_rejects_receipt_changes_and_extra_files(self): + MODULE.stage(self.source, self.manifest, self.expected, self.destination) + receipt_path = self.destination / "release.json" + receipt = json.loads(receipt_path.read_text()) + receipt["sourceHead"] = "b" * 40 + receipt_path.write_text(json.dumps(receipt)) + with self.assertRaises(ValueError): + MODULE.verify(self.destination, self.expected) + receipt["sourceHead"] = "a" * 40 + receipt_path.write_text(json.dumps(receipt)) + (self.destination / "unexpected.txt").write_text("not admitted") + with self.assertRaises(ValueError): + MODULE.verify(self.destination, self.expected) + + def test_verify_rejects_coherent_replacement_against_external_digest(self): + MODULE.stage(self.source, self.manifest, self.expected, self.destination) + (self.destination / "index.html").write_text("replacement") + manifest_path = self.destination / "assets-manifest.json" + manifest = json.loads(manifest_path.read_text()) + manifest["assets"]["index.html"] = hashlib.sha256(b"replacement").hexdigest() + manifest_path.write_text(json.dumps(manifest)) + receipt_path = self.destination / "release.json" + receipt = json.loads(receipt_path.read_text()) + receipt["manifestSha256"] = hashlib.sha256(manifest_path.read_bytes()).hexdigest() + receipt_path.write_text(json.dumps(receipt)) + with self.assertRaises(ValueError): + MODULE.verify(self.destination, self.expected) + + def test_hash_failure_leaves_no_destination(self): + (self.source / "index.html").write_text("corrupt") + with self.assertRaises(ValueError): + MODULE.stage(self.source, self.manifest, self.expected, self.destination) + self.assertFalse(self.destination.exists()) + + def test_refuses_unverified_manifest_and_existing_destination(self): + with self.assertRaises(ValueError): + MODULE.stage(self.source, self.manifest, "0" * 64, self.destination) + self.destination.mkdir(parents=True) + (self.destination / "draft.md").write_text("preserve") + with self.assertRaises(ValueError): + MODULE.stage(self.source, self.manifest, self.expected, self.destination) + self.assertEqual((self.destination / "draft.md").read_text(), "preserve") + + def test_refuses_traversal_and_symlink(self): + d = json.loads(self.manifest.read_text()) + d["assets"]["../outside"] = "0" * 64 + self.manifest.write_text(json.dumps(d)) + with self.assertRaises(ValueError): + MODULE.stage(self.source, self.manifest, hashlib.sha256(self.manifest.read_bytes()).hexdigest(), self.destination) + d["assets"].pop("../outside") + self.manifest.write_text(json.dumps(d)) + (self.source / "index.html").unlink() + (self.root / "other.html").write_text("not admitted") + (self.source / "index.html").symlink_to(self.root / "other.html") + with self.assertRaises(ValueError): + MODULE.stage(self.source, self.manifest, hashlib.sha256(self.manifest.read_bytes()).hexdigest(), self.destination) + self.assertFalse(self.destination.exists()) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/test_publication_media.py b/scripts/test_publication_media.py new file mode 100644 index 0000000..766c0d8 --- /dev/null +++ b/scripts/test_publication_media.py @@ -0,0 +1,83 @@ +"""Check real publication bytes and links without browser mocks.""" +import hashlib +import json +from pathlib import Path +import unittest + +ROOT = Path(__file__).resolve().parents[1] +MEDIA = ROOT / 'static/demos/knowledge-work' + + +class PublicationMediaTests(unittest.TestCase): + def test_published_bytes_match_reviewed_inventory(self): + manifest = json.loads((MEDIA / 'manifest.json').read_text()) + self.assertEqual(len(manifest['media']), 6) + expected = {'manifest.json'} + for entry in manifest['media']: + self.assertTrue(entry['publicDelivery']) + expected.add(entry['file']) + data = (MEDIA / entry['file']).read_bytes() + self.assertEqual(hashlib.sha256(data).hexdigest(), entry['sha256']) + self.assertEqual(len(data), int(entry['probe']['format']['size'])) + self.assertEqual({p.name for p in MEDIA.iterdir()}, expected) + + def test_unavailable_windows_download_is_not_advertised(self): + for name in ['themes/WarpDrive/templates/index.html', 'themes/WarpDrive/static/js/site.js', 'content/docs/installation.md']: + self.assertNotIn('x86_64-pc-windows-msvc.zip', (ROOT / name).read_text()) + self.assertNotIn('terraphim-server_{{ rv }}-1_amd64.deb', (ROOT / 'themes/WarpDrive/templates/index.html').read_text()) + self.assertIn("var base = 'https://downloads.terraphim.ai/terraphim-agent/'", (ROOT / 'themes/WarpDrive/static/js/site.js').read_text()) + self.assertIn('Native Windows archives are unavailable', (ROOT / 'content/docs/installation.md').read_text()) + + def test_paced_walkthroughs_are_short_and_continuously_captioned(self): + manifest = json.loads((MEDIA / 'manifest.json').read_text()) + self.assertIn('paced-v2', manifest['edition']) + durations = {e['file']: float(e['probe']['format']['duration']) for e in manifest['media']} + self.assertLess(durations['grammarly-for-knowledge-work-preview.mp4'], 90) + self.assertLess(durations['grep-live-execution-public.mp4'], 25) + for entry in manifest['edits']: + if 'segments' not in entry: + continue + cursor = 0 + source_end = 0 + for segment in entry['segments']: + self.assertAlmostEqual(segment['start'], cursor, places=5) + self.assertTrue(segment['text']) + self.assertGreater(segment['end'], segment['start']) + self.assertGreaterEqual(segment['sourceStart'], source_end) + self.assertGreaterEqual(segment['speed'], 1) + self.assertLessEqual(segment['speed'], 3) + cursor = segment['end'] + source_end = segment['sourceEnd'] + self.assertAlmostEqual(cursor, durations[entry['output']], delta=0.1) + if entry['output'] != 'grep-live-execution-public.mp4': + self.assertLess(durations[entry['output']], 70) + + def test_editor_is_listed_as_a_homepage_capability(self): + home = (ROOT / 'themes/WarpDrive/templates/index.html').read_text() + section = home.split('id="waypoints"', 1)[1].split('', 1)[0] + self.assertIn("@/capabilities/terraphim-editor.md", section) + self.assertIn('Terraphim Editor', section) + self.assertIn('Intelligent editor', section) + self.assertIn('alternative suggestions', section) + self.assertIn('knowledge graph and roles', section) + self.assertEqual(section.count('class="waypoint-link"'), 10) + self.assertNotIn('Nine capabilities', section) + page = (ROOT / 'content/capabilities/terraphim-editor.md').read_text() + for target in ['/editor/', '/how-tos/knowledge-work-editor/']: + self.assertIn(target, page) + self.assertIn('document-scoped synonyms', page) + self.assertIn('preview', page) + + def test_walkthrough_links_and_disclosures(self): + page = (ROOT / 'content/how-tos/knowledge-work-editor.md').read_text() + for entry in json.loads((MEDIA / 'manifest.json').read_text())['media']: + self.assertIn('/demos/knowledge-work/' + entry['file'], page) + for text in ['41 requirements', 'zero cuts', 'overstates', 'document-scoped synonym', 'Zed fork is separate', 'normalization repair']: + self.assertIn(text, page) + home = (ROOT / 'themes/WarpDrive/templates/index.html').read_text() + self.assertIn("get_url(path='editor/')", home) + self.assertIn("get_url(path='@/how-tos/knowledge-work-editor.md')", home) + + +if __name__ == '__main__': + unittest.main() diff --git a/static/demos/knowledge-work/chrome-online-editor.mp4 b/static/demos/knowledge-work/chrome-online-editor.mp4 new file mode 100644 index 0000000..1efc8b1 Binary files /dev/null and b/static/demos/knowledge-work/chrome-online-editor.mp4 differ diff --git a/static/demos/knowledge-work/firefox-online-editor.mp4 b/static/demos/knowledge-work/firefox-online-editor.mp4 new file mode 100644 index 0000000..ee499f0 Binary files /dev/null and b/static/demos/knowledge-work/firefox-online-editor.mp4 differ diff --git a/static/demos/knowledge-work/grammarly-for-knowledge-work-preview.mp4 b/static/demos/knowledge-work/grammarly-for-knowledge-work-preview.mp4 new file mode 100644 index 0000000..449a8ee Binary files /dev/null and b/static/demos/knowledge-work/grammarly-for-knowledge-work-preview.mp4 differ diff --git a/static/demos/knowledge-work/grep-live-execution-public.mp4 b/static/demos/knowledge-work/grep-live-execution-public.mp4 new file mode 100644 index 0000000..a96ea97 Binary files /dev/null and b/static/demos/knowledge-work/grep-live-execution-public.mp4 differ diff --git a/static/demos/knowledge-work/manifest.json b/static/demos/knowledge-work/manifest.json new file mode 100644 index 0000000..8f8f1cb --- /dev/null +++ b/static/demos/knowledge-work/manifest.json @@ -0,0 +1,425 @@ +{ + "schema": 1, + "sourceEditorCommit": "e2ee3ff9f6133cd67cd15533109c8020c20a0cdf", + "previewUrl": "https://f215f8d2.terraphim-ai.pages.dev/editor/", + "productionDeployment": false, + "knowledgeGraphSha256": "e850422ec72faa06aae0d6c2893a9f1d2e36d60a5aef79bd8f863daa2d71b015", + "media": [ + { + "file": "chrome-online-editor.mp4", + "sha256": "92a48e3bd2b79b46475bf1e318cad46335708bf2ed6654f1f928c4cb78f11de4", + "probe": { + "programs": [], + "stream_groups": [], + "streams": [ + { + "codec_name": "h264", + "codec_type": "video", + "width": 1440, + "height": 1080 + } + ], + "format": { + "duration": "44.433333", + "size": "579757" + } + }, + "scope": "finite actual-output demo; full all41 acceptance remains unproved", + "publicDelivery": true + }, + { + "file": "firefox-online-editor.mp4", + "sha256": "87a137e39bba5bd0bae681a3304a9406fcf8d17d25e56918c49fcb2413307aea", + "probe": { + "programs": [], + "stream_groups": [], + "streams": [ + { + "codec_name": "h264", + "codec_type": "video", + "width": 1440, + "height": 1080 + } + ], + "format": { + "duration": "49.033333", + "size": "850757" + } + }, + "scope": "finite actual-output demo; full all41 acceptance remains unproved", + "publicDelivery": true + }, + { + "file": "safari-online-editor-public.mp4", + "sha256": "57fc4c0104750e4b2aed9879caac92c5d7d54ec9c7f457fde3d34d32a7d8006f", + "probe": { + "programs": [], + "stream_groups": [], + "streams": [ + { + "codec_name": "h264", + "codec_type": "video", + "width": 1440, + "height": 1080 + } + ], + "format": { + "duration": "44.600000", + "size": "807106" + } + }, + "scope": "finite actual-output demo; full all41 acceptance remains unproved", + "publicDelivery": true + }, + { + "file": "zed-existing-extension-public.mp4", + "sha256": "96ced61ac72f4dca0de8f1377e7ff70d408ecfad134c4bafdfb636ae435e6512", + "probe": { + "programs": [], + "stream_groups": [], + "streams": [ + { + "codec_name": "h264", + "codec_type": "video", + "width": 1440, + "height": 1080 + } + ], + "format": { + "duration": "59.733333", + "size": "931049" + } + }, + "scope": "finite actual-output demo; full all41 acceptance remains unproved", + "publicDelivery": true + }, + { + "file": "grep-live-execution-public.mp4", + "sha256": "4393d99c486a81951a669e6a509f8006e6b169717246ffc6c727b67b01f58549", + "probe": { + "programs": [], + "stream_groups": [], + "streams": [ + { + "codec_name": "h264", + "codec_type": "video", + "width": 1440, + "height": 1080 + } + ], + "format": { + "duration": "21.000000", + "size": "595159" + } + }, + "scope": "Actual captured terminal subprocesses on Safari clean export; same KG/chunks, only --answer changes. External synthesis is interpretive; policy is not proof of practice.", + "publicDelivery": true + }, + { + "file": "grammarly-for-knowledge-work-preview.mp4", + "sha256": "d383d61011ea3895872b79745aa9bccbe0ef4023fb7d9e03412206d70c21802f", + "probe": { + "programs": [], + "stream_groups": [], + "streams": [ + { + "codec_name": "h264", + "codec_type": "video", + "width": 1440, + "height": 1080 + } + ], + "format": { + "duration": "77.433333", + "size": "1260456" + } + }, + "scope": "Chrome online editor, explicit Safari clean-export handoff, actual grep comparison. Candidate preview; not full 41 acceptance or production deployment.", + "publicDelivery": true + } + ], + "coverageDisclaimer": "These four refreshed captures do not replace the original all41 ledger; no acceptance row promoted.", + "edition": "paced-v2: burned-in explanatory text, original raw captures retained", + "priorEdits": [ + { + "input": "safari-online-editor.mp4", + "output": "safari-online-editor-public.mp4", + "retainedSourceSeconds": [ + 140, + 242.783333 + ], + "reason": "remove idle setup; raw retained" + }, + { + "input": "zed-existing-extension.mp4", + "output": "zed-existing-extension-public.mp4", + "sourceSeconds": [ + 0, + 208.498333 + ], + "rectangle": [ + 1110, + 0, + 30, + 30 + ], + "reason": "mask personal avatar", + "verification": "opaque inner region max luma <=20 over all decoded frames; 205s full frame visually inspected" + }, + { + "input": "grep-live-execution-r2.cast", + "output": "grep-live-execution-public.mp4", + "reason": "terminal rendering, idle gaps capped at five seconds, final frame extended twenty seconds; raw cast retained" + }, + { + "output": "grammarly-for-knowledge-work-preview.mp4", + "inputs": [ + "overview-intro.png", + "chrome-online-editor.mp4", + "overview-handoff.png", + "grep-live-execution-public.mp4", + "overview-outro.png" + ], + "reason": "disclosed title cards, chronological source clips, scale/pad to 1920x1080; no fabricated output" + } + ], + "edits": [ + { + "output": "chrome-online-editor.mp4", + "input": "chrome-online-editor.mp4 (prior edition)", + "segments": [ + { + "start": 0, + "end": 8.0, + "text": "Start with a public project brief: decisions, risks and terminology.", + "sourceStart": 8, + "sourceEnd": 20, + "speed": 1.5 + }, + { + "start": 8.0, + "end": 19.333333, + "text": "The shared knowledge graph suggests alternatives. Apply Judgment.", + "sourceStart": 20, + "sourceEnd": 37, + "speed": 1.5 + }, + { + "start": 19.333333, + "end": 29.833333, + "text": "Add Assessment as your own wording, then exercise Undo and Redo.", + "sourceStart": 37, + "sourceEnd": 58, + "speed": 2 + }, + { + "start": 29.833333, + "end": 35.833333, + "text": "Save and reopen the annotated document. Dialog waiting is cut.", + "sourceStart": 78, + "sourceEnd": 90, + "speed": 2 + }, + { + "start": 35.833333, + "end": 44.4675, + "text": "Assessment remains active after reopen. Other alternatives stay attached.", + "sourceStart": 112, + "sourceEnd": 129.268333, + "speed": 2 + } + ], + "method": "chronological cuts and disclosed speed changes; captions outside application frame" + }, + { + "output": "firefox-online-editor.mp4", + "input": "firefox-online-editor.mp4 (prior edition)", + "segments": [ + { + "start": 0, + "end": 11.333333, + "text": "Open the public brief and inspect alternatives from the same knowledge graph.", + "sourceStart": 0, + "sourceEnd": 17, + "speed": 1.5 + }, + { + "start": 11.333333, + "end": 21.833333, + "text": "Choose wording and add Assessment. Compare it directly in the document.", + "sourceStart": 17, + "sourceEnd": 38, + "speed": 2 + }, + { + "start": 21.833333, + "end": 28.833333, + "text": "Reopen the saved annotated document with Assessment still active.", + "sourceStart": 64, + "sourceEnd": 78, + "speed": 2 + }, + { + "start": 28.833333, + "end": 38.166667, + "text": "Set a note aside in Overflow, then return it to the document.", + "sourceStart": 78, + "sourceEnd": 92, + "speed": 1.5 + }, + { + "start": 38.166667, + "end": 49.046667, + "text": "Inspect the Lab. This short brief produces zero cuts; no reduction is claimed.", + "sourceStart": 92, + "sourceEnd": 108.32, + "speed": 1.5 + } + ], + "method": "chronological cuts and disclosed speed changes; captions outside application frame" + }, + { + "output": "safari-online-editor-public.mp4", + "input": "safari-online-editor-public.mp4 (prior edition)", + "segments": [ + { + "start": 0, + "end": 8.666667, + "text": "Use the same public brief and shared knowledge graph in Safari.", + "sourceStart": 0, + "sourceEnd": 13, + "speed": 1.5 + }, + { + "start": 8.666667, + "end": 18.666667, + "text": "Apply Judgment from the knowledge-graph alternatives.", + "sourceStart": 13, + "sourceEnd": 33, + "speed": 2 + }, + { + "start": 18.666667, + "end": 31.166667, + "text": "Create Assessment as your own alternative and make it active.", + "sourceStart": 43, + "sourceEnd": 68, + "speed": 2 + }, + { + "start": 31.166667, + "end": 36.666667, + "text": "Save the annotated document. Idle time around the dialog is removed.", + "sourceStart": 68, + "sourceEnd": 79, + "speed": 2 + }, + { + "start": 36.666667, + "end": 44.584795, + "text": "Reopen with Assessment preserved. Clean export is verified separately.", + "sourceStart": 91, + "sourceEnd": 102.877193, + "speed": 1.5 + } + ], + "method": "chronological cuts and disclosed speed changes; captions outside application frame" + }, + { + "output": "zed-existing-extension-public.mp4", + "input": "zed-existing-extension-public.mp4 (prior edition)", + "segments": [ + { + "start": 0, + "end": 12.0, + "text": "The existing extension offers completion from the same knowledge graph.", + "sourceStart": 0, + "sourceEnd": 18, + "speed": 1.5 + }, + { + "start": 12.0, + "end": 22.0, + "text": "Apply Judgment, then use native Undo and Redo with saved checkpoints.", + "sourceStart": 18, + "sourceEnd": 48, + "speed": 3 + }, + { + "start": 22.0, + "end": 34.5, + "text": "Ghost the Risk passage. Its text and anchor are retained in the file.", + "sourceStart": 57, + "sourceEnd": 82, + "speed": 2 + }, + { + "start": 34.5, + "end": 41.166667, + "text": "Inspect the saved annotation while exercising native history.", + "sourceStart": 96, + "sourceEnd": 116, + "speed": 3 + }, + { + "start": 41.166667, + "end": 45.5, + "text": "The saved ghost includes the exact Risk text and its source anchor.", + "sourceStart": 145, + "sourceEnd": 158, + "speed": 3 + }, + { + "start": 45.5, + "end": 59.715485, + "text": "Revive the passage and check Undo/Redo. Stock Zed does not have browser UI parity.", + "sourceStart": 166, + "sourceEnd": 208.646455, + "speed": 3 + } + ], + "method": "chronological cuts and disclosed speed changes; captions outside application frame" + }, + { + "output": "grep-live-execution-public.mp4", + "input": "grep-live-execution-public.mp4 (prior edition)", + "segments": [ + { + "start": 0, + "end": 8.0, + "text": "Without --answer: retrieve source evidence. No generated answer is requested.", + "sourceStart": 0, + "sourceEnd": 8, + "speed": 1 + }, + { + "start": 8.0, + "end": 12.0, + "text": "Same query, corpus and KG; add --answer. Provider waiting is compressed.", + "sourceStart": 8, + "sourceEnd": 14, + "speed": 1.5 + }, + { + "start": 12.0, + "end": 21.0, + "text": "Read the real synthesis. It overstates a review policy as observed practice.", + "sourceStart": 14, + "sourceEnd": 23, + "speed": 1 + } + ], + "method": "chronological cuts and disclosed speed changes; captions outside application frame" + }, + { + "output": "grammarly-for-knowledge-work-preview.mp4", + "inputs": [ + "intro 4s", + "paced Chrome", + "handoff 4s", + "paced grep", + "outro 4s" + ] + } + ] +} diff --git a/static/demos/knowledge-work/safari-online-editor-public.mp4 b/static/demos/knowledge-work/safari-online-editor-public.mp4 new file mode 100644 index 0000000..ce1f161 Binary files /dev/null and b/static/demos/knowledge-work/safari-online-editor-public.mp4 differ diff --git a/static/demos/knowledge-work/zed-existing-extension-public.mp4 b/static/demos/knowledge-work/zed-existing-extension-public.mp4 new file mode 100644 index 0000000..bc1283b Binary files /dev/null and b/static/demos/knowledge-work/zed-existing-extension-public.mp4 differ diff --git a/static/editor/alternatives-panel-f7a68a7707d04dfa.css b/static/editor/alternatives-panel-f7a68a7707d04dfa.css new file mode 100644 index 0000000..fbbd37a --- /dev/null +++ b/static/editor/alternatives-panel-f7a68a7707d04dfa.css @@ -0,0 +1,275 @@ +/* + * The alternatives side panel (issue #10, spec R-4.1 to R-4.4, section 10) + * ======================================================================== + * + * Consumes the design tokens from tokens.css; the panel adds only its own + * geometry and the dim level of the text behind it, defined once here: + * + * --te-alt-panel-width 18% of the window, as measured in the demo video + * (R-4.1; docs/requirements/video-measurements.md): + * about 205px at a 1140px window + * --te-alt-dim-opacity the main text while the panel is open (R-4.1) + * + * The DOM is built by public/js/alternatives-panel.js. The panel is fixed to + * the left edge; on windows wide enough, the writing column moves right so + * the panel never covers it, and it stays centred when there is room. + */ + +/* + * The 12rem floor keeps the three cursive tabs and the alternative lines + * legible; it takes over below a 1067px window (12rem is 18% of 1067px), so + * every window from there up gets the measured 18%. The 20rem cap stops the + * panel growing past what a list of single words needs on very wide + * screens; it is reached at 1778px. Below 48rem the narrow-screen rule at + * the end of this file replaces the width with an overlay. + */ +:root { + --te-alt-panel-width: clamp(12rem, 18vw, 20rem); + --te-alt-dim-opacity: 0.7; +} + +.te-alt-panel[hidden] { + display: none !important; +} + +.te-alt-panel { + position: fixed; + inset: 0 auto 0 0; + z-index: 20; + box-sizing: border-box; + width: var(--te-alt-panel-width); + display: flex; + flex-direction: column; + /* As in the video at 1:15 to 1:35: the tabs sit just above the level of + the document's first line (the page's top margin), and the key hint at + the foot, clear of the bottom-left corner controls (1.25rem offset plus + a 1.75rem control, and air), as on the Overflow panel. */ + padding: max(1.5rem, calc(var(--te-page-margin-top) - 2.5rem)) 1.25rem 4.25rem; + container-type: inline-size; + background-color: var(--te-color-panel); + border-right: 1px solid var(--te-color-panel-border); + color: var(--te-color-text); + font-family: var(--te-font-mono); + font-size: 0.9375rem; + line-height: var(--te-line-height); + color-scheme: dark; + overflow-y: auto; +} + +/* ---- Tabs (R-4.2): cursive, active lavender, the others cream ---- */ + +/* One line, as in the video (Word · Sentence · Paragraph is 70% of the + panel's width there). The display face is a stack, not a web font, so the + size follows the panel's content width (7cqi): the widest common fallback, + Bradley Hand Bold, still fits at the 12rem floor, and the size stops at + 1.3125rem on wide panels. */ +.te-alt-tabs { + display: flex; + align-items: baseline; + gap: 0.5rem; + flex-wrap: nowrap; + white-space: nowrap; + margin-bottom: 1.25rem; +} + +.te-alt-tab { + margin: 0; + padding: 0 0.125rem; + border: 0; + border-radius: 4px; + background: none; + color: var(--te-color-text); + font-family: var(--te-font-display); + font-size: min(1.3125rem, 7cqi); + line-height: 1.2; + cursor: pointer; +} + +.te-alt-tab:hover { + color: var(--te-color-accent); +} + +.te-alt-tab.te-alt-tab--active, +.te-alt-tab[aria-selected="true"] { + color: var(--te-color-accent); +} + +.te-alt-tab:focus-visible, +.te-alt-delete:focus-visible { + outline: 2px solid var(--te-color-accent); + outline-offset: 2px; +} + +/* ---- Status and hint ---- */ + +.te-alt-status { + margin: 0 0 0.75rem; + color: var(--te-color-text-dim); + font-size: 0.8125rem; +} + +/* The routine count ("Word: 5 alternatives, …") is announced but not shown: + the video has no status line. */ +.te-alt-status.te-alt-status--quiet { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0 0 0 0); + clip-path: inset(50%); + white-space: nowrap; + border: 0; +} + +/* The tab panel fills the rest of the height so the hint can sit at its + foot. */ +.te-alt-tabpanel { + display: flex; + flex: 1 1 auto; + flex-direction: column; + min-height: 0; +} + +.te-alt-hint { + margin: auto 0 0; + padding-top: 1rem; + color: var(--te-color-text-dim); + font-size: 0.75rem; +} + +/* ---- Lines (R-4.3, R-4.4) ---- */ + +.te-alt-list { + margin: 0; + padding: 0; + list-style: none; +} + +.te-alt-list[hidden] { + display: none; +} + +.te-alt-line { + display: grid; + grid-template-columns: 1.25rem 1fr auto; + align-items: center; + column-gap: 0.25rem; + border-radius: 4px; +} + +.te-alt-glyph { + justify-self: center; + color: var(--te-color-text-dim); +} + +/* The author's dot is small ("bullet point"); the robot is a touch larger. */ +.te-alt-glyph--human { + font-size: 0.3125rem; +} + +.te-alt-glyph--ai { + font-size: 0.75rem; +} + +.te-alt-glyph--new { + font-size: 0.625rem; + opacity: 0.6; +} + +.te-alt-input { + box-sizing: border-box; + width: 100%; + min-width: 0; + margin: 0; + padding: 0.125rem 0.25rem; + border: 0; + border-bottom: 1px solid transparent; + background: transparent; + /* Inactive lines sit back; the active one is brighter (R-4.4). */ + color: var(--te-color-text-dim); + font: inherit; + caret-color: var(--te-color-accent); +} + +.te-alt-input:focus { + outline: none; + border-bottom-color: var(--te-color-accent-soft); +} + +.te-alt-input:focus-visible { + border-bottom-color: var(--te-color-accent); +} + +.te-alt-input[readonly] { + cursor: default; +} + +.te-alt-input::placeholder { + color: var(--te-color-text-dim); + opacity: 0.7; +} + +.te-alt-line--active .te-alt-input { + color: var(--te-color-text); + font-weight: 700; +} + +.te-alt-line--active .te-alt-glyph { + color: var(--te-color-accent); +} + +.te-alt-line:hover:not(.te-alt-line--new) { + background-color: var(--te-color-popover-hover); +} + +.te-alt-delete { + margin: 0; + padding: 0 0.375rem; + border: 0; + border-radius: 4px; + background: none; + color: var(--te-color-text-dim); + font-size: 0.75rem; + cursor: pointer; + visibility: hidden; +} + +.te-alt-line:hover .te-alt-delete, +.te-alt-line:focus-within .te-alt-delete { + visibility: visible; +} + +.te-alt-delete:hover { + color: var(--te-color-text); +} + +/* ---- The writing surface while the panel is open (R-4.1) ---- */ + +.te-alt-open .markdown-input.te-surface { + opacity: var(--te-alt-dim-opacity); + transition: opacity 160ms ease-out; +} + +@media (min-width: 48rem) { + .te-app[data-mode="write-on"].te-alt-open .markdown-input.te-surface { + margin-left: max( + calc(var(--te-alt-panel-width) + 1rem), + calc((100% - var(--te-measure) - 3rem) / 2) + ); + margin-right: auto; + } +} + +@media (max-width: 47.99rem) { + .te-alt-panel { + width: min(100vw, 22rem); + } +} + +@media (prefers-reduced-motion: reduce) { + .te-alt-open .markdown-input.te-surface { + transition: none; + } +} diff --git a/static/editor/assets-manifest.json b/static/editor/assets-manifest.json new file mode 100644 index 0000000..5977400 --- /dev/null +++ b/static/editor/assets-manifest.json @@ -0,0 +1,140 @@ +{ + "schema": 1, + "sourceHead": "949b5252e89814ed294c1a238cef726663873a76", + "assets": { + "alternatives-panel-f7a68a7707d04dfa.css": "a99a407d3f6aa52f48e3013bf865ef4b44bd0b9e4028d9e68c1a6dd32f8a68a8", + "blocks-b2480c8f9f3d63b3.css": "383c0e9197f015d651363e3abd31fdf67b56a6b4bde72ebe764a7b1e417c5b4c", + "example.html": "fda475be8b87ff7a0789f167b4906357c3d8d7e7d1fcd0348f8c908d65e731d3", + "index.html": "2bc7bbe7cfa67a414162f594e9eaa07e6328afd716cebfb0a4c0a199a5ca6f30", + "js/rolegraph-worker.js": "8240803c0aef05acd66b6ec91eaee16e53a556252aa48c8487a4c3bd9a8924d9", + "lab-3fee6e89acf44fa1.css": "1fe0441a83177e9713ac112fac85336ad3315b3ac755ec8a625e008c4ca1f967", + "overflow-cc1be2ca3843f438.css": "8fa1a611df91541508b1532d3628addffecec02b2516a3f7a2c98bc385ada218", + "persistence-cb27cfbe192754a.css": "da87a8b790fadebdc4b451dce2535eb829291c799d7199a6734ce9cf19f68ffd", + "rolegraph/rolegraph-worker.js": "8240803c0aef05acd66b6ec91eaee16e53a556252aa48c8487a4c3bd9a8924d9", + "rolegraph/terraphim_rolegraph_browser.d.ts": "95094622b3805fed54e96cdcbb1c732d1244fbecceadb24dd13e0f28c1d8f7e2", + "rolegraph/terraphim_rolegraph_browser.js": "0f155f7ebe48ffd9958266c153f965447f98e26724e8fa994d1e18d879a9f4dc", + "rolegraph/terraphim_rolegraph_browser_bg.wasm": "7002fd7e4614ae03acc1593c7225fc82d586641ee96d991780339244ed5eea8a", + "rolegraph/terraphim_rolegraph_browser_bg.wasm.d.ts": "f6cc0427ac07ca31fc3882c5f9487347717f887a0c9ebba69a1323eda3a822ea", + "roles/default.json": "e850422ec72faa06aae0d6c2893a9f1d2e36d60a5aef79bd8f863daa2d71b015", + "selection-menu-317cedafb0e3952e.css": "7d0790ed1bf0c6e3955e5305d31ace2bd67f6df5f5f2b7be9692f9c79843891e", + "terraphim-editor-257aa4f4cf8c0c34.js": "ead25002c035e02fba7936c7f043475cc573c62b5b6a0d76dba952f2ee5b64eb", + "terraphim-editor-257aa4f4cf8c0c34_bg.wasm": "c861c569dbb87eb098e8d5e1614d9579b9d6de3297cda72575693c86144a76ec", + "terraphim-editor.min.css": "0a6c2cb03d65b43b844ab3cd80614716c362136c1347423861d780ac4e0461a9", + "terraphim-editor.min.js": "711819be5ab76ea6daea66352ff252b3c93535054da0140cfa664cfcb2b609d9", + "terraphim_editor.js": "ead25002c035e02fba7936c7f043475cc573c62b5b6a0d76dba952f2ee5b64eb", + "terraphim_editor_bg.wasm": "c861c569dbb87eb098e8d5e1614d9579b9d6de3297cda72575693c86144a76ec", + "tokens-36540e1e0e83eb54.css": "71e359d23d7ec44c9c838f74cf77f6e33aa3c33c57757cfc4cfb96e22a09de55", + "trim-a64aceacb4246fba.css": "fe79acb70b9906e404d30e19b7d5e1cf6589bc0aa155f1c1dc93ed18e6dee81d", + "vendor/fontawesome/6.5.2/LICENSE.txt": "9b914eae88817d63b576eab5aafde7068c7a1abae125d7cdfb034f1da43a9afc", + "vendor/fontawesome/6.5.2/css/brands.min.css": "e10425ad768bc98ff1fb272a0ac8420f9d1ba22f0612c08ff1010c95080ffe7e", + "vendor/fontawesome/6.5.2/css/fontawesome.min.css": "137b1cf3cea9a8adb7884343a9a5ddddf4280f59153f74dc782fb7f7bf0d0519", + "vendor/fontawesome/6.5.2/css/regular.min.css": "6f4f16d58da1c82c0c3a3436e021a3d39b4742f741192c546e73e947eacfd92f", + "vendor/fontawesome/6.5.2/css/solid.min.css": "e65dc5b48fb5f39b142360c57c3a215744c94e56c755c929cc3e88fe12aab4d3", + "vendor/fontawesome/6.5.2/webfonts/fa-brands-400.ttf": "e28096fa75a96ac77020155ea3a6dd7312983e84115366d4cf49a0c312ec6d51", + "vendor/fontawesome/6.5.2/webfonts/fa-brands-400.woff2": "232c6f6a7678304f9efaa26f30b1610debc2ba9f4cd636b5e6751c8d73761b92", + "vendor/fontawesome/6.5.2/webfonts/fa-regular-400.ttf": "9174757efc83e072436e873c22be1663d3c103b0a16d7fb73569af4918d4d351", + "vendor/fontawesome/6.5.2/webfonts/fa-regular-400.woff2": "c27da6f833431da5aa295c44540bfac0fd8270ba6a3c4346427006d8a7b34b76", + "vendor/fontawesome/6.5.2/webfonts/fa-solid-900.ttf": "b4990d0d0c5f5d38d62e936eea120674e584c7eea8dcee38a975c0cf9a37539b", + "vendor/fontawesome/6.5.2/webfonts/fa-solid-900.woff2": "ae17c16afbea216707b2203ea1cf9bdb45b9bfe47d0f4ae3258ddbc6294dd02f", + "vendor/manifest.json": "ab09bfd030127e9476d9bf35aa063b445d48324c903a8806bebaf9584a373141", + "vendor/shoelace/2.12.0/LICENSE.md": "ac9ffacbfc877b6f4253a481b3c0edbad0c019ac3506a3b51415b336f4a0c054", + "vendor/shoelace/2.12.0/cdn/assets/icons/LICENSE": "0623348b57e817b6897c41b6065380b599d5befe2839d83270566c605e430441", + "vendor/shoelace/2.12.0/cdn/assets/icons/code.svg": "e66b460f5fd8ca4327d155aa79510cb114d11c50d1b62d25c7520ad1baa02e1b", + "vendor/shoelace/2.12.0/cdn/assets/icons/link.svg": "0634bc395f62df3ff42772c56af5c07bb6eba6ab23a70613ad0f255737e6511f", + "vendor/shoelace/2.12.0/cdn/assets/icons/question-circle.svg": "b9408c681dfb0002a76f91c9ac864b7127ae52428aa81eca188985504a61d7e0", + "vendor/shoelace/2.12.0/cdn/assets/icons/type-bold.svg": "8e7adb4a29d9fd60445e2e9d96461ddc1212ef14bfd76f41ffb951fa9968eade", + "vendor/shoelace/2.12.0/cdn/assets/icons/type-h1.svg": "9a211647fe7df5c57c0b86e6625cd9c661e3df540f646c30465890aece8e1207", + "vendor/shoelace/2.12.0/cdn/assets/icons/type-h2.svg": "0a5ce744585975247d8784149d3807ad57f1cbf834a34a5ab8e394a8660ba934", + "vendor/shoelace/2.12.0/cdn/assets/icons/type-h3.svg": "c588ec487945d9306c1005196ed7f84bd314633423171559f0aefa6b18c97211", + "vendor/shoelace/2.12.0/cdn/assets/icons/type-italic.svg": "29722685928ea226f897ebb7784132e810e976ffb61e21f625d875467f853153", + "vendor/shoelace/2.12.0/cdn/assets/icons/type-underline.svg": "e1038bb0001fe6f8e4e7f250bd497b71df555a093a1970aa99be189a83a3089e", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.24R5WE5A.js": "d820009490ad7b49fb616d2eac3eab89a9159c7e3d29796e522b6b903fa01be1", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.27ILGUWR.js": "c7a8c01697a327f9a24f20a80892eba4289b275114876996b7468d95a889ad94", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.2AIXAFCV.js": "243637209fe98b86324a9e50bc4b119786f749087a83f769fa5d35540e9cee8c", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.2SZPKEN5.js": "a06102043565da0ed6dbff17f50fdacadbb9cae0cf43f1904535101fc8938413", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.2X5EERK2.js": "b08f0ec5efc3540bab614d7b5f6c4ccbdbf2ef9b517cf1e3ddf7d204d223aaa2", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.3GITBIC4.js": "65f42f91445f0b5123f13e695f2973cbeb3a643a10dccb86baa620de0f0a32ef", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.3TFKS637.js": "14f82f632089ec1a68784a8aa36aec1e4a713623749ad880fbbd0119f78da043", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.3Y6SB6QS.js": "f1cea1705e0c978806be53c89f206e9c4be9cac5348d77fae76b8d759ff2a9c9", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.43GPPE74.js": "bb0054a347cbb37a414c64621c5151385e305c5a51451b9346ecd636dfde9c1b", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.4S3RCQCG.js": "822f662c32036ec0fd981cee20a7726a5d21c160a57dcc5597bddcc639349a98", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.52GJFLW5.js": "1c85cb5aba7188eb4a8587a8076781f49a5819be22740985e9b0c31e3fd8e0a3", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.52OC5JIK.js": "8e6b5b64330fa64475326259787126ac9a69fd19e63e0d872461ad8f42d00f79", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.6LJMKLWZ.js": "14a4528f459174f043fc09a0c85870a3a13c622e99db52a52f653cc12cc49de3", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.6U4GOUZA.js": "68684064e5e3af813bcf7022ce4cfe32f9169cc87de96cd5a6541c51a2904813", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.7G6CFCYN.js": "008d0d801c35fdea8829b0a18af142786bac4aa5403d8d2450140f0523a57363", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.7I3HGH5I.js": "5ff9e71578f1148596cd76ff6518f4a9350c3a78dcdc7ec83a1f968bb575e113", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.ABG77QVN.js": "eb49eecff1bdad654b2ccd12a899c841708f42b4941d06a3a588c6e24fb2ae5c", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.B3ILE7LN.js": "0fb77ecfeabdd945703ef57a818c219a87889d5409f0287e9e8d3c6be3241d48", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.B4BZKR24.js": "8bdb8b4bf886773489e9b8436aaf39a63de270073002ff62341649d857ea27cd", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.BXWBYEBW.js": "fe753faaca5c419872f5816aebc4af4afa97ded7380ec52675cf0908929652f9", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.DOYC4G7X.js": "cbccc5fca4cded950114904fb9db9471eb581c03eea6242c0c8bc47bbd56ee9a", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.E7LAXGG4.js": "4a6f1bf858fb8735a9fde90e6c7ba7a1aff940d5d1240f3701802cb504208cd2", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.ESELY2US.js": "c4097a6aa14847ee845e5f0cfe35a3a4291962c383d08d3523e3056878af9720", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.FDFOFJDR.js": "fb369159ef9c20b824925cee0f9978ffdb3a6c521d2f07a8fc135291d535a177", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.FZX37GM3.js": "2cbb7c5c4a53cb51a470e8d569ea78933aeaf5d5ed17e5380059fb70aa1fab9c", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.G2QSU2BF.js": "7828cb2a3a091d166721f1080ea93372f7dda91dc08f2f85878b5f87d58cb52f", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.GL3UMKIL.js": "ebb9e48d84cb4d0a9dffe8c8f6e63bb4c59d0e8555710fb7adf5e34052edb226", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.HAL7R4WT.js": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.HF7GESMZ.js": "4f1d59a3fb42615764c821c9f633ef85fd2ec5e7701355c82ea22eac263950a4", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.HPQ3PGLN.js": "bffc0c50bd516e7403b794eb9af5e894775bc1a13bd2b5ecc66ddbed1b53f31b", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.HQAAUCQS.js": "e0c9a4907115439faf7167b42a66b711858b930809afbe5ef3d04ff733811b98", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.HTWHAKXT.js": "86ba3477e5fff6f7844ac3343b2b2812ca299c7f537d6904855a112111f90ac3", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.I5LOVG5T.js": "637e9bc9f021553298c96430c3057a266f3cf4d6746b687a4d5057bd4342acfd", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.JJNPZ52S.js": "e869c821cc70ece9f594c1cf6cb2d8458ee6de5b33d2f7c4abb75544aa1cb29d", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.M2U2QT2K.js": "7c7463cbf1286d791c43b09b02ddf87828b07f2d2156454e7c59edf593641f91", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.MKSGYDJ7.js": "8ec92f56da768dd469e9c4e1b2d84bf305598eeb487b5172f54f390973c4ebb0", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.MOUXRZGR.js": "4b6060f74725dfa6d2e00708eeb68622e80032c29e8a29690cd32c3e7cf43b16", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.MTYRJPGI.js": "66a0947074c14ad59db09bca8d072469ef928736c3705cc7deeb98e029b5324d", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.NEZCQBYJ.js": "89adf06f2beac497d981a3fc37b45698bb90fb349a72938ba4a60ad7cd22d959", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.NH3SRVOC.js": "76b91e0a54ce01582a347456246f7433e936029ab1f9e80a87ced715864c4fa9", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.NYIIDP5N.js": "32772b78bca61210992bc3a593669ce2621d213e72b0b382e60cc09ddd87a539", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.O27EHOBW.js": "1a9aeba601f49a8ddb7507d1be6dac6ada4757edd7a69c9252f3421040854476", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.OQXQSIH6.js": "f72e99e832ab8262e441cd7fc4aef6cae99dfa32e7aa2d663184dcde34e5be0d", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.OZPGMRHW.js": "3ba2b5e16c8626a2ed2df8f71152bcc0c95df1b1903093bbfab76dae8082c65a", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.P7ZG6EMR.js": "11da9e127aa1b30b0969bf5d90bfbeb62e0c6e35c7e0c165d35d7171358b06d5", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.PCNTRSPK.js": "d50534e993927bedc5b34e6d1561e767e7b8f72fa6357e62bfcb7b954ccc0e21", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.PNPJ7KDG.js": "0cdc32350809cc64b71ecca105bd7ea95f177ca11a7e65f0a8cdb70202f8bfde", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.QOTN4UPW.js": "f0a57a7554f90327e24beea70b9d933fe294549d9c3a3352051bb5403bf27bae", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.QPSNFEB2.js": "9363a2487d32bff55b2ac56dcb57bfe1c07630c74d35171cd00ab492d16a3a47", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.RBTTBYTX.js": "6be2e7bdfc5e93af07b81a78bd188f24e22e7b3f0f7597626a924e17bb30601a", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.RIBKWZXP.js": "1f1d6889fc039c40fbd5ef439b79c05fba241aa1e221619f1d6e8ee7042ded6b", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.RK73WSZS.js": "06df6b1b1b699b5a462620b1aeacad9bef7ca4706d3d0fe146292d7316bc3357", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.TDLFM7SW.js": "a1d916205572ca7dea93311989aac9706f7d66147bb7619ce8104107fc1ab5cc", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.WAIE6M2P.js": "047a6b9e403b0e2c6e7986dedfee63031379cb31ec23a2a08bf5b5ca1fa4cace", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.WFFSQ5E5.js": "408c5d6195fcd0e23b8d97797d96289a2cff9b298ca6d37680243b564121d1d0", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.XAOA43RZ.js": "223cef3722caa4e3fb6efb90d5d4bc67ec30d51683e6df6e163a1c29884f72ce", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.XQ4KBI5G.js": "6b8ea2bf689231baa347d7c13c65e018f0876cbfc73b2f32d217dd7c5b070899", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.XXIXGO5R.js": "1ed2486116dcc58a15b579402169f054027b1e7bef36ee258d98014976656e03", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.XZMYMJVO.js": "b4f349ee02e854ba3dd6888bdbda3248c91da52772de86261d630b22c1f865e4", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.YCHBWCKL.js": "bd70677c7a805a7f0af02c88b2c217f96ae6391679b04938252238efaaf4710f", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.YGMDV7QR.js": "f81b5a698455691fc798d0c778899017d2935cb288692f0ccb837b899bf46243", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.YPBP7XEQ.js": "65de5249054ea64c13504137a773c08a3ecedf2f6c9762bf9ca8e670b762a20a", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.YZETUBD6.js": "78fb7c2ef495d0c74629aa6aee0ae3592bbd4c5103d6d37bc179b7695f2f74b3", + "vendor/shoelace/2.12.0/cdn/chunks/chunk.ZL53POKZ.js": "1f0c820ee81e8cd783cefcba1b28f075b64014f72932823c134cfbd45aee61c1", + "vendor/shoelace/2.12.0/cdn/components/badge/badge.js": "d8ee2039ef00280a52f17661d088647e328c677e090830381f3749fa343eccd9", + "vendor/shoelace/2.12.0/cdn/components/button-group/button-group.js": "d40c5473fa0a54722f6bbb5442339e9ae7fc10b34a8bc7d5bc5ac6138713ed31", + "vendor/shoelace/2.12.0/cdn/components/button/button.js": "d2c4baaf63d05b25d7e5d9950ae2a4cecdedff13f484387767dffa937beaf3c9", + "vendor/shoelace/2.12.0/cdn/components/dialog/dialog.js": "62df6c895cd87fc0c7731b094695ed80b62a23fb13b895b124fcb9f1d9154ea9", + "vendor/shoelace/2.12.0/cdn/components/divider/divider.js": "79e2fd331684c7fc545ce40cfdf9bcc92f3e26ec40566a08703da4c9dde8ad6a", + "vendor/shoelace/2.12.0/cdn/components/icon/icon.js": "b2eb2f56da87f532693ad0bce20a9c258d19964a37f4ebc51db6893cf306c636", + "vendor/shoelace/2.12.0/cdn/components/input/input.js": "084441c5b23b3842cc405fbc5e9fa54676f558b6a3bba5884582c667d8b057c3", + "vendor/shoelace/2.12.0/cdn/components/split-panel/split-panel.js": "e1780bedaa9c3b17612eae9560c39f448fd03c1f224c953fdc4c6df44ad38ae4", + "vendor/shoelace/2.12.0/cdn/components/tooltip/tooltip.js": "7e435dd2b5b7aa59e00e2967b90c8a9db61df5de781d55ec7f6b689007499d5e", + "vendor/shoelace/2.12.0/cdn/themes/light.css": "7bc8fb762a0f357d171df3126797e53cc0210d798080472fac6f72093acaf708", + "vendor/shoelace/2.12.0/cdn/utilities/base-path.js": "81b967daca7f541a910bdfabf297dbb0ec3d7fbf4588a9fb8bb8bae1b2ae28e1", + "vendor/shoelace/2.12.0/cdn/utilities/icon-library.js": "dbbfd83ee038e1c5d94f7107e5117fdf835523cbf595bcc87c7ec21a76157b19", + "vendor/shoelace/2.12.0/third-party-notices/composed-offset-position/LICENSE.md": "086c588bc8fd118a6a316d4701cf46bba36aa934f94fcfd933a35127c24627e4", + "vendor/shoelace/2.12.0/third-party-notices/floating-ui_core/LICENSE": "0e4c9a9b6c71019cbbea3bdc20b01223110a9035700f9c960c8fcbf78c2325ce", + "vendor/shoelace/2.12.0/third-party-notices/floating-ui_dom/LICENSE": "0e4c9a9b6c71019cbbea3bdc20b01223110a9035700f9c960c8fcbf78c2325ce", + "vendor/shoelace/2.12.0/third-party-notices/floating-ui_utils/LICENSE": "0e4c9a9b6c71019cbbea3bdc20b01223110a9035700f9c960c8fcbf78c2325ce", + "vendor/shoelace/2.12.0/third-party-notices/lit-element/LICENSE": "45d31799d0db956cc3eb5469346abbd9b7025babc5ff29fab10d7095da992ef1", + "vendor/shoelace/2.12.0/third-party-notices/lit/LICENSE": "45d31799d0db956cc3eb5469346abbd9b7025babc5ff29fab10d7095da992ef1", + "vendor/shoelace/2.12.0/third-party-notices/lit_node_modules_lit-html/LICENSE": "2b66f9390afde29edfe4314daff93f01fb182e1178053e228120178987e5db75", + "vendor/shoelace/2.12.0/third-party-notices/lit_node_modules_lit_reactive-element/LICENSE": "45d31799d0db956cc3eb5469346abbd9b7025babc5ff29fab10d7095da992ef1", + "vendor/shoelace/2.12.0/third-party-notices/shoelace-style_localize/LICENSE.md": "ac9ffacbfc877b6f4253a481b3c0edbad0c019ac3506a3b51415b336f4a0c054", + "write-on-6a8fd9449457dd74.css": "bd3b7471c8a7ba32c38927d47eefe26e5680ffa6ec96262380f4cf383340674c" + }, + "qualification": "Hash-admitted Unicode Trim preview; not full41 parity", + "fullParityAccepted": false +} diff --git a/static/editor/blocks-b2480c8f9f3d63b3.css b/static/editor/blocks-b2480c8f9f3d63b3.css new file mode 100644 index 0000000..9d54cba --- /dev/null +++ b/static/editor/blocks-b2480c8f9f3d63b3.css @@ -0,0 +1,306 @@ +/* + * Blocks view (issue #19): the document body as a list of editable + * Markdown blocks. Plain mode only; Write_On mode never shows it. + * Colours, radii and fonts come from public/css/tokens.css (--te-*) and the + * Shoelace theme (--sl-*) already used by the plain toolbar. + * Design notes: docs/design/blocks-view.md + */ + +/* The surface is hidden (not removed) while the Blocks view is showing. */ +.markdown-input[hidden], +.te-blocks[hidden] { + display: none !important; +} + +.te-blocks { + box-sizing: border-box; + height: calc(100% - 50px); + overflow-y: auto; + padding: 1rem; + background: var(--sl-color-neutral-50, #fafafa); +} + +.te-blocks-list { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.te-block { + position: relative; + padding: 0.5rem 0.75rem 0.75rem; + border: 1px solid var(--sl-color-neutral-200, #e4e4e7); + border-left: 3px solid var(--sl-color-neutral-300, #d4d4d8); + border-radius: var(--te-radius-popover); + background: var(--sl-color-neutral-0, #fff); + cursor: default; + transition: border-color 0.15s ease, box-shadow 0.15s ease; +} + +.te-block:hover { + border-left-color: var(--te-color-accent-soft); +} + +/* Visible focus for keyboard users (and for the current block). */ +.te-block:focus { + outline: 2px solid var(--te-color-accent); + outline-offset: 2px; + border-left-color: var(--te-color-accent); +} + +.te-block-editing { + border-left-color: var(--te-color-accent); + box-shadow: 0 0 0 1px var(--te-color-accent-soft); +} + +.te-block-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + min-height: 1.5rem; +} + +.te-block-type { + font-family: var(--te-font-mono); + font-size: 0.6875rem; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--te-color-text-dim); +} + +.te-block-actions { + display: inline-flex; + gap: 0.125rem; + opacity: 0; + transition: opacity 0.15s ease; +} + +.te-block:hover .te-block-actions, +.te-block:focus .te-block-actions, +.te-block:focus-within .te-block-actions { + opacity: 1; +} + +.te-block-action, +.te-blocks-add { + font: inherit; + border: 1px solid transparent; + border-radius: 4px; + background: transparent; + color: var(--sl-color-neutral-600, #52525b); + cursor: pointer; +} + +.te-block-action { + width: 1.75rem; + height: 1.5rem; + font-size: 0.75rem; +} + +.te-block-action:hover, +.te-blocks-add:hover { + background: var(--sl-color-neutral-100, #f4f4f5); + color: var(--te-color-accent); +} + +.te-block-action:focus-visible, +.te-blocks-add:focus-visible { + outline: 2px solid var(--te-color-accent); + outline-offset: 1px; +} + +.te-block-body > :first-child { + margin-top: 0.25rem; +} + +.te-block-body > :last-child { + margin-bottom: 0; +} + +.te-block-body pre, +.te-block-source { + overflow-x: auto; + font-family: var(--te-font-mono); + white-space: pre-wrap; +} + +.te-block-body table { + border-collapse: collapse; +} + +.te-block-body th, +.te-block-body td { + padding: 0.25rem 0.5rem; + border: 1px solid var(--sl-color-neutral-200, #e4e4e7); +} + +.te-block-code { border-left-color: var(--sl-color-neutral-500, #71717a); } +.te-block-heading { border-left-color: var(--te-color-accent-soft); } + +.te-block-editor { + box-sizing: border-box; + display: block; + width: 100%; + margin-top: 0.25rem; + padding: 0.5rem; + border: 1px solid var(--te-color-accent-soft); + border-radius: 4px; + font-family: var(--te-font-mono); + font-size: 0.875rem; + line-height: 1.5; + resize: vertical; + caret-color: var(--te-color-accent); +} + +.te-block-editor:focus { + outline: 2px solid var(--te-color-accent); + outline-offset: 0; +} + +.te-blocks-add { + margin-top: 0.75rem; + padding: 0.375rem 0.75rem; + border-color: var(--sl-color-neutral-300, #d4d4d8); + border-style: dashed; +} + +.te-blocks-empty { + color: var(--te-color-text-dim); +} + +/* Drafts kept after an outside change overlapped an open block editor. + Non-blocking, above the surface and the blocks, in both views. */ +.te-blocks-drafts[hidden] { + display: none !important; +} + +.te-blocks-drafts { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin: 0.5rem 1rem 0; +} + +.te-blocks-drafts:focus-visible { + outline: 2px solid var(--te-color-accent); + outline-offset: 2px; +} + +.te-blocks-draft { + padding: 0.5rem 0.75rem; + border: 1px solid var(--te-color-accent-soft); + border-left: 3px solid var(--te-color-accent); + border-radius: var(--te-radius-popover); + background: var(--sl-color-neutral-0, #fff); +} + +.te-blocks-draft-message { + margin: 0 0 0.25rem; + font-size: 0.875rem; +} + +.te-blocks-draft-message i { + color: var(--te-color-accent); +} + +.te-blocks-draft-text { + max-height: 8rem; + margin: 0 0 0.5rem; + padding: 0.375rem 0.5rem; + overflow: auto; + font-family: var(--te-font-mono); + font-size: 0.8125rem; + white-space: pre-wrap; + background: var(--sl-color-neutral-50, #fafafa); + border-radius: 4px; +} + +.te-blocks-draft-actions { + display: flex; + gap: 0.5rem; +} + +.te-blocks-draft-actions button { + font: inherit; + font-size: 0.8125rem; + padding: 0.25rem 0.625rem; + border: 1px solid var(--sl-color-neutral-300, #d4d4d8); + border-radius: 4px; + background: transparent; + cursor: pointer; +} + +.te-blocks-draft-apply { + border-color: var(--te-color-accent) !important; + color: var(--te-color-accent); +} + +.te-blocks-draft-actions button:focus-visible { + outline: 2px solid var(--te-color-accent); + outline-offset: 1px; +} + +/* Move up / Move down (#58): no button where the block cannot move. */ +.te-block-action[hidden] { + display: none; +} + +/* Messages about refused actions, such as a move the model would not make + because it would split a span or ghost. Polite live region. */ +.te-blocks-notice[hidden] { + display: none !important; +} + +.te-blocks-notice { + display: flex; + align-items: flex-start; + gap: 0.5rem; + margin: 0 0 0.75rem; + padding: 0.5rem 0.75rem; + border: 1px solid var(--te-color-accent-soft); + border-left: 3px solid var(--te-color-accent); + border-radius: var(--te-radius-popover); + background: var(--sl-color-neutral-0, #fff); +} + +.te-blocks-notice-text { + flex: 1; + margin: 0; + font-size: 0.875rem; +} + +.te-blocks-notice-dismiss { + font: inherit; + font-size: 0.75rem; + width: 1.75rem; + height: 1.5rem; + border: 1px solid transparent; + border-radius: 4px; + background: transparent; + color: var(--sl-color-neutral-600, #52525b); + cursor: pointer; +} + +.te-blocks-notice-dismiss:hover { + background: var(--sl-color-neutral-100, #f4f4f5); + color: var(--te-color-accent); +} + +.te-blocks-notice-dismiss:focus-visible { + outline: 2px solid var(--te-color-accent); + outline-offset: 1px; +} + +/* Blocks is not part of Write_On: never shown there. */ +.te-app[data-mode="write-on"] .te-blocks, +.te-app[data-mode="write-on"] .te-view-toggle { + display: none !important; +} + +@media (prefers-reduced-motion: reduce) { + .te-block, + .te-block-actions { + transition: none; + } +} diff --git a/static/editor/example.html b/static/editor/example.html new file mode 100644 index 0000000..ce71efc --- /dev/null +++ b/static/editor/example.html @@ -0,0 +1,30 @@ + + + + + Terraphim Editor embed example + + + + + +

A host page with one container

+
+ + + + + diff --git a/static/editor/index.html b/static/editor/index.html new file mode 100644 index 0000000..dc2baab --- /dev/null +++ b/static/editor/index.html @@ -0,0 +1,12186 @@ + + + + + Markdown Editor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/static/editor/js/rolegraph-worker.js b/static/editor/js/rolegraph-worker.js new file mode 100644 index 0000000..4f8c6e4 --- /dev/null +++ b/static/editor/js/rolegraph-worker.js @@ -0,0 +1,29 @@ +// Module worker. Only the Rust bridge owns graph algorithms and validation. +let graph = null; +let initializing = false; +self.addEventListener('message', async ({ data }) => { + if (data?.type === 'init') { + if (initializing || graph) return; + initializing = true; + try { + const bridge = await import(data.glueUrl); + await bridge.default({ module_or_path: data.wasmUrl }); + graph = new bridge.BrowserRoleGraph(); + self.postMessage({ type: 'ready' }); + } catch (_) { + self.postMessage({ type: 'failed', error: { code: 'worker_failed', message: 'Graph worker initialization failed' } }); + } + return; + } + if (!graph) { + self.postMessage({ type: 'failed', error: { code: 'worker_failed', message: 'Graph worker is unavailable' } }); + return; + } + try { + // Parse only the wire envelope: snapshot/graph JSON remains opaque text. + const response = graph.dispatch(JSON.stringify(data)); + self.postMessage(JSON.parse(response)); + } catch (_) { + self.postMessage({ type: 'failed', error: { code: 'worker_failed', message: 'Graph dispatch failed' } }); + } +}); diff --git a/static/editor/lab-3fee6e89acf44fa1.css b/static/editor/lab-3fee6e89acf44fa1.css new file mode 100644 index 0000000..dc6b237 --- /dev/null +++ b/static/editor/lab-3fee6e89acf44fa1.css @@ -0,0 +1,359 @@ +/* + * The Lab popover and Lab marks (issue #14, spec R-8.1, R-8.2, section 10) + * ======================================================================== + * + * Consumes the design tokens from tokens.css; the Lab adds only its mark + * hues, defined once here. Marks are transient overlays drawn with tints + * and underlines, never with opacity: opacity is the ghost treatment + * (R-5.1), and a mark must never look like text that would go. + * + * --te-lab-wash-weak lavender wash weak sentences + * --te-lab-amber #d9a05b long sentences (underline), off-tone words (dotted) + * --te-lab-rose #e8778e typos (wavy), punctuation slips (wash) + * --te-lab-teal #6cc4b0 convoluted sentences (double), hedges (dashed) + * + * Each action shows its own marks only, so at most two styles are visible + * at a time. Sentence marks use background washes with + * box-decoration-break so wrapped lines each get the tint; word marks use + * text-decoration. Neither changes line height. + */ + +:root { + --te-lab-amber: #d9a05b; + --te-lab-rose: #e8778e; + --te-lab-teal: #6cc4b0; + --te-lab-wash-weak: rgba(140, 134, 230, 0.2); + --te-lab-wash-long: rgba(217, 160, 91, 0.12); + --te-lab-wash-convoluted: rgba(108, 196, 176, 0.12); + --te-lab-wash-punctuation: rgba(232, 119, 142, 0.3); + --te-lab-popover-width: min(32rem, calc(100vw - 2rem)); +} + +/* ---- Marks ---- */ + +.te-lab-mark { + -webkit-box-decoration-break: clone; + box-decoration-break: clone; + border-radius: 2px; + text-underline-offset: 0.22em; +} + +.te-lab-mark--weak-sentence { + background-color: var(--te-lab-wash-weak); +} + +.te-lab-mark--long-sentence { + background-color: var(--te-lab-wash-long); + text-decoration: underline solid var(--te-lab-amber) 1px; +} + +.te-lab-mark--convoluted-sentence { + background-color: var(--te-lab-wash-convoluted); + text-decoration: underline double var(--te-lab-teal) 1px; +} + +.te-lab-mark--typo { + text-decoration: underline wavy var(--te-lab-rose) 1px; + text-decoration-skip-ink: none; +} + +.te-lab-mark--punctuation { + background-color: var(--te-lab-wash-punctuation); + box-shadow: inset 0 -1px 0 var(--te-lab-rose); +} + +.te-lab-mark--off-tone { + text-decoration: underline dotted var(--te-lab-amber) 2px; +} + +.te-lab-mark--hedge { + text-decoration: underline dashed var(--te-lab-teal) 1px; +} + +.te-lab-mark--filler { + text-decoration: underline dashed var(--te-color-text-dim) 1px; +} + +/* ---- Popover (R-8.1) ---- */ + +.te-lab { + position: fixed; + left: 50%; + bottom: 4rem; + z-index: 30; + box-sizing: border-box; + width: var(--te-lab-popover-width); + max-height: calc(100vh - 6rem); + overflow-y: auto; + transform: translateX(-50%); + padding: 0.875rem 0.5rem 0.625rem; + border: 1px solid var(--te-color-panel-border); + border-radius: var(--te-radius-popover); + background: var(--te-color-popover); + box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55); + color: var(--te-color-text); + font-family: var(--te-font-mono); + font-size: 0.8125rem; + line-height: 1.5; + text-align: left; +} + +.te-lab[hidden], +.te-lab-chip[hidden], +.te-lab-results[hidden], +.te-lab-trim[hidden], +.te-lab-note[hidden], +.te-lab-proposals[hidden] { + display: none; +} + +@media (prefers-reduced-motion: no-preference) { + .te-lab:not([hidden]) { + animation: te-lab-rise 140ms ease-out; + } +} + +@keyframes te-lab-rise { + from { opacity: 0; transform: translate(-50%, 6px); } + to { opacity: 1; transform: translate(-50%, 0); } +} + +.te-lab-header { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 1rem; + padding: 0 0.625rem 0.5rem; +} + +.te-lab-title { + margin: 0; + font-family: var(--te-font-display); + font-size: 1.375rem; + font-weight: 400; + line-height: 1.1; + color: var(--te-color-accent); +} + +.te-lab-subtitle { + color: var(--te-color-text-dim); + font-size: 0.75rem; +} + +.te-lab-actions { + display: flex; + flex-direction: column; +} + +.te-lab button { + margin: 0; + border: 0; + background: none; + color: inherit; + font: inherit; + cursor: pointer; +} + +.te-lab button:focus-visible { + outline: 2px solid var(--te-color-accent); + outline-offset: -2px; + border-radius: 6px; +} + +.te-lab .te-lab-action { + display: grid; + grid-template-columns: 1fr auto; + align-items: center; + gap: 0.625rem; + width: 100%; + padding: 0.45rem 0.625rem; + border-radius: 6px; + text-align: left; +} + +.te-lab .te-lab-action:hover, +.te-lab .te-lab-action:focus-visible { + background: var(--te-color-popover-hover); +} + +.te-lab-action[aria-checked="true"] { + color: var(--te-color-accent); +} + +.te-lab-action-count { + min-width: 1.5rem; + color: var(--te-color-text-dim); + font-variant-numeric: tabular-nums; + text-align: right; +} + +/* What the focused action does: the menu's accessible description only, as + the video shows no description line (4:56 to 5:30; issue #107). */ +.te-lab-hint { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0 0 0 0); + clip-path: inset(50%); + white-space: nowrap; + border: 0; +} + +.te-lab-trim { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin: 0.5rem 0.625rem 0; +} + +.te-lab-results { + margin: 0.625rem 0.625rem 0; + padding-top: 0.625rem; + border-top: 1px solid var(--te-color-panel-border); +} + +.te-lab-legend { + display: flex; + flex-wrap: wrap; + gap: 0.25rem 1rem; + color: var(--te-color-text); +} + +.te-lab-legend-entry { + display: inline-flex; + align-items: baseline; + gap: 0.5ch; +} + +.te-lab-swatch { + padding: 0 0.15em; + color: var(--te-color-text-dim); +} + +.te-lab-proposals { + margin: 0.5rem 0 0; + padding: 0; + max-height: 9.5rem; + overflow-y: auto; + list-style: none; +} + +.te-lab-proposal { + display: grid; + grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto; + align-items: center; + gap: 0.625rem; + padding: 0.2rem 0; +} + +.te-lab-from { + overflow: hidden; + color: var(--te-lab-rose); + text-decoration: line-through; + text-decoration-color: rgba(232, 119, 142, 0.6); + text-overflow: ellipsis; + white-space: pre; +} + +.te-lab-to { + overflow: hidden; + color: var(--te-color-text); + text-overflow: ellipsis; + white-space: pre; +} + +.te-lab-arrow { + color: var(--te-color-text-dim); + font-size: 0.75rem; +} + +.te-lab .te-lab-accept { + padding: 0.15rem 0.6rem; + border: 1px solid var(--te-color-accent-soft); + border-radius: 999px; + color: var(--te-color-accent); + font-size: 0.75rem; +} + +.te-lab .te-lab-accept:hover { + border-color: var(--te-color-accent); +} + +.te-lab .te-lab-clear { + display: inline-flex; + align-items: center; + gap: 0.5ch; + margin-top: 0.5rem; + padding: 0.25rem 0; + color: var(--te-color-text-dim); + font-size: 0.75rem; +} + +.te-lab .te-lab-clear:hover { + color: var(--te-color-text); +} + +.te-lab-note { + margin: 0.5rem 0.625rem 0; + color: var(--te-lab-rose); + font-size: 0.75rem; +} + +/* ---- Closed-state chip beside the LAB pill ---- */ + +.te-lab-chip { + position: fixed; + z-index: 20; + display: inline-flex; + align-items: center; + gap: 0.75rem; + transform: translateY(-50%); + color: var(--te-color-text-dim); + font-family: var(--te-font-mono); + font-size: 0.6875rem; + line-height: 1; + white-space: nowrap; +} + +.te-lab-chip .te-lab-chip-clear { + margin: 0; + padding: 0.2rem 0; + border: 0; + background: none; + color: var(--te-color-accent); + font: inherit; + cursor: pointer; +} + +.te-lab-chip .te-lab-chip-clear:hover { + text-decoration: underline; + text-underline-offset: 3px; +} + +.te-lab-chip .te-lab-chip-clear:focus-visible { + outline: 2px solid var(--te-color-accent); + outline-offset: 2px; + border-radius: 4px; +} + +/* Screen-reader reasons referenced by the marks' aria-describedby. */ +.te-lab-descriptions { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0 0 0 0); + clip-path: inset(50%); + white-space: nowrap; + border: 0; +} + +@media (max-width: 640px) { + .te-lab-subtitle { display: none; } + .te-lab-chip { display: none; } +} diff --git a/static/editor/overflow-cc1be2ca3843f438.css b/static/editor/overflow-cc1be2ca3843f438.css new file mode 100644 index 0000000..4ceafc9 --- /dev/null +++ b/static/editor/overflow-cc1be2ca3843f438.css @@ -0,0 +1,171 @@ +/* + * Overflow panel (issue #12; spec R-6.1, R-6.2, R-6.4) + * ==================================================== + * + * The DOM is built by public/js/overflow.js. Consumes the tokens in + * public/css/tokens.css; no colours, fonts or radii are hard-coded here. + * + * A full-height panel flush with the right edge, as in the demo (x=2536 to + * 3095 of 3096, top to bottom; docs/requirements/video-measurements.md). + * The top-right keyboard glyph and the bottom-right XYZ tag stay where they + * are, drawn over the panel (the chrome's z-index is above the panel's), and + * the panel's padding keeps its content clear of both; the XYZ tag is lit + * while the panel is open (write-on.css). The title + * is the cursive display face in lavender; the text area uses the document's + * monospace face; the footer hint is dim. While it is open + * () the writing column is centred in the + * space left of the panel instead of running underneath it. + */ + +/* + * The same 18% of the window as the alternatives panel (R-6.1; measured in + * docs/requirements/video-measurements.md), with the same clamp: the 12rem + * floor keeps the title and its close button on one line and takes over below + * a 1067px window; the 20rem cap is reached at 1778px. Declared on :root so + * the writing column's clearance below uses the same value. Below 40rem the + * narrow-screen rule replaces the width. + */ +:root { + --te-overflow-width: clamp(12rem, 18vw, 20rem); +} + +.te-overflow { + position: fixed; + top: 0; + right: 0; + bottom: 0; + z-index: 18; + box-sizing: border-box; + display: flex; + flex-direction: column; + width: var(--te-overflow-width); + /* Clear of the corner controls: 1.25rem offset plus a 1.75rem control, + and a little air, at the top and the bottom. */ + padding: 3.75rem 1.25rem 4.25rem; + background-color: var(--te-color-panel); + border-left: 1px solid var(--te-color-panel-border); + color: var(--te-color-text); + font-family: var(--te-font-mono); +} + +.te-overflow[hidden] { + display: none; +} + +.te-overflow-header { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 0.5rem; +} + +.te-overflow-title { + margin: 0; + color: var(--te-color-accent); + font-family: var(--te-font-display); + font-size: 1.9rem; + font-weight: 400; + line-height: 1.1; +} + +.te-overflow-tools { + display: flex; + gap: 0.25rem; +} + +.te-overflow-button { + margin: 0; + padding: 0.25rem 0.4rem; + border: 0; + border-radius: 4px; + background: none; + color: var(--te-color-text-dim); + font: inherit; + font-size: 0.85rem; + cursor: pointer; +} + +.te-overflow-button:hover { + color: var(--te-color-text); + background-color: var(--te-color-popover-hover); +} + +.te-overflow-button:focus-visible { + outline: 2px solid var(--te-color-accent); + outline-offset: 2px; +} + +.te-overflow-note { + margin: 0.5rem 0 0; + color: var(--te-color-text-dim); + font-size: 0.75rem; + line-height: 1.4; +} + +.te-overflow-text { + flex: 1 1 auto; + min-height: 6rem; + margin: 0.5rem 0 0; + padding: 0.5rem 0; + border: 0; + border-top: 1px solid var(--te-color-panel-border); + background: transparent; + color: var(--te-color-text); + caret-color: var(--te-color-accent); + font-family: var(--te-font-mono); + font-size: 0.875rem; + line-height: var(--te-line-height); + resize: none; + outline: none; + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +.te-overflow-text::placeholder { + color: var(--te-color-text-dim); + opacity: 1; +} + +.te-overflow-text::selection { + background-color: var(--te-color-accent-soft); +} + +.te-overflow-text:focus-visible { + border-top-color: var(--te-color-accent-soft); +} + +.te-overflow-text[readonly] { + color: var(--te-color-text-dim); +} + +.te-overflow-hint { + margin: 0.5rem 0 0; + padding-top: 0.5rem; + border-top: 1px solid var(--te-color-panel-border); + color: var(--te-color-text-dim); + font-size: 0.75rem; + letter-spacing: 0.02em; +} + +/* Keep the writing column clear of the open panel on wide screens. */ +@media (min-width: 60rem) { + .te-app[data-mode="write-on"][data-te-overflow="open"], + #app.te-app[data-mode="write-on"][data-te-overflow="open"] { + padding-right: calc(var(--te-overflow-width) + 2rem); + } +} + +/* Narrow screens: a bottom sheet across the full width and the lower half + of the window. The bottom corner controls stay on top of it, as they do on + the full-height panel, and the padding keeps the text clear of them. */ +@media (max-width: 40rem) { + .te-overflow { + left: 0; + width: auto; + top: auto; + height: 50vh; + padding-top: 1rem; + border-left: 0; + border-top: 1px solid var(--te-color-panel-border); + } +} diff --git a/static/editor/persistence-cb27cfbe192754a.css b/static/editor/persistence-cb27cfbe192754a.css new file mode 100644 index 0000000..fd83db4 --- /dev/null +++ b/static/editor/persistence-cb27cfbe192754a.css @@ -0,0 +1,199 @@ +/* + * Save, open, drafts and the Markdown export view (issues #76, #73) + * ================================================================= + * Elements created by public/js/persistence.js. Every colour, radius and + * font comes from public/css/tokens.css (defined on :root, so the dialogs + * and notice look the same in plain and Write_On mode, like the shortcut + * reference). Design notes: docs/design/persistence.md + */ + +/* ---- Dirty indicator: a dot on the save controls ---- */ + +.te-chrome-save, +.te-files-toolbar sl-button { + position: relative; +} + +.te-chrome-save.te-dirty::after, +.te-files-toolbar sl-button.te-dirty::after { + content: ""; + position: absolute; + top: 0.125rem; + right: 0.125rem; + width: 0.375rem; + height: 0.375rem; + border-radius: 50%; + background: var(--te-color-accent); + box-shadow: 0 0 0 2px var(--te-color-bg); + pointer-events: none; +} + +/* Text kept for screen readers only (the toolbar buttons are icons). */ +.te-files-sr { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0 0 0 0); + clip-path: inset(50%); + white-space: nowrap; +} + +/* ---- Notice: draft restore and file errors (non-blocking) ---- */ + +.te-files-notice { + position: fixed; + z-index: 30; + left: 50%; + bottom: 4rem; + transform: translateX(-50%); + display: flex; + align-items: center; + gap: 1rem; + max-width: min(var(--te-measure), calc(100vw - 2rem)); + padding: 0.625rem 0.875rem; + border: 1px solid var(--te-color-panel-border); + border-radius: var(--te-radius-popover); + background: var(--te-color-popover); + color: var(--te-color-text); + font-family: var(--te-font-mono); + font-size: 0.8125rem; + line-height: 1.5; + box-shadow: 0 0.5rem 1.5rem rgba(10, 13, 28, 0.45); +} + +.te-files-notice[hidden] { + display: none; +} + +.te-files-notice[data-kind="error"] .te-files-notice-text { + color: var(--te-color-text-dim); +} + +.te-files-notice-actions { + display: flex; + gap: 0.5rem; + flex-shrink: 0; +} + +.te-files-notice-button, +.te-files-button { + padding: 0.25rem 0.625rem; + border: 1px solid var(--te-color-panel-border); + border-radius: 4px; + background: transparent; + color: var(--te-color-text); + font: inherit; + cursor: pointer; +} + +.te-files-notice-button:first-child, +.te-files-primary { + border-color: var(--te-color-accent); + color: var(--te-color-accent); +} + +.te-files-notice-button:hover, +.te-files-button:hover { + background: var(--te-color-popover-hover); +} + +.te-files-notice-button:focus-visible, +.te-files-button:focus-visible, +.te-files-markdown-text:focus-visible { + outline: 2px solid var(--te-color-accent); + outline-offset: 2px; +} + +.te-files-button i { + margin-right: 0.375rem; +} + +/* ---- Dialogs: unsaved changes and the Markdown export (native ) ---- */ + +.te-files-dialog { + width: min(26rem, calc(100vw - 2rem)); + padding: 1rem 1.25rem 1.25rem; + border: 1px solid var(--te-color-panel-border); + border-radius: var(--te-radius-popover); + background: var(--te-color-popover); + color: var(--te-color-text); + font-family: var(--te-font-mono); + font-size: 0.8125rem; + line-height: 1.5; + color-scheme: dark; +} + +.te-files-markdown { + width: min(48rem, calc(100vw - 2rem)); +} + +.te-files-dialog::backdrop { + background: rgba(10, 13, 28, 0.6); +} + +.te-files-dialog-header { + display: flex; + align-items: baseline; + justify-content: space-between; +} + +.te-files-dialog h2 { + margin: 0; + font-family: var(--te-font-display); + font-size: 1.5rem; + font-weight: 400; +} + +.te-files-dialog-text { + margin: 0.5rem 0 0.75rem; + color: var(--te-color-text-dim); +} + +.te-files-close { + padding: 0 0.25rem; + border: none; + font-size: 1.25rem; + color: var(--te-color-text-dim); +} + +.te-files-markdown-text { + box-sizing: border-box; + display: block; + width: 100%; + /* Grows with the export (where field-sizing is supported), between a + comfortable minimum and most of the viewport. */ + field-sizing: content; + min-height: 10rem; + max-height: min(60vh, 32rem); + padding: 0.75rem; + border: 1px solid var(--te-color-panel-border); + border-radius: 4px; + background: var(--te-color-panel); + color: var(--te-color-text); + font-family: var(--te-font-mono); + font-size: 0.8125rem; + line-height: var(--te-line-height); + resize: vertical; +} + +.te-files-dialog-footer { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 0.5rem; + margin-top: 0.75rem; +} + +.te-files-status { + flex: 1; + color: var(--te-color-text-dim); +} + +@media (max-width: 40rem) { + .te-files-notice { + flex-direction: column; + align-items: stretch; + bottom: 3.5rem; + } +} diff --git a/static/editor/release.json b/static/editor/release.json new file mode 100644 index 0000000..95421be --- /dev/null +++ b/static/editor/release.json @@ -0,0 +1,8 @@ +{ + "schema": 1, + "sourceHead": "949b5252e89814ed294c1a238cef726663873a76", + "manifestSha256": "3e0510de27b93c5aed5018058b55bd82798ad7ed535fd1c25812aef5a0aadc03", + "assets": 132, + "fullParityAccepted": false, + "qualification": "HASH-ADMITTED CANDIDATE; browser/deployment acceptance required" +} diff --git a/static/editor/rolegraph/rolegraph-worker.js b/static/editor/rolegraph/rolegraph-worker.js new file mode 100644 index 0000000..4f8c6e4 --- /dev/null +++ b/static/editor/rolegraph/rolegraph-worker.js @@ -0,0 +1,29 @@ +// Module worker. Only the Rust bridge owns graph algorithms and validation. +let graph = null; +let initializing = false; +self.addEventListener('message', async ({ data }) => { + if (data?.type === 'init') { + if (initializing || graph) return; + initializing = true; + try { + const bridge = await import(data.glueUrl); + await bridge.default({ module_or_path: data.wasmUrl }); + graph = new bridge.BrowserRoleGraph(); + self.postMessage({ type: 'ready' }); + } catch (_) { + self.postMessage({ type: 'failed', error: { code: 'worker_failed', message: 'Graph worker initialization failed' } }); + } + return; + } + if (!graph) { + self.postMessage({ type: 'failed', error: { code: 'worker_failed', message: 'Graph worker is unavailable' } }); + return; + } + try { + // Parse only the wire envelope: snapshot/graph JSON remains opaque text. + const response = graph.dispatch(JSON.stringify(data)); + self.postMessage(JSON.parse(response)); + } catch (_) { + self.postMessage({ type: 'failed', error: { code: 'worker_failed', message: 'Graph dispatch failed' } }); + } +}); diff --git a/static/editor/rolegraph/terraphim_rolegraph_browser.d.ts b/static/editor/rolegraph/terraphim_rolegraph_browser.d.ts new file mode 100644 index 0000000..919522d --- /dev/null +++ b/static/editor/rolegraph/terraphim_rolegraph_browser.d.ts @@ -0,0 +1,48 @@ +/* tslint:disable */ +/* eslint-disable */ + +export class BrowserRoleGraph { + free(): void; + [Symbol.dispose](): void; + dispatch(request_json: string): string; + constructor(); +} + +export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; + +export interface InitOutput { + readonly memory: WebAssembly.Memory; + readonly __wbg_browserrolegraph_free: (a: number, b: number) => void; + readonly browserrolegraph_dispatch: (a: number, b: number, c: number) => [number, number, number, number]; + readonly browserrolegraph_new: () => number; + readonly __wbindgen_exn_store: (a: number) => void; + readonly __externref_table_alloc: () => number; + readonly __wbindgen_externrefs: WebAssembly.Table; + readonly __wbindgen_malloc: (a: number, b: number) => number; + readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; + readonly __externref_table_dealloc: (a: number) => void; + readonly __wbindgen_free: (a: number, b: number, c: number) => void; + readonly __wbindgen_start: () => void; +} + +export type SyncInitInput = BufferSource | WebAssembly.Module; + +/** + * Instantiates the given `module`, which can either be bytes or + * a precompiled `WebAssembly.Module`. + * + * @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated. + * + * @returns {InitOutput} + */ +export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput; + +/** + * If `module_or_path` is {RequestInfo} or {URL}, makes a request and + * for everything else, calls `WebAssembly.instantiate` directly. + * + * @param {{ module_or_path: InitInput | Promise }} module_or_path - Passing `InitInput` directly is deprecated. + * + * @returns {Promise} + */ +export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise } | InitInput | Promise): Promise; diff --git a/static/editor/rolegraph/terraphim_rolegraph_browser.js b/static/editor/rolegraph/terraphim_rolegraph_browser.js new file mode 100644 index 0000000..d50cfb0 --- /dev/null +++ b/static/editor/rolegraph/terraphim_rolegraph_browser.js @@ -0,0 +1,279 @@ +/* @ts-self-types="./terraphim_rolegraph_browser.d.ts" */ + +export class BrowserRoleGraph { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + BrowserRoleGraphFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_browserrolegraph_free(ptr, 0); + } + /** + * @param {string} request_json + * @returns {string} + */ + dispatch(request_json) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(request_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.browserrolegraph_dispatch(this.__wbg_ptr, ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } + } + constructor() { + const ret = wasm.browserrolegraph_new(); + this.__wbg_ptr = ret; + BrowserRoleGraphFinalization.register(this, this.__wbg_ptr, this); + return this; + } +} +if (Symbol.dispose) BrowserRoleGraph.prototype[Symbol.dispose] = BrowserRoleGraph.prototype.free; +function __wbg_get_imports() { + const import0 = { + __proto__: null, + __wbg___wbindgen_throw_41e9ee4f547fc59a: function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }, + __wbg_getRandomValues_a608c4436c19407a: function() { return handleError(function (arg0, arg1) { + globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1)); + }, arguments); }, + __wbindgen_generic_0000000000000001: function(arg0, arg1) { + // Cast intrinsic for `Ref(String) -> Externref`. + const ret = getStringFromWasm0(arg0, arg1); + return ret; + }, + __wbindgen_init_externref_table: function() { + const table = wasm.__wbindgen_externrefs; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + }, + }; + return { + __proto__: null, + "./terraphim_rolegraph_browser_bg.js": import0, + }; +} + +const BrowserRoleGraphFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_browserrolegraph_free(ptr, 1)); + +function addToExternrefTable0(obj) { + const idx = wasm.__externref_table_alloc(); + wasm.__wbindgen_externrefs.set(idx, obj); + return idx; +} + +function getArrayU8FromWasm0(ptr, len) { + ptr = ptr >>> 0; + return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len); +} + +function getStringFromWasm0(ptr, len) { + return decodeText(ptr >>> 0, len); +} + +let cachedUint8ArrayMemory0 = null; +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + const idx = addToExternrefTable0(e); + wasm.__wbindgen_exn_store(idx); + } +} + +function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = cachedTextEncoder.encodeInto(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function takeFromExternrefTable0(idx) { + const value = wasm.__wbindgen_externrefs.get(idx); + wasm.__externref_table_dealloc(idx); + return value; +} + +let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); +cachedTextDecoder.decode(); +const MAX_SAFARI_DECODE_BYTES = 2146435072; +let numBytesDecoded = 0; +function decodeText(ptr, len) { + numBytesDecoded += len; + if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) { + cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + cachedTextDecoder.decode(); + numBytesDecoded = len; + } + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + +const cachedTextEncoder = new TextEncoder(); + +if (!('encodeInto' in cachedTextEncoder)) { + cachedTextEncoder.encodeInto = function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; + }; +} + +let WASM_VECTOR_LEN = 0; + +let wasmModule, wasmInstance, wasm; +function __wbg_finalize_init(instance, module) { + wasmInstance = instance; + wasm = instance.exports; + wasmModule = module; + cachedUint8ArrayMemory0 = null; + wasm.__wbindgen_start(); + return wasm; +} + +async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (!module.ok) { + throw new Error(`failed to fetch Wasm: ${module.status} ${module.statusText} fetching '${module.url}'`); + } + + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + } catch (e) { + const validResponse = expectedResponseType(module.type); + + if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { throw e; } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + } else { + return instance; + } + } + + function expectedResponseType(type) { + switch (type) { + case 'basic': case 'cors': case 'default': return true; + } + return false; + } +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + + if (module !== undefined) { + if (Object.getPrototypeOf(module) === Object.prototype) { + ({module} = module) + } else { + console.warn('using deprecated parameters for `initSync()`; pass a single object instead') + } + } + + const imports = __wbg_get_imports(); + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + const instance = new WebAssembly.Instance(module, imports); + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(module_or_path) { + if (wasm !== undefined) return wasm; + + + if (module_or_path !== undefined) { + if (Object.getPrototypeOf(module_or_path) === Object.prototype) { + ({module_or_path} = module_or_path) + } else { + console.warn('using deprecated parameters for the initialization function; pass a single object instead') + } + } + + if (module_or_path === undefined) { + module_or_path = new URL('terraphim_rolegraph_browser_bg.wasm', import.meta.url); + } + const imports = __wbg_get_imports(); + + if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { + module_or_path = fetch(module_or_path); + } + + const { instance, module } = await __wbg_load(await module_or_path, imports); + + return __wbg_finalize_init(instance, module); +} + +export { initSync, __wbg_init as default }; diff --git a/static/editor/rolegraph/terraphim_rolegraph_browser_bg.wasm b/static/editor/rolegraph/terraphim_rolegraph_browser_bg.wasm new file mode 100644 index 0000000..703023f Binary files /dev/null and b/static/editor/rolegraph/terraphim_rolegraph_browser_bg.wasm differ diff --git a/static/editor/rolegraph/terraphim_rolegraph_browser_bg.wasm.d.ts b/static/editor/rolegraph/terraphim_rolegraph_browser_bg.wasm.d.ts new file mode 100644 index 0000000..651903c --- /dev/null +++ b/static/editor/rolegraph/terraphim_rolegraph_browser_bg.wasm.d.ts @@ -0,0 +1,14 @@ +/* tslint:disable */ +/* eslint-disable */ +export const memory: WebAssembly.Memory; +export const __wbg_browserrolegraph_free: (a: number, b: number) => void; +export const browserrolegraph_dispatch: (a: number, b: number, c: number) => [number, number, number, number]; +export const browserrolegraph_new: () => number; +export const __wbindgen_exn_store: (a: number) => void; +export const __externref_table_alloc: () => number; +export const __wbindgen_externrefs: WebAssembly.Table; +export const __wbindgen_malloc: (a: number, b: number) => number; +export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; +export const __externref_table_dealloc: (a: number) => void; +export const __wbindgen_free: (a: number, b: number, c: number) => void; +export const __wbindgen_start: () => void; diff --git a/static/editor/roles/default.json b/static/editor/roles/default.json new file mode 100644 index 0000000..a8c1b2c --- /dev/null +++ b/static/editor/roles/default.json @@ -0,0 +1,91 @@ +{ + "name": "terraphim-editor-default", + "data": { + "alternative": { + "id": 100, + "nterm": "alternatives" + }, + "alternatives": { + "id": 100, + "nterm": "alternatives" + }, + "caf\u00e9": { + "id": 4, + "nterm": "caf\u00e9" + }, + "choice": { + "id": 1, + "nterm": "decision" + }, + "coffee shop": { + "id": 4, + "nterm": "caf\u00e9" + }, + "coffeehouse": { + "id": 4, + "nterm": "caf\u00e9" + }, + "decision": { + "id": 1, + "nterm": "decision" + }, + "employ": { + "id": 8, + "nterm": "use" + }, + "eraser": { + "id": 2, + "nterm": "eraser" + }, + "honour": { + "id": 5, + "nterm": "honour" + }, + "judgment": { + "id": 1, + "nterm": "decision" + }, + "large language model": { + "id": 3, + "nterm": "llm", + "display_value": "LLM" + }, + "llm": { + "id": 3, + "nterm": "llm", + "display_value": "LLM" + }, + "option": { + "id": 1, + "nterm": "decision" + }, + "options set": { + "id": 100, + "nterm": "alternatives" + }, + "paperclip": { + "id": 7, + "nterm": "paperclip" + }, + "privilege": { + "id": 5, + "nterm": "honour" + }, + "rubber": { + "id": 2, + "nterm": "eraser" + }, + "use": { + "id": 8, + "nterm": "use" + }, + "utilise": { + "id": 8, + "nterm": "use" + }, + "variants": { + "id": 100, + "nterm": "alternatives" + } + } +} diff --git a/static/editor/selection-menu-317cedafb0e3952e.css b/static/editor/selection-menu-317cedafb0e3952e.css new file mode 100644 index 0000000..9522c33 --- /dev/null +++ b/static/editor/selection-menu-317cedafb0e3952e.css @@ -0,0 +1,72 @@ +/* + * Ghosted text and the selection context menu (issue #11; spec R-5.1, + * R-7.3). Consumes public/css/tokens.css; nothing is hard-coded except the + * shadow. Both apply in plain and Write_On mode (the tokens live on :root). + * Design notes: docs/design/ghosts-and-menu.md + */ + +/* ---- Ghosts (R-5.1) ---- + The fade is applied to the text colour, not `opacity`, so that on ghosted + text the selection highlight and the inline-indicator underline (which + sets its own colour) stay fully visible: the text is still selectable and + its alternatives still show. Dots live in an overlay and are unaffected. */ + +.te-ghost { + color: color-mix(in srgb, currentColor calc(var(--te-ghost-opacity) * 100%), transparent); +} + +@supports not (color: color-mix(in srgb, red 50%, transparent)) { + .te-ghost { + opacity: var(--te-ghost-opacity); + } +} + +/* ---- Selection context menu (R-7.3) ---- */ + +.te-selection-menu { + position: fixed; + z-index: 1000; + box-sizing: border-box; + min-width: 18rem; + max-width: calc(100vw - 16px); + margin: 0; + padding: 0.375rem; + background-color: var(--te-color-popover); + border: 1px solid var(--te-color-panel-border); + border-radius: var(--te-radius-popover); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45); + color: var(--te-color-text); + font-family: var(--te-font-mono); + font-size: 0.8125rem; + line-height: 1.4; + color-scheme: dark; + outline: none; + user-select: none; +} + +.te-selection-menu-item { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 2.5rem; + padding: 0.4375rem 0.625rem; + border-radius: calc(var(--te-radius-popover) - 3px); + cursor: pointer; + outline: none; + white-space: nowrap; +} + +/* Hovered or keyboard-active row: slightly lighter (R-7.3). */ +.te-selection-menu-item--active { + background-color: var(--te-color-popover-hover); +} + +/* A visible focus ring for keyboard users. */ +.te-selection-menu-item:focus-visible { + box-shadow: inset 0 0 0 1px var(--te-color-accent); +} + +.te-selection-menu-key { + color: var(--te-color-text-dim); + font-variant-numeric: tabular-nums; +} diff --git a/static/editor/terraphim-editor-257aa4f4cf8c0c34.js b/static/editor/terraphim-editor-257aa4f4cf8c0c34.js new file mode 100644 index 0000000..a68e91d --- /dev/null +++ b/static/editor/terraphim-editor-257aa4f4cf8c0c34.js @@ -0,0 +1,1666 @@ +/** + * Browser binding with one owned session per instance; no DOM dependency. + */ +export class TerraphimHostSession { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + TerraphimHostSessionFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_terraphimhostsession_free(ptr, 0); + } + /** + * Commit exact host output after the native text transaction. + * @param {number} id + * @param {number} revision + * @param {string} source + * @returns {string} + */ + acknowledge(id, revision, source) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.terraphimhostsession_acknowledge(this.__wbg_ptr, id, revision, ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } + } + /** + * Open a host resource with exact source and revision. + * @param {string} resource + * @param {string} session + * @param {number} revision + * @param {string} source + */ + constructor(resource, session, revision, source) { + const ptr0 = passStringToWasm0(resource, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(session, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.terraphimhostsession_new(ptr0, len0, ptr1, len1, revision, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + this.__wbg_ptr = ret[0]; + TerraphimHostSessionFinalization.register(this, this.__wbg_ptr, this); + return this; + } + /** + * Stage a protocol command; committed source stays unchanged. + * @param {string} message + * @returns {string} + */ + propose(message) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(message, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.terraphimhostsession_propose(this.__wbg_ptr, ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } + } + /** + * Invalidate a named staged operation. + * @param {number} id + */ + reject(id) { + const ret = wasm.terraphimhostsession_reject(this.__wbg_ptr, id); + if (ret[1]) { + throw takeFromExternrefTable0(ret[0]); + } + } + /** + * Current source and render projection, encoded as protocol JSON. + * @returns {string} + */ + snapshot() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.terraphimhostsession_snapshot(this.__wbg_ptr); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } + } + /** + * Adopt actual external edits or native undo/redo. + * @param {number} revision + * @param {string} source + * @returns {string} + */ + synchronize(revision, source) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.terraphimhostsession_synchronize(this.__wbg_ptr, revision, ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } + } + /** + * Query committed authored KG synonyms, encoded as JSON. + * @param {string} span_id + * @param {string} text + * @returns {string} + */ + synonyms_for(span_id, text) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.terraphimhostsession_synonyms_for(this.__wbg_ptr, ptr0, len0, ptr1, len1); + deferred3_0 = ret[0]; + deferred3_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } + } +} +if (Symbol.dispose) TerraphimHostSession.prototype[Symbol.dispose] = TerraphimHostSession.prototype.free; + +/** + * @param {string} token + * @returns {any} + */ +export function abort_document_history_transaction(token) { + const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.abort_document_history_transaction(ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Appends alternative `text` from `source` to span `span_id`. Returns the + * change, or `null` if the span already has that text; throws on error. + * @param {string} span_id + * @param {string} text + * @param {string} source + * @returns {any} + */ +export function alt_add(span_id, text, source) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.alt_add(ptr0, len0, ptr1, len1, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * "AI alternatives for selection" through every provider, in the shape of + * the #10 panel operations so `MarkdownEditor.alternativeOp( + * 'append_alternatives', start, end, supplied)` records it as one undo step. + * `supplied` is the JSON the host's awaited providers produced (see + * [`parse_supplied`]); `""` for none. Returns the change, `null` when + * nothing new was left to add, and throws, changing nothing, when there is + * nothing to offer for the selection or another span overlaps it. + * @param {number} start + * @param {number} end + * @param {string} supplied + * @returns {any} + */ +export function alt_append_alternatives(start, end, supplied) { + const ptr0 = passStringToWasm0(supplied, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.alt_append_alternatives(start, end, ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Creates a span of `kind` (`"word"`, `"sentence"`, `"paragraph"`) over + * `start..end` (UTF-16) with its first alternative `text` from `source` + * (`"human"` or `"ai"`). Returns the change (see [`AltChange::to_json`]); + * throws, changing nothing, for an invalid or overlapping range, empty text + * or text equal to the original. + * @param {string} kind + * @param {number} start + * @param {number} end + * @param {string} text + * @param {string} source + * @returns {any} + */ +export function alt_create_span(kind, start, end, text, source) { + const ptr0 = passStringToWasm0(kind, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.alt_create_span(ptr0, len0, start, end, ptr1, len1, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Replaces the text of alternative `index` of span `span_id`. Returns the + * change (with the body edit when it was the active one), or `null` when the + * text is unchanged; throws on error. + * @param {string} span_id + * @param {number} index + * @param {string} text + * @returns {any} + */ +export function alt_edit(span_id, index, text) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.alt_edit(ptr0, len0, index, ptr1, len1); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * "AI alternatives for selection" (R-8.6, see [`kg_append`]), in the shape + * of the #10 panel operations so `MarkdownEditor.alternativeOp('kg_append', + * start, end)` records it as one undo step: returns the change (see + * `AltChange::to_json`), `null` when the span already offers every synonym, + * and throws, changing nothing, when there is no KG term at the selection + * or another span overlaps it. + * @param {number} start + * @param {number} end + * @returns {any} + */ +export function alt_kg_append(start, end) { + const ret = wasm.alt_kg_append(start, end); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Moves alternative `from` of span `span_id` to position `to`. Returns the + * change, or `null` when `from == to`; throws on error. + * @param {string} span_id + * @param {number} from + * @param {number} to + * @returns {any} + */ +export function alt_move(span_id, from, to) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.alt_move(ptr0, len0, from, to); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Removes alternative `index` of span `span_id`. Returns the change (its + * `after` is `null` when the span was removed); throws on error. + * @param {string} span_id + * @param {number} index + * @returns {any} + */ +export function alt_remove(span_id, index) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.alt_remove(ptr0, len0, index); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Replays a recorded panel operation (see [`DocumentSession::alt_restore`]): + * `target_json` is the span snapshot to put back, or `"null"` to leave the + * span out. Returns `{ detached, reattached, setAside, warning, notice, ... }`; + * throws, changing nothing, when the replay does not fit. + * @param {string} span_id + * @param {string} target_json + * @param {number} start + * @param {number} deleted_len + * @param {string} inserted + * @returns {any} + */ +export function alt_restore(span_id, target_json, start, deleted_len, inserted) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(target_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(inserted, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.alt_restore(ptr0, len0, ptr1, len1, start, deleted_len, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * The provider ids in the order they are asked, as a JSON array. + * @returns {any} + */ +export function alternatives_provider_ids() { + const ret = wasm.alternatives_provider_ids(); + return ret; +} + +/** + * Registers a host provider `id` (default model `model`) after the ones + * already there, so its answers can be supplied to + * [`alt_append_alternatives`]. Re-registering keeps its place; the + * `thesaurus` id is reserved and throws. + * @param {string} id + * @param {string} model + */ +export function alternatives_provider_register(id, model) { + const ptr0 = passStringToWasm0(id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(model, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.alternatives_provider_register(ptr0, len0, ptr1, len1); + if (ret[1]) { + throw takeFromExternrefTable0(ret[0]); + } +} + +/** + * Removes the provider `id`; `false` when there was none. + * @param {string} id + * @returns {boolean} + */ +export function alternatives_provider_unregister(id) { + const ptr0 = passStringToWasm0(id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.alternatives_provider_unregister(ptr0, len0); + return ret !== 0; +} + +/** + * Mirrors a surface edit (`start`, `deletedLength`, `insertedText`, UTF-16) + * in the model. Returns `{ detached, detachedGhosts, reattached, setAside, warning, notice }`; throws + * if the edit does not fit the model, in which case call + * [`sync_document_body`]. + * @param {number} start + * @param {number} deleted_len + * @param {string} inserted + * @returns {any} + */ +export function apply_edit(start, deleted_len, inserted) { + const ptr0 = passStringToWasm0(inserted, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.apply_edit(start, deleted_len, ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * @param {string} expected_body + * @param {string} target_body + * @param {string} operations_json + * @returns {any} + */ +export function begin_document_history_replay(expected_body, target_body, operations_json) { + const ptr0 = passStringToWasm0(expected_body, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(target_body, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(operations_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.begin_document_history_replay(ptr0, len0, ptr1, len1, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * @param {string} expected_body + * @param {string} level + * @param {string} kept_json + * @returns {any} + */ +export function begin_trim_ghost_transaction(expected_body, level, kept_json) { + const ptr0 = passStringToWasm0(expected_body, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(level, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(kept_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.begin_trim_ghost_transaction(ptr0, len0, ptr1, len1, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * `{ spans, ghosts, overflow, setAside: { spans, ghosts }, preservedBlock, + * kg }` of the current document, in UTF-16 offsets. `kg` holds the derived + * knowledge-graph spans (issue #13, see [`crate::kg::KgSpan::to_json`]); + * they are never saved. + * @returns {any} + */ +export function document_annotations() { + const ret = wasm.document_annotations(); + return ret; +} + +/** + * The body as the model holds it (should always equal the surface text). + * @returns {string} + */ +export function document_body() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.document_body(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * `{ words, chars }` of the body, ghosted text included. + * @returns {any} + */ +export function document_counts() { + const ret = wasm.document_counts(); + return ret; +} + +/** + * The overflow (stash) text of the current document. + * @returns {string} + */ +export function document_overflow() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.document_overflow(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * Query the current browser document's scoped authored KG synonym concept. + * @param {string} span_id + * @param {string} text + * @returns {string} + */ +export function document_synonyms(span_id, text) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.document_synonyms(ptr0, len0, ptr1, len1); + deferred3_0 = ret[0]; + deferred3_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** + * The current document as clean Markdown: active alternatives, no ghosted + * text, no overflow, no block. + * @returns {string} + */ +export function export_document() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.export_document(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * @param {string} token + * @returns {any} + */ +export function finish_document_history_transaction(token) { + const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.finish_document_history_transaction(ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Render a pending (debounced) preview immediately. + * + * Call this before reading the preview for save, export or tests. Returns + * `true` if a render was pending and has now run, `false` if the preview was + * already up to date (or no editor is initialised). From JavaScript in the + * Trunk build this is `window.wasmBindings.flush_preview()`. + * @returns {boolean} + */ +export function flush_preview() { + const ret = wasm.flush_preview(); + return ret !== 0; +} + +/** + * Ghosts `start..end` (UTF-16 offsets into the body) in the current + * document. Returns `{ ok: true, id, annotations }` with the updated + * annotations (as [`document_annotations`]), or `{ ok: false, error, kind }` + * with the document unchanged. Never throws. + * @param {number} start + * @param {number} end + * @returns {any} + */ +export function ghost_range(start, end) { + const ret = wasm.ghost_range(start, end); + return ret; +} + +/** + * LF source for a DOM presentation, with rebased annotations and untouched + * malformed block bytes. Native persisted source remains owned by its host. + * + * # Errors + * Refuses overflowing annotation coordinates. + * @param {string} source + * @returns {string} + */ +export function host_presentation_source(source) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.host_presentation_source(ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** + * Forgets the thesaurus: no KG spans until the next load. + */ +export function kg_clear_thesaurus() { + wasm.kg_clear_thesaurus(); +} + +/** + * Publish a prepared role and return its summary as JSON text. + * @param {string} token + * @returns {string} + */ +export function kg_commit_prepared_role(token) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.kg_commit_prepared_role(ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** + * Discard a prepared role; active state is untouched. + * @param {string} token + */ +export function kg_discard_prepared_role(token) { + const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.kg_discard_prepared_role(ptr0, len0); +} + +/** + * The derived KG spans of the open document (see [`KgSpan::to_json`]). + * `document_annotations` returns the same list as its `kg` field. + * @returns {any} + */ +export function kg_document_spans() { + const ret = wasm.kg_document_spans(); + return ret; +} + +/** + * Loads a thesaurus JSON for KG alternatives, replacing the current one. + * Returns `{ name, concepts, skipped }`; throws (keeping the previous + * thesaurus) if the JSON is not a thesaurus. + * @param {string} json + * @returns {any} + */ +export function kg_load_thesaurus(json) { + const ptr0 = passStringToWasm0(json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.kg_load_thesaurus(ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * The KG term at the selection `start..end` with its alternatives, or + * `null` (see [`kg_lookup`]). Read-only. + * @param {number} start + * @param {number} end + * @returns {any} + */ +export function kg_lookup_selection(start, end) { + const ret = wasm.kg_lookup_selection(start, end); + return ret; +} + +/** + * Prepare local role JSON without replacing the current KG engine. + * @param {string} json + * @returns {string} + */ +export function kg_prepare_role(json) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.kg_prepare_role(ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** + * Read-only active role snapshot for the Worker coordinator. + * @returns {string} + */ +export function kg_role_snapshot_json() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.kg_role_snapshot_json(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * Replace the single opt-in browser role observer; `None` unregisters it. + * Called synchronously, with no arguments, only after successful load, clear, + * or prepared commit. Read the authoritative snapshot inside the callback. + * @param {Function | null} [listener] + */ +export function kg_set_role_change_listener(listener) { + wasm.kg_set_role_change_listener(isLikeNone(listener) ? 0 : addToExternrefTable0(listener)); +} + +/** + * Makes alternative `index` of KG span `span_id` the text. Returns the same + * shape as `set_active_alternative` (`{ span, from, to, edit, range, ... }`, + * `edit` the exact UTF-16 change made to the body, or `null`); throws, + * changing nothing, for an unknown span, an invalid index or an edit that + * would change a block span's text. + * @param {string} span_id + * @param {number} index + * @returns {any} + */ +export function kg_swap_alternative(span_id, index) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.kg_swap_alternative(ptr0, len0, index); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * `[{ id, label }]` of the six Lab actions, in popover order. + * @returns {any} + */ +export function lab_actions() { + const ret = wasm.lab_actions(); + return ret; +} + +/** + * Runs Lab action `action` (an id from [`lab_actions`]) over the current + * document body and returns `[{ kind, start, end, score, reason, proposal }]` + * in UTF-16 offsets. Throws for an unknown action id. Never changes text. + * @param {string} action + * @returns {any} + */ +export function lab_mark(action) { + const ptr0 = passStringToWasm0(action, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.lab_mark(ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Render the editor markup into `root` with `initial` as the open document + * and wire the live preview to the surface inside `root` (issue #77). + * + * This is the entry point of the embeddable wrapper; [`run`] uses it for + * `#app`. Every lookup is scoped to `root`, so markup elsewhere on the page + * is never touched. `root` gains the `te-app` class, which the Write_On + * layout rules key on. There is one document session and one preview per + * module instance, so mounting replaces the previous editor: one editor + * per page (see docs/design/embedding.md). + * @param {Element} root + * @param {string} initial + */ +export function mount_editor(root, initial) { + const ptr0 = passStringToWasm0(initial, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.mount_editor(root, ptr0, len0); + if (ret[1]) { + throw takeFromExternrefTable0(ret[0]); + } +} + +/** + * Moves the body text `start..end` to `to` (UTF-16 code units; `to` is an + * insertion point in the pre-move body, outside the range), carrying the + * spans and ghosts inside the range. Returns + * `{ detached, detachedGhosts, reattached, setAside, warning, notice }` like [`apply_edit`]; + * throws (changing nothing) if the range or destination is invalid or the + * move would split a span or ghost. + * + * `MarkdownEditor.moveRange` calls this and then updates the editing surface + * without mirroring the change through [`apply_edit`]. + * @param {number} start + * @param {number} end + * @param {number} to + * @returns {any} + */ +export function move_document_range(start, end, to) { + const ret = wasm.move_document_range(start, end, to); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Opens a `.md` source as the current document and returns + * `{ body, warning, unresolved }`: put `body` on the editing surface and + * show `warning` (a string or `null`) as a non-blocking notice. + * @param {string} source + * @returns {any} + */ +export function open_document(source) { + const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.open_document(ptr0, len0); + return ret; +} + +/** + * The text a pull back (R-6.4, a copy) inserts at `at` (UTF-16) and where: + * the overflow range `from..to` (UTF-16; when empty, the line at `from`), + * separated as terraphim-lsp separates it. Returns `{ start, text }` and + * changes nothing; throws when there is nothing to pull back or for an + * invalid offset or range. `TeOverflowPanel.pullBack` inserts `text` at + * `start` through the normal edit path. + * @param {number} at + * @param {number} from + * @param {number} to + * @returns {any} + */ +export function overflow_pull_back_text(at, from, to) { + const ret = wasm.overflow_pull_back_text(at, from, to); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Synchronous model preflight for one issued receipt, before JS cursor/surface + * movement. Whole mixed replay groups require controller-level clone preflight. + * @param {string} change_json + * @param {boolean} undo + */ +export function preflight_document_ghost_change(change_json, undo) { + const ptr0 = passStringToWasm0(change_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.preflight_document_ghost_change(ptr0, len0, undo); + if (ret[1]) { + throw takeFromExternrefTable0(ret[0]); + } +} + +/** + * Current preview debounce delay in milliseconds. + * @returns {number} + */ +export function preview_delay() { + const ret = wasm.preview_delay(); + return ret >>> 0; +} + +/** + * Whether a debounced preview render is scheduled but has not yet run. + * @returns {boolean} + */ +export function preview_pending() { + const ret = wasm.preview_pending(); + return ret !== 0; +} + +/** + * Number of preview renders triggered by edits since the last [`run`]. + * The initial render on load is part of the template and is not counted. + * Intended for diagnostics and tests. + * @returns {number} + */ +export function preview_render_count() { + const ret = wasm.preview_render_count(); + return ret >>> 0; +} + +/** + * @param {string} token + * @param {string} source + * @returns {any} + */ +export function recover_document_history_transaction(token, source) { + const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.recover_document_history_transaction(ptr0, len0, ptr1, len1); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Convert Markdown to HTML with the `markdown` crate. + * + * Exported so that JavaScript (and the browser benchmarks) use exactly the + * same conversion as the live preview. + * @param {string} input + * @returns {string} + */ +export function render_markdown(input) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(input, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.render_markdown(ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** + * Replays an issued receipt, never arbitrary promotion ranges. Protected + * promotion is wired by Lab separately, not exposed as a JS bypass here. + * @param {string} change_json + * @param {boolean} undo + * @returns {any} + */ +export function replay_document_ghost_change(change_json, undo) { + const ptr0 = passStringToWasm0(change_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.replay_document_ghost_change(ptr0, len0, undo); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Replays a recorded overflow change `from` -> `to` (undo or redo of a + * stash) onto the current overflow, keeping text typed in the panel since + * (see [`rebase_overflow`]). Returns `{ ok: true, overflow, applied }` + * (`applied: false` when an undo could not find the stashed text intact at + * its recorded position and left the overflow unchanged) or + * `{ ok: false, error, kind }`. Never throws. + * @param {string} from + * @param {string} to + * @returns {any} + */ +export function replay_document_overflow(from, to) { + const ptr0 = passStringToWasm0(from, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(to, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.replay_document_overflow(ptr0, len0, ptr1, len1); + return ret; +} + +/** + * Revives `start..end` (UTF-16 offsets into the body) in the current + * document. Returns `{ ok: true, changed, annotations }`, or + * `{ ok: false, error, kind }` with the document unchanged. Never throws. + * @param {number} start + * @param {number} end + * @returns {any} + */ +export function revive_range(start, end) { + const ret = wasm.revive_range(start, end); + return ret; +} + +/** + * Render the full-page editor, with the welcome text, into `#app`. + * + * Errors when the page has no element with id `app`. + */ +export function run() { + const ret = wasm.run(); + if (ret[1]) { + throw takeFromExternrefTable0(ret[0]); + } +} + +/** + * The current document as `.md` text: body plus annotation block, set-aside + * annotations included. + * @returns {string} + */ +export function save_document() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.save_document(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * Makes alternative `index` of span `span_id` active (R-2.4): the span's + * text is swapped and a preceding "a"/"an" fixed up (R-2.6) in the model. + * Returns `{ span, from, to, edit, range, detached, reattached, setAside, + * warning, notice }`, where `edit` (`{ start, deletedLength, deletedText, + * insertedText }` in UTF-16 code units, or `null` when `index` was already + * active) is the exact change made to the body and `range` (`{ start, end }`) + * is the span's text afterwards. Throws, changing nothing, + * for an unknown span, an invalid index or a stale anchor. + * + * `MarkdownEditor.swapAlternative` calls this and then applies `edit` to + * the editing surface without mirroring it through [`apply_edit`]. + * @param {string} span_id + * @param {number} index + * @returns {any} + */ +export function set_active_alternative(span_id, index) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.set_active_alternative(ptr0, len0, index); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Replaces the overflow text (what the Overflow panel holds). Returns + * `{ ok: true, overflow }`, or `{ ok: false, error, kind }` (kind + * `"preserved-block"`) with nothing changed. Never throws. + * @param {string} text + * @returns {any} + */ +export function set_document_overflow(text) { + const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.set_document_overflow(ptr0, len0); + return ret; +} + +/** + * Set the preview debounce delay in milliseconds; `0` renders synchronously + * on every `input`. Applies to renders scheduled after the call; a render + * already pending keeps its original deadline. [`run`] resets the delay to + * [`DEFAULT_PREVIEW_DELAY_MS`]. Does nothing if no editor is initialised. + * @param {number} ms + */ +export function set_preview_delay(ms) { + wasm.set_preview_delay(ms); +} + +/** + * Module start: render the full-page editor into `#app` (the Trunk build). + * + * Does nothing on a page that embeds the editor through the + * `TeraphimEditor` wrapper (`public/js/terraphim-editor.js`): the wrapper + * sets `window.TE_EMBED = true` before it instantiates the module and then + * calls [`mount_editor`] on its own container, so a host page that happens + * to have an element with id `app` is never taken over. A page without + * `#app` is also left alone, so instantiating the module never fails for + * want of one (a failing start function would reject the glue's `init()`). + */ +export function start() { + wasm.start(); +} + +/** + * Stashes the body text `start..end` (UTF-16) in the overflow: removed + * from the body through the normal edit path and appended to the overflow + * (R-6.3). Returns `{ edit, overflow: { before, after }, detached, + * detachedGhosts, reattached, setAside, warning, notice }`; throws, + * changing nothing, for an empty or invalid range or while a malformed + * block is preserved. + * + * `MarkdownEditor.stashRange` calls this and then applies `edit` to the + * editing surface without mirroring it through [`apply_edit`]. + * @param {number} start + * @param {number} end + * @returns {any} + */ +export function stash_document_range(start, end) { + const ret = wasm.stash_document_range(start, end); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Replaces the model body with `text` if it differs and re-anchors; returns + * `{ changed, unresolved, notice }` (`notice` as for [`apply_edit`]). + * @param {string} text + * @returns {any} + */ +export function sync_document_body(text) { + const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.sync_document_body(ptr0, len0); + return ret; +} + +/** + * Explicit-selection kind inference through shared Rust boundaries/counter. + * @param {string} text + * @returns {string} + */ +export function text_infer_kind(text) { + let deferred2_0; + let deferred2_1; + try { + const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.text_infer_kind(ptr0, len0); + deferred2_0 = ret[0]; + deferred2_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * Shared automatic targeting; coordinates and return object are UTF-16. + * @param {string} text + * @param {number} utf16 + * @param {string} kind + * @returns {any} + */ +export function text_range_at(text, utf16, kind) { + const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(kind, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.text_range_at(ptr0, len0, utf16, ptr1, len1); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * The typed edits "Make the cuts" applies at `level` with `kept` kept: + * `{ text, edits: [{ start, end, insert, kind }] }` on the current body's + * UTF-16 offsets. Throws for a stale plan; never changes text itself. + * @param {string} level + * @param {string} kept + * @returns {any} + */ +export function trim_make_cuts(level, kept) { + const ptr0 = passStringToWasm0(level, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(kept, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.trim_make_cuts(ptr0, len0, ptr1, len1); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Computes the trim plan for the current document body and returns + * `{ totalWords, levels, cuts }` (UTF-16 offsets). Align the model with the + * surface first. Never changes text. + * @returns {any} + */ +export function trim_plan_json() { + const ret = wasm.trim_plan_json(); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * The status card for trim level `level` (`"slight"`, ...) with the cut + * ids in `kept` (a JSON array) kept, plus the active pieces to fade. Throws + * for an unknown level, bad ids, no plan or a stale plan. + * @param {string} level + * @param {string} kept + * @returns {any} + */ +export function trim_status(level, kept) { + const ptr0 = passStringToWasm0(level, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(kept, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.trim_status(ptr0, len0, ptr1, len1); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Forget the editor wired by the last [`mount_editor`]: cancel a pending + * preview render and drop the references to its nodes, so a destroyed + * embed leaves no timer behind. The surface's `input` listener stays on the + * (removed) surface and does nothing once the state is gone. + */ +export function unmount_editor() { + wasm.unmount_editor(); +} +function __wbg_get_imports() { + const import0 = { + __proto__: null, + __wbg___wbindgen_is_falsy_16bd49b68658263e: function(arg0) { + const ret = !arg0; + return ret; + }, + __wbg___wbindgen_is_undefined_8865fb403f8fe9d8: function(arg0) { + const ret = arg0 === undefined; + return ret; + }, + __wbg___wbindgen_rethrow_cb2e88c6b2a16733: function(arg0) { + throw arg0; + }, + __wbg___wbindgen_throw_41e9ee4f547fc59a: function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }, + __wbg__wbg_cb_unref_dcc1a90847f04c41: function(arg0) { + arg0._wbg_cb_unref(); + }, + __wbg_addEventListener_4d0db17c671ea324: function() { return handleError(function (arg0, arg1, arg2, arg3) { + arg0.addEventListener(getStringFromWasm0(arg1, arg2), arg3); + }, arguments); }, + __wbg_add_9ceab44da1353b80: function() { return handleError(function (arg0, arg1, arg2) { + arg0.add(getStringFromWasm0(arg1, arg2)); + }, arguments); }, + __wbg_call_6137034ef55c9d0f: function() { return handleError(function (arg0, arg1) { + const ret = arg0.call(arg1); + return ret; + }, arguments); }, + __wbg_classList_8a097a4837412bf6: function(arg0) { + const ret = arg0.classList; + return ret; + }, + __wbg_clearTimeout_c7bc4c3c7a7774af: function(arg0, arg1) { + arg0.clearTimeout(arg1); + }, + __wbg_document_9854e03c05fc8834: function(arg0) { + const ret = arg0.document; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_error_757e9472f8410341: function(arg0, arg1) { + let deferred0_0; + let deferred0_1; + try { + deferred0_0 = arg0; + deferred0_1 = arg1; + console.error(getStringFromWasm0(arg0, arg1)); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + } + }, + __wbg_error_c9cf3fc2064683a9: function(arg0) { + console.error(arg0); + }, + __wbg_getElementById_cc94972b404e4eaa: function(arg0, arg1, arg2) { + const ret = arg0.getElementById(getStringFromWasm0(arg1, arg2)); + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_getRandomValues_a608c4436c19407a: function() { return handleError(function (arg0, arg1) { + globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1)); + }, arguments); }, + __wbg_get_31af05bd4842a84f: function() { return handleError(function (arg0, arg1) { + const ret = Reflect.get(arg0, arg1); + return ret; + }, arguments); }, + __wbg_instanceof_Window_82d71df4eddf88bc: function(arg0) { + let result; + try { + result = arg0 instanceof Window; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }, + __wbg_new_227d7c05414eb861: function() { + const ret = new Error(); + return ret; + }, + __wbg_parse_0fc53dead14b3b42: function() { return handleError(function (arg0, arg1) { + const ret = JSON.parse(getStringFromWasm0(arg0, arg1)); + return ret; + }, arguments); }, + __wbg_querySelector_49877e2a9e3f670b: function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.querySelector(getStringFromWasm0(arg1, arg2)); + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, arguments); }, + __wbg_setTimeout_db7bbc18a17e152a: function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.setTimeout(arg1, arg2); + return ret; + }, arguments); }, + __wbg_set_innerHTML_7af59a832a09a074: function(arg0, arg1, arg2) { + arg0.innerHTML = getStringFromWasm0(arg1, arg2); + }, + __wbg_stack_3b0d974bbf31e44f: function(arg0, arg1) { + const ret = arg1.stack; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }, + __wbg_static_accessor_GLOBAL_266715b9d96ba635: function() { + const ret = typeof global === 'undefined' ? null : global; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_static_accessor_GLOBAL_THIS_10fb7dc1ae063179: function() { + const ret = typeof globalThis === 'undefined' ? null : globalThis; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_static_accessor_SELF_0b583911f537483a: function() { + const ret = typeof self === 'undefined' ? null : self; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_static_accessor_WINDOW_d7f903d1508cbdc4: function() { + const ret = typeof window === 'undefined' ? null : window; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_textContent_acd42c63564a445e: function(arg0, arg1) { + const ret = arg1.textContent; + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }, + __wbindgen_generic_0000000000000001: function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 86, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke___web_sys_aad0de371f77e982___features__gen_Event__Event______true_); + return ret; + }, + __wbindgen_generic_0000000000000002: function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 88, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke_______true_); + return ret; + }, + __wbindgen_generic_0000000000000003: function(arg0, arg1) { + // Cast intrinsic for `Ref(String) -> Externref`. + const ret = getStringFromWasm0(arg0, arg1); + return ret; + }, + __wbindgen_init_externref_table: function() { + const table = wasm.__wbindgen_externrefs; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + }, + }; + return { + __proto__: null, + "./terraphim-editor_bg.js": import0, + }; +} + +function wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke_______true_(arg0, arg1) { + wasm.wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke_______true_(arg0, arg1); +} + +function wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke___web_sys_aad0de371f77e982___features__gen_Event__Event______true_(arg0, arg1, arg2) { + wasm.wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke___web_sys_aad0de371f77e982___features__gen_Event__Event______true_(arg0, arg1, arg2); +} + +const TerraphimHostSessionFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_terraphimhostsession_free(ptr, 1)); + +function addToExternrefTable0(obj) { + const idx = wasm.__externref_table_alloc(); + wasm.__wbindgen_externrefs.set(idx, obj); + return idx; +} + +const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(state => wasm.__wbindgen_destroy_closure(state.a, state.b)); + +function getArrayU8FromWasm0(ptr, len) { + ptr = ptr >>> 0; + return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len); +} + +let cachedDataViewMemory0 = null; +function getDataViewMemory0() { + if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { + cachedDataViewMemory0 = new DataView(wasm.memory.buffer); + } + return cachedDataViewMemory0; +} + +function getStringFromWasm0(ptr, len) { + return decodeText(ptr >>> 0, len); +} + +let cachedUint8ArrayMemory0 = null; +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + const idx = addToExternrefTable0(e); + wasm.__wbindgen_exn_store(idx); + } +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +function makeMutClosure(arg0, arg1, f) { + const state = { a: arg0, b: arg1, cnt: 1 }; + const real = (...args) => { + + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try { + return f(a, state.b, ...args); + } finally { + state.a = a; + real._wbg_cb_unref(); + } + }; + real._wbg_cb_unref = () => { + if (--state.cnt === 0) { + wasm.__wbindgen_destroy_closure(state.a, state.b); + state.a = 0; + CLOSURE_DTORS.unregister(state); + } + }; + CLOSURE_DTORS.register(real, state, state); + return real; +} + +function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = cachedTextEncoder.encodeInto(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function takeFromExternrefTable0(idx) { + const value = wasm.__wbindgen_externrefs.get(idx); + wasm.__externref_table_dealloc(idx); + return value; +} + +let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); +cachedTextDecoder.decode(); +const MAX_SAFARI_DECODE_BYTES = 2146435072; +let numBytesDecoded = 0; +function decodeText(ptr, len) { + numBytesDecoded += len; + if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) { + cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + cachedTextDecoder.decode(); + numBytesDecoded = len; + } + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + +const cachedTextEncoder = new TextEncoder(); + +if (!('encodeInto' in cachedTextEncoder)) { + cachedTextEncoder.encodeInto = function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; + }; +} + +let WASM_VECTOR_LEN = 0; + +let wasmModule, wasmInstance, wasm; +function __wbg_finalize_init(instance, module) { + wasmInstance = instance; + wasm = instance.exports; + wasmModule = module; + cachedDataViewMemory0 = null; + cachedUint8ArrayMemory0 = null; + wasm.__wbindgen_start(); + return wasm; +} + +async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (!module.ok) { + throw new Error(`failed to fetch Wasm: ${module.status} ${module.statusText} fetching '${module.url}'`); + } + + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + } catch (e) { + const validResponse = expectedResponseType(module.type); + + if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { throw e; } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + } else { + return instance; + } + } + + function expectedResponseType(type) { + switch (type) { + case 'basic': case 'cors': case 'default': return true; + } + return false; + } +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + + if (module !== undefined) { + if (Object.getPrototypeOf(module) === Object.prototype) { + ({module} = module) + } else { + console.warn('using deprecated parameters for `initSync()`; pass a single object instead') + } + } + + const imports = __wbg_get_imports(); + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + const instance = new WebAssembly.Instance(module, imports); + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(module_or_path) { + if (wasm !== undefined) return wasm; + + + if (module_or_path !== undefined) { + if (Object.getPrototypeOf(module_or_path) === Object.prototype) { + ({module_or_path} = module_or_path) + } else { + console.warn('using deprecated parameters for the initialization function; pass a single object instead') + } + } + + if (module_or_path === undefined) { + module_or_path = new URL('terraphim-editor_bg.wasm', import.meta.url); + } + const imports = __wbg_get_imports(); + + if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { + module_or_path = fetch(module_or_path); + } + + const { instance, module } = await __wbg_load(await module_or_path, imports); + + return __wbg_finalize_init(instance, module); +} + +export { initSync, __wbg_init as default }; diff --git a/static/editor/terraphim-editor-257aa4f4cf8c0c34_bg.wasm b/static/editor/terraphim-editor-257aa4f4cf8c0c34_bg.wasm new file mode 100644 index 0000000..25f9c9c Binary files /dev/null and b/static/editor/terraphim-editor-257aa4f4cf8c0c34_bg.wasm differ diff --git a/static/editor/terraphim-editor.min.css b/static/editor/terraphim-editor.min.css new file mode 100644 index 0000000..67c33f5 --- /dev/null +++ b/static/editor/terraphim-editor.min.css @@ -0,0 +1 @@ +.terraphim-editor-container{height:100%;min-height:20rem;box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}.te-app .editor-container,.te-app sl-split-panel{height:100%}.te-app .markdown-input{width:100%;height:calc(100% - 50px);border:none;padding:1rem;resize:none;font-family:monospace;outline:0}.te-app .markdown-preview{height:100%;padding:1rem;overflow-y:auto;box-sizing:border-box}.command-menu{position:absolute;background:#fff;border:1px solid var(--sl-color-neutral-200);border-radius:var(--sl-border-radius-medium);box-shadow:var(--sl-shadow-medium);width:200px;max-height:300px;overflow-y:auto;z-index:1000}.command-list{padding:.5rem}.command-item{display:flex;align-items:center;gap:.5rem;padding:.5rem;cursor:pointer;border-radius:var(--sl-border-radius-small)}.command-item.selected,.command-item:hover{background:var(--sl-color-primary-100)}.command-item sl-icon{color:var(--sl-color-neutral-600)}:root{--te-color-bg:#0a0d1c;--te-color-text:#e8d9c4;--te-color-text-dim:#8f8781;--te-color-accent:#8c86e6;--te-color-accent-soft:rgba(140, 134, 230, 0.4);--te-color-panel:#11142a;--te-color-panel-border:#262a4a;--te-color-popover:#171a2e;--te-color-popover-hover:#1f2340;--te-radius-popover:8px;--te-font-mono:"JetBrains Mono","IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;--te-font-display:"Caveat","Segoe Script","Bradley Hand","Brush Script MT",cursive;--te-font-size-body:1.0625rem;--te-line-height:1.75;--te-ghost-opacity:0.1;--te-measure:70ch;--te-page-margin-top:clamp(3rem, 12vh, 8rem)}.te-app.write-on,.te-app:not([data-mode=write-on]):not([data-tools=on]),.te-app[data-mode=write-on],body[data-mode=write-on]{min-height:100vh;background-color:var(--te-color-bg);color:var(--te-color-text);font-family:var(--te-font-mono);font-size:var(--te-font-size-body);line-height:var(--te-line-height);color-scheme:dark}.te-app.write-on .markdown-preview,.te-app.write-on .te-surface,.te-app:not([data-mode=write-on]):not([data-tools=on]) .te-surface,.te-app[data-mode=write-on] .markdown-preview,.te-app[data-mode=write-on] .te-surface{box-sizing:content-box;max-width:var(--te-measure);margin-inline:auto;padding-top:var(--te-page-margin-top);background-color:transparent;color:inherit;font-family:var(--te-font-mono);line-height:var(--te-line-height);caret-color:var(--te-color-accent)}.te-app.write-on ::selection,.te-app:not([data-mode=write-on]):not([data-tools=on]) ::selection,.te-app[data-mode=write-on] ::selection{background-color:var(--te-color-accent-soft)}.te-app:not([data-mode=write-on]):not([data-tools=on]) .editor-container [slot=end],.te-app:not([data-mode=write-on]):not([data-tools=on]) .toolbar{display:none}.te-app:not([data-mode=write-on]):not([data-tools=on]) sl-split-panel{display:block!important;height:auto}.te-app:not([data-mode=write-on]):not([data-tools=on]) sl-split-panel::part(divider){display:none}#app.te-app:not([data-mode=write-on]):not([data-tools=on]),#app.te-app[data-mode=write-on],.te-app:not([data-mode=write-on]):not([data-tools=on]),.te-app[data-mode=write-on]{height:auto;min-height:100vh;padding:0}.te-app[data-mode=write-on] .editor-container [slot=end],.te-app[data-mode=write-on] .toolbar{display:none}.te-app[data-mode=write-on] sl-split-panel{display:block!important;height:auto}.te-app[data-mode=write-on] sl-split-panel::part(divider){display:none}.te-app:not([data-mode=write-on]):not([data-tools=on]) .markdown-input.te-surface,.te-app[data-mode=write-on] .markdown-input.te-surface{width:auto;height:auto;min-height:calc(100vh - var(--te-page-margin-top) - 6rem);padding-inline:1.5rem;padding-bottom:6rem;overflow:visible;outline:0;font-size:var(--te-font-size-body)}.te-app:not([data-mode=write-on]):not([data-tools=on]) .markdown-input.te-surface:empty::before,.te-app[data-mode=write-on] .markdown-input.te-surface:empty::before{color:var(--te-color-text-dim);opacity:1}.te-chrome [hidden]{display:none!important}:where(.te-chrome) button{margin:0;border:0;background:0 0;color:inherit;font:inherit;cursor:pointer}.te-chrome button:focus-visible{outline:2px solid var(--te-color-accent);outline-offset:2px;border-radius:4px}.te-chrome-counter{position:fixed;top:1px;left:20px;z-index:20;display:inline-flex;align-items:baseline;gap:.5ch;padding:0 .25rem;font-family:var(--te-font-mono);font-size:.75rem;line-height:18px;letter-spacing:.02em;color:var(--te-color-text-dim);white-space:nowrap;font-variant-numeric:tabular-nums}.te-chrome-counter:hover .te-chrome-counter-text{text-decoration:underline;text-decoration-color:var(--te-color-accent-soft);text-underline-offset:3px}.te-chrome-counter-arrow{visibility:hidden;color:var(--te-color-accent)}.te-app:not([data-mode=write-on]):not([data-tools=on]) .te-chrome-counter,.te-chrome-on .te-chrome-counter{top:1.25rem;left:1.5rem;font-size:.8125rem}.te-chrome-on .te-chrome-counter-arrow{visibility:visible}.te-chrome-group{position:fixed;z-index:21;display:flex;align-items:center;gap:1.25rem;font-family:var(--te-font-mono);font-size:.8125rem;line-height:1;color:var(--te-color-text-dim)}.te-chrome-top-centre{top:1.25rem;left:50%;transform:translateX(-50%)}.te-chrome-top-right{top:1.25rem;right:1.5rem}.te-chrome-bottom-left{bottom:1.25rem;left:1.5rem}.te-chrome-bottom-centre{bottom:1.25rem;left:50%;transform:translateX(-50%)}.te-chrome-bottom-right{bottom:1.25rem;right:1.5rem}.te-chrome-control{display:inline-flex;align-items:center;justify-content:center;min-width:1.75rem;min-height:1.75rem;padding:.25rem .375rem;transition:color 120ms ease}.te-chrome-control:hover{color:var(--te-color-text)}.te-chrome-dots{color:var(--te-color-accent);letter-spacing:.15em;font-size:.625rem}.te-chrome-markdown span{padding:.125rem .3rem;border:1px solid currentColor;border-radius:3px;font-size:.75rem}.te-chrome-keyboard,.te-chrome-open{font-size:1rem}.te-chrome-save{color:var(--te-color-accent);font-size:1rem}.te-chrome-lab{padding:.3rem .9rem;border:1px dashed var(--te-color-text-dim);border-radius:999px;letter-spacing:.2em;font-size:.6875rem}.te-chrome-lab:hover{border-color:var(--te-color-accent)}.te-chrome-overflow{padding:.25rem .55rem;border:1px solid var(--te-color-panel-border);border-radius:6px;background:var(--te-color-panel);letter-spacing:.08em;font-size:.6875rem}.te-chrome-overflow[aria-expanded=true]{border-color:var(--te-color-accent);color:var(--te-color-accent)}.te-chrome-shortcuts{width:min(28rem,calc(100vw - 2rem));padding:1rem 1.25rem 1.25rem;border:1px solid var(--te-color-panel-border);border-radius:var(--te-radius-popover);background:var(--te-color-popover);color:var(--te-color-text);font-family:var(--te-font-mono);font-size:.8125rem;line-height:1.5;color-scheme:dark}.te-chrome-shortcuts::backdrop{background:rgba(10,13,28,.6)}.te-chrome-shortcuts-header{display:flex;align-items:baseline;justify-content:space-between}.te-chrome-shortcuts h2{margin:0;font-family:var(--te-font-display);font-size:1.5rem;font-weight:400}.te-chrome-shortcuts h3{margin:1rem 0 .25rem;font-size:.6875rem;font-weight:400;letter-spacing:.12em;text-transform:uppercase;color:var(--te-color-text-dim)}.te-chrome-shortcuts dl{margin:0}.te-chrome-shortcut{display:flex;justify-content:space-between;gap:1rem;padding:.3rem .5rem;margin-inline:-0.5rem;border-radius:4px}.te-chrome-shortcut:hover{background:var(--te-color-popover-hover)}.te-chrome-shortcut dd,.te-chrome-shortcut dt{margin:0}.te-chrome-shortcut kbd{font-family:inherit;color:var(--te-color-text-dim)}.te-chrome-shortcut:hover kbd{color:var(--te-color-text)}.te-chrome-shortcuts-close{padding:0 .25rem;font-size:1.25rem;color:var(--te-color-text-dim)}@media (max-width:40rem){.te-chrome-group{gap:.75rem}.te-chrome-top-centre{top:3rem}}@media (prefers-reduced-motion:reduce){.te-chrome-control{transition:none}}.te-app[data-mode=write-on] .te-warning{display:flex;align-items:flex-start;gap:.75rem;margin:0 auto 1rem;max-width:var(--te-measure,42rem);padding:.5rem .75rem;border:1px solid var(--te-color-panel-border);border-radius:var(--te-radius-popover);background:var(--te-color-panel);color:var(--te-color-text-dim);font-family:var(--te-font-mono);font-size:.8125rem;line-height:1.5}.te-app[data-mode=write-on] .te-warning[hidden]{display:none}.te-app[data-mode=write-on] .te-warning-text{flex:1}.te-app[data-mode=write-on] .te-warning-dismiss{padding:0 .25rem;border:none;background:0 0;color:var(--te-color-text-dim);font:inherit;cursor:pointer}.te-app[data-mode=write-on] .te-warning-dismiss:focus-visible,.te-app[data-mode=write-on] .te-warning-dismiss:hover{color:var(--te-color-accent)}.te-app[data-mode=write-on] .te-indicator-host{--te-ind-dot:3px;--te-ind-dot-gap:3px;--te-ind-row-offset:2px;--te-ind-gutter:1.25rem;position:relative}.te-app[data-mode=write-on] .te-surface .te-ind-sentence,.te-app[data-mode=write-on] .te-surface .te-ind-word{text-decoration-line:underline;text-decoration-style:solid;text-decoration-thickness:1px;text-decoration-color:var(--te-color-accent-soft);text-underline-offset:2px;text-decoration-skip-ink:none}.te-app:not([data-mode=write-on]):not([data-tools=on]) .te-surface .te-heading,.te-app[data-mode=write-on] .te-surface .te-heading,.te-app[data-mode=write-on] .te-surface .te-ind-headline{font-weight:700;font-size:1.15em}.te-app:not([data-mode=write-on]):not([data-tools=on]) .te-surface .te-heading-marker,.te-app[data-mode=write-on] .te-surface .te-heading-marker{font-size:0}.te-app:not([data-mode=write-on]):not([data-tools=on]) .te-surface .te-heading-marker.te-heading-marker--shown,.te-app[data-mode=write-on] .te-surface .te-heading-marker.te-heading-marker--shown{font-size:1.15em}.te-app:not([data-mode=write-on]):not([data-tools=on]) .te-surface .te-heading-marker.te-heading-marker--shown:not(.te-ghost),.te-app[data-mode=write-on] .te-surface .te-heading-marker.te-heading-marker--shown:not(.te-ghost){color:var(--te-color-text-dim)}.te-indicators{display:none}.te-app[data-mode=write-on] .te-indicators{display:block;position:absolute;inset:0;overflow:visible;pointer-events:none;z-index:1}.te-app[data-mode=write-on] .te-indicators [hidden]{display:none}.te-app[data-mode=write-on] .te-ind-row{position:absolute;margin-top:var(--te-ind-row-offset)}.te-app[data-mode=write-on] .te-ind-row--centre{transform:translateX(-50%)}.te-app[data-mode=write-on] .te-ind-row--end{transform:translateX(-100%)}.te-app[data-mode=write-on] .te-ind-col,.te-app[data-mode=write-on] .te-ind-dots{display:flex;gap:var(--te-ind-dot-gap)}.te-app[data-mode=write-on] .te-ind-col{flex-direction:column}.te-app[data-mode=write-on] .te-ind-dot{display:block;flex:none;width:var(--te-ind-dot);height:var(--te-ind-dot);border-radius:50%;background:var(--te-color-accent-soft);pointer-events:auto;cursor:default}.te-app[data-mode=write-on] .te-ind-dot--active{background:var(--te-color-accent)}.te-app[data-mode=write-on] .te-ind-para{position:absolute;width:0}.te-app[data-mode=write-on] .te-ind-rule{position:absolute;top:0;right:var(--te-ind-gutter);width:1px;height:100%;background:var(--te-color-accent-soft)}.te-app[data-mode=write-on] .te-ind-para .te-ind-col{position:absolute;top:50%;transform:translateY(-50%);right:calc(var(--te-ind-gutter) + 1px + var(--te-ind-dot-gap))}.te-ind-descriptions{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}.te-ghost{color:color-mix(in srgb,currentColor calc(var(--te-ghost-opacity) * 100%),transparent)}@supports not (color:color-mix(in srgb,red 50%,transparent)){.te-ghost{opacity:var(--te-ghost-opacity)}}.te-selection-menu{position:fixed;z-index:1000;box-sizing:border-box;min-width:18rem;max-width:calc(100vw - 16px);margin:0;padding:.375rem;background-color:var(--te-color-popover);border:1px solid var(--te-color-panel-border);border-radius:var(--te-radius-popover);box-shadow:0 12px 32px rgba(0,0,0,.45);color:var(--te-color-text);font-family:var(--te-font-mono);font-size:.8125rem;line-height:1.4;color-scheme:dark;outline:0;user-select:none}.te-selection-menu-item{display:flex;align-items:baseline;justify-content:space-between;gap:2.5rem;padding:.4375rem .625rem;border-radius:calc(var(--te-radius-popover) - 3px);cursor:pointer;outline:0;white-space:nowrap}.te-selection-menu-item--active{background-color:var(--te-color-popover-hover)}.te-selection-menu-item:focus-visible{box-shadow:inset 0 0 0 1px var(--te-color-accent)}.te-selection-menu-key{color:var(--te-color-text-dim);font-variant-numeric:tabular-nums}:root{--te-lab-amber:#d9a05b;--te-lab-rose:#e8778e;--te-lab-teal:#6cc4b0;--te-lab-wash-weak:rgba(140, 134, 230, 0.2);--te-lab-wash-long:rgba(217, 160, 91, 0.12);--te-lab-wash-convoluted:rgba(108, 196, 176, 0.12);--te-lab-wash-punctuation:rgba(232, 119, 142, 0.3);--te-lab-popover-width:min(32rem, calc(100vw - 2rem))}.te-lab-mark{-webkit-box-decoration-break:clone;box-decoration-break:clone;border-radius:2px;text-underline-offset:0.22em}.te-lab-mark--weak-sentence{background-color:var(--te-lab-wash-weak)}.te-lab-mark--long-sentence{background-color:var(--te-lab-wash-long);text-decoration:underline solid var(--te-lab-amber) 1px}.te-lab-mark--convoluted-sentence{background-color:var(--te-lab-wash-convoluted);text-decoration:underline double var(--te-lab-teal) 1px}.te-lab-mark--typo{text-decoration:underline wavy var(--te-lab-rose) 1px;text-decoration-skip-ink:none}.te-lab-mark--punctuation{background-color:var(--te-lab-wash-punctuation);box-shadow:inset 0 -1px 0 var(--te-lab-rose)}.te-lab-mark--off-tone{text-decoration:underline dotted var(--te-lab-amber) 2px}.te-lab-mark--hedge{text-decoration:underline dashed var(--te-lab-teal) 1px}.te-lab-mark--filler{text-decoration:underline dashed var(--te-color-text-dim) 1px}.te-lab{position:fixed;left:50%;bottom:4rem;z-index:30;box-sizing:border-box;width:var(--te-lab-popover-width);max-height:calc(100vh - 6rem);overflow-y:auto;transform:translateX(-50%);padding:.875rem .5rem .625rem;border:1px solid var(--te-color-panel-border);border-radius:var(--te-radius-popover);background:var(--te-color-popover);box-shadow:0 18px 48px rgba(0,0,0,.55);color:var(--te-color-text);font-family:var(--te-font-mono);font-size:.8125rem;line-height:1.5;text-align:left}.te-lab-chip[hidden],.te-lab-note[hidden],.te-lab-proposals[hidden],.te-lab-results[hidden],.te-lab-trim[hidden],.te-lab[hidden]{display:none}@media (prefers-reduced-motion:no-preference){.te-lab:not([hidden]){animation:te-lab-rise 140ms ease-out}}@keyframes te-lab-rise{from{opacity:0;transform:translate(-50%,6px)}to{opacity:1;transform:translate(-50%,0)}}.te-lab-header{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;padding:0 .625rem .5rem}.te-lab-title{margin:0;font-family:var(--te-font-display);font-size:1.375rem;font-weight:400;line-height:1.1;color:var(--te-color-accent)}.te-lab-subtitle{color:var(--te-color-text-dim);font-size:.75rem}.te-lab-actions{display:flex;flex-direction:column}.te-lab button{margin:0;border:0;background:0 0;color:inherit;font:inherit;cursor:pointer}.te-lab button:focus-visible{outline:2px solid var(--te-color-accent);outline-offset:-2px;border-radius:6px}.te-lab .te-lab-action{display:grid;grid-template-columns:1fr auto;align-items:center;gap:.625rem;width:100%;padding:.45rem .625rem;border-radius:6px;text-align:left}.te-lab .te-lab-action:focus-visible,.te-lab .te-lab-action:hover{background:var(--te-color-popover-hover)}.te-lab-action[aria-checked=true]{color:var(--te-color-accent)}.te-lab-action-count{min-width:1.5rem;color:var(--te-color-text-dim);font-variant-numeric:tabular-nums;text-align:right}.te-lab-hint{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}.te-lab-trim{display:flex;flex-wrap:wrap;gap:.5rem;margin:.5rem .625rem 0}.te-lab-results{margin:.625rem .625rem 0;padding-top:.625rem;border-top:1px solid var(--te-color-panel-border)}.te-lab-legend{display:flex;flex-wrap:wrap;gap:.25rem 1rem;color:var(--te-color-text)}.te-lab-legend-entry{display:inline-flex;align-items:baseline;gap:.5ch}.te-lab-swatch{padding:0 .15em;color:var(--te-color-text-dim)}.te-lab-proposals{margin:.5rem 0 0;padding:0;max-height:9.5rem;overflow-y:auto;list-style:none}.te-lab-proposal{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr) auto;align-items:center;gap:.625rem;padding:.2rem 0}.te-lab-from{overflow:hidden;color:var(--te-lab-rose);text-decoration:line-through;text-decoration-color:rgba(232,119,142,0.6);text-overflow:ellipsis;white-space:pre}.te-lab-to{overflow:hidden;color:var(--te-color-text);text-overflow:ellipsis;white-space:pre}.te-lab-arrow{color:var(--te-color-text-dim);font-size:.75rem}.te-lab .te-lab-accept{padding:.15rem .6rem;border:1px solid var(--te-color-accent-soft);border-radius:999px;color:var(--te-color-accent);font-size:.75rem}.te-lab .te-lab-accept:hover{border-color:var(--te-color-accent)}.te-lab .te-lab-clear{display:inline-flex;align-items:center;gap:.5ch;margin-top:.5rem;padding:.25rem 0;color:var(--te-color-text-dim);font-size:.75rem}.te-lab .te-lab-clear:hover{color:var(--te-color-text)}.te-lab-note{margin:.5rem .625rem 0;color:var(--te-lab-rose);font-size:.75rem}.te-lab-chip{position:fixed;z-index:20;display:inline-flex;align-items:center;gap:.75rem;transform:translateY(-50%);color:var(--te-color-text-dim);font-family:var(--te-font-mono);font-size:.6875rem;line-height:1;white-space:nowrap}.te-lab-chip .te-lab-chip-clear{margin:0;padding:.2rem 0;border:0;background:0 0;color:var(--te-color-accent);font:inherit;cursor:pointer}.te-lab-chip .te-lab-chip-clear:hover{text-decoration:underline;text-underline-offset:3px}.te-lab-chip .te-lab-chip-clear:focus-visible{outline:2px solid var(--te-color-accent);outline-offset:2px;border-radius:4px}.te-lab-descriptions{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}@media (max-width:640px){.te-lab-subtitle{display:none}.te-lab-chip{display:none}}.te-trim-faded{color:color-mix(in srgb,currentColor calc(var(--te-ghost-opacity) * 100%),transparent)}@supports not (color:color-mix(in srgb,red 50%,transparent)){.te-trim-faded{opacity:var(--te-ghost-opacity)}}[data-te-trimming] .te-trim-faded{cursor:pointer}[data-te-trimming] .te-trim-faded:hover{text-decoration:underline dotted var(--te-color-accent-soft) 1px;text-underline-offset:0.22em}.te-trim-current{border-radius:2px;box-shadow:0 0 0 1px var(--te-color-accent);-webkit-box-decoration-break:clone;box-decoration-break:clone}.te-lab-trim{align-items:center;padding-top:.625rem;border-top:1px solid var(--te-color-panel-border)}.te-trim-label{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}.te-trim-levels{display:flex;flex:1 1 100%;flex-wrap:nowrap;gap:.25rem;min-width:0}@media (max-width:34rem){.te-trim-levels{flex-wrap:wrap}}.te-lab .te-trim-level{display:inline-flex;flex:1 1 0;flex-direction:column;align-items:center;gap:.125rem;min-width:0;padding:.375rem .125rem;border:1px solid var(--te-color-panel-border);border-radius:calc(var(--te-radius-popover) - 2px);color:var(--te-color-text);font-size:.6875rem;white-space:nowrap}.te-lab .te-trim-level:hover{background:var(--te-color-popover-hover)}.te-lab .te-trim-level[aria-checked=true]{border-color:var(--te-color-accent);color:var(--te-color-accent)}.te-trim-level-target{color:var(--te-color-text-dim);font-size:.625rem;font-variant-numeric:tabular-nums}.te-trim-card{position:fixed;left:50%;bottom:4rem;z-index:30;box-sizing:border-box;width:min(28rem,calc(100vw - 2rem));padding:.875rem 1rem .75rem;transform:translateX(-50%);border:1px solid var(--te-color-panel-border);border-radius:var(--te-radius-popover);background:var(--te-color-popover);box-shadow:0 18px 48px rgba(0,0,0,.55);color:var(--te-color-text);font-family:var(--te-font-mono);font-size:.8125rem;line-height:1.5;text-align:left;outline:0}.te-trim-card[hidden],.te-trim-walk[hidden]{display:none}.te-trim-card:focus-visible{box-shadow:0 0 0 2px var(--te-color-accent),0 18px 48px rgba(0,0,0,.55)}@media (prefers-reduced-motion:no-preference){.te-trim-card:not([hidden]){animation:te-trim-rise 160ms ease-out}}@keyframes te-trim-rise{from{opacity:0;transform:translate(-50%,6px)}to{opacity:1;transform:translate(-50%,0)}}.te-trim-card-head{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:.25rem 1rem}.te-trim-card-level{font-weight:700}.te-trim-card-count{color:var(--te-color-accent);font-variant-numeric:tabular-nums}.te-trim-meter{position:relative;height:3px;margin:.5rem 0 .625rem;border-radius:2px;background:color-mix(in srgb,var(--te-color-text) calc(var(--te-ghost-opacity) * 100%),transparent);overflow:hidden}.te-trim-meter-keep{position:absolute;inset:0 auto 0 0;width:100%;background:var(--te-color-text)}@media (prefers-reduced-motion:no-preference){.te-trim-meter-keep{transition:width 220ms ease-out}}.te-trim-card-hint,.te-trim-walk-text{margin:0 0 .625rem;color:var(--te-color-text-dim);font-size:.75rem}.te-trim-walk-text{color:var(--te-color-text);overflow-wrap:anywhere}.te-trim-actions,.te-trim-walk-buttons{display:flex;flex-wrap:wrap;gap:.375rem}.te-trim-walk-buttons{margin-bottom:.625rem}.te-trim-card button{display:inline-flex;align-items:center;gap:.5ch;margin:0;padding:.25rem .75rem;border:1px solid var(--te-color-panel-border);border-radius:999px;background:0 0;color:var(--te-color-text);font:inherit;font-size:.75rem;cursor:pointer}.te-trim-card button i{color:var(--te-color-text-dim);font-size:.6875rem}.te-trim-card button:hover:not(:disabled){background:var(--te-color-popover-hover)}.te-trim-card button:focus-visible{outline:2px solid var(--te-color-accent);outline-offset:1px}.te-trim-card button:disabled{color:var(--te-color-text-dim);cursor:default}.te-trim-card .te-trim-cut{border-color:var(--te-color-accent);color:var(--te-color-accent)}.te-trim-card .te-trim-cut i{color:var(--te-color-accent)}.te-trim-card .te-trim-done{margin-left:auto}.te-trim-live{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}.markdown-input[hidden],.te-blocks[hidden]{display:none!important}.te-blocks{box-sizing:border-box;height:calc(100% - 50px);overflow-y:auto;padding:1rem;background:var(--sl-color-neutral-50,#fafafa)}.te-blocks-list{display:flex;flex-direction:column;gap:.5rem}.te-block{position:relative;padding:.5rem .75rem .75rem;border:1px solid var(--sl-color-neutral-200,#e4e4e7);border-left:3px solid var(--sl-color-neutral-300,#d4d4d8);border-radius:var(--te-radius-popover);background:var(--sl-color-neutral-0,#fff);cursor:default;transition:border-color .15s ease,box-shadow .15s ease}.te-block:hover{border-left-color:var(--te-color-accent-soft)}.te-block:focus{outline:2px solid var(--te-color-accent);outline-offset:2px;border-left-color:var(--te-color-accent)}.te-block-editing{border-left-color:var(--te-color-accent);box-shadow:0 0 0 1px var(--te-color-accent-soft)}.te-block-head{display:flex;align-items:center;justify-content:space-between;gap:.5rem;min-height:1.5rem}.te-block-type{font-family:var(--te-font-mono);font-size:.6875rem;letter-spacing:.06em;text-transform:uppercase;color:var(--te-color-text-dim)}.te-block-actions{display:inline-flex;gap:.125rem;opacity:0;transition:opacity .15s ease}.te-block:focus .te-block-actions,.te-block:focus-within .te-block-actions,.te-block:hover .te-block-actions{opacity:1}.te-block-action,.te-blocks-add{font:inherit;border:1px solid transparent;border-radius:4px;background:0 0;color:var(--sl-color-neutral-600,#52525b);cursor:pointer}.te-block-action{width:1.75rem;height:1.5rem;font-size:.75rem}.te-block-action:hover,.te-blocks-add:hover{background:var(--sl-color-neutral-100,#f4f4f5);color:var(--te-color-accent)}.te-block-action:focus-visible,.te-blocks-add:focus-visible{outline:2px solid var(--te-color-accent);outline-offset:1px}.te-block-body>:first-child{margin-top:.25rem}.te-block-body>:last-child{margin-bottom:0}.te-block-body pre,.te-block-source{overflow-x:auto;font-family:var(--te-font-mono);white-space:pre-wrap}.te-block-body table{border-collapse:collapse}.te-block-body td,.te-block-body th{padding:.25rem .5rem;border:1px solid var(--sl-color-neutral-200,#e4e4e7)}.te-block-code{border-left-color:var(--sl-color-neutral-500,#71717a)}.te-block-heading{border-left-color:var(--te-color-accent-soft)}.te-block-editor{box-sizing:border-box;display:block;width:100%;margin-top:.25rem;padding:.5rem;border:1px solid var(--te-color-accent-soft);border-radius:4px;font-family:var(--te-font-mono);font-size:.875rem;line-height:1.5;resize:vertical;caret-color:var(--te-color-accent)}.te-block-editor:focus{outline:2px solid var(--te-color-accent);outline-offset:0}.te-blocks-add{margin-top:.75rem;padding:.375rem .75rem;border-color:var(--sl-color-neutral-300,#d4d4d8);border-style:dashed}.te-blocks-empty{color:var(--te-color-text-dim)}.te-blocks-drafts[hidden]{display:none!important}.te-blocks-drafts{display:flex;flex-direction:column;gap:.5rem;margin:.5rem 1rem 0}.te-blocks-drafts:focus-visible{outline:2px solid var(--te-color-accent);outline-offset:2px}.te-blocks-draft{padding:.5rem .75rem;border:1px solid var(--te-color-accent-soft);border-left:3px solid var(--te-color-accent);border-radius:var(--te-radius-popover);background:var(--sl-color-neutral-0,#fff)}.te-blocks-draft-message{margin:0 0 .25rem;font-size:.875rem}.te-blocks-draft-message i{color:var(--te-color-accent)}.te-blocks-draft-text{max-height:8rem;margin:0 0 .5rem;padding:.375rem .5rem;overflow:auto;font-family:var(--te-font-mono);font-size:.8125rem;white-space:pre-wrap;background:var(--sl-color-neutral-50,#fafafa);border-radius:4px}.te-blocks-draft-actions{display:flex;gap:.5rem}.te-blocks-draft-actions button{font:inherit;font-size:.8125rem;padding:.25rem .625rem;border:1px solid var(--sl-color-neutral-300,#d4d4d8);border-radius:4px;background:0 0;cursor:pointer}.te-blocks-draft-apply{border-color:var(--te-color-accent)!important;color:var(--te-color-accent)}.te-blocks-draft-actions button:focus-visible{outline:2px solid var(--te-color-accent);outline-offset:1px}.te-block-action[hidden]{display:none}.te-blocks-notice[hidden]{display:none!important}.te-blocks-notice{display:flex;align-items:flex-start;gap:.5rem;margin:0 0 .75rem;padding:.5rem .75rem;border:1px solid var(--te-color-accent-soft);border-left:3px solid var(--te-color-accent);border-radius:var(--te-radius-popover);background:var(--sl-color-neutral-0,#fff)}.te-blocks-notice-text{flex:1;margin:0;font-size:.875rem}.te-blocks-notice-dismiss{font:inherit;font-size:.75rem;width:1.75rem;height:1.5rem;border:1px solid transparent;border-radius:4px;background:0 0;color:var(--sl-color-neutral-600,#52525b);cursor:pointer}.te-blocks-notice-dismiss:hover{background:var(--sl-color-neutral-100,#f4f4f5);color:var(--te-color-accent)}.te-blocks-notice-dismiss:focus-visible{outline:2px solid var(--te-color-accent);outline-offset:1px}.te-app[data-mode=write-on] .te-blocks,.te-app[data-mode=write-on] .te-view-toggle{display:none!important}@media (prefers-reduced-motion:reduce){.te-block,.te-block-actions{transition:none}}:root{--te-alt-panel-width:clamp(12rem, 18vw, 20rem);--te-alt-dim-opacity:0.7}.te-alt-panel[hidden]{display:none!important}.te-alt-panel{position:fixed;inset:0 auto 0 0;z-index:20;box-sizing:border-box;width:var(--te-alt-panel-width);display:flex;flex-direction:column;padding:max(1.5rem,calc(var(--te-page-margin-top) - 2.5rem)) 1.25rem 4.25rem;container-type:inline-size;background-color:var(--te-color-panel);border-right:1px solid var(--te-color-panel-border);color:var(--te-color-text);font-family:var(--te-font-mono);font-size:.9375rem;line-height:var(--te-line-height);color-scheme:dark;overflow-y:auto}.te-alt-tabs{display:flex;align-items:baseline;gap:.5rem;flex-wrap:nowrap;white-space:nowrap;margin-bottom:1.25rem}.te-alt-tab{margin:0;padding:0 .125rem;border:0;border-radius:4px;background:0 0;color:var(--te-color-text);font-family:var(--te-font-display);font-size:min(1.3125rem, 7cqi);line-height:1.2;cursor:pointer}.te-alt-tab:hover{color:var(--te-color-accent)}.te-alt-tab.te-alt-tab--active,.te-alt-tab[aria-selected=true]{color:var(--te-color-accent)}.te-alt-delete:focus-visible,.te-alt-tab:focus-visible{outline:2px solid var(--te-color-accent);outline-offset:2px}.te-alt-status{margin:0 0 .75rem;color:var(--te-color-text-dim);font-size:.8125rem}.te-alt-status.te-alt-status--quiet{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}.te-alt-tabpanel{display:flex;flex:1 1 auto;flex-direction:column;min-height:0}.te-alt-hint{margin:auto 0 0;padding-top:1rem;color:var(--te-color-text-dim);font-size:.75rem}.te-alt-list{margin:0;padding:0;list-style:none}.te-alt-list[hidden]{display:none}.te-alt-line{display:grid;grid-template-columns:1.25rem 1fr auto;align-items:center;column-gap:.25rem;border-radius:4px}.te-alt-glyph{justify-self:center;color:var(--te-color-text-dim)}.te-alt-glyph--human{font-size:.3125rem}.te-alt-glyph--ai{font-size:.75rem}.te-alt-glyph--new{font-size:.625rem;opacity:.6}.te-alt-input{box-sizing:border-box;width:100%;min-width:0;margin:0;padding:.125rem .25rem;border:0;border-bottom:1px solid transparent;background:0 0;color:var(--te-color-text-dim);font:inherit;caret-color:var(--te-color-accent)}.te-alt-input:focus{outline:0;border-bottom-color:var(--te-color-accent-soft)}.te-alt-input:focus-visible{border-bottom-color:var(--te-color-accent)}.te-alt-input[readonly]{cursor:default}.te-alt-input::placeholder{color:var(--te-color-text-dim);opacity:.7}.te-alt-line--active .te-alt-input{color:var(--te-color-text);font-weight:700}.te-alt-line--active .te-alt-glyph{color:var(--te-color-accent)}.te-alt-line:hover:not(.te-alt-line--new){background-color:var(--te-color-popover-hover)}.te-alt-delete{margin:0;padding:0 .375rem;border:0;border-radius:4px;background:0 0;color:var(--te-color-text-dim);font-size:.75rem;cursor:pointer;visibility:hidden}.te-alt-line:focus-within .te-alt-delete,.te-alt-line:hover .te-alt-delete{visibility:visible}.te-alt-delete:hover{color:var(--te-color-text)}.te-alt-open .markdown-input.te-surface{opacity:var(--te-alt-dim-opacity);transition:opacity 160ms ease-out}@media (min-width:48rem){.te-app[data-mode=write-on].te-alt-open .markdown-input.te-surface{margin-left:max(calc(var(--te-alt-panel-width) + 1rem),calc((100% - var(--te-measure) - 3rem)/ 2));margin-right:auto}}@media (max-width:47.99rem){.te-alt-panel{width:min(100vw,22rem)}}@media (prefers-reduced-motion:reduce){.te-alt-open .markdown-input.te-surface{transition:none}}:root{--te-overflow-width:clamp(12rem, 18vw, 20rem)}.te-overflow{position:fixed;top:0;right:0;bottom:0;z-index:18;box-sizing:border-box;display:flex;flex-direction:column;width:var(--te-overflow-width);padding:3.75rem 1.25rem 4.25rem;background-color:var(--te-color-panel);border-left:1px solid var(--te-color-panel-border);color:var(--te-color-text);font-family:var(--te-font-mono)}.te-overflow[hidden]{display:none}.te-overflow-header{display:flex;align-items:baseline;justify-content:space-between;gap:.5rem}.te-overflow-title{margin:0;color:var(--te-color-accent);font-family:var(--te-font-display);font-size:1.9rem;font-weight:400;line-height:1.1}.te-overflow-tools{display:flex;gap:.25rem}.te-overflow-button{margin:0;padding:.25rem .4rem;border:0;border-radius:4px;background:0 0;color:var(--te-color-text-dim);font:inherit;font-size:.85rem;cursor:pointer}.te-overflow-button:hover{color:var(--te-color-text);background-color:var(--te-color-popover-hover)}.te-overflow-button:focus-visible{outline:2px solid var(--te-color-accent);outline-offset:2px}.te-overflow-note{margin:.5rem 0 0;color:var(--te-color-text-dim);font-size:.75rem;line-height:1.4}.te-overflow-text{flex:1 1 auto;min-height:6rem;margin:.5rem 0 0;padding:.5rem 0;border:0;border-top:1px solid var(--te-color-panel-border);background:0 0;color:var(--te-color-text);caret-color:var(--te-color-accent);font-family:var(--te-font-mono);font-size:.875rem;line-height:var(--te-line-height);resize:none;outline:0;white-space:pre-wrap;overflow-wrap:anywhere}.te-overflow-text::placeholder{color:var(--te-color-text-dim);opacity:1}.te-overflow-text::selection{background-color:var(--te-color-accent-soft)}.te-overflow-text:focus-visible{border-top-color:var(--te-color-accent-soft)}.te-overflow-text[readonly]{color:var(--te-color-text-dim)}.te-overflow-hint{margin:.5rem 0 0;padding-top:.5rem;border-top:1px solid var(--te-color-panel-border);color:var(--te-color-text-dim);font-size:.75rem;letter-spacing:.02em}@media (min-width:60rem){#app.te-app[data-mode=write-on][data-te-overflow=open],.te-app[data-mode=write-on][data-te-overflow=open]{padding-right:calc(var(--te-overflow-width) + 2rem)}}@media (max-width:40rem){.te-overflow{left:0;width:auto;top:auto;height:50vh;padding-top:1rem;border-left:0;border-top:1px solid var(--te-color-panel-border)}}.te-chrome-save,.te-files-toolbar sl-button{position:relative}.te-chrome-save.te-dirty::after,.te-files-toolbar sl-button.te-dirty::after{content:"";position:absolute;top:.125rem;right:.125rem;width:.375rem;height:.375rem;border-radius:50%;background:var(--te-color-accent);box-shadow:0 0 0 2px var(--te-color-bg);pointer-events:none}.te-files-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.te-files-notice{position:fixed;z-index:30;left:50%;bottom:4rem;transform:translateX(-50%);display:flex;align-items:center;gap:1rem;max-width:min(var(--te-measure),calc(100vw - 2rem));padding:.625rem .875rem;border:1px solid var(--te-color-panel-border);border-radius:var(--te-radius-popover);background:var(--te-color-popover);color:var(--te-color-text);font-family:var(--te-font-mono);font-size:.8125rem;line-height:1.5;box-shadow:0 .5rem 1.5rem rgba(10,13,28,.45)}.te-files-notice[hidden]{display:none}.te-files-notice[data-kind=error] .te-files-notice-text{color:var(--te-color-text-dim)}.te-files-notice-actions{display:flex;gap:.5rem;flex-shrink:0}.te-files-button,.te-files-notice-button{padding:.25rem .625rem;border:1px solid var(--te-color-panel-border);border-radius:4px;background:0 0;color:var(--te-color-text);font:inherit;cursor:pointer}.te-files-notice-button:first-child,.te-files-primary{border-color:var(--te-color-accent);color:var(--te-color-accent)}.te-files-button:hover,.te-files-notice-button:hover{background:var(--te-color-popover-hover)}.te-files-button:focus-visible,.te-files-markdown-text:focus-visible,.te-files-notice-button:focus-visible{outline:2px solid var(--te-color-accent);outline-offset:2px}.te-files-button i{margin-right:.375rem}.te-files-dialog{width:min(26rem,calc(100vw - 2rem));padding:1rem 1.25rem 1.25rem;border:1px solid var(--te-color-panel-border);border-radius:var(--te-radius-popover);background:var(--te-color-popover);color:var(--te-color-text);font-family:var(--te-font-mono);font-size:.8125rem;line-height:1.5;color-scheme:dark}.te-files-markdown{width:min(48rem,calc(100vw - 2rem))}.te-files-dialog::backdrop{background:rgba(10,13,28,.6)}.te-files-dialog-header{display:flex;align-items:baseline;justify-content:space-between}.te-files-dialog h2{margin:0;font-family:var(--te-font-display);font-size:1.5rem;font-weight:400}.te-files-dialog-text{margin:.5rem 0 .75rem;color:var(--te-color-text-dim)}.te-files-close{padding:0 .25rem;border:none;font-size:1.25rem;color:var(--te-color-text-dim)}.te-files-markdown-text{box-sizing:border-box;display:block;width:100%;field-sizing:content;min-height:10rem;max-height:min(60vh,32rem);padding:.75rem;border:1px solid var(--te-color-panel-border);border-radius:4px;background:var(--te-color-panel);color:var(--te-color-text);font-family:var(--te-font-mono);font-size:.8125rem;line-height:var(--te-line-height);resize:vertical}.te-files-dialog-footer{display:flex;align-items:center;justify-content:flex-end;gap:.5rem;margin-top:.75rem}.te-files-status{flex:1;color:var(--te-color-text-dim)}@media (max-width:40rem){.te-files-notice{flex-direction:column;align-items:stretch;bottom:3.5rem}} \ No newline at end of file diff --git a/static/editor/terraphim-editor.min.js b/static/editor/terraphim-editor.min.js new file mode 100644 index 0000000..37b4994 --- /dev/null +++ b/static/editor/terraphim-editor.min.js @@ -0,0 +1 @@ +(()=>{"use strict";if(window.TerraphimRuntimeAssets)return;const components=["input","icon","button","button-group","tooltip","split-panel","dialog","divider","badge"];const icons=new Set(["type-bold","type-italic","code","link","type-h1","type-h2","type-h3","type-underline","question-circle"]);const styles=new Map;let selectedBase=null;let promise=null;let owned=false;function resolveBase(options={},scriptURL=document.baseURI){const base=new URL(options.assetBase||".",options.assetBase?document.baseURI:scriptURL);if(!["http:","https:"].includes(base.protocol)||base.username||base.password||base.search||base.hash){throw new TypeError("Runtime assetBase must be an HTTP(S) directory URL without credentials, query or fragment")}if(base.origin!==new URL(document.baseURI).origin&&options.allowExternalAssets!==true){throw new TypeError("Cross-origin runtime assets require allowExternalAssets: true and host CSP/CORS approval")}if(!base.pathname.endsWith("/"))base.pathname+="/";return base.href}function loadStyle(url){const href=new URL(url,document.baseURI).href;if(styles.has(href))return styles.get(href);const result=new Promise(((resolve,reject)=>{let link=[...document.querySelectorAll('link[rel="stylesheet"]')].find((item=>item.href===href));if(link&&link.sheet){resolve();return}const created=!link;if(!link){link=document.createElement("link");link.rel="stylesheet";link.href=href}const timer=setTimeout((()=>fail()),15e3);const fail=()=>{clearTimeout(timer);link.remove();reject(new Error(`Runtime stylesheet unavailable: ${href}`))};link.addEventListener("load",(()=>{clearTimeout(timer);resolve()}),{once:true});link.addEventListener("error",fail,{once:true});if(created)document.head.appendChild(link)}));styles.set(href,result);result.catch((()=>{if(styles.get(href)===result)styles.delete(href)}));return result}function iconResolver(base){return name=>typeof name==="string"&&/^[a-z0-9-]+$/.test(name)&&icons.has(name)?new URL(`assets/icons/${name}.svg`,base).href:""}function load(options={},scriptURL=document.baseURI){let base;try{base=resolveBase(options,scriptURL);if(selectedBase&&selectedBase!==base){throw new Error("Incompatible runtime assetBase: Shoelace registration is page-global")}if(!owned&&components.some((name=>customElements.get(`sl-${name}`)))){throw new Error("Host-owned Shoelace detected; use loadShoelace: false and provide all runtime styles/icons")}}catch(error){return Promise.reject(error)}if(promise)return promise;selectedBase=base;const shoelace=new URL("vendor/shoelace/2.12.0/cdn/",base).href;const fontawesome=new URL("vendor/fontawesome/6.5.2/css/",base).href;promise=(async()=>{await Promise.all([loadStyle(new URL("themes/light.css",shoelace).href),...["fontawesome","solid","regular","brands"].map((name=>loadStyle(new URL(`${name}.min.css`,fontawesome).href)))]);const basePath=await import(new URL("utilities/base-path.js",shoelace).href);basePath.setBasePath(shoelace);const library=await import(new URL("utilities/icon-library.js",shoelace).href);library.registerIconLibrary("default",{resolver:iconResolver(shoelace)});owned=true;await Promise.all(components.map((async name=>{const module=await import(new URL(`components/${name}/${name}.js`,shoelace).href);const registered=customElements.get(`sl-${name}`);if(!registered||!(registered===module.default||registered.prototype instanceof module.default)){throw new Error(`Incompatible Shoelace element sl-${name}; use loadShoelace: false for host-managed components`)}})));return{assetBase:base,shoelaceBase:shoelace}})();promise.catch((()=>{promise=null}));return promise}window.TerraphimRuntimeAssets={resolveBase:resolveBase,loadStyle:loadStyle,iconResolver:iconResolver,load:load,ready:null}})();const EditorConfig={shortcuts:[{name:"type-bold",key:"ctrl+b",prefix:"**",suffix:"**",desc:"Bold text"},{name:"type-italic",key:"ctrl+i",prefix:"_",suffix:"_",desc:"Italic text"},{name:"code",key:"ctrl+k",prefix:"`",suffix:"`",desc:"Inline code"},{name:"link",key:"ctrl+l",prefix:"[",suffix:"](url)",desc:"Create link"},{name:"type-h1",key:"ctrl+h",prefix:"# ",suffix:"",desc:"Heading"}],commands:[{name:"Heading 1",icon:"type-h1",prefix:"# ",suffix:""},{name:"Heading 2",icon:"type-h2",prefix:"## ",suffix:""},{name:"Heading 3",icon:"type-h3",prefix:"### ",suffix:""},{name:"Bold",icon:"type-bold",prefix:"**",suffix:"**"},{name:"Italic",icon:"type-italic",prefix:"_",suffix:"_"},{name:"Underline",icon:"type-underline",prefix:"",suffix:""}],initialContent:`# Welcome to Markdown Editor!\n\nThis is a simple markdown editor built with:\n- Rust\n- WebAssembly\n- Shoelace components\n\n## Try it out\n1. Edit this text on the left\n2. See the preview on the right\n3. Use **bold**, *italic*, or \`code\` formatting\n\n---\n\n> Made with ❤️ using Rust and WASM`,styles:{editorWidth:"100%",editorHeight:"100%",previewWidth:"100%",previewHeight:"100%",defaultSplitPosition:50}};if(typeof exports!=="undefined"){exports.EditorConfig=EditorConfig}else{window.EditorConfig=EditorConfig}const WRITE_ON_STORAGE_PREFIX="terraphim-editor:write-on:";const TOOLS_TOGGLE_KEY="ctrl+shift+l";const OVERFLOW_TOGGLE_KEY="ctrl+shift+d";const WRITE_ON_SELECTION_SHORTCUTS=[{key:"ctrl+shift+a",desc:"Alternatives for selection"},{key:"ctrl+shift+g",desc:"AI alternatives for selection"},{key:"ctrl+/",desc:"Ghost it / Revive"},{key:"ctrl+shift+x",desc:"Stash this in Overflow"}];function documentModelAvailable(editor){try{return!!(editor&&typeof editor.documentApi==="function"&&editor.documentApi())}catch(e){return false}}const WORD_RE=/[\p{Alphabetic}\p{N}_]+(?:['\u2019\-./][\p{Alphabetic}\p{N}_]+)*/gu;function countsFor(editor){if(documentModelAvailable(editor)&&typeof editor.counts==="function"){try{const c=editor.counts();if(c&&Number.isFinite(c.words)&&Number.isFinite(c.chars)){return{words:c.words,chars:c.chars}}}catch(e){}}const text=editor&&editor.surface?editor.surface.getText():"";const words=(text.match(WORD_RE)||[]).length;return{words:words,chars:Array.from(text).length}}function writeOnHash(text){let h=2166136261;for(let i=0;i>>0}return h.toString(16).padStart(8,"0")}function formatShortcutKey(key){return key.split("+").map((part=>part.length===1?part.toUpperCase():part.charAt(0).toUpperCase()+part.slice(1))).join("+")}class WriteOnChrome{constructor(editor,options={}){this.editor=editor;this.signal=options.signal;this.mount=options.mount||editor.input&&editor.input.closest(".editor-container")||document.body;this.destroyed=false;this.mode="plain";this.tools=null;this.ownsBodyMode=false;const root=editor.root;this.pageMode=!!(editor.config&&editor.config.standalone===true)||!root||root===document.body||root.id==="app";this.build();this.offChange=editor.surface?editor.surface.onChange((()=>this.refresh())):()=>{};this.setTools(!!(editor.config&&editor.config.tools===true),{silent:true});this.documentChanged()}build(){const listen=(el,type,fn)=>el.addEventListener(type,fn,{signal:this.signal});const root=document.createElement("div");root.className="te-chrome";this.root=root;const counter=document.createElement("button");counter.type="button";counter.className="te-chrome-counter";counter.setAttribute("aria-pressed","false");counter.title="Toggle Write_On mode";const counterText=document.createElement("span");counterText.className="te-chrome-counter-text";const arrow=document.createElement("span");arrow.className="te-chrome-counter-arrow";arrow.setAttribute("aria-hidden","true");arrow.textContent="←";counter.append(counterText,arrow);listen(counter,"click",(()=>this.toggle()));this.counter=counter;this.counterText=counterText;const corners=document.createElement("div");corners.className="te-chrome-corners";corners.hidden=true;this.corners=corners;const group=where=>{const g=document.createElement("div");g.className=`te-chrome-group te-chrome-${where}`;corners.appendChild(g);return g};const control=(parent,opts)=>{const b=document.createElement("button");b.type="button";b.className=`te-chrome-control ${opts.className}`;b.dataset.control=opts.name;b.setAttribute("aria-label",opts.label);b.title=opts.key?`${opts.label} (${formatShortcutKey(opts.key)})`:opts.label;if(opts.key)b.setAttribute("aria-keyshortcuts",formatShortcutKey(opts.key).replace("Ctrl","Control"));if(opts.icon){const i=document.createElement("i");i.className=opts.icon;i.setAttribute("aria-hidden","true");b.appendChild(i)}else{const span=document.createElement("span");span.setAttribute("aria-hidden","true");span.textContent=opts.text;b.appendChild(span)}listen(b,"click",(e=>{if(!this.isWriteOn())return;opts.onClick(e)}));parent.appendChild(b);return b};const top=group("top-centre");control(top,{name:"alternatives",className:"te-chrome-dots",text:"●●●",label:"Alternatives panel",onClick:()=>this.emit("te:open-panel",{panel:"alternatives"})});control(top,{name:"markdown",className:"te-chrome-markdown",text:"M↓",label:"Markdown view and export",onClick:()=>this.markdown()});const topRight=group("top-right");control(topRight,{name:"shortcuts",className:"te-chrome-keyboard",icon:"fa-regular fa-keyboard",label:"Keyboard shortcuts",onClick:()=>this.openShortcuts()});const bottomLeft=group("bottom-left");control(bottomLeft,{name:"save",className:"te-chrome-save",icon:"fa-solid fa-floppy-disk",label:"Save document",onClick:()=>this.save()});control(bottomLeft,{name:"open",className:"te-chrome-open",icon:"fa-regular fa-folder-open",label:"Open document",onClick:()=>this.open()});const bottomCentre=group("bottom-centre");control(bottomCentre,{name:"lab",className:"te-chrome-lab",text:"LAB",label:"Lab",onClick:()=>this.emit("te:lab")});const bottomRight=group("bottom-right");control(bottomRight,{name:"overflow",className:"te-chrome-overflow",text:"XYZ",label:"Overflow panel",key:OVERFLOW_TOGGLE_KEY,onClick:()=>this.emit("te:overflow")});root.append(counter,corners,this.buildShortcutReference(listen));this.mount.appendChild(root);const keyScope=this.editor.root||this.mount;keyScope.addEventListener("keydown",(e=>{if(e.isComposing||e.defaultPrevented)return;if(!e.ctrlKey||!e.shiftKey||e.altKey||e.metaKey)return;if((e.key||"").toLowerCase()!=="l")return;e.preventDefault();e.stopPropagation();this.toggleTools()}),{signal:this.signal,capture:true})}shortcutEntries(){const config=this.editor&&this.editor.config||{};const editorShortcuts=(config.shortcuts||[]).map((s=>({key:s.key,desc:s.desc})));return[{title:"Formatting",items:editorShortcuts},{title:"Selection",items:config.writeOnShortcuts||WRITE_ON_SELECTION_SHORTCUTS},{title:"Editor",items:this.editorShortcuts()}]}editorShortcuts(){const items=[{key:"/",desc:"Command palette"},{key:TOOLS_TOGGLE_KEY,desc:"Show or hide the toolbar and preview"},{key:OVERFLOW_TOGGLE_KEY,desc:"Show or hide the Overflow panel"}];if(typeof window.TePersistence==="function"){items.push({key:"ctrl+s",desc:"Save"},{key:"ctrl+o",desc:"Open"})}return items}buildShortcutReference(listen){const dialog=document.createElement("dialog");dialog.className="te-chrome-shortcuts";dialog.setAttribute("aria-labelledby","te-chrome-shortcuts-title");const header=document.createElement("div");header.className="te-chrome-shortcuts-header";const title=document.createElement("h2");title.id="te-chrome-shortcuts-title";title.textContent="Keyboard shortcuts";const close=document.createElement("button");close.type="button";close.className="te-chrome-shortcuts-close";close.setAttribute("aria-label","Close keyboard shortcuts");close.textContent="×";listen(close,"click",(()=>dialog.close()));header.append(title,close);dialog.appendChild(header);for(const section of this.shortcutEntries()){const h=document.createElement("h3");h.textContent=section.title;const list=document.createElement("dl");for(const item of section.items){const row=document.createElement("div");row.className="te-chrome-shortcut";row.dataset.key=item.key;const dt=document.createElement("dt");dt.textContent=item.desc;const dd=document.createElement("dd");const kbd=document.createElement("kbd");kbd.textContent=formatShortcutKey(item.key);dd.appendChild(kbd);row.append(dt,dd);list.appendChild(row)}dialog.append(h,list)}listen(dialog,"click",(e=>{if(e.target===dialog)dialog.close()}));this.shortcutsDialog=dialog;return dialog}emit(type,detail={},cancelable=false){const ev=new CustomEvent(type,{bubbles:true,cancelable:cancelable,detail:Object.assign({editor:this.editor},detail)});this.root.dispatchEvent(ev);return ev}save(){const files=this.editor&&this.editor.persistence;if(files&&!files.destroyed)return files.save();const ev=this.emit("te:save",{available:documentModelAvailable(this.editor)},true);if(ev.defaultPrevented||!documentModelAvailable(this.editor))return null;if(typeof this.editor.saveDocument!=="function")return null;try{return this.editor.saveDocument()}catch(err){console.error("saveDocument failed",err);return null}}open(){const files=this.editor&&this.editor.persistence;if(files&&!files.destroyed)return files.open();this.emit("te:open",{},true);return Promise.resolve(false)}markdown(){const files=this.editor&&this.editor.persistence;if(files&&!files.destroyed)return files.showMarkdown();this.emit("te:markdown",{},true);return null}openShortcuts(){const dialog=this.shortcutsDialog;if(!dialog.open){if(typeof dialog.showModal==="function")dialog.showModal();else dialog.setAttribute("open","")}this.emit("te:shortcuts")}refresh(){if(this.destroyed)return;const{words:words,chars:chars}=countsFor(this.editor);this.counterText.textContent=`${words} ${words===1?"word":"words"} ${chars} ${chars===1?"char":"chars"}`}documentKey(){const ed=this.editor||{};if(typeof ed.documentKey==="string"&&ed.documentKey)return ed.documentKey;const configKey=ed.config&&ed.config.documentKey;if(typeof configKey==="string"&&configKey)return configKey;return`h:${writeOnHash(this.initialText)}`}storageKey(){return WRITE_ON_STORAGE_PREFIX+this.key}documentChanged(){if(this.destroyed)return;this.initialText=this.editor.surface?this.editor.surface.getText():"";this.key=this.documentKey();let stored=null;try{stored=window.localStorage.getItem(this.storageKey())}catch(e){stored=null}this.setMode(stored==="1"?"write-on":"plain",{persist:false});this.refresh()}isWriteOn(){return this.mode==="write-on"}toggle(){this.setMode(this.isWriteOn()?"plain":"write-on")}setMode(mode,{persist:persist=true}={}){if(this.destroyed)return;const next=mode==="write-on"?"write-on":"plain";const changed=next!==this.mode;this.mode=next;const on=next==="write-on";if(on&&this.pageMode){document.body.dataset.mode="write-on";this.ownsBodyMode=true}else if(!on&&this.ownsBodyMode){delete document.body.dataset.mode;this.ownsBodyMode=false}const root=this.editor.root;if(root&&root!==document.body){if(on)root.dataset.mode="write-on";else delete root.dataset.mode}this.root.classList.toggle("te-chrome-on",on);this.corners.hidden=!on;this.counter.setAttribute("aria-pressed",on?"true":"false");if(!on&&this.shortcutsDialog.open)this.shortcutsDialog.close();if(persist){try{window.localStorage.setItem(this.storageKey(),on?"1":"0")}catch(e){}}if(changed)this.emit("te:mode-change",{mode:next})}toolsVisible(){return this.tools===true}toggleTools(){this.setTools(!this.toolsVisible())}setTools(on,{silent:silent=false}={}){if(this.destroyed)return;const next=!!on;const changed=next!==this.tools;this.tools=next;const root=this.editor.root;if(root&&root!==document.body)root.dataset.tools=next?"on":"off";if(changed&&!silent)this.emit("te:tools-change",{visible:next})}destroy(){if(this.destroyed)return;if(this.shortcutsDialog&&this.shortcutsDialog.open)this.shortcutsDialog.close();if(this.ownsBodyMode){delete document.body.dataset.mode;this.ownsBodyMode=false}const root=this.editor.root;if(root&&root!==document.body){delete root.dataset.mode;delete root.dataset.tools}this.destroyed=true;this.offChange();this.root.remove()}}window.WriteOnChrome=WriteOnChrome;window.countsFor=countsFor;window.documentModelAvailable=documentModelAvailable;window.WRITE_ON_STORAGE_PREFIX=WRITE_ON_STORAGE_PREFIX;window.TOOLS_TOGGLE_KEY=TOOLS_TOGGLE_KEY;const TE_LAYER_NAME=/^[a-z][a-z0-9-]*$/;function teHeadingMarkerLength(line){const m=/^ {0,3}#{1,6}(?:[ \t]+|$)/.exec(line);return m?m[0].length:0}class TeDecorationRegistry{constructor(surface){this.surface=surface;this.layers=new Map;this.batchDepth=0;this.pending=false;this.applyListeners=new Set;this.renderCount=0;this.offChange=surface.onChange((change=>this.mapEdit(change&&change.edit)))}mapEdit(edit){if(!edit)return;for(const[name,items]of this.layers){if(items.length)this.layers.set(name,EditorSurface.mapRanges(items,edit))}}destroy(){this.offChange()}set(name,items){if(typeof name!=="string"||!TE_LAYER_NAME.test(name)){throw new Error(`Invalid decoration layer name: ${name}`)}this.layers.set(name,(items||[]).map(((item,i)=>({id:item.id===undefined||item.id===null?String(i):String(item.id),start:item.start,end:item.end,className:item.className||"",data:item.data,attributes:EditorSurface.sanitiseAttributes(item.attributes)}))));return this.apply()}clear(name){return this.set(name,[])}get(name){return(this.layers.get(name)||[]).map((d=>({...d})))}names(){return Array.from(this.layers.keys())}owns(id){const sep=String(id).indexOf(":");return sep>0&&this.layers.has(String(id).slice(0,sep))}current(name){const prefix=`${name}:`;return this.surface.getDecorations().filter((d=>d.id.startsWith(prefix))).map((d=>({...d,id:d.id.slice(prefix.length)})))}batch(fn){this.batchDepth+=1;try{fn()}finally{this.batchDepth-=1}if(this.batchDepth===0&&this.pending)return this.apply();return false}onApply(callback){this.applyListeners.add(callback);return()=>this.applyListeners.delete(callback)}merged(){const out=this.surface.getDecorations().filter((d=>!this.owns(d.id)));for(const[name,items]of this.layers){for(const item of items)out.push({...item,id:`${name}:${item.id}`})}return out}static signature(d){return`${d.start}|${d.end}|${d.className||""}|${d.attributes?JSON.stringify(d.attributes):""}`}apply(){if(this.batchDepth>0){this.pending=true;return false}this.pending=false;const surface=this.surface;if(!surface||surface.destroyed)return false;const len=surface.getText().length;const next=this.merged().filter((d=>{const s=Math.max(0,Math.min(len,d.start|0));const e=Math.max(0,Math.min(len,d.end|0));return e>s}));const have=new Map(surface.getDecorations().map((d=>[d.id,TeDecorationRegistry.signature(d)])));const same=have.size===next.length&&next.every((d=>have.get(d.id)===TeDecorationRegistry.signature(d)));if(same)return false;surface.setDecorations(next);this.renderCount+=1;for(const listener of this.applyListeners){try{listener()}catch(err){console.error("Decoration apply listener failed",err)}}return true}}let teIndicatorInstances=0;class TeIndicatorLayer{constructor(editor,options={}){this.editor=editor;this.surface=editor.surface;this.registry=editor.decorations;this.delay=options.delay===undefined?120:options.delay;this.instance=++teIndicatorInstances;this.active=false;this.destroyed=false;this.spans=new Map;this.timer=null;this.frame=0;this.refreshCount=0;this.layoutCount=0;this.abortController=new AbortController;const signal=this.abortController.signal;if(options.signal){if(options.signal.aborted)this.abortController.abort();else options.signal.addEventListener("abort",(()=>this.destroy()),{signal:signal})}this.hoverSpan=null;this.pointer=null;this.hoverHeld=false;this.surface.root.addEventListener("mouseover",(e=>this.onPointer(e)),{signal:signal});this.surface.root.addEventListener("mousemove",(e=>this.onPointer(e)),{signal:signal,passive:true});this.surface.root.addEventListener("mouseleave",(()=>this.clearHover()),{signal:signal});window.addEventListener("blur",(()=>this.clearHover()),{signal:signal});this.surface.root.addEventListener("keydown",(e=>this.onCycleKey(e)),{signal:signal});this.offChange=this.surface.onChange((change=>this.onSurfaceChange(change)));this.ownApply=false;this.offApply=this.registry.onApply((()=>{if(!this.ownApply)this.hoverHeld=false;this.scheduleLayout()}));document.addEventListener("te:mode-change",(e=>{if(!e.detail||!e.detail.editor||e.detail.editor===this.editor){this.setActive(e.detail&&e.detail.mode==="write-on")}}),{signal:signal});const relayout=()=>{this.hoverHeld=false;this.scheduleLayout()};window.addEventListener("resize",relayout,{signal:signal});this.surface.root.addEventListener("scroll",(()=>{this.hoverHeld=false;this.scheduleLayout()}),{signal:signal,passive:true});if(typeof ResizeObserver==="function"){this.resizeObserver=new ResizeObserver(relayout)}if(typeof MutationObserver==="function"){this.rootObserver=new MutationObserver(relayout)}if(document.fonts&&document.fonts.ready){document.fonts.ready.then(relayout).catch((()=>{}))}this.setActive(this.isWriteOn())}static get LAYER(){return"indicators"}isWriteOn(){const chrome=this.editor.chrome;if(chrome&&typeof chrome.isWriteOn==="function")return chrome.isWriteOn();return document.body.dataset.mode==="write-on"}setActive(on){if(this.destroyed)return;on=!!on;if(on===this.active){if(on)this.flush();return}this.active=on;if(on){this.mountOverlay();this.flush()}else{this.cancelPending();this.clearHover();this.registry.clear(TeIndicatorLayer.LAYER);this.removeAllSpans();this.unmountOverlay()}}mountOverlay(){const host=this.surface.root.parentElement;if(!host)return;this.host=host;host.classList.add("te-indicator-host");if(!this.overlay){const overlay=document.createElement("div");overlay.className="te-indicators";overlay.setAttribute("aria-hidden","true");overlay.addEventListener("mousedown",(e=>{if(e.target.closest(".te-ind-dot"))e.preventDefault()}),{signal:this.abortController.signal});overlay.addEventListener("click",(e=>this.onDotClick(e)),{signal:this.abortController.signal});this.overlay=overlay;const descriptions=document.createElement("div");descriptions.className="te-ind-descriptions";this.descriptions=descriptions}host.appendChild(this.overlay);host.appendChild(this.descriptions);if(this.resizeObserver)this.resizeObserver.observe(this.surface.root);const layoutRoot=this.editor.root||host.closest(".te-app");if(this.rootObserver&&layoutRoot){this.rootObserver.observe(layoutRoot,{attributes:true,attributeFilter:["class","style","data-mode","data-tools","data-te-overflow"]})}}unmountOverlay(){if(this.resizeObserver)this.resizeObserver.disconnect();if(this.rootObserver)this.rootObserver.disconnect();if(this.overlay)this.overlay.remove();if(this.descriptions)this.descriptions.remove();if(this.host)this.host.classList.remove("te-indicator-host")}cancelPending(){if(this.timer!==null){clearTimeout(this.timer);this.timer=null}if(this.frame){cancelAnimationFrame(this.frame);this.frame=0}}destroy(){if(this.destroyed)return;this.cancelPending();if(this.active&&this.surface&&!this.surface.destroyed){this.registry.clear(TeIndicatorLayer.LAYER)}this.destroyed=true;this.active=false;this.offChange();this.offApply();if(this.resizeObserver)this.resizeObserver.disconnect();if(this.rootObserver)this.rootObserver.disconnect();this.abortController.abort();this.removeAllSpans();this.unmountOverlay()}onSurfaceChange(change){if(!this.active)return;const swap=change&&change.edit&&(change.edit.swap||change.edit.kg);this.hoverHeld=!!swap&&this.hoverSpan!==null&&String(swap.span)===this.hoverSpan;this.scheduleLayout();if(this.timer!==null)clearTimeout(this.timer);this.timer=setTimeout((()=>{this.timer=null;this.refresh()}),this.delay)}scheduleLayout(){if(!this.active||this.frame)return;this.frame=requestAnimationFrame((()=>{this.frame=0;this.layout()}))}flush(){if(!this.active)return;this.cancelPending();this.refresh()}pending(){return this.timer!==null}readSpans(){const editor=this.editor;if(typeof editor.documentApi!=="function"||!editor.documentApi())return[];try{const ann=editor.annotations();const spans=ann&&Array.isArray(ann.spans)?ann.spans:[];const kg=ann&&Array.isArray(ann.kg)?ann.kg:[];return spans.concat(kg).filter((s=>s&&s.anchor&&Array.isArray(s.alts)&&s.alts.length>1))}catch(err){return[]}}static isHeadline(text,start){const lineStart=text.lastIndexOf("\n",start-1)+1;const lineEnd=text.indexOf("\n",start);const marker=teHeadingMarkerLength(text.slice(lineStart,lineEnd<0?text.length:lineEnd));return marker>0&&start>=lineStart+marker}static describe(kind,count,activeIndex){const label=kind.charAt(0).toUpperCase()+kind.slice(1);return`${label}: ${count} alternatives, ${activeIndex+1} of ${count} active`}refresh(){if(!this.active||this.destroyed)return;this.refreshCount+=1;const text=this.surface.getText();const items=[];const seen=new Set;for(const span of this.readSpans()){const kind=span.kind==="paragraph"||span.kind==="sentence"?span.kind:"word";const headline=kind==="sentence"&&TeIndicatorLayer.isHeadline(text,span.anchor.start);const count=span.alts.length;const activeIndex=Math.max(0,Math.min(count-1,span.active|0));const id=String(span.id);seen.add(id);const descId=`te-ind-desc-${this.instance}-${id}`;const fromKg=span.source==="kg";const key=`${kind}|${headline}|${count}|${activeIndex}|${fromKg}`;let entry=this.spans.get(id);if(!entry||entry.key!==key){if(entry)this.removeSpanElements(entry);entry=this.buildSpan(id,kind,headline,count,activeIndex,descId,key);if(fromKg){entry.el.dataset.source="kg";entry.desc.textContent+=" (knowledge graph synonyms)"}this.spans.set(id,entry)}const classes=["te-ind",`te-ind-${kind}`];if(headline)classes.push("te-ind-headline");items.push({id:id,start:span.anchor.start,end:span.anchor.end,className:classes.join(" "),attributes:{"aria-describedby":descId}})}for(const[id,entry]of this.spans){if(!seen.has(id)){this.removeSpanElements(entry);this.spans.delete(id)}}this.ownApply=true;try{this.registry.set(TeIndicatorLayer.LAYER,items)}finally{this.ownApply=false}this.layout()}buildSpan(id,kind,headline,count,activeIndex,descId,key){const el=document.createElement("div");const dots=document.createElement("div");dots.className=kind==="paragraph"?"te-ind-col":"te-ind-dots";for(let i=0;ir.width>0&&r.height>0))}layout(){if(!this.active||this.destroyed||!this.host||this.spans.size===0)return;this.layoutCount+=1;const live=new Map(this.registry.current(TeIndicatorLayer.LAYER).map((d=>[d.id,d])));const hostRect=this.host.getBoundingClientRect();const originX=hostRect.left+this.host.clientLeft-this.host.scrollLeft;const originY=hostRect.top+this.host.clientTop-this.host.scrollTop;const root=this.surface.root;const rootRect=root.getBoundingClientRect();const contentLeft=rootRect.left+root.clientLeft+(parseFloat(getComputedStyle(root).paddingLeft)||0);for(const entry of this.spans.values()){const deco=live.get(entry.id);const rects=deco?this.rects(deco.start,deco.end):[];if(rects.length===0){entry.el.hidden=true;continue}entry.el.hidden=false;const first=rects[0];const last=rects[rects.length-1];let x;let y;if(entry.kind==="paragraph"){x=contentLeft;y=first.top;entry.el.style.height=`${Math.max(0,last.bottom-first.top)}px`}else if(entry.kind==="word"){const left=Math.min(...rects.map((r=>r.left)));const right=Math.max(...rects.map((r=>r.right)));x=(left+right)/2;y=Math.max(...rects.map((r=>r.bottom)))}else{x=last.right;y=last.bottom}entry.el.style.left=`${x-originX}px`;entry.el.style.top=`${y-originY}px`}}onDotClick(e){const dot=e.target.closest(".te-ind-dot");if(!dot||!this.overlay.contains(dot))return;const holder=dot.closest("[data-span-id]");const entry=holder&&this.spans.get(holder.dataset.spanId);if(!entry)return;const index=Number(dot.dataset.index);const active=index===entry.activeIndex;const event=new CustomEvent("te:dot",{bubbles:true,cancelable:true,detail:{editor:this.editor,spanId:entry.id,index:index,kind:entry.headline?"headline":entry.kind,active:active}});this.overlay.dispatchEvent(event);if(!event.defaultPrevented&&!active)this.jump(entry.id,index)}onPointer(e){if(!this.active)return;this.pointer={x:e.clientX,y:e.clientY};this.hoverHeld=false;this.hoverSpan=this.spanOfElement(e.target)}clearHover(){this.hoverSpan=null;this.pointer=null;this.hoverHeld=false}spanOfElement(el){const prefix=`${TeIndicatorLayer.LAYER}:`;const root=this.surface.root;let node=el&&el.closest?el.closest("[data-te-decoration]"):null;while(node&&root.contains(node)&&node!==root){const token=(node.getAttribute("data-te-decoration")||"").split(" ").find((t=>t.startsWith(prefix)));if(token)return token.slice(prefix.length);node=node.parentElement?node.parentElement.closest("[data-te-decoration]"):null}return null}spanAtPointer(){if(!this.pointer||typeof document.elementsFromPoint!=="function")return null;const root=this.surface.root;for(const el of document.elementsFromPoint(this.pointer.x,this.pointer.y)){if(el===root||!root.contains(el))continue;const id=this.spanOfElement(el);if(id!==null)return id}return null}hoveredSpan(){const id=this.hoverSpan;if(id===null||!this.spans.has(id))return null;if(this.hoverHeld||this.spanAtPointer()===id)return id;this.hoverSpan=null;this.hoverHeld=false;return null}spanAtSelection(){const sel=this.surface.getSelectionOffsets();const live=this.registry.current(TeIndicatorLayer.LAYER);const hit=live.find((d=>d.start<=sel.start&&sel.end<=d.end));return hit&&this.spans.has(hit.id)?hit.id:null}commandMenuOpen(){const menu=this.editor.commandMenu;return!!menu&&menu.isConnected&&menu.style.display!=="none"}onCycleKey(e){if(!this.active||this.destroyed||e.isComposing)return;if(e.key!=="ArrowUp"&&e.key!=="ArrowDown")return;if(e.ctrlKey||e.metaKey||e.shiftKey||this.commandMenuOpen())return;if(this.pending())this.flush();const id=e.altKey?this.spanAtSelection():this.hoveredSpan();if(id===null)return;e.preventDefault();this.cycle(id,e.key==="ArrowDown"?1:-1)}cycle(spanId,step){if(this.pending())this.flush();const entry=this.spans.get(String(spanId));if(!entry||entry.count<2)return null;const next=((entry.activeIndex+step)%entry.count+entry.count)%entry.count;return this.jump(entry.id,next)}jump(spanId,index){if(typeof this.editor.swapAlternative!=="function")return null;try{return this.editor.swapAlternative(spanId,index)}catch(err){console.warn("Could not swap the alternative",err);return null}}}class TeHeadingLayer{constructor(editor,options={}){this.editor=editor;this.surface=editor.surface;this.registry=editor.decorations;this.active=false;this.destroyed=false;this.frame=0;this.selectionFrame=0;this.headings=[];this.shown=new Set;this.abortController=new AbortController;const signal=this.abortController.signal;if(options.signal){if(options.signal.aborted)this.abortController.abort();else options.signal.addEventListener("abort",(()=>this.destroy()),{signal:signal})}this.offChange=this.surface.onChange((()=>this.schedule()));this.offApply=this.registry.onApply((()=>this.applyShown(true)));document.addEventListener("selectionchange",(()=>this.scheduleSelection()),{signal:signal});const refreshMode=e=>{if(!e.detail||!e.detail.editor||e.detail.editor===this.editor){this.setActive(this.shouldStyle())}};document.addEventListener("te:mode-change",refreshMode,{signal:signal});document.addEventListener("te:tools-change",refreshMode,{signal:signal});this.surface.root.addEventListener("compositionend",(()=>{this.setActive(this.shouldStyle())}),{signal:signal});this.setActive(this.shouldStyle())}static get LAYER(){return"headings"}static scan(text){const out=[];let fence=null;let start=0;while(start<=text.length){const nl=text.indexOf("\n",start);const end=nl<0?text.length:nl;const line=text.slice(start,end);const f=/^ {0,3}(`{3,}|~{3,})/.exec(line);if(fence){if(f&&f[1][0]===fence[0]&&f[1].length>=fence.length&&/^ {0,3}[`~]+[ \t]*$/.test(line))fence=null}else if(f&&!(f[1][0]==="`"&&line.slice(f[0].length).includes("`"))){fence=f[1]}else{const marker=teHeadingMarkerLength(line);if(marker>0)out.push({start:start,end:end,markerEnd:start+marker})}if(nl<0)break;start=nl+1}return out}isWriteOn(){const chrome=this.editor.chrome;const root=this.editor.root||this.surface.root.closest(".te-app");return chrome&&typeof chrome.isWriteOn==="function"?chrome.isWriteOn():!!root&&root.dataset.mode==="write-on"}shouldStyle(){const chrome=this.editor.chrome;const root=this.editor.root||this.surface.root.closest(".te-app");const tools=chrome&&typeof chrome.toolsVisible==="function"?chrome.toolsVisible():!!root&&root.dataset.tools==="on";return this.isWriteOn()||!tools}setActive(on){if(this.destroyed||this.surface.composing)return;on=!!on;if(on===this.active){if(on)this.flush();return}this.active=on;if(on){this.flush()}else{this.cancelPending();this.headings=[];this.shown.clear();this.registry.clear(TeHeadingLayer.LAYER)}}schedule(){if(!this.active||this.frame)return;this.frame=requestAnimationFrame((()=>{this.frame=0;this.refresh()}))}scheduleSelection(){if(!this.active||this.selectionFrame)return;this.selectionFrame=requestAnimationFrame((()=>{this.selectionFrame=0;this.applyShown(false)}))}cancelPending(){if(this.frame)cancelAnimationFrame(this.frame);if(this.selectionFrame)cancelAnimationFrame(this.selectionFrame);this.frame=0;this.selectionFrame=0}flush(){if(!this.active)return;if(this.frame)cancelAnimationFrame(this.frame);this.frame=0;this.refresh()}ranges(){return this.headings.map((h=>({...h})))}refresh(){if(!this.active||this.destroyed||this.surface.composing)return;this.headings=TeHeadingLayer.scan(this.surface.getText());const items=[];this.headings.forEach(((h,i)=>{if(h.end>h.start)items.push({id:`h${i}`,start:h.start,end:h.end,className:"te-heading"});items.push({id:`m${i}`,start:h.start,end:h.markerEnd,className:"te-heading-marker"})}));this.registry.set(TeHeadingLayer.LAYER,items);this.applyShown(true)}touched(){const out=new Set;const root=this.surface.root;const sel=document.getSelection();if(!sel||sel.rangeCount===0||!root.contains(sel.anchorNode)||!root.contains(sel.focusNode))return out;const{start:start,end:end}=this.surface.getSelectionOffsets();this.headings.forEach(((h,i)=>{if(start<=h.end&&end>=h.start)out.add(i)}));return out}applyShown(force){if(!this.active||this.destroyed)return;const next=this.touched();const same=next.size===this.shown.size&&[...next].every((i=>this.shown.has(i)));if(same&&!force)return;this.shown=next;const root=this.surface.root;for(const el of root.querySelectorAll(".te-heading-marker")){const ids=(el.getAttribute("data-te-decoration")||"").split(" ");const id=ids.find((d=>d.startsWith(`${TeHeadingLayer.LAYER}:m`)));const i=id?Number(id.slice(TeHeadingLayer.LAYER.length+2)):-1;el.classList.toggle("te-heading-marker--shown",next.has(i))}if(!same&&this.editor.indicators&&typeof this.editor.indicators.scheduleLayout==="function"){this.editor.indicators.scheduleLayout()}}destroy(){if(this.destroyed)return;this.cancelPending();if(this.active&&this.surface&&!this.surface.destroyed)this.registry.clear(TeHeadingLayer.LAYER);this.destroyed=true;this.active=false;this.offChange();this.offApply();this.abortController.abort()}}window.TeDecorationRegistry=TeDecorationRegistry;window.TeIndicatorLayer=TeIndicatorLayer;window.TeHeadingLayer=TeHeadingLayer;class TeGhostLayer{constructor(editor,options={}){this.editor=editor;this.surface=editor.surface;this.registry=editor.decorations;this.delay=options.delay===undefined?120:options.delay;this.destroyed=false;this.timer=null;this.frame=0;this.ghosts=[];this.modelGhosts=0;this.count=0;this.refreshCount=0;this.abortController=new AbortController;if(options.signal){if(options.signal.aborted)this.abortController.abort();else options.signal.addEventListener("abort",(()=>this.destroy()),{signal:this.abortController.signal})}this.offChange=this.surface.onChange((()=>this.onSurfaceChange()));this.flush()}static get LAYER(){return"ghosts"}api(){const editor=this.editor;const api=typeof editor.documentApi==="function"?editor.documentApi():null;return api&&typeof api.ghost_range==="function"&&typeof api.revive_range==="function"?api:null}available(){return!this.destroyed&&!!this.api()}onSurfaceChange(){if(this.destroyed)return;const now=this.registry.get(TeGhostLayer.LAYER).length;const dropped=now{this.frame=0;this.refresh()}))}}else if(this.modelGhosts>0&&!this.frame){if(this.timer!==null)clearTimeout(this.timer);this.timer=setTimeout((()=>{this.timer=null;this.refresh()}),this.delay)}}cancelPending(){if(this.timer!==null){clearTimeout(this.timer);this.timer=null}if(this.frame){cancelAnimationFrame(this.frame);this.frame=0}}pending(){return this.timer!==null||this.frame!==0}flush(){this.refresh()}read(){this.refresh();return this.ghosts.map((g=>({...g})))}refresh(annotations){if(this.destroyed||!this.surface||this.surface.destroyed)return;this.cancelPending();let ann=annotations;if(!ann&&this.api()){try{ann=this.editor.annotations()}catch(err){ann=null}}if(!ann&&!this.api())return;this.refreshCount+=1;const live=ann&&Array.isArray(ann.ghosts)?ann.ghosts:[];const setAside=ann&&ann.setAside&&Array.isArray(ann.setAside.ghosts)?ann.setAside.ghosts.length:0;this.ghosts=live.filter((g=>g&&g.anchor)).map((g=>({id:String(g.id),start:g.anchor.start,end:g.anchor.end})));this.modelGhosts=this.ghosts.length+setAside;this.registry.set(TeGhostLayer.LAYER,this.ghosts.map((g=>({id:g.id,start:g.start,end:g.end,className:"te-ghost"}))));this.count=this.registry.get(TeGhostLayer.LAYER).length}containing(start,end,ghosts=this.ghosts){return ghosts.find((g=>g.start<=start&&end<=g.end))||null}targetFor(sel){if(!this.available()||!sel)return null;const start=Math.min(sel.start,sel.end);const end=Math.max(sel.start,sel.end);const ghosts=this.read();if(start===end){const g=this.containing(start,end,ghosts);return g?{action:"revive",start:g.start,end:g.end,whole:true}:null}return this.containing(start,end,ghosts)?{action:"revive",start:start,end:end}:{action:"ghost",start:start,end:end}}ghost(start,end){return this.change("ghost",start,end)}revive(start,end){return this.change("revive",start,end)}apply(target){if(!target)return{ok:false,error:"Nothing to ghost or revive",kind:"empty"};return this.change(target.action,target.start,target.end)}toggle(sel=this.surface.getSelectionOffsets()){return this.apply(this.targetFor(sel))}change(action,start,end){const api=this.destroyed?null:this.api();if(!api)return{ok:false,error:"The WASM document API does not offer ghosting",kind:"unavailable"};const s=Math.max(0,Math.min(start,end)|0);const e=Math.max(0,Math.max(start,end)|0);this.editor.alignDocumentModel(api);const result=action==="ghost"?api.ghost_range(s,e):api.revive_range(s,e);if(!result||!result.ok){console.warn(`Could not ${action} ${s}..${e}:`,result&&result.error);return result||{ok:false,error:"No result",kind:"other"}}this.refresh(result.annotations);this.surface.root.dispatchEvent(new CustomEvent("te:ghosts-change",{bubbles:true,detail:{editor:this.editor,action:action,start:s,end:e,id:result.id===undefined?null:result.id,changed:result.changed===undefined?true:!!result.changed,ghosts:this.ghosts.map((g=>({...g})))}}));return result}destroy(){if(this.destroyed)return;this.cancelPending();if(this.surface&&!this.surface.destroyed)this.registry.clear(TeGhostLayer.LAYER);this.destroyed=true;this.offChange();this.abortController.abort()}}function teShortcutLabels(key){const parts=String(key).split("+").filter(Boolean);const name={ctrl:"Ctrl",shift:"Shift",alt:"Alt",meta:"Cmd"};const aria={ctrl:"Control",shift:"Shift",alt:"Alt",meta:"Meta"};const last=parts[parts.length-1]||"";const keyText=last.length===1?last.toUpperCase():last;return{text:parts.slice(0,-1).map((p=>name[p]||p)).concat(keyText).join("+"),aria:parts.slice(0,-1).map((p=>aria[p]||p)).concat(keyText).join("+")}}function teShortcutMatches(e,key){const parts=String(key).toLowerCase().split("+").filter(Boolean);const want=parts[parts.length-1];if(!want||e.isComposing)return false;const mods=new Set(parts.slice(0,-1));if(mods.has("ctrl")!==e.ctrlKey)return false;if(mods.has("meta")!==e.metaKey)return false;if(mods.has("alt")!==e.altKey)return false;const pressed=(e.key||"").toLowerCase();if(/^[a-z0-9]$/.test(want)){if(mods.has("shift")!==e.shiftKey)return false;return pressed===want||e.code===`Key${want.toUpperCase()}`||e.code===`Digit${want}`}const codes={"/":"Slash",".":"Period",",":"Comma",";":"Semicolon"};return pressed===want||codes[want]!==undefined&&e.code===codes[want]&&!e.shiftKey}let teSelectionMenuInstances=0;class TeSelectionMenu{constructor(editor,options={}){this.editor=editor;this.surface=editor.surface;this.instance=++teSelectionMenuInstances;this.items=[];this.element=null;this.visible=[];this.activeIndex=-1;this.context=null;this.openController=null;this.destroyed=false;this.abortController=new AbortController;const signal=this.abortController.signal;if(options.signal){if(options.signal.aborted)this.abortController.abort();else options.signal.addEventListener("abort",(()=>this.destroy()),{signal:signal})}const root=this.surface.root;root.addEventListener("contextmenu",(e=>this.onContextMenu(e)),{signal:signal});root.addEventListener("keydown",(e=>this.onSurfaceKeyDown(e)),{signal:signal});if(editor.ghosts)this.register(TeSelectionMenu.ghostItem(editor))}static get SLOTS(){return["alternatives","ai-alternatives","ghost","stash"]}static ghostItem(editor){return{id:"ghost",key:"ctrl+/",label:ctx=>ctx.ghost&&ctx.ghost.action==="revive"?"Revive":"Ghost it",available:ctx=>{const layer=editor.ghosts;if(!layer||!layer.available())return false;if(ctx.ghost===undefined)ctx.ghost=layer.targetFor(ctx);return!!ctx.ghost},run:ctx=>editor.ghosts.apply(ctx.ghost||editor.ghosts.targetFor(ctx))}}register(item){if(!item||typeof item.id!=="string"||typeof item.run!=="function"){throw new Error("A selection menu item needs an id and a run function")}this.unregister(item.id);this.items.push(item);const slots=TeSelectionMenu.SLOTS;const rank=it=>{const i=slots.indexOf(it.id);return i===-1?slots.length:i};this.items=this.items.map(((it,i)=>[it,i])).sort(((a,b)=>rank(a[0])-rank(b[0])||a[1]-b[1])).map((x=>x[0]));return()=>this.unregister(item.id)}unregister(id){this.items=this.items.filter((it=>it.id!==id))}ids(){return this.items.map((it=>it.id))}isOpen(){return!!this.element}contextFor(sel){const start=Math.min(sel.start,sel.end);const end=Math.max(sel.start,sel.end);return{editor:this.editor,start:start,end:end,collapsed:start===end,text:this.surface.getText().slice(start,end),selection:{start:sel.start,end:sel.end,direction:sel.direction}}}availableItems(ctx){return this.items.filter((it=>{try{return typeof it.available!=="function"||!!it.available(ctx)}catch(err){console.error(`Selection menu item "${it.id}" failed`,err);return false}}))}onContextMenu(e){if(this.destroyed)return;if(this.isOpen()){e.preventDefault();return}let sel=this.surface.getSelectionOffsets();const pointer=!(e.clientX===0&&e.clientY===0)&&e.button!==-1;if(sel.start===sel.end&&pointer){const at=this.offsetAtPoint(e.clientX,e.clientY);if(at>=0)sel={start:at,end:at,direction:"none"}}const point=pointer?{x:e.clientX,y:e.clientY}:null;if(this.open(sel,point))e.preventDefault()}onSurfaceKeyDown(e){if(this.destroyed||e.isComposing)return;if(e.key==="ContextMenu"||e.key==="F10"&&e.shiftKey&&!e.ctrlKey&&!e.altKey&&!e.metaKey){if(this.open(this.surface.getSelectionOffsets(),null))e.preventDefault();return}if(!(e.ctrlKey||e.metaKey))return;const item=this.items.find((it=>it.key&&teShortcutMatches(e,it.key)));if(!item)return;const ctx=this.contextFor(this.surface.getSelectionOffsets());if(this.availableItems(ctx).includes(item)){e.preventDefault();this.run(item,ctx)}}offsetAtPoint(x,y){let node=null;let offset=0;if(document.caretRangeFromPoint){const r=document.caretRangeFromPoint(x,y);if(r){node=r.startContainer;offset=r.startOffset}}else if(document.caretPositionFromPoint){const p=document.caretPositionFromPoint(x,y);if(p){node=p.offsetNode;offset=p.offset}}return node?this.surface.pointToOffset(node,offset):-1}open(sel,point=null){if(this.destroyed)return false;this.close({restoreFocus:false});const ctx=this.contextFor(sel);const items=this.availableItems(ctx);if(items.length===0)return false;this.context=ctx;this.visible=items;const menu=document.createElement("div");menu.className="te-selection-menu";menu.id=`te-selection-menu-${this.instance}`;menu.setAttribute("role","menu");menu.setAttribute("aria-label","Selection actions");menu.setAttribute("aria-orientation","vertical");menu.tabIndex=-1;items.forEach(((item,i)=>{const row=document.createElement("div");row.className="te-selection-menu-item";row.setAttribute("role","menuitem");row.tabIndex=-1;row.dataset.item=item.id;row.dataset.index=String(i);const label=document.createElement("span");label.className="te-selection-menu-label";label.textContent=typeof item.label==="function"?item.label(ctx):String(item.label||item.id);row.appendChild(label);if(item.key){const keys=teShortcutLabels(item.key);row.setAttribute("aria-keyshortcuts",keys.aria);const hint=document.createElement("span");hint.className="te-selection-menu-key";hint.setAttribute("aria-hidden","true");hint.textContent=keys.text;row.appendChild(hint)}menu.appendChild(row)}));document.body.appendChild(menu);this.element=menu;this.surface.root.setAttribute("aria-controls",menu.id);this.position(point||this.selectionPoint(ctx));this.openController=new AbortController;const signal=this.openController.signal;menu.addEventListener("keydown",(e=>this.onMenuKeyDown(e)),{signal:signal});menu.addEventListener("mousedown",(e=>e.preventDefault()),{signal:signal});menu.addEventListener("click",(e=>{const row=e.target.closest(".te-selection-menu-item");if(row&&menu.contains(row))this.activate(Number(row.dataset.index))}),{signal:signal});menu.addEventListener("mousemove",(e=>{const row=e.target.closest(".te-selection-menu-item");if(row&&menu.contains(row))this.setActive(Number(row.dataset.index))}),{signal:signal});menu.addEventListener("focusout",(e=>{if(!e.relatedTarget||!menu.contains(e.relatedTarget)){if(e.relatedTarget)this.close({restoreFocus:false})}}),{signal:signal});const outside=e=>{if(this.element&&!this.element.contains(e.target))this.close({restoreFocus:false})};document.addEventListener("mousedown",outside,{signal:signal,capture:true});document.addEventListener("touchstart",outside,{signal:signal,capture:true,passive:true});window.addEventListener("scroll",(()=>this.close({restoreFocus:false})),{signal:signal,capture:true,passive:true});window.addEventListener("resize",(()=>this.close({restoreFocus:false})),{signal:signal});window.addEventListener("blur",(()=>this.close({restoreFocus:false})),{signal:signal});this.setActive(0);return true}selectionPoint(ctx){const range=this.surface.rangeForOffsets(ctx.start,ctx.end);const rects=Array.from(range.getClientRects()).filter((r=>r.width>0&&r.height>0));if(rects.length>0){const lastTop=Math.max(...rects.map((r=>r.top)));const line=rects.filter((r=>r.bottom>lastTop));const bottom=Math.max(...line.map((r=>r.bottom)));const glyph=bottom-Math.min(...line.map((r=>r.top)));const lineHeight=parseFloat(getComputedStyle(this.surface.root).lineHeight)||glyph;return{x:Math.min(...line.map((r=>r.left))),y:bottom+Math.max(0,(lineHeight-glyph)/2)+4}}const caret=this.surface.getCaretRect(ctx.end);return{x:caret.left,y:caret.bottom+4}}position(point){const menu=this.element;const margin=8;const box=menu.getBoundingClientRect();const vw=document.documentElement.clientWidth||window.innerWidth;const vh=document.documentElement.clientHeight||window.innerHeight;let left=point.x;let top=point.y;if(left+box.width>vw-margin)left=vw-margin-box.width;if(top+box.height>vh-margin)top=Math.max(margin,point.y-box.height-8);menu.style.left=`${Math.max(margin,left)}px`;menu.style.top=`${Math.max(margin,top)}px`}rows(){return this.element?Array.from(this.element.querySelectorAll(".te-selection-menu-item")):[]}setActive(index){const rows=this.rows();if(rows.length===0)return;const i=(index%rows.length+rows.length)%rows.length;this.activeIndex=i;rows.forEach(((row,j)=>row.classList.toggle("te-selection-menu-item--active",j===i)));if(document.activeElement!==rows[i])rows[i].focus({preventScroll:true})}onMenuKeyDown(e){const n=this.visible.length;switch(e.key){case"ArrowDown":this.setActive(this.activeIndex+1);break;case"ArrowUp":this.setActive(this.activeIndex-1);break;case"Home":this.setActive(0);break;case"End":this.setActive(n-1);break;case"Enter":case" ":this.activate(this.activeIndex);break;case"Escape":case"Tab":this.close({restoreFocus:true});break;default:{const index=this.visible.findIndex((it=>it.key&&teShortcutMatches(e,it.key)));if(index===-1)return;this.activate(index)}}e.preventDefault();e.stopPropagation()}activate(index){const item=this.visible[index];const ctx=this.context;if(!item||!ctx)return;this.close({restoreFocus:true});this.run(item,ctx)}run(item,ctx){try{return item.run(ctx)}catch(err){console.error(`Selection menu item "${item.id}" failed`,err);return undefined}}close({restoreFocus:restoreFocus=true}={}){if(!this.element)return;const ctx=this.context;if(this.openController)this.openController.abort();this.openController=null;this.element.remove();this.element=null;this.visible=[];this.activeIndex=-1;this.context=null;if(this.surface.root)this.surface.root.removeAttribute("aria-controls");if(restoreFocus&&ctx&&!this.surface.destroyed){this.surface.focus();const sel=ctx.selection;this.surface.setSelectionOffsets(sel.start,sel.end,sel.direction==="none"?undefined:sel.direction)}}destroy(){if(this.destroyed)return;this.close({restoreFocus:false});this.destroyed=true;this.items=[];this.abortController.abort()}}window.TeGhostLayer=TeGhostLayer;window.TeSelectionMenu=TeSelectionMenu;let teTrimInstances=0;const TE_TRIM_LAYER="lab-trim";const TE_TRIM_HINT="Faded words would go. Click one to keep it.";class TeLabTrim{constructor(lab,slot,options={}){this.lab=lab;this.editor=lab.editor;this.surface=lab.surface;this.registry=lab.registry;this.slot=slot;this.instance=++teTrimInstances;this.destroyed=false;this.level="original";this.plan=null;this.planText=null;this.kept=new Set;this.status=null;this.active=[];this.cardOpen=false;this.walkIndex=-1;this.stale=false;this.applying=false;this.resetTimer=null;this.abortController=new AbortController;const signal=this.abortController.signal;if(options.signal){if(options.signal.aborted)this.abortController.abort();else options.signal.addEventListener("abort",(()=>this.destroy()),{signal:signal})}this.build();this.updateProtectionAvailability();this.surface.root.addEventListener("click",(e=>this.onSurfaceClick(e)),{signal:signal});this.offChange=this.surface.onChange((change=>this.onSurfaceChange(change)))}static get LAYER(){return TE_TRIM_LAYER}api(){const api=typeof this.editor.documentApi==="function"?this.editor.documentApi():null;return api&&typeof api.trim_plan_json==="function"&&typeof api.trim_status==="function"&&typeof api.trim_make_cuts==="function"?api:null}roleSnapshot(){const api=this.api();if(!api||typeof api.kg_role_snapshot_json!=="function")return null;try{return JSON.parse(api.kg_role_snapshot_json())}catch(_){return null}}updateProtectionAvailability(){const role=this.roleSnapshot();const ready=role&&role.loaded===true&&role.protectionReady===true&&!this.protectionFailure;for(const b of this.levelButtons)b.disabled=b.dataset.level!=="original"&&!ready;this.protectionNote.textContent=ready?"Every local-role occurrence is protected; the target may be unreachable.":this.protectionFailure||role&&role.protectionError||"Load a local role/thesaurus to trim with KG protection.";return!!ready}onRoleChange(){if(this.destroyed)return;this.plan=null;this.planText=null;this.protectionFailure=null;this.reset();this.updateProtectionAvailability();this.announce("Trim cleared: the local role changed.")}get trimming(){return this.level!=="original"}build(){const signal=this.abortController.signal;const el=(tag,className,text)=>{const node=document.createElement(tag);if(className)node.className=className;if(text!==undefined)node.textContent=text;return node};const id=name=>`te-trim-${name}-${this.instance}`;const levels=[["original","Original",""],["slight","Slight trim","~10%"],["tighten","Tighten more","~20%"],["sharper","Even sharper","~30%"],["half","Cut in half","~50%"]];const label=el("span","te-trim-label","Trim");label.id=id("label");const group=el("div","te-trim-levels");group.setAttribute("role","radiogroup");group.setAttribute("aria-labelledby",label.id);this.levelButtons=levels.map((([level,name,target])=>{const b=el("button","te-trim-level");b.type="button";b.dataset.level=level;b.setAttribute("role","radio");b.setAttribute("aria-checked",level==="original"?"true":"false");b.tabIndex=level==="original"?0:-1;b.append(el("span","te-trim-level-name",name));if(target)b.append(el("span","te-trim-level-target",target));b.addEventListener("click",(()=>this.select(level,{focusCard:true})),{signal:signal});group.appendChild(b);return b}));group.addEventListener("keydown",(e=>this.onLevelKey(e)),{signal:signal});const protectionNote=el("p","te-trim-protection");protectionNote.id=id("protection");protectionNote.setAttribute("role","status");group.setAttribute("aria-describedby",protectionNote.id);this.protectionNote=protectionNote;this.slot.replaceChildren(label,group,protectionNote);this.slot.hidden=false;const card=el("section","te-trim-card");card.id=id("card");card.hidden=true;card.tabIndex=-1;card.setAttribute("aria-labelledby",id("level"));const head=el("div","te-trim-card-head");const levelName=el("strong","te-trim-card-level");levelName.id=id("level");const count=el("span","te-trim-card-count");count.setAttribute("role","status");count.setAttribute("aria-live","polite");head.append(levelName,count);const meter=el("div","te-trim-meter");meter.setAttribute("aria-hidden","true");const meterKeep=el("span","te-trim-meter-keep");meter.append(meterKeep);const hint=el("p","te-trim-card-hint",TE_TRIM_HINT);this.protectionCardNote=el("p","te-trim-card-protection");const walk=el("div","te-trim-walk");walk.hidden=true;const walkText=el("p","te-trim-walk-text");walkText.setAttribute("aria-live","polite");const walkButtons=el("div","te-trim-walk-buttons");const button=(cls,icon,text,onClick,keys)=>{const b=el("button",cls);b.type="button";if(icon){const i=el("i",icon);i.setAttribute("aria-hidden","true");b.append(i)}b.append(el("span","",text));if(keys)b.setAttribute("aria-keyshortcuts",keys);b.addEventListener("click",onClick,{signal:signal});return b};this.prevButton=button("te-trim-prev","fa-solid fa-chevron-left","Previous",(()=>this.step(-1)),"ArrowLeft");this.keepButton=button("te-trim-keep","fa-solid fa-thumbtack","Keep",(()=>this.keepCurrent()),"K");this.nextButton=button("te-trim-next","fa-solid fa-chevron-right","Next",(()=>this.step(1)),"ArrowRight");this.stopButton=button("te-trim-stop","","Stop",(()=>this.stopWalk(true)),"Escape");walkButtons.append(this.prevButton,this.keepButton,this.nextButton,this.stopButton);walk.append(walkText,walkButtons);const actions=el("div","te-trim-actions");this.cutButton=button("te-trim-cut","fa-solid fa-scissors","Make the cuts",(()=>this.makeCuts()));this.walkButton=button("te-trim-walk-start","fa-solid fa-shoe-prints","Walk through",(()=>this.startWalk()));this.doneButton=button("te-trim-done","","Done",(()=>this.done()));actions.append(this.cutButton,this.walkButton,this.doneButton);card.append(head,meter,hint,this.protectionCardNote,walk,actions);card.addEventListener("keydown",(e=>this.onCardKey(e)),{signal:signal});this.card=card;this.levelName=levelName;this.count=count;this.meterKeep=meterKeep;this.walk=walk;this.walkText=walkText;const live=el("div","te-trim-live");live.setAttribute("role","status");live.setAttribute("aria-live","polite");this.live=live;const mount=this.editor.chrome&&this.editor.chrome.root||document.body;mount.append(card,live)}onLevelKey(e){const current=this.levelButtons.indexOf(document.activeElement);if(current<0)return;const n=this.levelButtons.length;let next=null;if(e.key==="ArrowRight"||e.key==="ArrowDown")next=(current+1)%n;else if(e.key==="ArrowLeft"||e.key==="ArrowUp")next=(current-1+n)%n;else if(e.key==="Home")next=0;else if(e.key==="End")next=n-1;if(next===null)return;e.preventDefault();if(this.levelButtons[next].disabled)next=0;this.levelButtons.forEach(((b,i)=>{b.tabIndex=i===next?0:-1}));this.levelButtons[next].focus()}onCardKey(e){if(e.key==="Escape"){e.preventDefault();e.stopPropagation();if(this.walkIndex>=0)this.stopWalk(true);else this.done();return}if(this.walkIndex<0||e.ctrlKey||e.metaKey||e.altKey)return;if(e.key==="ArrowRight"){e.preventDefault();this.step(1)}else if(e.key==="ArrowLeft"){e.preventDefault();this.step(-1)}else if(e.key==="k"||e.key==="K"){e.preventDefault();this.keepCurrent()}}select(level,opts={}){if(this.destroyed)return null;if(level==="original"){this.reset();return null}const api=this.api();if(!api||!this.updateProtectionAvailability()){this.announce(this.protectionNote.textContent);return null}this.cancelPendingReset();this.stale=false;this.level=level;const status=this.refresh();if(!status)return null;this.cardOpen=true;this.lab.close(false);this.sync();if(opts.focusCard)this.card.focus({preventScroll:true});return status}ensurePlan(api){const text=this.surface.getText();const role=this.roleSnapshot();if(!role||role.loaded!==true||role.protectionReady!==true)throw new Error("Load a local role/thesaurus to trim with KG protection.");if(this.plan&&this.planText===text&&this.plan.roleGeneration===role.generation)return this.plan;this.plan=api.trim_plan_json();if(!this.plan||this.plan.kgProtected!==true||this.plan.roleGeneration!==role.generation){this.plan=null;throw new Error("The WASM trim API does not provide current KG protection.")}this.planText=text;this.kept.clear();return this.plan}refresh(){const api=this.api();if(!api||!this.trimming)return null;let status;try{this.editor.alignDocumentModel(api);this.ensurePlan(api);status=api.trim_status(this.level,JSON.stringify(Array.from(this.kept)))}catch(err){try{this.plan=null;this.ensurePlan(api);status=api.trim_status(this.level,JSON.stringify([]))}catch(err2){this.refuse(err2);return null}}if(!status||status.kgProtected!==true||status.roleGeneration!==this.plan.roleGeneration){this.refuse(new Error("The WASM trim API does not provide current KG protection."));return null}this.status=status;this.active=Array.from(status.active||[]);if(this.walkIndex>=this.active.length)this.walkIndex=this.active.length-1;this.draw();this.renderCard();this.emit("te:trim",{level:this.level,status:status});return status}draw(){const current=this.walkIndex>=0?this.active[this.walkIndex]:null;const items=this.active.map((c=>({id:`${c.id}-${c.start}`,start:c.start,end:c.end,className:c===current?"te-trim-faded te-trim-current":"te-trim-faded",data:{cut:c.id,reason:c.reason,tier:c.tier}})));this.keepFocus((()=>this.registry.set(TE_TRIM_LAYER,items)))}keepFocus(fn){const focused=document.activeElement;const ours=focused&&(this.card.contains(focused)||this.lab.root.contains(focused))?focused:null;const result=fn();if(ours&&ours.isConnected&&document.activeElement!==ours&&!ours.closest("[hidden]")){ours.focus({preventScroll:true})}return result}sync(){if(this.destroyed)return;this.updateProtectionAvailability();for(const b of this.levelButtons){const on=b.dataset.level===this.level;b.setAttribute("aria-checked",on?"true":"false");b.tabIndex=on?0:-1}this.card.hidden=!(this.cardOpen&&!this.lab.isOpen);if(this.cardOpen)this.surface.root.dataset.teTrimming="";else delete this.surface.root.dataset.teTrimming}renderCard(){const status=this.status;if(!status)return;this.levelName.textContent=status.label;this.count.textContent=status.cardText;this.protectionCardNote.textContent=`Every local-role occurrence is protected (${status.protectionCount}); retention outranks the target.`;const before=status.words_before||0;const share=before?100*status.words_after/before:100;this.meterKeep.style.width=`${share}%`;const nothing=this.active.length===0;this.cutButton.disabled=nothing;this.walkButton.disabled=nothing;if(this.walkIndex>=0)this.renderWalk()}onSurfaceClick(e){if(!this.cardOpen||this.stale||!this.trimming)return;const seg=e.target&&e.target.closest?e.target.closest("[data-te-decoration]"):null;if(!seg||!this.surface.root.contains(seg))return;const cut=this.cutAtSegment(seg);if(cut!==null)this.keep(cut)}cutAtSegment(seg){const ids=(seg.getAttribute("data-te-decoration")||"").split(/\s+/).filter((t=>t.startsWith(`${TE_TRIM_LAYER}:`))).map((t=>t.slice(TE_TRIM_LAYER.length+1)));if(ids.length===0)return null;const items=this.registry.get(TE_TRIM_LAYER).filter((item=>ids.includes(item.id)));return this.outermost(items.map((item=>item.data&&item.data.cut)))}outermost(cutIds){const cuts=this.plan?this.plan.cuts:[];let best=null;for(const cid of cutIds){const c=cuts.find((x=>x.id===cid));if(!c)continue;const width=c.end-c.start;const bestWidth=best?best.end-best.start:-1;if(width>bestWidth||width===bestWidth&&c.startc.id===cutId)))return null;this.kept.add(cutId);const status=this.refresh();if(status)this.announce(`Kept. ${status.cardText}`);return status}startWalk(){if(this.active.length===0)return;this.walkIndex=0;this.walk.hidden=false;this.draw();this.renderWalk();this.revealCurrent();this.nextButton.focus({preventScroll:true})}step(delta){if(this.walkIndex<0||this.active.length===0)return;const n=this.active.length;this.walkIndex=(this.walkIndex+delta+n)%n;this.draw();this.renderWalk();this.revealCurrent()}keepCurrent(){if(this.walkIndex<0)return null;const piece=this.active[this.walkIndex];if(!piece)return null;const covering=this.active.filter((c=>c.start<=piece.start&&piece.end<=c.end)).map((c=>c.id));const status=this.keep(this.outermost(covering));if(this.active.length===0)this.stopWalk(true);else{this.draw();this.renderWalk();this.revealCurrent();if(!this.card.contains(document.activeElement))this.keepButton.focus({preventScroll:true})}return status}stopWalk(focus=false){if(this.walkIndex<0)return;this.walkIndex=-1;this.walk.hidden=true;this.draw();if(focus&&this.cardOpen)this.walkButton.focus({preventScroll:true})}renderWalk(){const c=this.active[this.walkIndex];if(!c)return;const text=this.surface.getText().slice(c.start,c.end).trim();const short=text.length>60?`${text.slice(0,57)}…`:text;this.walkText.textContent=`${this.walkIndex+1} of ${this.active.length}: ${c.reason}, “${short}”`}revealCurrent(){const c=this.active[this.walkIndex];if(!c)return;const el=this.surface.root.querySelector(`[data-te-decoration~="${TE_TRIM_LAYER}:${c.id}-${c.start}"]`);if(el&&typeof el.scrollIntoView==="function")el.scrollIntoView({block:"nearest"})}makeCuts(){if(this.destroyed||this.stale||!this.trimming)return null;const api=this.api();if(!api)return null;let made;try{this.editor.alignDocumentModel(api);made=api.trim_make_cuts(this.level,JSON.stringify(Array.from(this.kept)))}catch(err){this.refuse(err);return null}const edits=Array.from(made.edits||[]);if(edits.length===0)return null;const level=this.level;this.applying=true;let applied;try{applied=this.surface.replaceRanges(edits,{source:"trim"})}finally{this.applying=false}if(this.surface.getText()!==made.text)console.error("trim: surface text differs from the engine result");const words=this.status?this.status.cardText:"";this.reset(false);this.announce(`Cuts made. ${words}`);this.surface.focus();this.emit("te:trim-cut",{level:level,edits:applied});return applied}done(){if(!this.cardOpen)return;let committed;try{committed=this.editor.commitTrimGhosts(this.level,Array.from(this.kept))}catch(error){this.announce(`Done refused: ${error.error||error.message||error}`);return false}this.stopWalk(false);this.plan=null;this.planText=null;this.reset();this.announce(committed?"Done: cuts saved as ghosts. Undo restores them.":"Done: no cuts to save.");return true}reset(focusPill=false){if(this.destroyed)return;this.cancelPendingReset();const hadFocus=this.card.contains(document.activeElement);this.level="original";this.kept.clear();this.status=null;this.active=[];this.walkIndex=-1;this.walk.hidden=true;this.cardOpen=false;this.stale=false;if(this.surface&&!this.surface.destroyed&&this.registry.get(TE_TRIM_LAYER).length){this.keepFocus((()=>this.registry.clear(TE_TRIM_LAYER)))}this.sync();if((focusPill||hadFocus)&&this.lab.pill())this.lab.pill().focus()}onSurfaceChange(change){if(this.protectionFailure){this.protectionFailure=null;this.updateProtectionAvailability()}if(this.destroyed||this.applying||!this.trimming)return;if(change&&change.source==="trim")return;this.stale=true;this.cardOpen=false;this.card.hidden=true;if(this.resetTimer===null){this.resetTimer=setTimeout((()=>{this.resetTimer=null;if(!this.stale)return;this.reset(false);this.announce("Trim cleared: the text changed.")}),0)}}cancelPendingReset(){if(this.resetTimer!==null){clearTimeout(this.resetTimer);this.resetTimer=null}}announce(text){this.live.textContent=text}refuse(error){const message=`Trim is unavailable: ${error&&error.message?error.message:error}`;this.plan=null;this.planText=null;this.protectionFailure=message;this.reset();this.updateProtectionAvailability();this.announce(message)}emit(type,detail){this.card.dispatchEvent(new CustomEvent(type,{bubbles:true,detail:Object.assign({editor:this.editor},detail)}))}destroy(){if(this.destroyed)return;this.cancelPendingReset();if(this.surface&&!this.surface.destroyed&&this.registry)this.registry.clear(TE_TRIM_LAYER);if(this.surface&&this.surface.root)delete this.surface.root.dataset.teTrimming;this.destroyed=true;this.offChange();this.abortController.abort();this.card.remove();this.live.remove();this.slot.replaceChildren();this.slot.hidden=true}}window.TeLabTrim=TeLabTrim;let teLabInstances=0;const TE_LAB_LAYER="lab-marks";const TE_LAB_HINTS={typos_and_punctuation:"Proposes fixes for misspellings and punctuation slips. Nothing changes until you accept one.",weakest_sentences:"Marks the most hedged, least central sentences. Paragraph openers are left alone.",long_sentences:"Marks sentences of 30 words or more.",convoluted_sentences:"Marks sentences carrying many clauses, asides and breaks.",off_tone:"Marks informal words that do not fit a professional register.",hedges_and_filler:"Marks hedges and filler words you could fade or cut."};const TE_LAB_KINDS={typo:["typo","typos"],punctuation:["punctuation slip","punctuation slips"],weak_sentence:["weak sentence","weak sentences"],long_sentence:["long sentence","long sentences"],convoluted_sentence:["convoluted sentence","convoluted sentences"],off_tone:["off-tone word","off-tone words"],hedge:["hedge","hedges"],filler:["filler word","filler words"]};class TeLabPopover{constructor(editor,options={}){this.editor=editor;this.surface=editor.surface;this.registry=editor.decorations;this.instance=++teLabInstances;this.isOpen=false;this.destroyed=false;this.action=null;this.actions=[];this.lastFocusIndex=0;this.abortController=new AbortController;const signal=this.abortController.signal;if(options.signal){if(options.signal.aborted)this.abortController.abort();else options.signal.addEventListener("abort",(()=>this.destroy()),{signal:signal})}this.build();this.editor.root.addEventListener("te:role-change",(e=>{if(e.detail&&e.detail.editor===this.editor&&this.trim)this.trim.onRoleChange()}),{signal:signal});document.addEventListener("te:lab",(e=>{if(!e.detail||!e.detail.editor||e.detail.editor===this.editor)this.toggle()}),{signal:signal});document.addEventListener("te:mode-change",(e=>{if(e.detail&&e.detail.editor&&e.detail.editor!==this.editor)return;if(!e.detail||e.detail.mode!=="write-on"){this.close(false);this.clear();if(this.trim)this.trim.reset()}}),{signal:signal});document.addEventListener("pointerdown",(e=>this.onOutsidePointer(e)),{signal:signal,capture:true});window.addEventListener("resize",(()=>this.position()),{signal:signal});this.offChange=this.surface.onChange((()=>{if(this.action!==null)this.renderResults()}))}static get LAYER(){return TE_LAB_LAYER}static kindClass(kind){return`te-lab-mark--${String(kind).replace(/_/g,"-")}`}static describeCount(kind,count){const names=TE_LAB_KINDS[kind]||[String(kind).replace(/_/g," "),String(kind).replace(/_/g," ")];return`${count} ${count===1?names[0]:names[1]}`}static visible(text){return String(text).replace(/ /g,"␣")}api(){const api=typeof this.editor.documentApi==="function"?this.editor.documentApi():null;return api&&typeof api.lab_mark==="function"&&typeof api.lab_actions==="function"?api:null}pill(){const chrome=this.editor.chrome;return chrome&&chrome.root?chrome.root.querySelector('[data-control="lab"]'):null}build(){const signal=this.abortController.signal;const id=name=>`te-lab-${name}-${this.instance}`;const el=(tag,className,text)=>{const node=document.createElement(tag);if(className)node.className=className;if(text!==undefined)node.textContent=text;return node};const root=el("div","te-lab");root.id=id("popover");root.hidden=true;root.setAttribute("role","dialog");root.setAttribute("aria-modal","false");root.setAttribute("aria-labelledby",id("title"));this.root=root;const header=el("div","te-lab-header");const title=el("h2","te-lab-title","The Lab guide…");title.id=id("title");const sub=el("span","te-lab-subtitle","what each idea does");header.append(title,sub);const menu=el("div","te-lab-actions");menu.setAttribute("role","menu");menu.setAttribute("aria-label","Lab actions");menu.setAttribute("aria-describedby",id("hint"));this.menu=menu;const api=this.api();let actions=[];if(api){try{actions=Array.from(api.lab_actions()||[])}catch(err){console.error("lab_actions failed",err)}}this.actions=actions;this.items=actions.map(((action,i)=>{const item=el("button","te-lab-action");item.type="button";item.setAttribute("role","menuitemradio");item.setAttribute("aria-checked","false");item.tabIndex=i===0?0:-1;item.dataset.action=action.id;const label=el("span","te-lab-action-label",action.label);const count=el("span","te-lab-action-count");count.setAttribute("aria-hidden","true");item.append(label,count);item.addEventListener("click",(()=>this.run(action.id)),{signal:signal});item.addEventListener("focus",(()=>this.showHint(action.id)),{signal:signal});item.addEventListener("mouseenter",(()=>this.showHint(action.id)),{signal:signal});menu.appendChild(item);return item}));menu.addEventListener("keydown",(e=>this.onMenuKey(e)),{signal:signal});const hint=el("p","te-lab-hint");hint.id=id("hint");this.hint=hint;const trim=el("div","te-lab-trim");trim.dataset.slot="trim";trim.hidden=true;this.trimSlot=trim;this.trim=api&&typeof window.TeLabTrim==="function"?new window.TeLabTrim(this,trim,{signal:signal}):null;const results=el("div","te-lab-results");results.hidden=true;const legend=el("div","te-lab-legend");legend.setAttribute("role","status");legend.setAttribute("aria-live","polite");const proposals=el("ul","te-lab-proposals");proposals.setAttribute("aria-label","Proposed fixes");const clear=el("button","te-lab-clear");clear.type="button";const clearIcon=el("i","fa-solid fa-eraser");clearIcon.setAttribute("aria-hidden","true");clear.append(clearIcon,el("span","","Clear marks"));clear.addEventListener("click",(()=>this.clear(true)),{signal:signal});results.append(legend,proposals,clear);this.results=results;this.legend=legend;this.proposals=proposals;this.clearButton=clear;proposals.addEventListener("click",(e=>{const button=e.target.closest("[data-mark]");if(button&&proposals.contains(button))this.accept(button.dataset.mark,true)}),{signal:signal});const note=el("p","te-lab-note");note.setAttribute("role","alert");note.hidden=true;this.note=note;if(!api){note.textContent="The Lab needs the WebAssembly document model, which did not load. Reload the page to try again.";note.hidden=false}root.append(header,menu,hint,trim,results,note);root.addEventListener("keydown",(e=>{if(e.key==="Escape"){e.preventDefault();e.stopPropagation();this.close(true)}}),{signal:signal});const chip=el("div","te-lab-chip");chip.hidden=true;const chipText=el("span","te-lab-chip-text");chipText.setAttribute("role","status");const chipClear=el("button","te-lab-chip-clear","Clear marks");chipClear.type="button";chipClear.addEventListener("click",(()=>this.clear()),{signal:signal});chip.append(chipText,chipClear);this.chip=chip;this.chipText=chipText;const descriptions=el("div","te-lab-descriptions");this.descriptions=descriptions;const mount=this.editor.chrome&&this.editor.chrome.root||document.body;mount.append(root,chip,descriptions);const pill=this.pill();if(pill){pill.setAttribute("aria-haspopup","dialog");pill.setAttribute("aria-expanded","false");pill.setAttribute("aria-controls",root.id)}this.showHint(actions.length?actions[0].id:null)}showHint(actionId){this.hint.textContent=actionId&&TE_LAB_HINTS[actionId]||""}toggle(){if(this.isOpen)this.close(true);else this.open()}open(){if(this.destroyed||this.isOpen)return;this.isOpen=true;this.root.hidden=false;const pill=this.pill();if(pill)pill.setAttribute("aria-expanded","true");this.renderResults();if(this.trim)this.trim.sync();this.position();const checked=this.items.findIndex((item=>item.dataset.action===this.action));this.focusItem(checked>=0?checked:0)}close(restoreFocus=true){if(!this.isOpen)return;const hadFocus=this.root.contains(document.activeElement);this.isOpen=false;this.root.hidden=true;const pill=this.pill();if(pill)pill.setAttribute("aria-expanded","false");this.renderResults();if(this.trim)this.trim.sync();if(restoreFocus&&hadFocus&&pill)pill.focus()}onOutsidePointer(e){if(!this.isOpen)return;const t=e.target;const pill=this.pill();if(this.root.contains(t)||this.chip.contains(t)||pill&&pill.contains(t))return;this.close(false)}position(){if(this.destroyed)return;const pill=this.pill();if(!pill)return;const r=pill.getBoundingClientRect();if(r.width===0&&r.height===0)return;if(this.isOpen){this.root.style.left=`${r.left+r.width/2}px`;this.root.style.bottom=`${Math.max(0,window.innerHeight-r.top+10)}px`}if(!this.chip.hidden){this.chip.style.left=`${r.right+12}px`;this.chip.style.top=`${r.top+r.height/2}px`}}focusItem(index){if(this.items.length===0)return;const i=(index+this.items.length)%this.items.length;this.items.forEach(((item,j)=>{item.tabIndex=j===i?0:-1}));this.lastFocusIndex=i;this.items[i].focus()}onMenuKey(e){const current=this.items.indexOf(document.activeElement);if(current<0)return;let next=null;if(e.key==="ArrowDown")next=current+1;else if(e.key==="ArrowUp")next=current-1;else if(e.key==="Home")next=0;else if(e.key==="End")next=this.items.length-1;if(next===null)return;e.preventDefault();this.focusItem(next)}run(id){if(this.destroyed)return null;const api=this.api();if(!api)return null;let marks;try{this.editor.alignDocumentModel(api);marks=Array.from(api.lab_mark(id)||[])}catch(err){console.error("lab_mark failed",err);this.note.textContent=`The Lab could not run this action: ${err&&err.message?err.message:err}`;this.note.hidden=false;return null}this.note.hidden=true;const text=this.surface.getText();const descriptions=document.createDocumentFragment();const items=marks.map(((m,i)=>{const markId=`m${i}`;const descId=`te-lab-desc-${this.instance}-${markId}`;const desc=document.createElement("span");desc.id=descId;const name=(TE_LAB_KINDS[m.kind]||[m.kind])[0];desc.textContent=`Lab: ${name}. ${m.reason}${typeof m.proposal==="string"?`. Proposed: “${m.proposal}”`:""}`;descriptions.appendChild(desc);return{id:markId,start:m.start,end:m.end,className:`te-lab-mark ${TeLabPopover.kindClass(m.kind)}`,attributes:{"aria-describedby":descId,"data-lab-kind":m.kind},data:{kind:m.kind,reason:m.reason,proposal:typeof m.proposal==="string"?m.proposal:null,text:text.slice(m.start,m.end),score:m.score}}}));this.descriptions.replaceChildren(descriptions);this.action=id;this.keepFocus((()=>this.registry.set(TE_LAB_LAYER,items)));for(const item of this.items){item.setAttribute("aria-checked",item.dataset.action===id?"true":"false")}this.renderResults();this.emit("te:lab-marks",{action:id,count:items.length});return marks}keepFocus(fn){const focused=document.activeElement;const ours=focused&&(this.root.contains(focused)||this.chip.contains(focused))?focused:null;const result=fn();if(ours&&ours.isConnected&&document.activeElement!==ours&&!ours.closest("[hidden]")){ours.focus({preventScroll:true})}return result}marks(){return this.registry?this.registry.get(TE_LAB_LAYER):[]}clear(focusMenu=false){if(this.destroyed)return;const had=this.action!==null||this.marks().length>0;this.action=null;const fromChip=this.chip.contains(document.activeElement);if(this.surface&&!this.surface.destroyed)this.keepFocus((()=>this.registry.clear(TE_LAB_LAYER)));this.descriptions.replaceChildren();for(const item of this.items)item.setAttribute("aria-checked","false");this.renderResults();if(focusMenu&&this.isOpen)this.focusItem(this.lastFocusIndex);if(fromChip&&this.pill())this.pill().focus();if(had)this.emit("te:lab-marks",{action:null,count:0})}accept(markId,keepFocus=false){const mark=this.marks().find((m=>m.id===String(markId)));if(!mark||!mark.data||typeof mark.data.proposal!=="string")return false;if(this.surface.getText().slice(mark.start,mark.end)!==mark.data.text)return false;const index=Array.from(this.proposals.querySelectorAll("[data-mark]")).findIndex((b=>b.dataset.mark===mark.id));this.surface.replaceRange(mark.start,mark.end,mark.data.proposal,{source:"lab"});if(keepFocus&&this.isOpen){const buttons=this.proposals.querySelectorAll("[data-mark]");if(buttons.length)buttons[Math.min(Math.max(index,0),buttons.length-1)].focus();else this.clearButton.focus()}this.emit("te:lab-accept",{start:mark.start,end:mark.end,text:mark.data.text,proposal:mark.data.proposal});return true}renderResults(){if(this.destroyed)return;const marks=this.action===null?[]:this.marks();const counts=new Map;for(const m of marks){const kind=m.data?m.data.kind:"";counts.set(kind,(counts.get(kind)||0)+1)}const label=(this.actions.find((a=>a.id===this.action))||{}).label||"";const summary=Array.from(counts,(([kind,n])=>TeLabPopover.describeCount(kind,n))).join(", ");for(const item of this.items){const count=item.querySelector(".te-lab-action-count");count.textContent=item.dataset.action===this.action?String(marks.length):""}this.results.hidden=this.action===null;if(this.action!==null){const legend=this.legend;legend.replaceChildren();if(marks.length===0){legend.textContent=`${label}: nothing to mark.`}else{for(const[kind,n]of counts){const entry=document.createElement("span");entry.className="te-lab-legend-entry";const swatch=document.createElement("span");swatch.className=`te-lab-swatch te-lab-mark ${TeLabPopover.kindClass(kind)}`;swatch.setAttribute("aria-hidden","true");swatch.textContent="abc";entry.append(swatch,document.createTextNode(TeLabPopover.describeCount(kind,n)));legend.appendChild(entry)}}const rows=marks.filter((m=>m.data&&typeof m.data.proposal==="string"));const list=document.createDocumentFragment();for(const m of rows){const li=document.createElement("li");li.className="te-lab-proposal";const from=document.createElement("span");from.className="te-lab-from";from.textContent=TeLabPopover.visible(m.data.text);const arrow=document.createElement("i");arrow.className="fa-solid fa-arrow-right-long te-lab-arrow";arrow.setAttribute("aria-hidden","true");const to=document.createElement("span");to.className="te-lab-to";to.textContent=TeLabPopover.visible(m.data.proposal);const button=document.createElement("button");button.type="button";button.className="te-lab-accept";button.dataset.mark=m.id;button.textContent="Accept";button.setAttribute("aria-label",`Accept: replace “${m.data.text}” with “${m.data.proposal}”`);li.append(from,arrow,to,button);list.appendChild(li)}this.proposals.replaceChildren(list);this.proposals.hidden=rows.length===0}const showChip=!this.isOpen&&marks.length>0;this.chip.hidden=!showChip;if(showChip){this.chipText.textContent=summary;this.position()}}emit(type,detail){this.root.dispatchEvent(new CustomEvent(type,{bubbles:true,detail:Object.assign({editor:this.editor},detail)}))}destroy(){if(this.destroyed)return;if(this.surface&&!this.surface.destroyed&&this.registry)this.registry.clear(TE_LAB_LAYER);if(this.trim)this.trim.destroy();this.destroyed=true;this.isOpen=false;this.offChange();this.abortController.abort();const pill=this.pill();if(pill){pill.removeAttribute("aria-haspopup");pill.removeAttribute("aria-expanded");pill.removeAttribute("aria-controls")}this.root.remove();this.chip.remove();this.descriptions.remove()}}window.TeLabPopover=TeLabPopover;const BLOCKS_VIEW_STORAGE_KEY="terraphim-editor:view";const BLOCK_LABELS={heading:"Heading",paragraph:"Paragraph",list:"List",code:"Code",quote:"Quote",table:"Table",rule:"Rule"};const RE_BLANK=/^[ \t\r]*$/;const RE_FENCE=/^ {0,3}(`{3,}|~{3,})/;const RE_ATX=/^ {0,3}(#{1,6})(?:[ \t]|\r?$)/;const RE_RULE=/^ {0,3}([-*_])(?:[ \t]*\1){2,}[ \t\r]*$/;const RE_SETEXT=/^ {0,3}(=+|-+)[ \t\r]*$/;const RE_QUOTE=/^ {0,3}>/;const RE_LIST=/^ {0,3}(?:[-+*]|\d{1,9}[.)])(?:[ \t]|\r?$)/;const RE_INDENTED=/^(?: {4}|\t)/;const RE_TABLE_DELIM=/^ {0,3}\|?[ \t]*:?-+:?[ \t]*(?:\|[ \t]*:?-+:?[ \t]*)*\|?[ \t\r]*$/;class BlocksView{constructor(editor,options={}){this.editor=editor;this.surface=editor.surface;this.signal=options.signal;this.view="text";this.destroyed=false;this.model={lead:"",blocks:[]};this.focusIndex=0;this.editing=null;this.applying=false;this.dirty=false;this.drafts=[];this.nextDraftId=1;this.resumeAfterWriteOn=false;this.build();this.offChange=this.surface.onChange((change=>this.onSurfaceChange(change)));this.listen(document,"te:mode-change",(e=>this.onModeChange(e)));this.listen(document,"te:tools-change",(e=>this.onToolsChange(e)));if(BlocksView.loadPreference()==="blocks"){if(this.writeOnActive()||!this.toolsVisible())this.resumeAfterWriteOn=true;else this.setView("blocks",{persist:false,focus:false})}}static parse(source){const src=String(source);const lines=[];let at=0;while(at<=src.length){const nl=src.indexOf("\n",at);const end=nl<0?src.length:nl;lines.push({start:at,end:end,text:src.slice(at,end)});if(nl<0)break;at=nl+1}const blank=i=>RE_BLANK.test(lines[i].text);const raw=[];let i=0;const n=lines.length;while(i{const nextStart=k+1=0;i-=1){if(offset>=blocks[i].start)return i}return 0}static serialise(model){let out=model.lead||"";for(const b of model.blocks)out+=b.text+b.sep;return out}static loadPreference(){try{return window.localStorage.getItem(BLOCKS_VIEW_STORAGE_KEY)}catch(e){return null}}static savePreference(view){try{window.localStorage.setItem(BLOCKS_VIEW_STORAGE_KEY,view)}catch(e){}}listen(target,type,fn,options={}){target.addEventListener(type,fn,{...options,signal:this.signal})}build(){const root=this.surface.root;const toolbar=this.editor.toolbar?this.editor.toolbar.parentNode:null;if(toolbar){const divider=document.createElement("sl-divider");divider.setAttribute("vertical","");divider.className="te-view-divider";const group=document.createElement("sl-button-group");group.className="te-view-toggle";group.setAttribute("label","View");const button=(view,label,icon)=>{const b=document.createElement("sl-button");b.setAttribute("size","small");b.dataset.view=view;b.setAttribute("aria-pressed","false");b.setAttribute("title",`${label} view`);const i=document.createElement("i");i.className=`${icon} te-view-icon`;i.setAttribute("aria-hidden","true");b.append(i,document.createTextNode(` ${label}`));this.listen(b,"click",(()=>this.setView(view)));group.appendChild(b);return b};this.textButton=button("text","Text","fa-solid fa-align-left");this.blocksButton=button("blocks","Blocks","fa-solid fa-layer-group");const anchor=this.editor.toolbar.nextSibling;toolbar.insertBefore(divider,anchor);toolbar.insertBefore(group,anchor);this.toggleGroup=group;this.toggleDivider=divider}const container=document.createElement("div");container.className="te-blocks";container.hidden=true;container.setAttribute("aria-label","Document blocks");const list=document.createElement("div");list.className="te-blocks-list";list.setAttribute("role","list");list.setAttribute("aria-label","Blocks");const add=document.createElement("button");add.type="button";add.className="te-blocks-add";const addIcon=document.createElement("i");addIcon.className="fa-solid fa-plus";addIcon.setAttribute("aria-hidden","true");add.append(addIcon,document.createTextNode(" Add paragraph"));this.listen(add,"click",(()=>this.insertBlockAfter(this.model.blocks.length-1)));const region=document.createElement("div");region.className="te-blocks-notice-region";region.setAttribute("role","status");region.setAttribute("aria-live","polite");const notice=document.createElement("div");notice.className="te-blocks-notice";const noticeText=document.createElement("p");noticeText.className="te-blocks-notice-text";const dismiss=document.createElement("button");dismiss.type="button";dismiss.className="te-blocks-notice-dismiss";dismiss.setAttribute("aria-label","Dismiss message");dismiss.title="Dismiss message";dismiss.tabIndex=-1;const dismissIcon=document.createElement("i");dismissIcon.className="fa-solid fa-xmark";dismissIcon.setAttribute("aria-hidden","true");dismiss.appendChild(dismissIcon);this.listen(dismiss,"click",(()=>{this.showNotice(null);this.setCurrent(this.focusIndex,true)}));notice.append(noticeText,dismiss);notice.hidden=true;region.appendChild(notice);this.noticeRegion=region;this.noticeElement=notice;this.noticeText=noticeText;this.noticeDismiss=dismiss;container.append(region,list,add);root.parentNode.insertBefore(container,root.nextSibling);const drafts=document.createElement("div");drafts.className="te-blocks-drafts";drafts.hidden=true;drafts.tabIndex=-1;drafts.setAttribute("role","region");drafts.setAttribute("aria-label","Kept block drafts");drafts.setAttribute("aria-live","polite");root.parentNode.insertBefore(drafts,root);this.draftsElement=drafts;this.listen(drafts,"click",(e=>{const b=e.target.closest("[data-draft-action]");if(!b)return;const id=Number(b.dataset.draftId);if(b.dataset.draftAction==="apply")this.applyDraft(id);else this.discardDraft(id)}));this.container=container;this.list=list;this.addButton=add;this.listen(list,"keydown",(e=>this.onKeyDown(e)));this.listen(list,"mousedown",(e=>{if(this.editing&&e.target.closest('.te-block-action[data-action="up"], .te-block-action[data-action="down"]')){e.preventDefault()}}));this.listen(list,"click",(e=>this.onClick(e)));this.listen(list,"dblclick",(e=>{const card=e.target.closest(".te-block");if(card&&!e.target.closest("textarea"))this.editBlock(Number(card.dataset.index))}));this.listen(list,"focusin",(e=>{const card=e.target.closest(".te-block");if(card&&e.target===card)this.setCurrent(Number(card.dataset.index),false)}))}render(focus=false){this.dirty=false;this.model=BlocksView.parse(this.surface.getText());const blocks=this.model.blocks;if(this.focusIndex>=blocks.length)this.focusIndex=Math.max(0,blocks.length-1);const cards=blocks.map(((b,i)=>this.renderCard(b,i,blocks.length)));const keep=this.editing&&this.editing.card.parentNode===this.list?this.editing:null;if(keep){const k=keep.isNew?blocks.filter((b=>b.start{if(!keep.isNew&&i===k)return;if(ifrag.appendChild(c)));if(blocks.length===0){const empty=document.createElement("p");empty.className="te-blocks-empty";empty.textContent="The document is empty. Add a paragraph to start.";frag.appendChild(empty)}this.list.textContent="";this.list.appendChild(frag);if(focus)this.focusCard(this.focusIndex)}renderCard(block,index,total){const card=document.createElement("div");card.className=`te-block te-block-${block.type}`;card.setAttribute("role","listitem");card.dataset.index=String(index);card.dataset.type=block.type;card.tabIndex=index===this.focusIndex?0:-1;const label=BLOCK_LABELS[block.type]+(block.level?` ${block.level}`:"");card.setAttribute("aria-label",`${label}, block ${index+1} of ${total}. Enter to edit, Shift+Enter to add a paragraph below, Alt+Up or Alt+Down to move, Delete to remove, Tab for actions.`);card.setAttribute("aria-keyshortcuts","Enter F2 Shift+Enter Alt+ArrowUp Alt+ArrowDown Delete");const head=document.createElement("div");head.className="te-block-head";const tag=document.createElement("span");tag.className="te-block-type";tag.textContent=block.type==="heading"?`H${block.level}`:BLOCK_LABELS[block.type];const actions=document.createElement("span");actions.className="te-block-actions";const action=(name,label,icon,keys,hidden=false)=>{const b=document.createElement("button");b.type="button";b.className="te-block-action";b.dataset.action=name;b.hidden=hidden;b.tabIndex=index===this.focusIndex&&!hidden?0:-1;b.setAttribute("aria-label",label);b.setAttribute("aria-keyshortcuts",keys);b.title=label;const i=document.createElement("i");i.className=icon;i.setAttribute("aria-hidden","true");b.appendChild(i);actions.appendChild(b)};action("edit","Edit block (Enter)","fa-solid fa-pen","Enter F2");action("up","Move block up (Alt+Up)","fa-solid fa-arrow-up","Alt+ArrowUp",index===0);action("down","Move block down (Alt+Down)","fa-solid fa-arrow-down","Alt+ArrowDown",index===total-1);action("insert","Add paragraph below (Shift+Enter)","fa-solid fa-plus","Shift+Enter");action("delete","Delete block (Delete)","fa-regular fa-trash-can","Delete");head.append(tag,actions);const body=document.createElement("div");body.className="te-block-body";const html=this.renderMarkdown(block.text);if(html===null){const pre=document.createElement("pre");pre.className="te-block-source";pre.textContent=block.text;body.appendChild(pre)}else{body.innerHTML=html}card.append(head,body);return card}renderMarkdown(text){const api=this.editor.config&&this.editor.config.bindings||window.wasmBindings;if(!api||typeof api.render_markdown!=="function")return null;try{return api.render_markdown(text)}catch(e){return null}}cards(){return Array.from(this.list.querySelectorAll(":scope > .te-block"))}setCurrent(index,focus=true){const cards=this.cards();if(cards.length===0)return;const i=Math.max(0,Math.min(cards.length-1,index));this.focusIndex=i;cards.forEach(((c,k)=>{c.tabIndex=k===i?0:-1;for(const b of c.querySelectorAll(".te-block-action"))b.tabIndex=k===i&&!b.hidden?0:-1}));if(focus)cards[i].focus()}focusCard(index){this.setCurrent(index,true)}isActive(){return this.view==="blocks"}writeOnActive(){const chrome=this.editor.chrome;return!!(chrome&&typeof chrome.isWriteOn==="function"&&chrome.isWriteOn())}toolsVisible(){const chrome=this.editor.chrome;return!(chrome&&typeof chrome.toolsVisible==="function")||chrome.toolsVisible()}setView(view,{persist:persist=true,focus:focus=true}={}){if(this.destroyed)return;const next=view==="blocks"?"blocks":"text";if(next==="blocks"&&this.writeOnActive())return;if(persist)BlocksView.savePreference(next);if(next===this.view)return;if(next==="blocks"){const sel=this.surface.getSelectionOffsets();this.view="blocks";this.render(false);this.focusIndex=this.blockIndexAt(sel.start);this.surface.root.hidden=true;this.container.hidden=false;this.setCurrent(this.focusIndex,focus)}else{let target=null;if(this.editing){target=this.commitEdit({rerender:false})}if(!target){const b=this.model.blocks[this.focusIndex];target=b?{start:b.start,end:b.start}:null}this.view="text";this.editing=null;this.container.hidden=true;this.surface.root.hidden=false;this.list.textContent="";if(target){if(focus)this.surface.root.focus({preventScroll:true});this.surface.setSelectionOffsets(target.start,target.end)}}this.updateToggle();this.container.dispatchEvent(new CustomEvent("te:view-change",{bubbles:true,detail:{view:this.view,editor:this.editor}}))}toggle(){this.setView(this.isActive()?"text":"blocks")}updateToggle(){for(const[b,view]of[[this.textButton,"text"],[this.blocksButton,"blocks"]]){if(!b)continue;const on=this.view===view;b.setAttribute("aria-pressed",on?"true":"false");b.setAttribute("variant",on?"primary":"default")}this.container.dataset.view=this.view}blockIndexAt(offset){return BlocksView.indexIn(this.model,offset)}onModeChange(e){const mode=e.detail&&e.detail.mode;if(mode==="write-on"){if(this.isActive()){this.resumeAfterWriteOn=true;this.setView("text",{persist:false,focus:false})}}else if(mode==="plain"&&this.resumeAfterWriteOn&&this.toolsVisible()){this.resumeAfterWriteOn=false;this.setView("blocks",{persist:false,focus:false})}}onToolsChange(e){if(!e.detail||e.detail.editor!==this.editor)return;if(!e.detail.visible){if(this.isActive()){this.resumeAfterWriteOn=true;this.setView("text",{persist:false,focus:false})}}else if(this.resumeAfterWriteOn&&!this.writeOnActive()){this.resumeAfterWriteOn=false;this.setView("blocks",{persist:false,focus:false})}}onSurfaceChange(change){const edit=change&&change.edit;const whole=!!edit&&BlocksView.replacesWhole(change);if(edit&&this.drafts.length)this.mapDrafts(edit,whole);let overlapped=null;if(this.editing&&edit&&!this.mapEditing(edit,whole)){overlapped=this.editing;this.editing=null}const hadFocus=this.container.contains(document.activeElement);if(overlapped)this.keepDraft(overlapped,edit,whole);else if(edit&&this.drafts.length)this.renderDrafts();if(!this.isActive())return;if(this.applying){this.dirty=true;return}this.render(hadFocus&&!this.editing&&!overlapped);if(overlapped&&hadFocus)this.draftsElement.focus({preventScroll:true})}static replacesWhole(change){const e=change.edit;const oldLength=change.text.length-e.insertedText.length+e.deletedLength;if(change.source==="open")return true;if(e.move)return false;return e.start===0&&e.deletedLength===oldLength&&oldLength>0}static mapThroughMove(move,a,b){const{start:start,end:end,to:to}=move;const len=end-start;const down=to>end;const lo=down?end:to;const hi=down?to:start;const inside=down?to-end:to-start;const between=down?-len:len;const inZone=(zs,ze)=>a>=zs&&(a===b?a=Math.max(end,to))return[a,b];return null}mapEditing(edit,whole){if(whole)return false;const ed=this.editing;if(edit.move){const mapped=BlocksView.mapThroughMove(edit.move,ed.start,ed.start+ed.original.length);if(!mapped)return false;ed.start=mapped[0];return true}const a=edit.start;const b=a+edit.deletedLength;const s=ed.start;const e=s+ed.original.length;if(b<=s){ed.start=s+edit.insertedText.length-edit.deletedLength;return true}return a>=e}mapDrafts(edit,whole){const a=edit.start;const b=a+edit.deletedLength;const delta=edit.insertedText.length-edit.deletedLength;for(const d of this.drafts){if(d.anchor===null)continue;if(whole)d.anchor=null;else if(edit.move)d.anchor=BlocksView.mapThroughMove(edit.move,d.anchor,d.anchor)[0];else if(b<=d.anchor)d.anchor+=delta;else if(a{const b=document.createElement("button");b.type="button";b.className=`te-blocks-draft-${action}`;b.dataset.draftAction=action;b.dataset.draftId=String(d.id);b.textContent=label;actions.appendChild(b)};const target=this.draftTarget(d);button("apply",target.mode==="replace"?"Replace block":"Insert as new paragraph");actions.querySelector('[data-draft-action="apply"]').dataset.draftMode=target.mode;button("discard","Discard");item.append(msg,pre,actions);el.appendChild(item)}}keptDrafts(){return this.drafts.map((d=>({...d})))}pendingDrafts(){const out=this.drafts.map((d=>({value:d.value,originalText:d.originalText,anchor:d.anchor,index:d.index,isNew:d.isNew})));const ed=this.editing;if(ed){const value=window.EditorSurface.normaliseNewlines(ed.textarea.value);if(ed.isNew?value.trim()!=="":value!==ed.original){out.push({value:value,originalText:ed.isNew?"":ed.original,anchor:ed.start,index:ed.index,isNew:!!ed.isNew})}}return out}draftTarget(draft,text=this.surface.getText()){const model=BlocksView.parse(text);const blocks=model.blocks;if(blocks.length===0)return{mode:"insert",at:text.length};if(draft.anchor===null){const b=blocks[Math.min(draft.index,blocks.length-1)];if(!draft.isNew&&draft.indexb.startd.id===id));if(!draft)return null;if(this.editing)this.commitEdit({rerender:false});this.drafts=this.drafts.filter((d=>d!==draft));this.renderDrafts();const target=this.draftTarget(draft);let caret;this.applying=true;try{if(target.mode==="replace"){const b=target.block;const d=window.EditorSurface.diff(b.text,draft.value);caret=b.start;if(d){this.surface.replaceRange(b.start+d.start,b.start+d.start+d.deletedLength,d.insertedText,{source:"blocks",selectStart:b.start})}}else{caret=this.insertParagraph(target.at,draft.value)}}finally{this.applying=false}if(this.isActive()){this.render(false);this.setCurrent(this.blockIndexAt(caret),true)}return target.mode}discardDraft(id){const before=this.drafts.length;this.drafts=this.drafts.filter((d=>d.id!==id));this.renderDrafts();if(this.isActive()&&this.drafts.length===0&&before)this.setCurrent(this.focusIndex,true);return this.drafts.length{ta.rows=Math.max(2,ta.value.split("\n").length+1)}));const remember=()=>{if(this.editing&&this.editing.textarea===ta&&document.activeElement===ta){this.editing.selection=[ta.selectionStart,ta.selectionEnd,ta.selectionDirection]}};this.listen(document,"selectionchange",remember);this.listen(ta,"select",remember);this.listen(ta,"keyup",remember);this.listen(ta,"blur",(e=>{if(this.editing&&this.editing.textarea===ta)this.commitEdit({focus:!e.relatedTarget})}));ta.focus({preventScroll:true});ta.setSelectionRange(ta.value.length,ta.value.length);return ta}commitEdit({rerender:rerender=true,focus:focus}={}){const edit=this.editing;if(!edit)return null;this.editing=null;const ta=edit.textarea;const value=window.EditorSurface.normaliseNewlines(ta.value);const selStart=Math.min(ta.selectionStart,value.length);const selEnd=Math.min(ta.selectionEnd,value.length);let base=edit.start;this.applying=true;try{const text=this.surface.getText();const stale=edit.isNew?edit.start>text.length:text.slice(edit.start,edit.start+edit.original.length)!==edit.original;if(stale){this.keepDraft(edit,null,false)}else if(edit.isNew){if(value.trim()!==""){base=this.insertParagraph(edit.start,value);this.surface.setSelectionOffsets(base+selStart,base+selEnd);this.focusIndex=edit.index}}else if(value!==edit.original){const d=window.EditorSurface.diff(edit.original,value);const from=edit.start+d.start;this.surface.replaceRange(from,from+d.deletedLength,d.insertedText,{source:"blocks",selectStart:edit.start+selStart,selectEnd:edit.start+selEnd})}}finally{this.applying=false}if(rerender&&this.isActive()){const hadFocus=focus===undefined?this.container.contains(document.activeElement)||document.activeElement===document.body:focus;this.render(false);if(this.model.blocks.length){this.focusIndex=this.blockIndexAt(base+selStart);this.setCurrent(this.focusIndex,hadFocus)}}return{start:base+selStart,end:base+selEnd}}cancelEdit(){const edit=this.editing;if(!edit)return null;this.editing=null;const kept=this.keepDraft(edit,null,false,"escaped");this.render(false);this.setCurrent(edit.index,true);return kept}insertBlockAfter(index){if(!this.isActive())return null;if(this.editing)this.commitEdit();const blocks=this.model.blocks;const text=this.surface.getText();let at;if(index+10)from=blocks[index-1].end}this.apply(from,to,"");this.focusIndex=Math.max(0,Math.min(index,this.model.blocks.length-1));this.setCurrent(this.focusIndex,true)}static planMove(model,text,index,dir){const blocks=model.blocks;const j=dir<0?index-1:index;const p=blocks[j];const q=blocks[j+1];if(!p||!q)return null;const r=blocks[j+2];const pairEnd=r?r.start:text.length;const range=dir<0?[q.start,pairEnd,p.start]:[p.start,q.start,pairEnd];const blank=sep=>/\n[ \t\r]*\n/.test(sep);const between=blank(p.sep)?p.sep:"\n\n";const after=!r||blank(q.sep)?q.sep:"\n\n";const base=p.start;const predicted=text.slice(0,base)+q.text+between+p.text+after+text.slice(pairEnd);const expected=blocks.map((b=>b.text));expected[j]=q.text;expected[j+1]=p.text;const got=BlocksView.parse(predicted);if(got.lead!==model.lead||got.blocks.length!==expected.length||got.blocks.some(((b,k)=>b.text!==expected[k]))){return{error:"Block not moved: it would merge with the block next to it."}}const fixes=[];const sepAfter=base+q.text.length+q.sep.length+p.text.length;if(p.sep!==after)fixes.push({at:sepAfter,from:p.sep,to:after});if(q.sep!==between)fixes.push({at:base+q.text.length,from:q.sep,to:between});const movedStart=dir<0?base:base+q.text.length+between.length;return{range:range,fixes:fixes,text:predicted,movedStart:movedStart}}moveBlock(index,direction,{focusAction:focusAction=null}={}){if(this.destroyed)return false;const dir=direction==="up"||direction<0?-1:1;let i=index;if(this.editing){const ed=this.editing;const before=BlocksView.parse(this.surface.getText()).blocks.length;this.commitEdit({rerender:false});const after=BlocksView.parse(this.surface.getText()).blocks.length;if(ed.index=0&&i1)this.surface.squashHistory(steps,"move");this.surface.setSelectionOffsets(plan.movedStart,plan.movedStart)}catch(err){refused="Block not moved: "+String(err&&err.message?err.message:err);if(steps>0){if(steps>1)this.surface.squashHistory(steps,"move");this.surface.undo()}}finally{this.applying=false}}this.showNotice(refused);if(this.isActive()){this.render(false);const at=refused?i:target;const card=this.cards()[at];const button=focusAction&&card?card.querySelector(`.te-block-action[data-action="${focusAction}"]`):null;this.setCurrent(at,!(button&&!button.hidden));if(button&&!button.hidden)button.focus()}return!refused}fixUpBlocker(plan){if(!plan.fixes.length||typeof this.editor.annotations!=="function")return null;let annotations;try{annotations=this.editor.annotations()}catch(err){return null}const[start,end,to]=plan.range;const move={start:start,end:end,to:to};const items=[...annotations.spans||[],...annotations.ghosts||[]];for(const item of items){const a=item.anchor.start;const b=item.anchor.end;let mapped=BlocksView.mapThroughMove(move,a,b);if(!mapped){if(a<=Math.min(start,to)&&b>=Math.max(end,to))mapped=[a,b];else continue}const[gs,ge]=mapped;for(const fix of plan.fixes){const d=window.EditorSurface.diff(fix.from,fix.to);const at=fix.at+d.start;const touches=d.deletedLength===0?gsgs;if(touches){return`Block not moved: restoring the blank line between the blocks would change the text of "${item.id}"; move whole spans and ghosts only.`}}}return null}showNotice(message){if(!this.noticeElement)return;this.noticeElement.hidden=!message;this.noticeDismiss.tabIndex=message?0:-1;this.noticeText.textContent=message||""}apply(from,to,insert){if(this.editing)this.commitEdit({rerender:false});this.applying=true;try{this.surface.replaceRange(from,to,insert,{source:"blocks",selectStart:from})}finally{this.applying=false}this.render(false)}undo(){if(this.editing)this.commitEdit({rerender:false});this.applying=true;let done;try{done=this.surface.undo()}finally{this.applying=false}if(done)this.afterHistory();return done}redo(){if(this.editing)this.commitEdit({rerender:false});this.applying=true;let done;try{done=this.surface.redo()}finally{this.applying=false}if(done)this.afterHistory();return done}afterHistory(){const{start:start}=this.surface.getSelectionOffsets();this.render(false);this.focusIndex=this.blockIndexAt(start);this.setCurrent(this.focusIndex,true)}onClick(e){const button=e.target.closest(".te-block-action");if(!button)return;const card=button.closest(".te-block");const index=Number(card.dataset.index);switch(button.dataset.action){case"edit":this.editBlock(index);break;case"insert":this.insertBlockAfter(index);break;case"up":this.moveBlock(index,"up",{focusAction:"up"});break;case"down":this.moveBlock(index,"down",{focusAction:"down"});break;case"delete":this.deleteBlock(index);break;default:break}}onKeyDown(e){if(e.isComposing)return;const mod=e.ctrlKey||e.metaKey;if(e.target.classList&&e.target.classList.contains("te-block-editor")){if(e.key==="Escape"){e.preventDefault();this.cancelEdit()}else if(e.key==="Enter"&&mod){e.preventDefault();this.commitEdit()}return}const card=e.target.closest&&e.target.closest(".te-block");if(card&&e.key==="Escape"&&e.target.classList&&e.target.classList.contains("te-block-action")){e.preventDefault();card.focus();return}if(!card||e.target!==card)return;const index=Number(card.dataset.index);const key=e.key;if(e.altKey&&!mod&&!e.shiftKey&&(key==="ArrowUp"||key==="ArrowDown")){e.preventDefault();this.moveBlock(index,key==="ArrowUp"?"up":"down");return}if(e.altKey)return;if(mod){const k=key.toLowerCase();if(k==="z"&&!e.shiftKey){e.preventDefault();this.undo()}else if(k==="z"&&e.shiftKey||k==="y"&&!e.shiftKey){e.preventDefault();this.redo()}return}switch(key){case"ArrowDown":e.preventDefault();this.setCurrent(index+1);break;case"ArrowUp":e.preventDefault();this.setCurrent(index-1);break;case"Home":e.preventDefault();this.setCurrent(0);break;case"End":e.preventDefault();this.setCurrent(this.model.blocks.length-1);break;case"Enter":e.preventDefault();if(e.shiftKey)this.insertBlockAfter(index);else this.editBlock(index);break;case"F2":e.preventDefault();this.editBlock(index);break;case"Delete":e.preventDefault();this.deleteBlock(index);break;default:break}}destroy(){if(this.destroyed)return[];const drafts=this.pendingDrafts();if(drafts.length&&this.surface.root){this.surface.root.dispatchEvent(new CustomEvent("te:blocks-drafts",{bubbles:true,detail:{editor:this.editor,drafts:drafts.map((d=>({...d})))}}))}this.destroyed=true;this.editing=null;this.offChange();if(this.surface.root)this.surface.root.hidden=false;this.container.remove();this.draftsElement.remove();if(this.toggleGroup)this.toggleGroup.remove();if(this.toggleDivider)this.toggleDivider.remove();this.drafts=[];return drafts}}window.BlocksView=BlocksView;window.BLOCKS_VIEW_STORAGE_KEY=BLOCKS_VIEW_STORAGE_KEY;let teAltPanelInstances=0;const TE_ALT_KINDS=["word","sentence","paragraph"];const TE_ALT_KIND_LABEL={word:"Word",sentence:"Sentence",paragraph:"Paragraph"};const teAltText={binding(api,name){if(!api||typeof api[name]!=="function"){throw new Error("Shared text WASM bindings are unavailable. Reload with the current editor WASM package.")}return api[name]},wordAt(text,pos,api=window.wasmBindings){return teAltText.rangeFor("word",text,pos,api)},trimRange(text,start,end){let s=start;let e=end;while(s\s*)/.exec(text.slice(s,e));if(lead)s+=lead[0].length;while(e>s&&/\s/.test(text[e-1]))e-=1;return e>s?{start:s,end:e}:null},paragraphAt(text,pos,api=window.wasmBindings){return teAltText.rangeFor("paragraph",text,pos,api)},sentenceAt(text,pos,api=window.wasmBindings){return teAltText.rangeFor("sentence",text,pos,api)},rangeFor(kind,text,pos,api=window.wasmBindings){if(!Number.isInteger(pos)||pos<0||pos>text.length)throw new Error("Caret offset is outside the current text.");return teAltText.binding(api,"text_range_at")(text,pos,kind)},inferKind(text,api=window.wasmBindings){return teAltText.binding(api,"text_infer_kind")(text)}};class TeAlternativesPanel{constructor(editor,options={}){this.editor=editor;this.surface=editor.surface;this.instance=++teAltPanelInstances;this.destroyed=false;this.opened=false;this.tab="word";this.target=null;this.reference=0;this.lastSpan=null;this.span=null;this.returnFocus=null;this.frame=0;this.retargetFrame=0;this.live=null;this.liveBusy=false;this.renderCount=0;this.abortController=new AbortController;const signal=this.abortController.signal;this.signal=signal;if(options.signal){if(options.signal.aborted)this.abortController.abort();else options.signal.addEventListener("abort",(()=>this.destroy()),{signal:signal})}this.build();document.addEventListener("te:open-panel",(e=>{const d=e.detail||{};if(d.panel!=="alternatives"||d.editor&&d.editor!==this.editor)return;this.returnFocus=this.chromeControl();this.toggle({focus:true,fromControl:true})}),{signal:signal});document.addEventListener("te:dot",(e=>{const d=e.detail||{};if(d.editor&&d.editor!==this.editor)return;if(!d.spanId)return;this.open({spanId:String(d.spanId)},{focus:false,fromControl:true});this.returnFocus=this.surface.root;this.savedSelection=null}),{signal:signal});document.addEventListener("te:mode-change",(e=>{const d=e.detail||{};if(d.editor&&d.editor!==this.editor)return;if(d.mode!=="write-on")this.close({restoreFocus:false})}),{signal:signal});document.addEventListener("selectionchange",(()=>this.onSelectionChange()),{signal:signal});this.offChange=this.surface.onChange((()=>this.scheduleRefresh()));this.surface.root.addEventListener("keydown",(e=>this.onSurfaceKeyDown(e)),{signal:signal});this.registerMenuItem()}static get text(){return teAltText}build(){const signal=this.signal;const id=name=>`te-alt-${this.instance}-${name}`;const root=document.createElement("aside");root.className="te-alt-panel";root.id=id("panel");root.hidden=true;root.setAttribute("aria-label","Alternatives");const tablist=document.createElement("div");tablist.className="te-alt-tabs";tablist.setAttribute("role","tablist");tablist.setAttribute("aria-label","Granularity");this.tabs={};for(const kind of TE_ALT_KINDS){const tab=document.createElement("button");tab.type="button";tab.className="te-alt-tab";tab.id=id(`tab-${kind}`);tab.dataset.kind=kind;tab.setAttribute("role","tab");tab.setAttribute("aria-controls",id("tabpanel"));tab.textContent=TE_ALT_KIND_LABEL[kind];tab.addEventListener("click",(()=>this.setTab(kind,{focusTab:true})),{signal:signal});tablist.appendChild(tab);this.tabs[kind]=tab}tablist.addEventListener("keydown",(e=>this.onTabKeyDown(e)),{signal:signal});const tabpanel=document.createElement("div");tabpanel.className="te-alt-tabpanel";tabpanel.id=id("tabpanel");tabpanel.setAttribute("role","tabpanel");const status=document.createElement("p");status.className="te-alt-status";status.setAttribute("role","status");status.setAttribute("aria-live","polite");const list=document.createElement("ul");list.className="te-alt-list";list.setAttribute("aria-label","Alternatives, one per line");list.addEventListener("keydown",(e=>this.onLineKeyDown(e)),{signal:signal});list.addEventListener("focusout",(e=>this.onLineBlur(e)),{signal:signal});list.addEventListener("input",(e=>this.onLineInput(e)),{signal:signal});list.addEventListener("compositionend",(e=>this.onLineInput(e)),{signal:signal});list.addEventListener("click",(e=>this.onListClick(e)),{signal:signal});const hint=document.createElement("p");hint.className="te-alt-hint";hint.textContent="↑↓ choose · Alt+↑↓ reorder · Enter adds · Esc closes";tabpanel.append(status,list,hint);root.append(tablist,tabpanel);root.addEventListener("keydown",(e=>this.onPanelKeyDown(e)),{signal:signal});this.root=root;this.tablist=tablist;this.tabpanel=tabpanel;this.statusEl=status;this.list=list;this.ids={panel:root.id};const host=this.editor.root||this.editor.input&&this.editor.input.closest(".te-app")||document.body;host.appendChild(root);this.host=host}chromeControl(){const chrome=this.editor.chrome;return chrome&&chrome.root?chrome.root.querySelector('[data-control="alternatives"]'):null}modelAvailable(){const api=this.editor.documentApi?this.editor.documentApi():null;return!!(api&&typeof api.alt_create_span==="function"&&typeof api.document_annotations==="function")}liveSpans(){if(!this.modelAvailable())return[];const ann=this.editor.annotations();return ann&&Array.isArray(ann.spans)?ann.spans:[]}static spanAround(spans,start,end){return spans.find((s=>s.anchor.start<=start&&end<=s.anchor.end))||null}isOpen(){return this.opened}isWriteOn(){const chrome=this.editor.chrome;if(chrome&&typeof chrome.isWriteOn==="function")return chrome.isWriteOn();return document.body.dataset.mode==="write-on"}ensureWriteOn(){if(this.isWriteOn())return;const chrome=this.editor.chrome;if(chrome&&typeof chrome.setMode==="function")chrome.setMode("write-on")}toggle(options={}){if(this.opened){this.close({restoreFocus:true});return false}this.open(undefined,options);return true}open(where,options={}){if(this.destroyed||!this.modelAvailable())return false;this.ensureWriteOn();const wasOpen=this.opened;if(!wasOpen)this.savedSelection=this.surface.getSelectionOffsets();if(!options.fromControl){const active=document.activeElement;if(!wasOpen||!(active&&this.root.contains(active))){const chromeRoot=this.editor.chrome&&this.editor.chrome.root;const fromChrome=!!active&&!!chromeRoot&&chromeRoot.contains(active);this.returnFocus=fromChrome?active:this.surface.root;if(!fromChrome)this.savedSelection=this.surface.getSelectionOffsets()}}this.resolve(where);this.opened=true;this.root.hidden=false;this.host.classList.add("te-alt-open");const control=this.chromeControl();if(control){control.setAttribute("aria-expanded","true");control.setAttribute("aria-controls",this.root.id)}this.render();if(options.focus!==false)this.focusLine(this.activeLineIndex());return true}close({restoreFocus:restoreFocus=true}={}){if(!this.opened)return;this.finishLive();this.commitFocused();this.opened=false;this.root.hidden=true;this.host.classList.remove("te-alt-open");const control=this.chromeControl();if(control)control.setAttribute("aria-expanded","false");const back=this.returnFocus;this.returnFocus=null;if(!restoreFocus)return;if(!back||back===this.surface.root||!back.isConnected){this.surface.focus();const sel=this.savedSelection;if(sel)this.surface.setSelectionOffsets(sel.start,sel.end)}else if(typeof back.focus==="function"){back.focus()}}openForSelection(ctx){this.returnFocus=this.surface.root;this.savedSelection=ctx&&ctx.selection?ctx.selection:this.surface.getSelectionOffsets();if(!ctx||ctx.collapsed)return this.open(undefined,{fromControl:true});const text=this.surface.getText();const range=teAltText.trimRange(text,ctx.start,ctx.end)||{start:ctx.start,end:ctx.end};return this.open({start:range.start,end:range.end},{fromControl:true})}onSurfaceKeyDown(e){if(this.destroyed||e.defaultPrevented||e.isComposing)return;if(typeof teShortcutMatches!=="function"||!teShortcutMatches(e,"ctrl+shift+a"))return;if(!this.modelAvailable())return;const sel=this.surface.getSelectionOffsets();if(sel.start!==sel.end&&this.editor.selectionMenu)return;e.preventDefault();this.openForSelection({collapsed:sel.start===sel.end,start:sel.start,end:sel.end,selection:sel})}registerMenuItem(){const menu=this.editor.selectionMenu;if(!menu||typeof menu.register!=="function")return;this.offMenu=menu.register({id:"alternatives",label:"Alternatives for selection",key:"ctrl+shift+a",available:ctx=>this.modelAvailable()&&!!ctx&&!ctx.collapsed,run:ctx=>this.openForSelection(ctx)})}resolve(where){try{this.resolveTarget(where)}catch(error){this.targetingRefusal(error)}}targetingRefusal(error){this.span=null;this.target={message:String(error&&error.message?error.message:error)}}resolveTarget(where){const spans=this.liveSpans();const text=this.surface.getText();if(where&&where.spanId){const span=spans.find((s=>s.id===where.spanId));if(span){this.showSpan(span);return}}if(where&&Number.isInteger(where.start)&&Number.isInteger(where.end)&&where.end>where.start){const inside=TeAlternativesPanel.spanAround(spans,where.start,where.end);if(inside){this.showSpan(inside);return}if(where.start<0||where.end>text.length)throw new Error("Selected range is outside the current text.");const kind=where.kind||teAltText.inferKind(text.slice(where.start,where.end),this.editor.documentApi());if(!TE_ALT_KINDS.includes(kind))throw new Error(`Unknown text kind: ${kind}`);this.tab=kind;this.reference=where.start;this.setPending(spans,kind,where.start,where.end);return}const sel=this.surface.getSelectionOffsets();this.reference=sel.start;const at=TeAlternativesPanel.spanAround(spans,sel.start,sel.start);if(at){this.showSpan(at);return}this.resolveTab(spans)}resolveTab(spans=this.liveSpans()){const text=this.surface.getText();const pos=this.reference;const at=TeAlternativesPanel.spanAround(spans,pos,pos);if(at&&at.kind===this.tab){this.showSpan(at);return}let range;try{range=teAltText.rangeFor(this.tab,text,pos,this.editor.documentApi())}catch(error){this.targetingRefusal(error);return}if(!range){this.target={message:`Put the caret in a ${this.tab} to list its alternatives.`};this.span=null;return}this.setPending(spans,this.tab,range.start,range.end)}setPending(spans,kind,start,end){const text=this.surface.getText();const clash=spans.find((s=>starts.id===t.spanId));if(span){this.showSpan(span)}else if(this.lastSpan){const text=this.surface.getText();const{kind:kind,start:start}=this.lastSpan;const range=teAltText.rangeFor(kind,text,Math.min(start,text.length),this.editor.documentApi());this.tab=kind;if(range)this.setPending(spans,kind,range.start,range.end);else this.resolveTab(spans)}}else if(t.pending){const p=t.pending;const span=spans.find((s=>s.anchor.start===p.start&&s.anchor.end===p.end));if(span)this.showSpan(span);else if(this.surface.getText().slice(p.start,p.end)!==p.text)this.resolveTab(spans);else this.setPending(spans,p.kind,p.start,p.end)}else{this.resolveTab(spans)}this.render()}scheduleRefresh(){if(!this.opened||this.destroyed||this.frame)return;this.frame=requestAnimationFrame((()=>{this.frame=0;this.refresh()}))}flush(){if(this.frame){cancelAnimationFrame(this.frame);this.frame=0}this.refresh()}onSelectionChange(){if(!this.opened||this.destroyed||this.retargetFrame)return;if(document.activeElement!==this.surface.root)return;this.retargetFrame=requestAnimationFrame((()=>{this.retargetFrame=0;if(!this.opened||document.activeElement!==this.surface.root)return;const sel=this.surface.getSelectionOffsets();if(this.span&&this.span.anchor.start<=sel.start&&sel.start<=this.span.anchor.end)return;const at=TeAlternativesPanel.spanAround(this.liveSpans(),sel.start,sel.start);if(!at||this.span&&at.id===this.span.id)return;this.savedSelection=sel;this.showSpan(at);this.render()}))}lines(){if(this.span){return this.span.alts.map(((a,i)=>({text:a.text,source:a.source,model:a.model,active:i===this.span.active})))}if(this.target&&this.target.pending){return[{text:this.target.pending.text,source:"original",active:true}]}return[]}activeLineIndex(){if(this.span)return this.span.active;if(this.target&&this.target.pending)return 1;return-1}render(){this.renderCount+=1;for(const kind of TE_ALT_KINDS){const on=kind===this.tab;const tab=this.tabs[kind];tab.setAttribute("aria-selected",on?"true":"false");tab.tabIndex=on?0:-1;tab.classList.toggle("te-alt-tab--active",on)}this.tabpanel.setAttribute("aria-labelledby",this.tabs[this.tab].id);const focused=this.list.contains(document.activeElement)?document.activeElement:null;const focusedIndex=focused?this.lineInputs().indexOf(focused):-1;const focusedNew=!!focused&&focused.classList.contains("te-alt-input--new");const draft=focused&&focused.value!==focused.dataset.committed?focused.value:null;this.rendering=true;try{this.list.textContent=""}finally{this.rendering=false}const t=this.target||{};if(t.message){this.setStatus(t.message);this.list.hidden=true;if(focused)this.tabs[this.tab].focus();return}this.list.hidden=false;const lines=this.lines();const total=lines.length;lines.forEach(((line,i)=>this.list.appendChild(this.renderLine(line,i,total))));this.list.appendChild(this.renderNewLine());if(t.pending){this.setStatus(`New ${t.pending.kind}: type a first alternative and press Enter.`)}else if(!this.statusSticky){this.setStatus(`${TE_ALT_KIND_LABEL[this.tab]}: ${total} alternatives, ${this.span.active+1} of ${total} active.`,false,{quiet:true})}this.statusSticky=false;if(focused){const inputs=this.lineInputs();const input=focusedNew?inputs[inputs.length-1]:inputs[Math.min(focusedIndex,inputs.length-1)];if(draft!==null&&(focusedNew||!input.readOnly))input.value=draft;input.focus()}}renderLine(line,index,total){const li=document.createElement("li");li.className="te-alt-line";li.dataset.index=String(index);li.dataset.source=line.source;if(line.active)li.classList.add("te-alt-line--active");const glyph=document.createElement("i");glyph.setAttribute("aria-hidden","true");glyph.className=line.source==="ai"?"te-alt-glyph te-alt-glyph--ai fa-solid fa-robot":"te-alt-glyph te-alt-glyph--human fa-solid fa-circle";const input=document.createElement("input");input.type="text";input.className="te-alt-input";input.value=line.text;input.dataset.committed=line.text;input.spellcheck=false;input.setAttribute("autocomplete","off");const who=line.source==="ai"?line.model==="kg"?"knowledge graph synonym":"suggested by AI":line.source==="original"?"original, read-only":"written by you";input.setAttribute("aria-label",`Alternative ${index+1} of ${total}, ${who}${line.active?", active":""}`);if(index===0)input.readOnly=true;li.append(glyph,input);if(index>0){const del=document.createElement("button");del.type="button";del.className="te-alt-delete";del.tabIndex=-1;del.setAttribute("aria-label",`Delete alternative ${index+1}`);del.title="Delete";const x=document.createElement("i");x.className="fa-solid fa-xmark";x.setAttribute("aria-hidden","true");del.appendChild(x);li.appendChild(del)}return li}renderNewLine(){const li=document.createElement("li");li.className="te-alt-line te-alt-line--new";const glyph=document.createElement("i");glyph.setAttribute("aria-hidden","true");glyph.className="te-alt-glyph te-alt-glyph--new fa-solid fa-plus";const input=document.createElement("input");input.type="text";input.className="te-alt-input te-alt-input--new";input.dataset.committed="";input.placeholder="New alternative";input.spellcheck=false;input.setAttribute("autocomplete","off");input.setAttribute("aria-label","New alternative: type it and press Enter");li.append(glyph,input);return li}setStatus(message,sticky=false,{quiet:quiet=false}={}){this.statusEl.textContent=message||"";this.statusEl.classList.toggle("te-alt-status--quiet",quiet);if(sticky)this.statusSticky=true}lineInputs(){return Array.from(this.list.querySelectorAll(".te-alt-input"))}focusedLineIndex(){const inputs=this.lineInputs();return inputs.indexOf(document.activeElement)}focusLine(index){const inputs=this.lineInputs();if(inputs.length===0){const tab=this.tabs[this.tab];if(tab)tab.focus();return}const input=inputs[Math.max(0,Math.min(index,inputs.length-1))];input.focus();try{input.setSelectionRange(input.value.length,input.value.length)}catch(err){}}attempt(fn){try{return fn()}catch(err){this.setStatus(`Not changed: ${err&&err.message?err.message:err}`,true);this.render();return null}}after(change){if(this.frame){cancelAnimationFrame(this.frame);this.frame=0}if(change&&change.after){this.showSpan(change.after);this.render()}else{this.refresh()}return change}addAlternative(text,{source:source="human"}={}){const value=String(text);if(!value)return null;const t=this.target||{};if(t.pending){const p=t.pending;return this.attempt((()=>this.after(this.editor.alternativeOp("create_span",p.kind,p.start,p.end,value,source))))}if(!t.spanId)return null;return this.attempt((()=>{const change=this.editor.alternativeOp("add",t.spanId,value,source);if(!change)this.setStatus(`“${value}” is already in the list.`,true);return this.after(change)}))}editAlternative(index,text){const t=this.target||{};if(!t.spanId||index<1)return null;if(String(text)==="")return this.removeAlternative(index);return this.attempt((()=>this.after(this.editor.alternativeOp("edit",t.spanId,index,String(text)))))}removeAlternative(index){const t=this.target||{};if(!t.spanId||index<1)return null;return this.attempt((()=>this.after(this.editor.alternativeOp("remove",t.spanId,index))))}moveAlternative(from,to){const t=this.target||{};if(!t.spanId)return null;return this.attempt((()=>this.after(this.editor.alternativeOp("move",t.spanId,from,to))))}setActiveIndex(index){const t=this.target||{};if(!t.spanId||!this.span||index<0||index>=this.span.alts.length)return null;if(index===this.span.active)return null;return this.attempt((()=>{const change=this.activate(t.spanId,index);this.refresh();return change}))}activate(spanId,index){return this.editor.swapAlternative(spanId,index)}commitLine(input){if(!input||input.readOnly)return false;if(input.classList.contains("te-alt-input--new"))return this.commitNewLine(input);if(input.value===input.dataset.committed)return false;const index=Number(input.closest(".te-alt-line").dataset.index);return this.editAlternative(index,input.value)!==null}commitNewLine(input){const value=input.value;if(!value.trim())return false;input.value="";const limit=this.liftLimit();const base=this.historyTop();if(this.addActive(value)!==-1)this.foldSince(base);this.restoreLimit(limit);return true}historyTop(){return this.surface.history[this.surface.historyIndex]||null}stepsSince(base){const at=this.surface.history.indexOf(base);return at===-1?-1:this.surface.historyIndex-at}foldSince(base){const steps=this.stepsSince(base);if(steps>1)this.surface.squashHistory(steps,"alternatives");return steps}addActive(value){const change=this.addAlternative(value);if(!change||!this.span)return-1;const index=this.span.alts.findIndex(((a,i)=>i>0&&a.text===value));if(index===-1)return-1;this.setActiveIndex(index);return index}lineIndexOf(input){if(input.classList.contains("te-alt-input--new"))return this.lines().length;return Number(input.closest(".te-alt-line").dataset.index)}onLineInput(e){if(this.destroyed||!this.opened||this.rendering||e.isComposing)return;const input=e.target;if(!(input instanceof HTMLInputElement)||input.readOnly)return;if(input.classList.contains("te-alt-input--new")){if(!this.live)this.startLive(input);return}if(this.live&&this.lineIndexOf(input)===this.live.index)this.updateLive(input)}startLive(input){const value=input.value;const t=this.target||{};if(!value.trim()||!t.spanId&&!t.pending)return;const caret=[input.selectionStart,input.selectionEnd];const limit=this.liftLimit();const base=this.historyTop();input.value="";const index=this.whileBusy((()=>this.addActive(value)));if(index===-1){this.restoreLimit(limit);this.restoreLine(this.lines().length,value,caret);return}this.live={spanId:this.span.id,index:index,base:base,refused:null,limit:limit};this.restoreLine(index,null,caret)}updateLive(input){const value=input.value;const caret=[input.selectionStart,input.selectionEnd];if(!value.trim()){this.cancelLive({keep:value,caret:caret});return}if(value===input.dataset.committed)return;const{index:index}=this.live;const change=this.whileBusy((()=>this.editAlternative(index,value)));this.live.refused=change?null:value;this.restoreLine(index,value,caret)}whileBusy(fn){const was=this.liveBusy;this.liveBusy=true;try{return fn()}finally{this.liveBusy=was}}finishLive(){if(!this.live)return;const{base:base,index:index,refused:refused}=this.live;this.endLive();this.foldSince(base);this.surface.trimHistory();if(refused!==null){const input=this.lineInputs()[index];const kept=input?input.dataset.committed:"";if(input)input.value=kept;this.setStatus(`“${refused}” is already in the list; kept “${kept}”.`,true)}}endLive(){this.surface.historyLimit=this.live.limit;this.live=null}liftLimit(){const limit=this.surface.historyLimit;this.surface.historyLimit=Infinity;return limit}restoreLimit(limit){this.surface.historyLimit=limit;this.surface.trimHistory()}cancelLive({keep:keep="",caret:caret=null}={}){if(!this.live)return;const{base:base}=this.live;this.endLive();const steps=this.foldSince(base);if(steps>=1){this.surface.undo();this.surface.forgetRedo()}this.surface.trimHistory();this.flush();if(this.opened)this.restoreLine(this.lines().length,keep,caret)}restoreLine(index,value,caret){const inputs=this.lineInputs();const input=inputs[Math.max(0,Math.min(index,inputs.length-1))];if(!input)return;if(value!==null&&value!==undefined&&input.value!==value)input.value=value;input.focus();const end=input.value.length;try{input.setSelectionRange(Math.min(caret?caret[0]:end,end),Math.min(caret?caret[1]:end,end))}catch(err){}}commitFocused(){const active=document.activeElement;if(!this.list||!(active instanceof HTMLInputElement)||!this.list.contains(active))return false;return this.commitLine(active)}pendingDrafts(){if(!this.list)return[];const t=this.target||{};const inputs=this.lineInputs();const out=[];inputs.forEach(((input,index)=>{const isNew=input.classList.contains("te-alt-input--new");if(input.readOnly||input.value===(input.dataset.committed||""))return;if(isNew&&!input.value.trim())return;out.push({value:input.value,index:index,isNew:isNew,spanId:t.spanId||null,pending:t.pending?{...t.pending}:null})}));return out}onLineBlur(e){if(this.destroyed||!this.opened||this.rendering)return;const input=e.target;if(!(input instanceof HTMLInputElement)||!input.isConnected)return;if(this.liveBusy)return;if(this.live&&!input.classList.contains("te-alt-input--new")&&this.lineIndexOf(input)===this.live.index){this.finishLive()}if(input.value===(input.dataset.committed||""))return;const to=e.relatedTarget;const inputs=this.lineInputs();const toIndex=to&&this.list.contains(to)?inputs.indexOf(to):-1;const toNew=toIndex!==-1&&to.classList.contains("te-alt-input--new");if(!this.commitLine(input)||toIndex===-1)return;this.focusLine(toNew?this.lines().length:toIndex)}onListClick(e){const del=e.target.closest&&e.target.closest(".te-alt-delete");if(!del)return;const index=Number(del.closest(".te-alt-line").dataset.index);this.removeAlternative(index);this.focusLine(Math.min(index,this.lineInputs().length-1))}onLineKeyDown(e){const input=e.target;if(!(input instanceof HTMLInputElement)||e.isComposing)return;const isNew=input.classList.contains("te-alt-input--new");const index=isNew?this.lines().length:Number(input.closest(".te-alt-line").dataset.index);const count=this.lines().length;const plain=!e.ctrlKey&&!e.metaKey&&!e.shiftKey;if(e.key==="Enter"&&plain&&!e.altKey&&this.live&&this.live.refused!==null&&index===this.live.index){e.preventDefault();this.setStatus(`“${this.live.refused}” is already in the list.`,true);return}if((e.key==="Enter"||e.key==="ArrowUp"||e.key==="ArrowDown")&&plain)this.finishLive();if(e.key==="Enter"&&plain&&!e.altKey){e.preventDefault();if(isNew){if(!this.commitNewLine(input))return;this.focusLine(this.lines().length)}else{this.commitLine(input);this.focusLine(this.lines().length)}return}if((e.key==="ArrowUp"||e.key==="ArrowDown")&&plain){const step=e.key==="ArrowUp"?-1:1;if(e.altKey){e.preventDefault();if(isNew||index<1)return;const to=index+step;if(to<1||to>=count)return;if(this.commitLine(input))return;this.moveAlternative(index,to);this.focusLine(to);return}e.preventDefault();this.commitLine(input);const next=(isNew?this.lines().length:index)+step;if(next<0)return;if(next>=this.lines().length){this.focusLine(this.lines().length);return}if(this.span)this.setActiveIndex(next);this.focusLine(next)}}onPanelKeyDown(e){if(this.destroyed||e.isComposing)return;if(e.key==="Escape"){e.preventDefault();e.stopPropagation();if(this.live)this.cancelLive();this.close({restoreFocus:true});return}const mod=e.ctrlKey||e.metaKey;if(!mod)return;if(typeof teShortcutMatches==="function"&&teShortcutMatches(e,"ctrl+shift+a")){e.preventDefault();this.close({restoreFocus:true});return}const key=(e.key||"").toLowerCase();const undo=key==="z"&&!e.shiftKey&&!e.altKey;const redo=key==="z"&&e.shiftKey&&!e.altKey||key==="y"&&!e.shiftKey&&!e.altKey;if(!undo&&!redo)return;this.finishLive();const input=e.target instanceof HTMLInputElement?e.target:null;if(input&&input.value!==(input.dataset.committed||""))return;e.preventDefault();const index=this.focusedLineIndex();if(undo)this.surface.undo();else this.surface.redo();this.flush();this.focusLine(index===-1?this.activeLineIndex():Math.min(index,this.lines().length))}onTabKeyDown(e){const order=TE_ALT_KINDS;let i=order.indexOf(this.tab);if(e.key==="ArrowRight")i=(i+1)%order.length;else if(e.key==="ArrowLeft")i=(i+order.length-1)%order.length;else if(e.key==="Home")i=0;else if(e.key==="End")i=order.length-1;else if(e.key==="ArrowDown"){e.preventDefault();this.focusLine(this.activeLineIndex());return}else return;e.preventDefault();this.setTab(order[i],{focusTab:true})}setTab(kind,{focusTab:focusTab=false}={}){if(!TE_ALT_KINDS.includes(kind))throw new Error(`Unknown tab ${kind}`);this.finishLive();this.commitFocused();this.tab=kind;if(this.opened){this.resolveTab();this.render()}if(focusTab)this.tabs[kind].focus()}destroy(){if(this.destroyed)return[];this.finishLive();const drafts=this.pendingDrafts();if(drafts.length&&this.surface.root){this.surface.root.dispatchEvent(new CustomEvent("te:alt-drafts",{bubbles:true,detail:{editor:this.editor,drafts:drafts.map((d=>({...d})))}}))}this.destroyed=true;this.opened=false;if(this.frame)cancelAnimationFrame(this.frame);if(this.retargetFrame)cancelAnimationFrame(this.retargetFrame);this.frame=0;this.retargetFrame=0;this.abortController.abort();if(this.offChange)this.offChange();if(this.offMenu)this.offMenu();if(this.host)this.host.classList.remove("te-alt-open");if(this.root)this.root.remove();this.root=null;return drafts}}window.TeAlternativesPanel=TeAlternativesPanel;class TeOverflowPanel{constructor(editor,options={}){this.editor=editor;this.surface=editor.surface;this.delay=options.delay===undefined?250:options.delay;this.destroyed=false;this.timer=null;this.returnFocus=null;this.pageDrag=null;this.panelDrag=null;this.pointerDrag=null;this.readOnly=false;this.unregisterItem=()=>{};this.abortController=new AbortController;if(options.signal){if(options.signal.aborted)this.abortController.abort();else options.signal.addEventListener("abort",(()=>this.destroy()),{signal:this.abortController.signal})}this.mount=options.mount||editor.input&&editor.input.closest(".editor-container")||document.body;this.build();this.listen();this.offChange=this.surface?this.surface.onChange((change=>this.onSurfaceChange(change))):()=>{};if(editor.selectionMenu&&typeof editor.selectionMenu.register==="function"){this.unregisterItem=editor.selectionMenu.register(this.stashItem())}this.refresh()}static get HINT(){return"ctrl+⏎ or drag into the page to use"}build(){const n=TeOverflowPanel.nextId=(TeOverflowPanel.nextId||0)+1;const root=document.createElement("aside");root.className="te-overflow";root.id=`te-overflow-${n}`;root.setAttribute("role","complementary");root.setAttribute("aria-labelledby",`te-overflow-title-${n}`);root.hidden=true;const header=document.createElement("div");header.className="te-overflow-header";const title=document.createElement("h2");title.className="te-overflow-title";title.id=`te-overflow-title-${n}`;title.textContent="Overflow";const tools=document.createElement("div");tools.className="te-overflow-tools";const button=(cls,icon,label)=>{const b=document.createElement("button");b.type="button";b.className=`te-overflow-button ${cls}`;b.setAttribute("aria-label",label);b.title=label;const i=document.createElement("i");i.className=icon;i.setAttribute("aria-hidden","true");b.appendChild(i);tools.appendChild(b);return b};this.closeButton=button("te-overflow-close","fa-solid fa-xmark","Close Overflow");header.append(title,tools);const note=document.createElement("p");note.className="te-overflow-note";note.setAttribute("role","status");note.hidden=true;const area=document.createElement("textarea");area.className="te-overflow-text";area.spellcheck=false;area.setAttribute("aria-label","Overflow text");area.setAttribute("aria-describedby",`te-overflow-hint-${n}`);area.placeholder="Select text on the page and press Ctrl+Shift+X to stash it here, or just write.";const hint=document.createElement("p");hint.className="te-overflow-hint";hint.id=`te-overflow-hint-${n}`;hint.textContent=TeOverflowPanel.HINT;root.append(header,note,area,hint);this.mount.appendChild(root);this.root=root;this.area=area;this.note=note}listen(){const signal=this.abortController.signal;const on=(el,type,fn,opts={})=>el.addEventListener(type,fn,{signal:signal,...opts});on(document,"te:overflow",(e=>{if(e.detail&&e.detail.editor&&e.detail.editor!==this.editor)return;this.toggle()}));on(document,"te:mode-change",(e=>{if(e.detail&&e.detail.editor&&e.detail.editor!==this.editor)return;if(e.detail&&e.detail.mode!=="write-on")this.close()}));on(this.closeButton,"click",(()=>this.close()));const toggleKey=typeof OVERFLOW_TOGGLE_KEY==="string"?OVERFLOW_TOGGLE_KEY:"ctrl+shift+d";const onToggleKey=e=>{if(e.defaultPrevented||typeof teShortcutMatches!=="function"||!teShortcutMatches(e,toggleKey))return;if(!this.isOpen()&&!this.usable())return;e.preventDefault();e.stopPropagation();this.toggle()};const keyScope=this.editor.root||this.mount;on(keyScope,"keydown",onToggleKey,{capture:true});if(!keyScope.contains(this.root))on(this.root,"keydown",onToggleKey,{capture:true});on(this.root,"keydown",(e=>{if(e.key==="Escape"&&!e.isComposing){e.preventDefault();e.stopPropagation();this.close()}}));on(this.area,"input",(()=>this.schedule()));on(this.area,"blur",(()=>this.flush()));on(this.area,"keydown",(e=>{if(e.key==="Enter"&&(e.ctrlKey||e.metaKey)&&!e.altKey&&!e.shiftKey&&!e.isComposing){e.preventDefault();this.pullBack()}}));on(this.area,"dragstart",(e=>{if(e.dataTransfer)e.dataTransfer.effectAllowed="copy";const start=Math.min(this.area.selectionStart,this.area.selectionEnd);const end=Math.max(this.area.selectionStart,this.area.selectionEnd);this.panelDrag=start===end?null:{start:start,end:end,text:this.area.value.slice(start,end)}}));on(this.area,"dragend",(()=>{this.panelDrag=null}));if(this.surface){on(this.surface.root,"dragstart",(()=>{const sel=this.surface.getSelectionOffsets();this.pageDrag=sel.start===sel.end?null:{start:sel.start,end:sel.end,text:this.surface.getText().slice(sel.start,sel.end)}}));on(this.surface.root,"dragend",(()=>{this.pageDrag=null}));on(this.surface.root,"dragover",(e=>{if(!this.panelDrag||!e.dataTransfer||!Array.from(e.dataTransfer.types).includes("text/plain"))return;e.preventDefault();e.dataTransfer.dropEffect="copy"}));on(this.surface.root,"drop",(e=>this.onPageDrop(e)),{capture:true})}on(this.root,"dragover",(e=>{if(!this.pageDrag||!this.canStash())return;e.preventDefault();if(e.dataTransfer)e.dataTransfer.dropEffect="copy"}));on(this.root,"drop",(e=>this.onPanelDrop(e)));if(this.surface){on(document,"pointerdown",(()=>{this.pointerDrag=null}),{capture:true});on(this.surface.root,"pointerdown",(e=>this.beginPointerStash(e)));on(document,"pointermove",(e=>{const drag=this.pointerDrag;if(!drag||e.pointerId!==drag.id)return;if(!(e.buttons&1)){this.pointerDrag=null;return}if(Math.hypot(e.clientX-drag.x,e.clientY-drag.y)>=5)drag.moved=true}));on(document,"pointerup",(e=>this.finishPointerStash(e)));on(document,"pointercancel",(()=>{this.pointerDrag=null}));on(document,"dragstart",(()=>{this.pointerDrag=null}),{capture:true});on(document,"keydown",(e=>{if(e.key==="Escape")this.pointerDrag=null}),{capture:true});on(window,"blur",(()=>{this.pointerDrag=null}))}}beginPointerStash(e){this.pointerDrag=null;if(e.button!==0||!e.isPrimary||e.pointerType!=="mouse"||!this.isOpen()||!this.canStash())return;const selection=window.getSelection();if(!selection||selection.rangeCount!==1||selection.isCollapsed)return;const range=selection.getRangeAt(0);if(!this.surface.root.contains(range.startContainer)||!this.surface.root.contains(range.endContainer))return;const inside=Array.from(range.getClientRects()).some((r=>e.clientX>=r.left&&e.clientX<=r.right&&e.clientY>=r.top&&e.clientY<=r.bottom));if(!inside)return;const{start:start,end:end}=this.surface.getSelectionOffsets();if(start===end)return;this.pointerDrag={id:e.pointerId,x:e.clientX,y:e.clientY,moved:false,start:start,end:end,source:this.surface.getText()}}finishPointerStash(e){const drag=this.pointerDrag;if(!drag||drag.id!==e.pointerId)return;this.pointerDrag=null;if(!drag.moved||e.button!==0||!this.isOpen()||!this.canStash()||!this.root.contains(e.target)||this.surface.getText()!==drag.source)return;e.preventDefault();this.stash(drag.start,drag.end)}api(){const editor=this.editor;const api=typeof editor.documentApi==="function"?editor.documentApi():null;return api&&typeof api.document_overflow==="function"&&typeof api.set_document_overflow==="function"?api:null}text(){this.flush();const api=this.api();return api?api.document_overflow():""}preservedBlock(){const api=this.api();if(!api||typeof api.document_annotations!=="function")return false;try{const ann=api.document_annotations();return!!(ann&&ann.preservedBlock)}catch(err){return false}}setPageFlag(open){const chrome=this.editor.chrome;const targets=[this.editor.root];if(!chrome||chrome.pageMode!==false)targets.push(document.body);for(const el of targets){if(!el)continue;if(open)el.dataset.teOverflow="open";else delete el.dataset.teOverflow}}usable(){if(this.destroyed||!this.api())return false;const chrome=this.editor.chrome;return!chrome||typeof chrome.isWriteOn!=="function"||chrome.isWriteOn()}canStash(){if(!this.usable())return false;const api=this.api();return typeof api.stash_document_range==="function"&&typeof this.editor.stashRange==="function"&&!this.preservedBlock()}schedule(){if(this.destroyed||this.readOnly)return;if(this.timer!==null)clearTimeout(this.timer);this.timer=setTimeout((()=>{this.timer=null;this.write()}),this.delay)}pending(){return this.timer!==null}flush(){if(this.timer===null)return;clearTimeout(this.timer);this.timer=null;this.write()}write(){const api=this.api();if(!api||this.readOnly)return;const result=api.set_document_overflow(this.area.value);if(result&&result.ok)this.changed("edit")}refresh(){if(this.destroyed)return;this.pointerDrag=null;if(this.timer!==null){clearTimeout(this.timer);this.timer=null}const api=this.api();const value=api?api.document_overflow():"";if(this.area.value!==value)this.area.value=value;this.readOnly=this.preservedBlock();this.area.readOnly=this.readOnly;this.note.hidden=!this.readOnly;this.note.textContent=this.readOnly?"Overflow is read-only: the annotation block at the end of this file could not be read. Repair it to stash again.":""}onSurfaceChange(change){if(this.destroyed||!change)return;this.pointerDrag=null;if(change.source==="open"||change.edit&&change.edit.overflow){this.refresh();if(change.edit&&change.edit.overflow&&change.source!=="stash")this.changed(change.source)}}changed(action){if(!this.surface||this.surface.destroyed)return;this.surface.root.dispatchEvent(new CustomEvent("te:overflow-change",{bubbles:true,detail:{editor:this.editor,action:action,overflow:this.area.value}}))}control(){const chrome=this.editor.chrome;return chrome&&chrome.root?chrome.root.querySelector('[data-control="overflow"]'):null}isOpen(){return!this.destroyed&&!this.root.hidden}open({focus:focus=true}={}){if(!this.usable())return false;if(!this.isOpen()){const active=document.activeElement;this.returnFocus=active&&active!==document.body&&!this.root.contains(active)?active:null;this.refresh();this.root.hidden=false;this.setPageFlag(true);this.syncControl()}if(focus)this.area.focus({preventScroll:true});return true}close({restoreFocus:restoreFocus=true}={}){this.pointerDrag=null;if(this.destroyed||this.root.hidden)return;this.flush();const hadFocus=this.root.contains(document.activeElement);this.root.hidden=true;this.setPageFlag(false);this.syncControl();if(restoreFocus&&hadFocus){const back=this.returnFocus;if(back&&back.isConnected&&back.getClientRects().length>0)back.focus({preventScroll:true});else if(this.surface)this.surface.focus()}this.returnFocus=null}toggle(){if(this.isOpen())this.close();else this.open()}syncControl(){const control=this.control();if(!control)return;control.setAttribute("aria-controls",this.root.id);control.setAttribute("aria-expanded",this.isOpen()?"true":"false")}stashItem(){return{id:"stash",label:"Stash this in Overflow",key:"ctrl+shift+x",available:ctx=>!ctx.collapsed&&this.canStash(),run:ctx=>this.stash(ctx.start,ctx.end)}}stash(start,end){if(!this.canStash())return{ok:false,error:"Stashing is not available here"};let outcome;try{outcome=this.editor.stashRange(start,end)}catch(err){console.warn("Could not stash:",err);return{ok:false,error:String(err&&err.message?err.message:err)}}this.open({focus:false});this.area.scrollTop=this.area.scrollHeight;this.changed("stash");return{ok:true,...outcome}}pullBackText(){const value=this.area.value;let start=Math.min(this.area.selectionStart,this.area.selectionEnd);let end=Math.max(this.area.selectionStart,this.area.selectionEnd);if(start===end){start=value.lastIndexOf("\n",start-1)+1;const nl=value.indexOf("\n",end);end=nl===-1?value.length:nl}const text=value.slice(start,end);return text?{text:text,start:start,end:end}:null}pullBack(){if(this.destroyed||!this.surface)return null;const picked=this.pullBackText();if(!picked)return null;return this.pullBackAt(this.surface.getSelectionOffsets().end,picked)}pullBackAt(at,picked){const{selectionStart:selectionStart,selectionEnd:selectionEnd,selectionDirection:selectionDirection}=this.area;const focused=document.activeElement===this.area;let start=at;let text=picked.text;const api=this.api();if(api&&typeof api.overflow_pull_back_text==="function"){this.flush();try{({start:start,text:text}=api.overflow_pull_back_text(at,picked.start,picked.end))}catch(err){console.warn("Could not pull back:",err);return null}}const edit=this.surface.replaceRange(start,start,text,{source:"overflow"});if(focused){this.area.focus({preventScroll:true});this.area.setSelectionRange(selectionStart,selectionEnd,selectionDirection)}return{start:edit.start,end:edit.start+edit.insertedText.length}}onPageDrop(e){const drag=this.panelDrag;this.panelDrag=null;if(!drag)return;const data=e.dataTransfer?e.dataTransfer.getData("text/plain"):"";if(data!==drag.text||this.area.value.slice(drag.start,drag.end)!==drag.text)return;e.preventDefault();e.stopImmediatePropagation();this.pullBackAt(this.surface.dropOffset(e),drag)}onPanelDrop(e){const drag=this.pageDrag;this.pageDrag=null;if(!drag)return;const data=e.dataTransfer?e.dataTransfer.getData("text/plain"):"";if(data&&data!==drag.text)return;if(this.surface.getText().slice(drag.start,drag.end)!==drag.text)return;e.preventDefault();this.stash(drag.start,drag.end)}destroy(){if(this.destroyed)return;this.flush();this.destroyed=true;this.unregisterItem();this.offChange();this.abortController.abort();if(!this.root.hidden)this.setPageFlag(false);const control=this.control();if(control){control.removeAttribute("aria-expanded");control.removeAttribute("aria-controls")}this.root.remove()}}window.TeOverflowPanel=TeOverflowPanel;const TE_DRAFT_PREFIX="terraphim-editor:draft:";const TE_DIRTY_TITLE_PREFIX="• ";const TE_OPEN_EXTENSIONS=[".md",".markdown",".txt"];const TE_PICKER_TYPES=[{description:"Markdown",accept:{"text/markdown":[".md",".markdown"],"text/plain":[".txt"]}}];function teIsOpenableFile(file){if(!file)return false;const name=String(file.name||"").toLowerCase();if(TE_OPEN_EXTENSIONS.some((ext=>name.endsWith(ext))))return true;const type=String(file.type||"");return type==="text/markdown"||type==="text/plain"||type==="text/x-markdown"}function teSuggestedFileName(key,text){const clean=s=>s.replace(/[<>:"/\\|?*\u0000-\u001f]/g,"-").trim();if(typeof key==="string"&&key&&!key.startsWith("h:")){const base=clean(key.split(/[\\/]/).pop()||"");if(base&&base!=="."&&base!==".."){const lower=base.toLowerCase();return TE_OPEN_EXTENSIONS.some((ext=>lower.endsWith(ext)))?base:`${base}.md`}}const heading=/^ {0,3}#{1,6}[ \t]+(.+?)[ \t]*#*[ \t]*$/m.exec(String(text||""));if(heading){const slug=heading[1].toLowerCase().replace(/[^\p{L}\p{N}]+/gu,"-").replace(/^-+|-+$/g,"").slice(0,60).replace(/-+$/,"");if(slug)return`${slug}.md`}return"untitled.md"}function teExportFileName(name){const m=/^(.*?)(\.(md|markdown|txt))?$/i.exec(name||"untitled.md");return`${m[1]||"untitled"}-export.md`}function teDraftHash(text){if(typeof window.writeOnHash==="function")return window.writeOnHash(text);let h=2166136261;for(let i=0;i>>0}return h.toString(16).padStart(8,"0")}let teHandleDb=null;function teIdb(request){return new Promise(((resolve,reject)=>{request.onsuccess=()=>resolve(request.result);request.onerror=()=>reject(request.error)}))}function teOpenHandleDb(){if(!teHandleDb){const req=window.indexedDB.open("terraphim-editor-files",1);req.onupgradeneeded=()=>req.result.createObjectStore("handles",{keyPath:"id"});teHandleDb=teIdb(req).catch((err=>{teHandleDb=null;throw err}))}return teHandleDb}async function teHandleId(handle){if(!window.indexedDB||!handle||typeof handle.isSameEntry!=="function")throw new Error("no handle store");const db=await teOpenHandleDb();const all=await teIdb(db.transaction("handles").objectStore("handles").getAll());for(const rec of all){try{if(rec.handle&&await rec.handle.isSameEntry(handle))return rec.id}catch(err){}}const id=window.crypto&&typeof window.crypto.randomUUID==="function"?window.crypto.randomUUID():`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;await teIdb(db.transaction("handles","readwrite").objectStore("handles").put({id:id,handle:handle}));return id}class TePersistence{constructor(editor,options={}){this.editor=editor;this.signal=options.signal;const config=editor.config||{};this.config=config;this.fsAccess=config.fileSystemAccess!==false&&typeof window.showSaveFilePicker==="function"&&typeof window.showOpenFilePicker==="function";this.autosaveDelay=Number.isFinite(config.autosaveDelay)?Math.max(0,config.autosaveDelay):1e3;this.destroyed=false;this.handle=null;this.fileName=null;this.generation=0;this.openSeq=0;this.draftId=null;this.identityReady=Promise.resolve();this.standalone=config.standalone===true;this.dirty=false;this.timer=null;this.quiet=0;this.pendingDraft=null;this.lastWrite=Promise.resolve(false);this.objectUrls=new Set;this.nodes=[];this.dialogs=[];this.build();this.offChange=editor.surface?editor.surface.onChange((()=>this.onEdit())):()=>{};this.listen();const text=editor.surface?editor.surface.getText():"";this.key=this.initialKey(text);this.draftId=this.key;this.cleanText=this.serialise();this.checkDraft(null)}on(el,type,fn,opts={}){el.addEventListener(type,fn,Object.assign({signal:this.signal},opts))}modelAvailable(){return typeof window.documentModelAvailable==="function"?window.documentModelAvailable(this.editor):!!(this.editor.documentApi&&this.editor.documentApi())}serialise(){if(!this.modelAvailable())return null;try{const api=this.editor.documentApi();this.editor.alignDocumentModel(api);return api.save_document()}catch(err){console.error("Serialising the document failed",err);return null}}initialKey(text){const ed=this.editor;if(typeof ed.documentKey==="string"&&ed.documentKey)return ed.documentKey;if(typeof this.config.documentKey==="string"&&this.config.documentKey)return this.config.documentKey;if(ed.chrome&&typeof ed.chrome.key==="string"&&ed.chrome.key)return ed.chrome.key;return`h:${teDraftHash(text)}`}emit(type,detail={},cancelable=false){const chrome=this.editor.chrome;if(chrome&&!chrome.destroyed&&typeof chrome.emit==="function"){return chrome.emit(type,detail,cancelable)}const ev=new CustomEvent(type,{bubbles:true,cancelable:cancelable,detail:Object.assign({editor:this.editor},detail)});(this.editor.input||document).dispatchEvent(ev);return ev}suggestedName(){if(this.fileName)return this.fileName;const ed=this.editor;const key=typeof ed.documentKey==="string"&&ed.documentKey||this.config.documentKey||this.key;return teSuggestedFileName(key,ed.surface?ed.surface.getText():"")}build(){const notice=document.createElement("div");notice.className="te-files-notice";notice.setAttribute("role","region");notice.setAttribute("aria-label","Document notice");notice.hidden=true;const text=document.createElement("span");text.className="te-files-notice-text";text.setAttribute("aria-live","polite");const actions=document.createElement("span");actions.className="te-files-notice-actions";notice.append(text,actions);document.body.appendChild(notice);this.nodes.push(notice);this.notice=notice;this.noticeText=text;this.noticeActions=actions;const input=document.createElement("input");input.type="file";input.accept=".md,.markdown,.txt,text/markdown,text/plain";input.className="te-files-input";input.hidden=true;input.tabIndex=-1;input.setAttribute("aria-hidden","true");this.on(input,"change",(()=>{const file=input.files&&input.files[0];input.value="";if(file)this.lastOpen=this.openFile(file,null)}));document.body.appendChild(input);this.nodes.push(input);this.fileInput=input;this.buildToolbar()}buildToolbar(){const container=this.editor.input&&this.editor.input.closest(".editor-container");const toolbar=container&&container.querySelector(".toolbar");if(!toolbar)return;const group=document.createElement("sl-button-group");group.setAttribute("label","File");group.className="te-files-toolbar";const button=(name,icon,label,tip,onClick)=>{const tooltip=document.createElement("sl-tooltip");tooltip.setAttribute("content",tip);const b=document.createElement("sl-button");b.setAttribute("size","small");b.dataset.teFile=name;const i=document.createElement("i");i.className=icon;i.setAttribute("aria-hidden","true");const span=document.createElement("span");span.className="te-files-sr";span.textContent=label;b.append(i,span);this.on(b,"click",onClick);tooltip.appendChild(b);group.appendChild(tooltip);return b};button("open","fa-regular fa-folder-open","Open document","Open (Ctrl+O)",(()=>this.open()));this.toolbarSave=button("save","fa-solid fa-floppy-disk","Save document","Save (Ctrl+S)",(()=>this.save()));button("markdown","fa-brands fa-markdown","Markdown export","Markdown export",(()=>this.showMarkdown()));const divider=document.createElement("sl-divider");divider.setAttribute("vertical","");divider.className="te-files-toolbar-divider";toolbar.insertBefore(divider,toolbar.firstChild);toolbar.insertBefore(group,divider);this.nodes.push(group,divider)}showNotice(message,actions=[],kind="info"){if(this.destroyed)return;this.noticeActions.replaceChildren();if(!message){this.notice.hidden=true;this.noticeText.textContent="";delete this.notice.dataset.kind;return}this.notice.dataset.kind=kind;this.noticeText.textContent=message;const all=actions.length?actions:[{label:"Dismiss",onClick:()=>this.showNotice(null)}];for(const action of all){const b=document.createElement("button");b.type="button";b.className="te-files-notice-button";b.dataset.action=action.name||action.label.toLowerCase();b.textContent=action.label;this.on(b,"click",action.onClick);this.noticeActions.appendChild(b)}this.notice.hidden=false}makeDialog(className,titleText){const dialog=document.createElement("dialog");dialog.className=`te-files-dialog ${className}`;const id=`${className}-title`;dialog.setAttribute("aria-labelledby",id);const header=document.createElement("div");header.className="te-files-dialog-header";const title=document.createElement("h2");title.id=id;title.textContent=titleText;header.appendChild(title);dialog.appendChild(header);document.body.appendChild(dialog);this.nodes.push(dialog);this.dialogs.push(dialog);this.on(dialog,"click",(e=>{if(e.target===dialog)dialog.close()}));return{dialog:dialog,header:header}}openDialog(dialog,focusEl){const back=document.activeElement;this.on(dialog,"close",(()=>{if(dialog.dataset.teReplaced)return;if(back&&back.isConnected&&typeof back.focus==="function")back.focus()}),{once:true});if(typeof dialog.showModal==="function")dialog.showModal();else dialog.setAttribute("open","");if(focusEl)focusEl.focus()}dialogButton(parent,label,opts={}){const b=document.createElement("button");b.type="button";b.className=`te-files-button ${opts.className||""}`.trim();if(opts.name)b.dataset.action=opts.name;if(opts.icon){const i=document.createElement("i");i.className=opts.icon;i.setAttribute("aria-hidden","true");b.appendChild(i)}const span=document.createElement("span");span.textContent=label;b.appendChild(span);if(opts.ariaLabel)b.setAttribute("aria-label",opts.ariaLabel);if(opts.onClick)this.on(b,"click",opts.onClick);parent.appendChild(b);return b}listen(){this.on(document,"keydown",(e=>{if(this.destroyed||e.defaultPrevented||e.isComposing)return;if(!(e.ctrlKey||e.metaKey)||e.altKey||e.shiftKey)return;if(!this.standalone&&!this.ownsFocus(e.target))return;const key=(e.key||"").toLowerCase();if(key==="s"){e.preventDefault();this.save()}else if(key==="o"){e.preventDefault();this.open()}}));const nudge=()=>this.schedule();this.on(document,"input",nudge);this.on(document,"keyup",nudge);this.on(document,"pointerup",nudge);this.on(window,"pagehide",(()=>this.flushDraft()));this.on(document,"dragover",(e=>{if(!this.isFileDrag(e))return;e.preventDefault();if(e.dataTransfer)e.dataTransfer.dropEffect="copy"}));this.on(document,"drop",(e=>this.onDrop(e)))}ownsFocus(target){if(!(target instanceof Node))return false;const el=target.nodeType===1?target:target.parentElement;if(!el)return false;const container=this.editor.input&&(this.editor.input.closest(".editor-container")||this.editor.input);if(container&&container.contains(el))return true;return!!el.closest(".te-chrome, .te-overflow, .te-alt-panel, .te-blocks, .te-selection-menu, .te-files-dialog, .te-files-notice")}isFileDrag(e){if(this.destroyed||!e.dataTransfer)return false;const types=Array.from(e.dataTransfer.types||[]);if(!types.includes("Files"))return false;return this.inScope(e.target)}inScope(target){if(!(target instanceof Node))return false;if(target===document.body||target===document.documentElement||target===document)return this.standalone;const el=target.nodeType===1?target:target.parentElement;if(!el)return false;const container=this.editor.input&&(this.editor.input.closest(".editor-container")||this.editor.input);if(container&&container.contains(el))return true;return!!el.closest(".te-chrome, .te-overflow, .te-alt-panel, .te-blocks, .te-files-notice")}onDrop(e){if(!this.isFileDrag(e))return;e.preventDefault();const dt=e.dataTransfer;const files=Array.from(dt.files||[]);const index=files.findIndex(teIsOpenableFile);if(index<0){this.showNotice(files.length?`${files[0].name} is not a Markdown or text file.`:"Nothing to open in that drop.",[],"error");return}const file=files[index];let handlePromise=null;if(this.fsAccess&&dt.items){const items=Array.from(dt.items).filter((it=>it.kind==="file"));const item=items[index];if(item&&typeof item.getAsFileSystemHandle==="function"){try{handlePromise=item.getAsFileSystemHandle()}catch(err){handlePromise=null}}}const go=async()=>{let handle=null;if(handlePromise){try{const h=await handlePromise;if(h&&h.kind==="file")handle=h}catch(err){handle=null}}return this.openFile(file,handle)};this.lastOpen=go()}onEdit(){if(this.destroyed||this.quiet>0||!this.modelAvailable())return;this.setDirty(true);this.schedule()}schedule(){if(this.destroyed||this.quiet>0||!this.modelAvailable())return;if(this.timer!==null)clearTimeout(this.timer);this.timer=setTimeout((()=>{this.timer=null;this.tick()}),this.autosaveDelay)}tick(){if(this.destroyed)return;const text=this.serialise();if(text===null)return;const dirty=text!==this.cleanText;this.setDirty(dirty);if(this.pendingDraft&&!dirty)return;this.writeDraft(text)}flushDraft(){if(this.timer===null)return;clearTimeout(this.timer);this.timer=null;this.tick()}isDirty(){this.flushDraft();return this.dirty}setDirty(dirty){if(this.destroyed)return;const changed=dirty!==this.dirty;this.dirty=dirty;if(!changed)return;this.applyTitle(dirty);const control=this.editor.chrome&&this.editor.chrome.root?this.editor.chrome.root.querySelector('[data-control="save"]'):null;for(const el of[control,this.toolbarSave]){if(!el)continue;el.classList.toggle("te-dirty",dirty);if(el===control)el.setAttribute("aria-label",dirty?"Save document (unsaved changes)":"Save document")}this.emit("te:dirty-change",{dirty:dirty})}markClean(){if(this.destroyed)return;this.cancelTick();const text=this.serialise();if(text===null)return;this.cleanText=text;this.setDirty(false);this.writeDraft(text)}documentChanged(name){if(this.destroyed||this.quiet>0)return;this.generation+=1;this.cancelTick();this.handle=null;this.fileName=typeof name==="string"&&name?name:null;const key=this.editor.documentKey;if(typeof key==="string"&&key)this.key=key;this.draftId=this.key;this.identityReady=Promise.resolve();this.cleanText=this.serialise();this.setDirty(false);this.showNotice(null);this.checkDraft(null)}applyTitle(dirty){if(this.config.dirtyTitle===false)return;if(dirty){if(this.markedTitle!==undefined)return;this.baseTitle=document.title;document.title=TE_DIRTY_TITLE_PREFIX+this.baseTitle;this.markedTitle=document.title}else if(this.markedTitle!==undefined){if(document.title===this.markedTitle)document.title=this.baseTitle;this.markedTitle=undefined}}draftKey(key=this.draftId){return TE_DRAFT_PREFIX+key}readDraft(key=this.draftId){if(key===null)return null;try{const raw=window.localStorage.getItem(this.draftKey(key));if(!raw)return null;const d=JSON.parse(raw);return d&&typeof d.text==="string"?d:null}catch(err){return null}}writeDraft(text){if(this.draftId===null)return;try{window.localStorage.setItem(this.draftKey(),JSON.stringify({v:1,text:text,savedAt:Date.now(),name:this.fileName}))}catch(err){}}removeDraft(key=this.draftId){if(key===null)return;try{window.localStorage.removeItem(this.draftKey(key))}catch(err){}}checkDraft(file){this.pendingDraft=null;const draft=this.readDraft();if(!draft||this.cleanText===null||draft.text===this.cleanText){this.hideDraftNotice();return}if(file&&Number.isFinite(file.lastModified)&&Number(draft.savedAt)<=file.lastModified){this.hideDraftNotice();return}this.pendingDraft=draft;const when=Number.isFinite(Number(draft.savedAt))?new Date(Number(draft.savedAt)):null;const stamp=when?when.toLocaleString(undefined,{dateStyle:"medium",timeStyle:"short"}):"an earlier session";const whose=draft.name?draft.name:"this document";this.showNotice(`Unsaved draft of ${whose} from ${stamp} found.`,[{name:"restore",label:"Restore",onClick:()=>this.restoreDraft()},{name:"discard",label:"Discard",onClick:()=>this.discardDraft()}],"draft")}hideDraftNotice(){if(this.notice.dataset.kind==="draft")this.showNotice(null)}restoreDraft(){const draft=this.pendingDraft;if(!draft||this.destroyed)return false;this.pendingDraft=null;this.showNotice(null);this.quiet+=1;try{this.editor.openDocument(draft.text,this.key)}finally{this.quiet-=1}this.cancelTick();const text=this.serialise();this.setDirty(text!==this.cleanText);if(text!==null)this.writeDraft(text);if(this.editor.surface)this.editor.surface.focus();this.emit("te:draft-restored",{text:draft.text});return true}discardDraft(){this.pendingDraft=null;this.showNotice(null);this.removeDraft();if(this.dirty)this.tick();if(this.editor.surface)this.editor.surface.focus()}cancelTick(){if(this.timer!==null)clearTimeout(this.timer);this.timer=null}rekey(name){if(!name||name===this.key)return;this.key=name;this.editor.documentKey=name;const chrome=this.editor.chrome;if(chrome&&!chrome.destroyed&&typeof chrome.setMode==="function"){chrome.key=name;chrome.setMode(chrome.mode)}}moveDraft(from,to){if(from===null||to===null||from===to)return;const draft=this.readDraft(from);if(draft){try{window.localStorage.setItem(this.draftKey(to),JSON.stringify(Object.assign(draft,{name:this.fileName})))}catch(err){}}this.removeDraft(from)}setDraftIdentity({handle:handle=null,name:name=null,size:size=null,lastModified:lastModified=null}={},onReady=null){const gen=this.generation;const fileKey=name&&Number.isFinite(lastModified)?`file:${name}|${Number.isFinite(size)?size:""}|${lastModified}`:null;const fallback=fileKey||this.key;if(!handle){this.draftId=fallback;this.identityReady=Promise.resolve();if(onReady)onReady();return this.identityReady}this.draftId=null;this.identityReady=teHandleId(handle).then((id=>`fs:${id}`),(()=>fallback)).then((key=>{if(this.destroyed||gen!==this.generation)return;this.draftId=key;if(onReady)onReady();if(this.dirty)this.tick()}));return this.identityReady}save(){if(this.destroyed)return null;const available=this.modelAvailable();const ev=this.emit("te:save",{available:available},true);if(ev.defaultPrevented||!available)return null;let text;try{text=this.editor.saveDocument()}catch(err){console.error("saveDocument failed",err);return null}this.cancelTick();this.writeDraft(text);const gen=this.generation;const write=this.writeFile(text,this.suggestedName(),{keepHandle:true,generation:gen});this.lastWrite=write.then((result=>{if(!result||this.destroyed)return false;if(gen!==this.generation){this.emit("te:written",{name:result.name,method:result.method,text:text,stale:true});return true}if(result.keepDirty){this.emit("te:written",{name:result.name,method:result.method,text:text,copy:true});return true}this.cleanText=text;const now=this.serialise();this.setDirty(now!==null&&now!==text);this.emit("te:written",{name:result.name,method:result.method,text:text});return true}));return text}writeFile(text,name,{keepHandle:keepHandle=false,generation:generation=this.generation}={}){if(!this.fsAccess)return Promise.resolve(this.download(text,name));const pick=()=>{try{return window.showSaveFilePicker({suggestedName:name,types:TE_PICKER_TYPES})}catch(err){return Promise.reject(err)}};let handlePromise;let refused=null;if(keepHandle&&this.handle){const kept=this.handle;handlePromise=this.ensureWritable(kept).then((ok=>{if(ok)return kept;refused=kept.name;return pick()}))}else{handlePromise=pick()}return handlePromise.then((async handle=>{try{const writable=await handle.createWritable();await writable.write(text);await writable.close()}catch(err){console.error("Writing the file failed",err);this.showNotice(`Could not write ${handle.name}: ${err.message||err.name}.`,[],"error");return null}if(keepHandle&&handle!==this.handle){let meta={};try{const written=await handle.getFile();meta={name:written.name,size:written.size,lastModified:written.lastModified}}catch(err){meta={name:handle.name}}if(generation===this.generation&&!this.destroyed&&handle!==this.handle){const oldDraft=this.draftId;this.handle=handle;this.fileName=handle.name;this.rekey(handle.name);this.setDraftIdentity(Object.assign({handle:handle},meta),(()=>this.moveDraft(oldDraft,this.draftId)))}}if(refused)this.showNotice(`No permission to write ${refused}: saved as ${handle.name} instead.`,[],"error");return{name:handle.name,method:"file"}}),(err=>{if(err&&err.name==="AbortError"){if(refused)this.showNotice(`No permission to write ${refused}: nothing was saved.`,[],"error");return null}console.warn("File picker unavailable, downloading instead",err);const result=this.download(text,name);if(refused){result.keepDirty=true;this.showNotice(`No permission to write ${refused}: downloaded a copy as ${result.name} instead; ${refused} itself is unchanged.`,[],"error")}return result}))}ensureWritable(handle){const opts={mode:"readwrite"};if(!handle||typeof handle.queryPermission!=="function")return Promise.resolve(true);let query;try{query=Promise.resolve(handle.queryPermission(opts))}catch(err){query=Promise.reject(err)}return query.then((state=>{if(state==="granted")return true;if(typeof handle.requestPermission!=="function")return false;return Promise.resolve(handle.requestPermission(opts)).then((next=>next==="granted"),(()=>false))}),(()=>true))}download(text,name){const blob=new Blob([text],{type:"text/markdown;charset=utf-8"});const url=URL.createObjectURL(blob);this.objectUrls.add(url);const a=document.createElement("a");a.href=url;a.download=name;a.className="te-files-download";a.hidden=true;document.body.appendChild(a);a.click();a.remove();setTimeout((()=>{URL.revokeObjectURL(url);this.objectUrls.delete(url)}),3e4);return{name:name,method:"download"}}open(){if(this.destroyed)return Promise.resolve(false);const ev=this.emit("te:open",{available:this.modelAvailable()},true);if(ev.defaultPrevented||!this.modelAvailable())return Promise.resolve(false);if(!this.fsAccess){this.fileInput.click();return Promise.resolve(false)}let picked;try{picked=window.showOpenFilePicker({types:TE_PICKER_TYPES,multiple:false})}catch(err){picked=Promise.reject(err)}this.lastOpen=picked.then((async([handle])=>this.openFile(await handle.getFile(),handle)),(err=>{if(err&&err.name!=="AbortError"){console.warn("File picker unavailable, using the file input",err);this.fileInput.click()}return false}));return this.lastOpen}async openFile(file,handle=null){if(this.destroyed)return false;const seq=++this.openSeq;const gen=this.generation;const current=()=>!this.destroyed&&seq===this.openSeq&&gen===this.generation;if(!teIsOpenableFile(file)){this.showNotice(`${file&&file.name?file.name:"That file"} is not a Markdown or text file.`,[],"error");return false}let text;try{text=await file.text()}catch(err){this.showNotice(`Could not read ${file.name}.`,[],"error");return false}if(!current())return false;if(this.isDirty()){const choice=await this.askReplace(file.name);if(choice==="cancel"||!current())return false;if(choice==="save"){const saved=this.save();if(saved===null||!await this.lastWrite||!current())return false}}this.load(text,file.name,handle,file.lastModified,file.size);return true}load(text,name,handle=null,lastModified=null,size=null){this.generation+=1;this.quiet+=1;try{this.editor.openDocument(text,name)}finally{this.quiet-=1}this.cancelTick();this.handle=handle;this.fileName=name||null;if(name)this.key=name;this.cleanText=this.serialise();this.setDirty(false);this.showNotice(null);this.pendingDraft=null;const file=Number.isFinite(lastModified)?{lastModified:lastModified}:null;this.setDraftIdentity({handle:handle,name:name,size:size,lastModified:lastModified},(()=>this.checkDraft(file)));this.emit("te:opened",{name:name,text:text})}askReplace(name){return new Promise((resolve=>{const{dialog:dialog}=this.makeDialog("te-files-replace","Unsaved changes");const p=document.createElement("p");p.className="te-files-dialog-text";p.textContent=`Opening ${name} replaces the current document, which has unsaved changes.`;dialog.appendChild(p);const footer=document.createElement("div");footer.className="te-files-dialog-footer";dialog.appendChild(footer);let choice="cancel";const pick=c=>()=>{choice=c;dialog.close()};const first=this.dialogButton(footer,"Save first",{name:"save",className:"te-files-primary",onClick:pick("save")});this.dialogButton(footer,"Discard changes",{name:"discard",onClick:pick("discard")});this.dialogButton(footer,"Cancel",{name:"cancel",onClick:pick("cancel")});this.on(dialog,"close",(()=>{this.removeDialog(dialog);resolve(choice)}),{once:true});this.replaceDialog=dialog;this.openDialog(dialog,first)}))}removeDialog(dialog){dialog.remove();this.nodes=this.nodes.filter((n=>n!==dialog));this.dialogs=this.dialogs.filter((n=>n!==dialog));if(this.replaceDialog===dialog)this.replaceDialog=null;if(this.markdownDialog===dialog)this.markdownDialog=null}exportText(){if(this.modelAvailable()){try{return this.editor.exportDocument()}catch(err){console.error("exportDocument failed",err)}}return this.editor.surface?this.editor.surface.getText():""}showMarkdown(){if(this.destroyed)return null;const ev=this.emit("te:markdown",{},true);if(ev.defaultPrevented)return null;if(this.markdownDialog){const old=this.markdownDialog;old.dataset.teReplaced="true";old.close();this.removeDialog(old)}const text=this.exportText();const{dialog:dialog,header:header}=this.makeDialog("te-files-markdown","Markdown");const close=this.dialogButton(header,"×",{name:"close",className:"te-files-close",ariaLabel:"Close Markdown export",onClick:()=>dialog.close()});close.querySelector("span").setAttribute("aria-hidden","true");const note=document.createElement("p");note.className="te-files-dialog-text";note.id="te-files-markdown-note";note.textContent="Clean Markdown: the active alternatives, without ghosted text, Overflow or the annotation block.";const area=document.createElement("textarea");area.className="te-files-markdown-text";area.readOnly=true;area.spellcheck=false;area.value=text;area.setAttribute("aria-label","Exported Markdown");area.setAttribute("aria-describedby",note.id);const footer=document.createElement("div");footer.className="te-files-dialog-footer";const status=document.createElement("span");status.className="te-files-status";status.setAttribute("role","status");footer.appendChild(status);this.dialogButton(footer,"Copy",{name:"copy",icon:"fa-regular fa-copy",onClick:()=>{this.lastCopy=this.copyExport(area,status)}});this.dialogButton(footer,"Download .md",{name:"download",icon:"fa-solid fa-download",className:"te-files-primary",onClick:()=>{const name=teExportFileName(this.suggestedName());this.lastWrite=this.writeFile(area.value,name).then((r=>{if(r){status.textContent=r.method==="file"?`Saved ${r.name}.`:`Downloaded ${r.name}.`;this.emit("te:written",{name:r.name,method:r.method,text:area.value,export:true})}return!!r}))}});dialog.append(note,area,footer);this.on(dialog,"close",(()=>this.removeDialog(dialog)),{once:true});this.markdownDialog=dialog;this.openDialog(dialog,area);return dialog}async copyExport(area,status){const copied="Copied to the clipboard.";if(navigator.clipboard&&typeof navigator.clipboard.writeText==="function"){try{await navigator.clipboard.writeText(area.value);status.textContent=copied;return"clipboard"}catch(err){}}area.focus();area.select();let ok=false;try{ok=document.execCommand("copy")}catch(err){ok=false}status.textContent=ok?copied:"Copying is blocked here: the text is selected, press Ctrl+C (Cmd+C on a Mac).";return ok?"execCommand":"selected"}destroy(){if(this.destroyed)return;this.flushDraft();this.cancelTick();for(const d of this.dialogs){if(d.open)d.close()}this.destroyed=true;this.offChange();for(const node of this.nodes)node.remove();this.nodes=[];this.dialogs=[];for(const url of this.objectUrls)URL.revokeObjectURL(url);this.objectUrls.clear();this.applyTitle(false)}}window.TePersistence=TePersistence;window.teSuggestedFileName=teSuggestedFileName;window.teExportFileName=teExportFileName;window.teIsOpenableFile=teIsOpenableFile;window.TE_DRAFT_PREFIX=TE_DRAFT_PREFIX;(function(root){"use strict";function storageError(code,message){const err=new Error(message);err.name="RoleGraphStorageError";err.code=code;return err}class TeRoleGraphStorage{constructor({enabled:enabled=false,namespace:namespace,indexedDB:indexedDB=root.indexedDB}={}){if(enabled&&(typeof namespace!=="string"||!namespace.trim()))throw storageError("invalid_input","Explicit persistence namespace required");this.enabled=enabled===true;this.namespace=namespace;this.indexedDB=indexedDB;this.db=null;this.opening=null;this.closed=false;this.transactions=new Set}_open(){if(!this.enabled||this.closed||!this.indexedDB)return Promise.reject(storageError("storage_failed","Graph persistence is unavailable or disabled"));if(this.db)return Promise.resolve(this.db);if(this.opening)return this.opening;this.opening=new Promise(((resolve,reject)=>{let request;try{request=this.indexedDB.open("te-rolegraph:"+this.namespace,1)}catch(_){reject(storageError("storage_failed","Cannot open graph storage"));return}let settled=false;const fail=()=>{if(!settled){settled=true;reject(storageError("storage_failed","Cannot open graph storage"))}};this.cancelOpen=fail;request.onupgradeneeded=()=>{const db=request.result;if(!db.objectStoreNames.contains("snapshots"))db.createObjectStore("snapshots")};request.onerror=fail;request.onblocked=fail;request.onsuccess=()=>{const db=request.result;if(settled||this.closed){db.close();fail();return}settled=true;this.db=db;db.onversionchange=()=>{db.close();if(this.db===db)this.db=null};resolve(db)}})).finally((()=>{this.opening=null;this.cancelOpen=null}));return this.opening}async _transaction(mode,operation){const db=await this._open();if(this.closed)throw storageError("storage_failed","Graph storage is closed");return new Promise(((resolve,reject)=>{let tx;let result=null;let settled=false;const finish=failure=>{if(settled)return;settled=true;this.transactions.delete(tx);if(failure)reject(storageError("storage_failed","Graph storage transaction failed"));else resolve(result)};try{tx=db.transaction("snapshots",mode);this.transactions.add(tx);tx.oncomplete=()=>finish(false);tx.onabort=()=>finish(true);tx.onerror=()=>finish(true);operation(tx.objectStore("snapshots"),(value=>{result=value}))}catch(_){if(tx){try{tx.abort()}catch(_){}}finish(true)}}))}save(snapshotText){if(typeof snapshotText!=="string"||(new TextEncoder).encode(snapshotText).length>16*1024*1024)return Promise.reject(storageError("invalid_input","Snapshot text exceeds storage limit"));return this._transaction("readwrite",(store=>store.put(snapshotText,"checkpoint:v1")))}load(){return this._transaction("readonly",((store,result)=>{const request=store.get("checkpoint:v1");request.onsuccess=()=>result(request.result===undefined?null:request.result)})).then((text=>{if(text!==null&&typeof text!=="string")throw storageError("storage_failed","Invalid stored snapshot text");return text}))}close(){if(this.closed)return;this.closed=true;if(this.cancelOpen)this.cancelOpen();for(const tx of this.transactions){try{tx.abort()}catch(_){}}if(this.db)this.db.close();this.db=null}}root.TeRoleGraphStorage=TeRoleGraphStorage})(typeof window!=="undefined"?window:globalThis);(function(root){"use strict";const OPS=new Set(["prepare_role","prepare_snapshot","activate_role","clear_role","index_documents","remove_document","match","connected","documents_for_term","learning_documents","query","set_triggers","document","nodes","edges","index_learning","adjust_learning_rank","stats","export_snapshot"]);const IMPORTS=new Set(["prepare_role","prepare_snapshot","activate_role","index_documents"]);const decimal=value=>typeof value==="string"&&/^(0|[1-9][0-9]*)$/.test(value);class RoleGraphError extends Error{constructor(code,message){super(message);this.name="RoleGraphError";this.code=code}}const error=(code,message)=>new RoleGraphError(code,message||code);class TeRoleGraphClient{constructor(options={}){this.options=options;this.generation="0";this.revision="0";this.state="starting";this.queue=[];this.active=null;this.sequence=0;this.epoch=0;this.authoritativeEpoch=0;this.lifecycle=Promise.resolve();this.hasRole=false;this.checkpoint=null;this.listeners=new Set;this.destroyed=false;if(typeof options.onStateChange==="function")this.listeners.add(options.onStateChange);this.ready=this._start();this.ready.catch((()=>{}))}onStateChange(listener){this.listeners.add(listener);return()=>this.listeners.delete(listener)}_state(state){this.state=state;for(const listener of this.listeners){try{listener({state:state,generation:this.generation,revision:this.revision})}catch(_){}}}_start(){return new Promise(((resolve,reject)=>{this.startup={resolve:resolve,reject:reject};try{const base=this.options.assetBase;if(!base&&!(this.options.workerUrl&&this.options.glueUrl&&this.options.wasmUrl))throw error("invalid_input","Explicit graph asset base or URLs required");const url=(override,relative)=>override||new URL(relative,base).href;this.worker=(this.options.workerFactory||((workerUrl,config)=>new Worker(workerUrl,config)))(url(this.options.workerUrl,"js/rolegraph-worker.js"),{type:"module"});this.messageListener=event=>this._receive(event.data);this.errorListener=()=>this._fail(error("worker_failed","Graph worker failed"));this.worker.addEventListener("message",this.messageListener);this.worker.addEventListener("error",this.errorListener);this.worker.addEventListener("messageerror",this.errorListener);this.startTimer=setTimeout((()=>this._fail(error("timeout","Graph worker initialization timed out"))),this.options.importTimeout??3e4);this.worker.postMessage({type:"init",glueUrl:url(this.options.glueUrl,"rolegraph/terraphim_rolegraph_browser.js"),wasmUrl:url(this.options.wasmUrl,"rolegraph/terraphim_rolegraph_browser_bg.wasm")})}catch(_){this._fail(error("worker_failed","Graph worker could not start"))}}))}_receive(data){if(this.destroyed||this.state==="failed")return;if(data?.type==="failed"){this._fail(error("worker_failed","Graph worker failed"));return}if(data?.type==="ready"){if(!this.startup)return;clearTimeout(this.startTimer);const startup=this.startup;this.startup=null;if(this.state==="starting")this._state("ready");startup.resolve();this._pump();return}const job=this.active;if(!job||data?.requestId!==job.request.requestId)return;const generation=["activate_role","clear_role"].includes(job.request.op)&&data.ok===true?job.request.payload.nextGeneration:job.request.generation;const activation=["activate_role","clear_role"].includes(job.request.op);if(data.version!==1||data.generation!==generation||!decimal(data.revision)||typeof data.ok!=="boolean"||data.ok&&(activation?data.revision!=="0":BigInt(data.revision){if(!this.destroyed&&epoch===this.epoch&&this.state!=="failed")this._state("ready")}))}}_pump(){if(this.active||this.startup||!this.worker||this.destroyed)return;const job=this.queue.shift();if(!job)return;this.active=job;job.request.generation=this.generation;if(job.request.expectedRevision===undefined)job.request.expectedRevision=this.revision;job.timer=setTimeout((()=>this._fail(error("timeout","Graph operation timed out"))),IMPORTS.has(job.request.op)?this.options.importTimeout??3e4:this.options.queryTimeout??5e3);try{this.worker.postMessage(job.request)}catch(_){this._fail(error("worker_failed","Graph request failed"))}}command(op,payload={},options={}){return this._command(op,payload,options,false)}_command(op,payload={},options={},internal=false){if(this.destroyed)return Promise.reject(error("destroyed"));if(!OPS.has(op))return Promise.reject(error("invalid_input","Unsupported graph operation"));if(!internal&&["prepare_role","prepare_snapshot","activate_role","clear_role"].includes(op))return Promise.reject(error("invalid_input","Use role lifecycle APIs"));if(!internal&&this.state==="syncing")return Promise.reject(error("syncing"));if(this.state==="failed")return Promise.reject(error("worker_failed"));if(this.queue.length>=32)return Promise.reject(error("resource_limit","Graph queue is full"));if(options.signal?.aborted)return Promise.reject(error("cancelled"));return new Promise(((resolve,reject)=>{const job={resolve:resolve,reject:reject,internal:internal,epoch:this.epoch,signal:options.signal,request:{version:1,requestId:String(++this.sequence),generation:this.generation,expectedRevision:options.expectedRevision,op:op,payload:payload}};job.abort=()=>{if(this.active===job){this._fail(error("cancelled","Running graph operation cancelled"));return}const index=this.queue.indexOf(job);if(index<0)return;this.queue.splice(index,1);this._cleanup(job);reject(error("cancelled"))};if(job.signal)job.signal.addEventListener("abort",job.abort,{once:true});this.queue.push(job);this._pump()}))}_latest(epoch){if(this.destroyed)throw error("destroyed");if(epoch!==this.epoch)throw error("stale_generation")}_latestAuthoritative(epoch){if(this.destroyed)throw error("destroyed");if(epoch!==this.authoritativeEpoch)throw error("stale_generation")}_transition(task,authoritative=false){if(this.destroyed)return Promise.reject(error("destroyed"));if(this.state==="failed"&&!authoritative)return Promise.reject(error("worker_failed"));const epoch=++this.epoch;if(authoritative)this.authoritativeEpoch=epoch;this._state("syncing");if(this.active&&!this.active.internal){this.active.reject(error("stale_generation"));if(this.active.signal)this.active.signal.removeEventListener("abort",this.active.abort)}for(const job of this.queue.splice(0)){this._cleanup(job);job.reject(error("stale_generation"))}const operation=this.lifecycle.catch((()=>{})).then((async()=>{if(authoritative){try{await this.ready}catch(_){}this._latestAuthoritative(epoch)}else{await this.ready;this._latest(epoch);if(!this.worker||this.state==="failed")throw error("worker_failed")}try{return await task(epoch)}finally{if(epoch===this.epoch&&!this.destroyed&&this.state!=="failed")this._state("ready")}}));this.lifecycle=operation;return operation}syncRole({generation:generation,thesaurusText:thesaurusText,name:name}={}){if(!decimal(generation)||thesaurusText!==null&&typeof thesaurusText!=="string")return Promise.reject(error("invalid_input"));return this._transition((async epoch=>{try{if(!this.worker){this.generation="0";this.revision="0";this.hasRole=false;this._state("syncing");this.ready=this._start();await this.ready;this._latestAuthoritative(epoch)}if(thesaurusText===null||thesaurusText===""){await this._command("clear_role",{nextGeneration:generation},{},true);this.hasRole=false;this.checkpoint=null;this._latest(epoch);return}const candidate=await this._command("prepare_role",{thesaurusText:thesaurusText,roleName:name},{},true);this._latestAuthoritative(epoch);await this._command("activate_role",{token:candidate.token,nextGeneration:generation},{},true);this.hasRole=true;this.checkpoint=null;this._latest(epoch)}catch(reason){if(epoch===this.authoritativeEpoch&&reason.code!=="stale_generation"&&!this.destroyed)this._fail(reason);throw reason}}),true)}loadRole(thesaurusText,options={}){return this._load("prepare_role",{thesaurusText:thesaurusText,roleName:options.name})}importSnapshot(snapshotText){return this._load("prepare_snapshot",{snapshotText:snapshotText})}_load(op,payload){return this._transition((async epoch=>{const coordinator=this.options.roleCoordinator;if(!coordinator?.prepare||!coordinator?.commit||!coordinator?.discard||coordinator.commit.constructor?.name==="AsyncFunction")throw error("invalid_input","Role coordinator requires a synchronous atomic commit");const previous={generation:this.generation,hasRole:this.hasRole,checkpoint:this.checkpoint};let mainToken;let activated=false;let committed=false;try{const candidate=await this._command(op,payload,{},true);this._latest(epoch);if(previous.hasRole)previous.checkpoint=(await this._command("export_snapshot",{},{},true)).snapshotText;this._latest(epoch);mainToken=await coordinator.prepare(candidate.thesaurusText);this._latest(epoch);await this._command("activate_role",{token:candidate.token,nextGeneration:String(BigInt(this.generation)+1n)},{},true);activated=true;this._latest(epoch);const commitResult=coordinator.commit(mainToken);if(commitResult&&typeof commitResult.then==="function")throw error("invalid_input","Role coordinator commit must be synchronous");committed=true;this.hasRole=true;this.checkpoint=null;this._latest(epoch)}catch(reason){if(mainToken!==undefined){try{await coordinator.discard(mainToken)}catch(_){}}if(!committed&&(activated||this.state==="failed")){if(!this.destroyed)await this._rollback(previous)}throw reason instanceof RoleGraphError?reason:error("invalid_input","Main role preparation or commit failed")}}))}async _rollback(previous){if(previous.hasRole&&typeof previous.checkpoint!=="string"){this._fail(error("worker_failed","No valid rollback checkpoint"));return}try{this._stopWorker();this.generation="0";this.revision="0";this._state("syncing");this.ready=this._start();await this.ready;if(previous.hasRole){const candidate=await this._command("prepare_snapshot",{snapshotText:previous.checkpoint},{},true);await this._command("activate_role",{token:candidate.token,nextGeneration:previous.generation},{},true)}else await this._command("clear_role",{nextGeneration:previous.generation},{},true);this.hasRole=previous.hasRole;this.checkpoint=previous.checkpoint}catch(_){this._fail(error("worker_failed","Graph rollback failed"))}}indexDocuments(documents,options){return this.command("index_documents",{documents:documents},options)}removeDocument(id,options){return this.command("remove_document",{id:id},options)}query(text,options={}){const{signal:signal,expectedRevision:expectedRevision,...payload}=options;return this.command("query",{mode:"graph",...payload,text:text},{signal:signal,expectedRevision:expectedRevision})}async exportSnapshot(){return(await this.command("export_snapshot")).snapshotText}setPersistence(options){if(this.destroyed)throw error("destroyed");if(!root.TeRoleGraphStorage)throw error("storage_failed","Graph storage script unavailable");if(this.storage)this.storage.close();this.storage=new root.TeRoleGraphStorage(options)}async saveCheckpoint(){if(!this.storage)throw error("storage_failed","Persistence is not enabled");const epoch=this.epoch;const text=await this.exportSnapshot();await this.storage.save(text);if(epoch===this.epoch&&!this.destroyed)this.checkpoint=text;return text}async restoreCheckpoint(){if(!this.storage)throw error("storage_failed","Persistence is not enabled");const epoch=this.epoch;const text=await this.storage.load();if(text===null)throw error("not_found","No graph checkpoint");this._latest(epoch);await this.importSnapshot(text);return text}destroy(){if(this.destroyed)return;this.destroyed=true;++this.epoch;this._fail(error("destroyed"));this._state("destroyed");if(this.storage)this.storage.close();this.listeners.clear()}}TeRoleGraphClient.Error=RoleGraphError;root.TeRoleGraphClient=TeRoleGraphClient})(typeof window!=="undefined"?window:globalThis);class EditorSurface{constructor(root,options={}){this.root=root;this.decorations=[];this.changeListeners=new Set;this.composing=false;this.programmatic=false;this.pendingSource=null;this.historyOwner=options.historyOwner||null;this.history=[];this.historyIndex=-1;this.historyLimit=options.historyLimit||500;this.coalesceMs=options.coalesceMs===undefined?1e3:options.coalesceMs;this.nextDecorationId=1;this.pendingHint=null;this.abortController=new AbortController;this.destroyed=false;this.plaintextOnly=EditorSurface.enablePlaintextEditing(root);root.setAttribute("role","textbox");root.setAttribute("aria-multiline","true");root.spellcheck=false;this.text=this.serialise().text;this.render();this.lastSelection={start:this.text.length,end:this.text.length,direction:"none"};this.record("init",this.lastSelection);this.handlers={beforeinput:e=>this.onBeforeInput(e),input:e=>this.onInput(e),keydown:e=>this.onKeyDown(e),compositionstart:()=>{this.composing=true;this.pendingHint=null},compositionend:()=>this.onCompositionEnd(),paste:e=>this.onPaste(e),drop:e=>this.onDrop(e),copy:e=>this.onCopy(e,false),cut:e=>this.onCopy(e,true)};const signal=this.abortController.signal;root.addEventListener("beforeinput",this.handlers.beforeinput,{signal:signal});root.addEventListener("input",this.handlers.input,{capture:true,signal:signal});root.addEventListener("keydown",this.handlers.keydown,{signal:signal});root.addEventListener("compositionstart",this.handlers.compositionstart,{signal:signal});root.addEventListener("compositionend",this.handlers.compositionend,{signal:signal});root.addEventListener("paste",this.handlers.paste,{signal:signal});root.addEventListener("drop",this.handlers.drop,{signal:signal});root.addEventListener("copy",this.handlers.copy,{signal:signal});root.addEventListener("cut",this.handlers.cut,{signal:signal});this.pendingSelection=null;this.onSelectionChange=()=>{this.pendingHint=null;const sel=document.getSelection();if(sel&&sel.rangeCount>0&&this.root.contains(sel.anchorNode)&&this.root.contains(sel.focusNode)){this.pendingSelection=[sel.anchorNode,sel.anchorOffset,sel.focusNode,sel.focusOffset]}};document.addEventListener("selectionchange",this.onSelectionChange,{signal:signal})}destroy(){if(this.destroyed)return;this.destroyed=true;this.abortController.abort();this.changeListeners.clear();this.pendingSelection=null;this.pendingHint=null}static enablePlaintextEditing(root){try{root.contentEditable="plaintext-only"}catch(e){}if(root.contentEditable!=="plaintext-only"){root.contentEditable="true";return false}return true}getText(){return this.text}setText(text,options={}){return this.replaceRange(0,this.text.length,text,{source:"api",...options})}replaceRange(start,end,insert,options={}){const source=options.source||"api";const old=this.text;let s=this.snap(this.clamp(Math.min(start,end)),old);let e=this.snap(this.clamp(Math.max(start,end)),old);const inserted=EditorSurface.normaliseNewlines(String(insert));const edit={start:s,deletedLength:e-s,deletedText:old.slice(s,e),insertedText:inserted};if(options.move)edit.move={...options.move};if(options.swap)edit.swap={...options.swap};if(options.overflow)edit.overflow={...options.overflow};if(options.kg)edit.kg={...options.kg};this.text=old.slice(0,s)+inserted+old.slice(e);this.decorations=EditorSurface.mapRanges(this.decorations,edit);if(options.alt)edit.alt={...options.alt};this.render();const selStart=options.selectStart===undefined?s+inserted.length:options.selectStart;const selEnd=options.selectEnd===undefined?selStart:options.selectEnd;this.setSelectionOffsets(selStart,selEnd);this.record(source,this.lastSelection,edit);this.emitChange(edit,source);this.dispatchInput(source==="paste"?"insertFromPaste":"insertReplacementText",inserted);return edit}replaceRanges(edits,options={}){const source=options.source||"api";const sorted=(edits||[]).map((e=>({start:Math.min(e.start,e.end),end:Math.max(e.start,e.end),insert:String(e.insert||"")}))).sort(((a,b)=>a.start-b.start||a.end-b.end));for(let i=1;i=0;i--){const old=this.text;const s=this.snap(this.clamp(sorted[i].start),old);const e=this.snap(this.clamp(sorted[i].end),old);const inserted=EditorSurface.normaliseNewlines(sorted[i].insert);if(s===e&&inserted==="")continue;if(old.slice(s,e)===inserted)continue;const edit={start:s,deletedLength:e-s,deletedText:old.slice(s,e),insertedText:inserted};this.text=old.slice(0,s)+inserted+old.slice(e);this.decorations=EditorSurface.mapRanges(this.decorations,edit);applied.push(edit);this.emitChange(edit,source)}if(applied.length===0)return[];this.render();const first=applied[applied.length-1];const selStart=options.selectStart===undefined?first.start+first.insertedText.length:options.selectStart;const selEnd=options.selectEnd===undefined?selStart:options.selectEnd;this.setSelectionOffsets(selStart,selEnd);this.record(source,this.lastSelection,applied[0]);const entry=this.history[this.historyIndex];if(entry&&entry.text===this.text){entry.edits=applied.map((edit=>({start:edit.start,deletedText:edit.deletedText,insertedText:edit.insertedText})))}this.dispatchInput("insertReplacementText",null);return applied}replaceSelection(insert,source="api"){const{start:start,end:end}=this.getSelectionOffsets();return this.replaceRange(start,end,insert,{source:source})}getSelectionOffsets(){const live=this.readSelection();if(live){this.lastSelection=live}else if(this.pendingSelection){const[a,ao,f,fo]=this.pendingSelection;if(this.root.contains(a)&&this.root.contains(f)){const x=this.serialise(a,ao).offset;const y=this.serialise(f,fo).offset;this.lastSelection={start:Math.min(x,y),end:Math.max(x,y),direction:x===y?"none":xe){[s,e]=[e,s];dir=dir||"backward"}if(!dir)dir=s===e?"none":"forward";if(!this.isCanonical().ok)this.render();this.lastSelection={start:s,end:e,direction:s===e?"none":dir};this.pendingSelection=null;if(!this.root.isConnected)return{...this.lastSelection};const anchor=this.offsetToPoint(dir==="backward"?e:s);const focus=this.offsetToPoint(dir==="backward"?s:e);const sel=document.getSelection();if(sel)sel.setBaseAndExtent(anchor.node,anchor.offset,focus.node,focus.offset);return{...this.lastSelection}}pointToOffset(node,offset){if(!this.root.contains(node))return-1;return this.serialise(node,offset).offset}offsetToPoint(offset){const target=this.snap(this.clamp(offset));const walker=document.createTreeWalker(this.root,NodeFilter.SHOW_TEXT);let acc=0;let last=null;let node;while(node=walker.nextNode()){const len=node.data.length;if(target<=acc+len)return{node:node,offset:target-acc};acc+=len;last=node}if(last)return{node:last,offset:last.data.length};return{node:this.root,offset:0}}rangeForOffsets(start,end=start){if(!this.isCanonical().ok)this.render();const a=this.offsetToPoint(Math.min(start,end));const b=this.offsetToPoint(Math.max(start,end));const range=document.createRange();range.setStart(a.node,a.offset);range.setEnd(b.node,b.offset);return range}getCaretRect(offset){const at=offset===undefined?this.getSelectionOffsets().start:offset;const rects=this.rangeForOffsets(at).getClientRects();if(rects.length>0){const r=rects[0];return{left:r.left,top:r.top,bottom:r.bottom,height:r.height}}const box=this.root.getBoundingClientRect();const style=getComputedStyle(this.root);const left=box.left+(parseFloat(style.paddingLeft)||0);const top=box.top+(parseFloat(style.paddingTop)||0);const lineHeight=parseFloat(style.lineHeight)||parseFloat(style.fontSize)*1.2||16;return{left:left,top:top,bottom:top+lineHeight,height:lineHeight}}setDecorations(list){const len=this.text.length;const next=[];for(const item of list||[]){const s=this.snap(Math.max(0,Math.min(len,item.start|0)));const e=this.snap(Math.max(0,Math.min(len,item.end|0)));if(e<=s)continue;next.push({id:item.id===undefined?String(this.nextDecorationId++):String(item.id),start:s,end:e,className:item.className||"",data:item.data,attributes:EditorSurface.sanitiseAttributes(item.attributes)})}next.sort(((a,b)=>a.start-b.start||a.end-b.end));const sel=this.getSelectionOffsets();this.decorations=next;this.render();if(this.readSelection()!==null||document.activeElement===this.root){this.setSelectionOffsets(sel.start,sel.end,sel.direction)}return this.getDecorations()}static sanitiseAttributes(attributes,warn=true){if(!attributes||typeof attributes!=="object")return null;const out={};let any=false;for(const[name,value]of Object.entries(attributes)){if(EditorSurface.isSafeAttributeName(name)){out[name]=String(value);any=true}else if(warn){console.warn(`EditorSurface: decoration attribute "${name}" is not allowed and was ignored`)}}return any?out:null}static isSafeAttributeName(name){if(typeof name!=="string"||name==="data-te-decoration")return false;return name==="role"||/^aria-[a-z]+$/.test(name)||/^data-[a-z0-9_.-]*[a-z0-9_]$/.test(name)}getDecorations(){return this.decorations.map((d=>({...d})))}decorationsAt(offset){return this.decorations.filter((d=>d.start<=offset&&offset({...d})))}clearDecorations(){return this.setDecorations([])}onChange(callback){this.changeListeners.add(callback);return()=>this.changeListeners.delete(callback)}resetHistory(){this.history=[];this.historyIndex=-1;this.record("init",this.lastSelection)}squashHistory(count,source=null){const first=this.historyIndex-count+1;if(count<2||first<1)return false;const group=this.history.slice(first,this.historyIndex+1);if(group.some((entry=>!entry.edits)))return false;const last=group[group.length-1];const merged={...last,source:source||last.source,edits:group.flatMap((entry=>entry.edits))};if(group.some((entry=>entry.ghostHistory||entry.edits.some((step=>step.ghost)))))merged.ghostHistory=true;this.history.splice(first,count,merged);this.historyIndex=first;return true}trimHistory(){const extra=this.history.length-this.historyLimit;if(extra<=0)return;const drop=Math.min(extra,this.historyIndex);if(drop<=0)return;this.history.splice(0,drop);this.historyIndex-=drop}forgetRedo(){this.history.length=this.historyIndex+1}canUndo(){if(this.historyOwner)return true;return this.historyIndex>0}canRedo(){if(this.historyOwner)return true;return this.historyIndexstep.ghost))){return this.replayGhostHistory(this.historyIndex-1,"undo",undone.edits)}this.historyIndex-=1;const steps=undone.edits?undone.edits.slice().reverse().map(EditorSurface.invertStep):null;this.restore(this.history[this.historyIndex],"undo",steps);return true}redo(){if(this.historyOwner){this.historyOwner.redo();return true}if(!this.canRedo())return false;const next=this.history[this.historyIndex+1];if(next.ghostHistory||next.edits&&next.edits.some((step=>step.ghost))){return this.replayGhostHistory(this.historyIndex+1,"redo",next.edits)}this.historyIndex+=1;const entry=this.history[this.historyIndex];this.restore(entry,"redo",entry.edits);return true}replayGhostHistory(index,source,steps){if(!this.ghostHistoryAdapter)return false;try{if(source==="undo"&&Array.isArray(steps))steps=steps.slice().reverse().map(EditorSurface.invertStep);this.ghostHistoryAdapter.replayGhostHistory(index,source,steps);return true}catch(error){this.ghostHistoryAdapter.ghostHistoryNotice(error);return false}}focus(){const had=this.readSelection();this.root.focus({preventScroll:true});if(!had){const{start:start,end:end,direction:direction}=this.lastSelection;this.setSelectionOffsets(start,end,direction)}}serialise(stopNode=null,stopOffset=0){const parts=[];let length=0;let found=-1;let afterBlock=false;const emit=s=>{if(s){parts.push(s);length+=s.length;afterBlock=false}};const visit=node=>{const children=node.childNodes;for(let i=0;i0&&!afterBlock)emit("\n");visit(child);if(EditorSurface.hasFollowingContent(child)){emit("\n");afterBlock=true}}else{visit(child)}}}if(node===stopNode&&stopOffset>=children.length&&found<0)found=length};visit(this.root);return{text:parts.join(""),offset:found}}isCanonical(){const children=this.root.childNodes;let hasPlaceholder=false;for(let i=0;ix-y));for(let i=0;i+1d.start<=s&&d.end>=e));if(covering.length===0){frag.appendChild(document.createTextNode(piece))}else{const span=document.createElement("span");const classes=["te-decoration"];for(const d of covering){if(d.className)classes.push(...d.className.split(/\s+/).filter(Boolean))}span.className=Array.from(new Set(classes)).join(" ");span.setAttribute("data-te-decoration",covering.map((d=>d.id)).join(" "));for(const d of covering){if(!d.attributes)continue;for(const[name,value]of Object.entries(d.attributes))span.setAttribute(name,String(value))}span.appendChild(document.createTextNode(piece));frag.appendChild(span)}}root.textContent="";root.appendChild(frag)}if(text.endsWith("\n")){const br=document.createElement("br");br.className="te-placeholder";root.appendChild(br)}}sync(source,hint=null,inputType=""){const shape=this.isCanonical();const text=shape.ok?this.root.textContent:this.serialise().text;const old=this.text;let edit;let sel;if(hint&&hint.exact){edit=EditorSurface.diff(old,text,hint)}else{const ends=old===text?null:EditorSurface.commonEnds(old,text);if(!ends||!EditorSurface.isAmbiguous(old,text,ends)){edit=ends&&EditorSurface.diff(old,text,null,ends)}else{const growth=text.length-old.length;const minimal={deletedLength:Math.max(0,-growth),insertedLength:Math.max(0,growth)};let resolved=null;let pre=hint||(this.pendingSelection===null?this.lastSelection:null);if(pre&&pre.start===pre.end&&growth<0){const k=pre.start;const d=minimal.deletedLength;if(/Backward$/.test(inputType)&&k-d>=0)pre={start:k-d,end:k};else if(/Forward$/.test(inputType)&&k+d<=old.length)pre={start:k,end:k+d};else pre=null}if(pre){const candidate=EditorSurface.diff(old,text,pre,ends);if(candidate.deletedLength===minimal.deletedLength&&candidate.insertedText.length===minimal.insertedLength){resolved=candidate}else{sel=this.readSelection();const caret=sel&&sel.start===sel.end?sel.start:null;const end=candidate.start+candidate.insertedText.length;if(caret===null?hint!==null:end===caret)resolved=candidate}}if(!resolved){if(sel===undefined)sel=this.readSelection();if(sel&&sel.start===sel.end)resolved=EditorSurface.diff(old,text,{caret:sel.start},ends)}edit=resolved||EditorSurface.diff(old,text,null,ends)}}let needsRender=!shape.ok||shape.hasPlaceholder!==text.endsWith("\n");if(edit&&this.decorations.length>0){const editEnd=edit.start+edit.deletedLength;if(this.decorations.some((d=>d.start<=editEnd&&d.end>=edit.start)))needsRender=true}if(sel===undefined)sel=needsRender?this.readSelection():null;if(edit)this.decorations=EditorSurface.mapRanges(this.decorations,edit);this.text=text;if(needsRender){this.render();if(sel)this.setSelectionOffsets(sel.start,sel.end,sel.direction)}else if(edit){const caret=edit.start+edit.insertedText.length;this.lastSelection={start:caret,end:caret,direction:"none"};this.pendingSelection=null}if(edit){this.record(source,this.lastSelection,edit);this.emitChange(edit,source)}}record(source,sel,edit=null){const now=performance.now();const step=edit?{start:edit.start,deletedText:edit.deletedText,insertedText:edit.insertedText}:null;if(step&&edit.move)step.move=edit.move;if(step&&edit.swap)step.swap=edit.swap;if(step&&edit.overflow)step.overflow=edit.overflow;if(step&&edit.ghost)step.ghost=edit.ghost;const entry={text:this.text,start:sel?sel.start:this.text.length,end:sel?sel.end:this.text.length,source:source,time:now,edits:step?[step]:null};if(step&&edit.ghost)entry.ghostHistory=true;if(step&&(edit.alt||edit.ghost)){if(edit.alt)step.alt=edit.alt;this.history.length=this.historyIndex+1;this.history.push(entry);if(this.history.length>this.historyLimit)this.history.shift();this.historyIndex=this.history.length-1;return}const top=this.history[this.historyIndex];if(top&&top.text===entry.text&&!(step&&(step.move||step.swap))){top.start=entry.start;top.end=entry.end;return}this.history.length=this.historyIndex+1;const coalesce=top&&this.historyIndex>0&&(source==="typing"||source==="delete")&&!top.ghostHistory&&!(top.edits&&top.edits.some((step=>step.ghost)))&&top.source===source&&now-top.timethis.historyLimit)this.history.shift();this.historyIndex=this.history.length-1}}restore(entry,source,steps){const applied=steps?EditorSurface.replaySteps(this.text,steps,entry.text):null;let caret=null;if(applied){for(let i=0;i{if(this.pendingHint===hint)this.pendingHint=null}),0)}}preEditRange(e){let start=-1;let end=-1;let exact=false;const ranges=typeof e.getTargetRanges==="function"?e.getTargetRanges():[];if(ranges.length>0){const r=ranges[0];start=this.pointToOffset(r.startContainer,r.startOffset);end=this.pointToOffset(r.endContainer,r.endOffset);exact=start>=0&&end>=0}if(start<0||end<0){const sel=this.readSelection();if(!sel)return null;start=sel.start;end=sel.end}return{start:Math.min(start,end),end:Math.max(start,end),exact:exact,inputType:e.inputType||"",text:this.text,event:e}}onInput(e){if(this.programmatic)return;if(e.isComposing||this.composing)return;const type=e.inputType||"";const source=type?type.startsWith("delete")?"delete":"typing":this.pendingSource||"typing";const h=this.pendingHint;const hint=h&&!h.event.defaultPrevented&&h.text===this.text&&h.inputType===(e.inputType||"")?h:null;this.pendingSource=null;this.pendingHint=null;this.sync(source,hint,e.inputType||"")}onCompositionEnd(){this.composing=false;this.pendingSource=null;this.pendingHint=null;this.sync("composition")}onKeyDown(e){if(e.isComposing||this.composing)return;if(!(e.ctrlKey||e.metaKey)||e.altKey)return;const key=(e.key||"").toLowerCase();if(key==="z"&&!e.shiftKey){e.preventDefault();this.undo()}else if(key==="z"&&e.shiftKey||key==="y"&&!e.shiftKey){e.preventDefault();this.redo()}}onPaste(e){e.preventDefault();const data=e.clipboardData?e.clipboardData.getData("text/plain"):"";if(data)this.replaceSelection(data,"paste")}onDrop(e){if(e.defaultPrevented)return;e.preventDefault();const data=e.dataTransfer?e.dataTransfer.getData("text/plain"):"";if(!data)return;const offset=this.dropOffset(e);this.replaceRange(offset,offset,data,{source:"drop"})}dropOffset(e){let offset=null;if(document.caretRangeFromPoint){const range=document.caretRangeFromPoint(e.clientX,e.clientY);if(range&&this.root.contains(range.startContainer)){offset=this.pointToOffset(range.startContainer,range.startOffset)}}return offset===null||offset<0?this.getSelectionOffsets().start:offset}onCopy(e,cut){const{start:start,end:end}=this.getSelectionOffsets();if(start===end||!e.clipboardData)return;e.preventDefault();e.clipboardData.setData("text/plain",this.text.slice(start,end));if(cut)this.replaceRange(start,end,"",{source:"cut"})}clamp(offset){const n=Number.isFinite(offset)?Math.trunc(offset):0;return Math.max(0,Math.min(this.text.length,n))}snap(offset,text=this.text){if(offset>0&&offset=55296&&hi<=56319&&lo>=56320&&lo<=57343)return offset-1}return offset}static normaliseNewlines(text){return text.replace(/\r\n?/g,"\n")}static isBlock(node){return/^(DIV|P|LI|UL|OL|H[1-6]|PRE|BLOCKQUOTE|SECTION|ARTICLE)$/.test(node.tagName)}static isPlaceholderBr(br,root){const parent=br.parentNode;if(parent!==root&&!EditorSurface.isBlock(parent))return false;let next=br.nextSibling;while(next&&next.nodeType===Node.TEXT_NODE&&next.data==="")next=next.nextSibling;return next===null}static hasFollowingContent(node){let next=node.nextSibling;while(next){if(next.nodeType===Node.ELEMENT_NODE)return true;if(next.nodeType===Node.TEXT_NODE&&next.data!=="")return true;next=next.nextSibling}return false}static commonEnds(a,b){const min=Math.min(a.length,b.length);let prefix=0;while(prefix=0&&start<=end&&end<=a.length){pMax=Math.min(pMax,start);sMax=Math.min(sMax,a.length-end)}else if(Number.isInteger(caret)&&caret>=0&&caret<=b.length){pMax=Math.min(pMax,caret);sMax=Math.min(sMax,b.length-caret);suffixFirst=true}}const fixPrefix=n=>{if(n>0){const c=a.charCodeAt(n-1);if(c>=55296&&c<=56319)return n-1}return n};const fixSuffix=n=>{if(n>0){const c=a.charCodeAt(a.length-n);if(c>=56320&&c<=57343)return n-1}return n};let p;let s;if(suffixFirst){s=fixSuffix(Math.min(suffix,sMax));p=fixPrefix(Math.min(prefix,pMax,min-s))}else{p=fixPrefix(Math.min(prefix,pMax));s=fixSuffix(Math.min(suffix,sMax,min-p))}return{start:p,deletedLength:a.length-p-s,deletedText:a.slice(p,a.length-s),insertedText:b.slice(p,b.length-s)}}static invertStep(step){const inverse={...step,start:step.start,deletedText:step.insertedText,insertedText:step.deletedText};if(step.alt)inverse.alt={...step.alt,span:step.alt.span,before:step.alt.after,after:step.alt.before};if(step.move)inverse.move={...step.move,...EditorSurface.invertMove(step.move)};if(step.swap)inverse.swap={...step.swap,span:step.swap.span,from:step.swap.to,to:step.swap.from};if(step.overflow)inverse.overflow={...step.overflow,before:step.overflow.after,after:step.overflow.before};if(step.ghost)inverse.ghost={...step.ghost,change:step.ghost.change,undo:typeof step.ghost.undo==="boolean"?!step.ghost.undo:step.ghost.undo};return inverse}static invertMove({start:start,end:end,to:to}){const len=end-start;return to>end?{start:to-len,end:to,to:start}:{start:to,end:to+len,to:end}}static appendStep(steps,next,before){const last=steps[steps.length-1];if(!last)return[next];if([last,next].some((step=>step.ghost||step.alt||step.move||step.swap||step.overflow)))return[...steps,next];const lastEnd=last.start+last.insertedText.length;const nextEnd=next.start+next.deletedText.length;if(next.start>lastEnd||nextEndt.length||t.slice(step.start,end)!==step.deletedText)return null;t=t.slice(0,step.start)+step.insertedText+t.slice(end);const edit={start:step.start,deletedLength:step.deletedText.length,deletedText:step.deletedText,insertedText:step.insertedText};if(step.alt)edit.alt=step.alt;if(step.move)edit.move=step.move;if(step.swap)edit.swap=step.swap;if(step.overflow)edit.overflow=step.overflow;if(step.ghost)edit.ghost=step.ghost;out.push({edit:edit,text:t})}return t===expected?out:null}static isAmbiguous(a,b,ends=null){if(a===b)return false;const{prefix:prefix,suffix:suffix}=ends||EditorSurface.commonEnds(a,b);return prefix+suffix>Math.min(a.length,b.length)}static mapRanges(ranges,edit){if(!edit||ranges.length===0)return ranges;if(edit.deletedLength===0&&edit.insertedText==="")return ranges;const editStart=edit.start;const editEnd=edit.start+edit.deletedLength;const delta=edit.insertedText.length-edit.deletedLength;const out=[];for(const r of ranges){if(r.end<=editStart)out.push(r);else if(r.start>=editEnd)out.push({...r,start:r.start+delta,end:r.end+delta})}return out}}const TeKg={isKgSpan(id){return/^kg-\d+-\d+$/.test(String(id))},menuItem(editor){return{id:"ai-alternatives",label:"AI alternatives for selection",key:"ctrl+shift+g",available:ctx=>{if(!ctx)return false;if(ctx.kgTerm===undefined)ctx.kgTerm=editor.kgLookup(ctx.start,ctx.end);return!!ctx.kgTerm||editor.hasAlternativesProviders()&&ctx.start!==ctx.end},run:ctx=>editor.alternativesForSelection(ctx).catch((err=>{console.error("AI alternatives for selection failed",err);return null}))}}};window.TeKg=TeKg;const TeProviderRefs=new Map;const TE_ICON_NAME=/^[a-z0-9-]+$/;function teConfigIcon(name){const icon=document.createElement("sl-icon");if(typeof name==="string"&&TE_ICON_NAME.test(name))icon.setAttribute("name",name);return icon}function teTextElement(tag,text,className){const el=document.createElement(tag);if(className)el.className=className;el.textContent=text==null?"":String(text);return el}function teConfigShortcutMatches(e,key){const parts=String(key).toLowerCase().split("+").filter(Boolean);const want=parts.pop();if(!want)return false;const mods=new Set(parts);if(mods.has("ctrl")!==e.ctrlKey||mods.has("alt")!==e.altKey||mods.has("meta")!==e.metaKey){return false}if((e.key||"").toLowerCase()!==want)return false;return!/^[a-z0-9]$/.test(want)||mods.has("shift")===e.shiftKey}class MarkdownEditor{constructor(config){this.config=config;this.shortcuts=config.shortcuts;this.commands=config.commands.map((cmd=>({...cmd,action:()=>this.wrapSelectedText(cmd.prefix,cmd.suffix)})));this.abortController=new AbortController;this.createdNodes=[];this.destroyed=false;this.warningListeners=new Set;this.warningElement=null;this.warning=null;this.warningKind=null;this.setAsideCount=0;this.suppressModelSync=false;this.graphClient=null;this.graphAttachment=null}destroy(){if(this.destroyed)return[];this.destroyed=true;this.detachGraphDocument();if(this.graphRoleListenerApi){this.graphRoleListenerApi.kg_set_role_change_listener(undefined);this.graphRoleListenerApi=null}if(this.graphClient)this.graphClient.destroy();const altDrafts=this.altPanel?this.altPanel.destroy()||[]:[];this.abortController.abort();for(const node of this.createdNodes){if(node.tagName==="SL-DIALOG"&&node.open)node.open=false;node.remove()}this.createdNodes=[];const drafts=(this.blocks?this.blocks.destroy():[]).map((d=>({kind:"block",...d})));if(this.persistence)this.persistence.destroy();if(this.overflow)this.overflow.destroy();if(this.chrome)this.chrome.destroy();if(this.selectionMenu)this.selectionMenu.destroy();if(this.ghosts)this.ghosts.destroy();if(this.indicators)this.indicators.destroy();if(this.headings)this.headings.destroy();if(this.lab)this.lab.destroy();for(const d of altDrafts)drafts.push({kind:"alternative",...d});if(this.decorations)this.decorations.destroy();if(this.surface)this.surface.destroy();for(const id of Array.from((this.alternativeProviders||new Map).keys())){this.unregisterAlternativesProvider(id)}this.warningListeners.clear();return drafts}initialize(root){const scope=root||document.getElementById("app")||document;this.input=scope.querySelector(".markdown-input");this.toolbar=scope.querySelector("#formatting-toolbar");this.shortcutsList=scope.querySelector("#shortcuts-list");this.dialog=scope.querySelector(".shortcuts-dialog");this.helpButton=scope.querySelector("#show-help");if(!this.input||!this.toolbar||!this.shortcutsList||!this.dialog||!this.helpButton){console.error("Required DOM elements not found");return}this.root=scope===document?this.input.closest(".te-app")||this.input.parentElement:scope;if(this.shortcuts.length===0){console.error("No shortcuts available");return}this.surface=new EditorSurface(this.input,{historyOwner:this.config.historyOwner});this.connectDocumentModel();this.observeRolePublications();this.setupShortcuts();this.setupHelpDialog();this.setupCommandPalette();if(typeof window.WriteOnChrome==="function"){this.chrome=new window.WriteOnChrome(this,{signal:this.abortController.signal})}if(typeof window.TeDecorationRegistry==="function"){this.decorations=new window.TeDecorationRegistry(this.surface);if(typeof window.TeIndicatorLayer==="function"){this.indicators=new window.TeIndicatorLayer(this,{signal:this.abortController.signal})}if(typeof window.TeHeadingLayer==="function"){this.headings=new window.TeHeadingLayer(this,{signal:this.abortController.signal})}if(typeof window.TeGhostLayer==="function"){this.ghosts=new window.TeGhostLayer(this,{signal:this.abortController.signal})}if(typeof window.TeSelectionMenu==="function"){this.selectionMenu=new window.TeSelectionMenu(this,{signal:this.abortController.signal});this.selectionMenu.register(TeKg.menuItem(this))}if(typeof window.TeLabPopover==="function"){this.lab=new window.TeLabPopover(this,{signal:this.abortController.signal})}if(typeof window.TeAlternativesPanel==="function"){this.altPanel=new window.TeAlternativesPanel(this,{signal:this.abortController.signal})}}if(typeof window.BlocksView==="function"){this.blocks=new window.BlocksView(this,{signal:this.abortController.signal})}if(typeof window.TeOverflowPanel==="function"){this.overflow=new window.TeOverflowPanel(this,{signal:this.abortController.signal})}if(!this.config.hostManaged&&typeof window.TePersistence==="function"){this.persistence=new window.TePersistence(this,{signal:this.abortController.signal})}}wrapSelectedText(prefix,suffix){const{start:start,end:end}=this.surface.getSelectionOffsets();const text=this.surface.getText();const selection=text.substring(start,end);const wrappedText=selection?selection:"text";this.surface.focus();this.surface.replaceRange(start,end,prefix+wrappedText+suffix,{source:"format",selectStart:start+prefix.length,selectEnd:selection?end+prefix.length:start+prefix.length+4})}setupShortcuts(){this.shortcuts.forEach((shortcut=>{const button=document.createElement("sl-tooltip");button.setAttribute("content",String(shortcut.key));const slButton=document.createElement("sl-button");slButton.setAttribute("size","small");slButton.setAttribute("variant","default");slButton.appendChild(teConfigIcon(shortcut.name));button.appendChild(slButton);button.querySelector("sl-button").addEventListener("click",(()=>{this.wrapSelectedText(shortcut.prefix,shortcut.suffix)}),{signal:this.abortController.signal});this.toolbar.appendChild(button);this.createdNodes.push(button)}));this.input.addEventListener("keydown",(e=>{if(e.isComposing)return;const shortcut=this.shortcuts.find((s=>teConfigShortcutMatches(e,s.key)));if(shortcut){e.preventDefault();this.wrapSelectedText(shortcut.prefix,shortcut.suffix)}}),{signal:this.abortController.signal})}setupHelpDialog(){this.shortcuts.forEach((shortcut=>{const item=document.createElement("div");item.className="shortcut-item";const badge=teTextElement("sl-badge",shortcut.key);badge.setAttribute("variant","neutral");item.append(teConfigIcon(shortcut.name),teTextElement("span",shortcut.desc,"shortcut-desc"),badge);this.shortcutsList.appendChild(item);this.createdNodes.push(item)}));this.helpButton.addEventListener("click",(()=>this.dialog.show()),{signal:this.abortController.signal})}setupCommandPalette(){const signal=this.abortController.signal;const commandMenu=document.createElement("div");commandMenu.classList.add("command-menu");commandMenu.style.display="none";commandMenu.setAttribute("tabindex","0");this.commandMenu=commandMenu;const commandList=document.createElement("div");commandList.classList.add("command-list");commandMenu.appendChild(commandList);document.body.appendChild(commandMenu);this.createdNodes.push(commandMenu);let selectedIndex=-1;let visibleItems=[];let slashPosition=null;this.commands.forEach((cmd=>{const item=document.createElement("div");item.classList.add("command-item");item.append(teConfigIcon(cmd.icon),teTextElement("span",cmd.name));item.addEventListener("click",(()=>{if(slashPosition!==null&&this.surface.getText().charAt(slashPosition)==="/"){this.surface.replaceRange(slashPosition,slashPosition+1,"",{source:"command"})}cmd.action();hideCommandMenu()}),{signal:signal});commandList.appendChild(item)}));const updateSelection=()=>{visibleItems=Array.from(commandList.querySelectorAll(".command-item"));visibleItems.forEach(((item,index)=>{if(index===selectedIndex){item.classList.add("selected");item.scrollIntoView({block:"nearest"})}else{item.classList.remove("selected")}}))};const positionCommandMenu=()=>{if(commandMenu.style.display==="none")return;const caret=this.surface.getCaretRect();const menuRect=commandMenu.getBoundingClientRect();const viewportWidth=window.innerWidth;const viewportHeight=window.innerHeight;let left=caret.left;let top=caret.bottom+4;if(left+menuRect.width>viewportWidth){left=viewportWidth-menuRect.width-10}if(left<0){left=10}if(top+menuRect.height>viewportHeight){top=caret.top-menuRect.height-10}if(top<0){top=10}commandMenu.style.position="fixed";commandMenu.style.left=`${left}px`;commandMenu.style.top=`${top}px`};const showCommandMenu=()=>{commandMenu.style.display="block";selectedIndex=0;updateSelection();positionCommandMenu();commandMenu.focus()};const hideCommandMenu=()=>{commandMenu.style.display="none";selectedIndex=-1;slashPosition=null;this.surface.focus()};commandMenu.addEventListener("keydown",(e=>{switch(e.key){case"ArrowDown":e.preventDefault();selectedIndex=Math.min(selectedIndex+1,visibleItems.length-1);if(selectedIndex===-1&&visibleItems.length>0)selectedIndex=0;updateSelection();break;case"ArrowUp":e.preventDefault();selectedIndex=Math.max(selectedIndex-1,0);updateSelection();break;case"Enter":e.preventDefault();if(selectedIndex>=0&&selectedIndex{if(e.key==="/"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.isComposing){e.preventDefault();const{start:start}=this.surface.getSelectionOffsets();this.surface.replaceSelection("/","typing");slashPosition=start;showCommandMenu()}}),{signal:signal});document.addEventListener("click",(e=>{if(commandMenu.style.display==="none")return;if(!commandMenu.contains(e.target)&&!this.input.contains(e.target)){hideCommandMenu()}}),{signal:signal});window.addEventListener("scroll",positionCommandMenu,{signal:signal});window.addEventListener("resize",positionCommandMenu,{signal:signal});this.input.addEventListener("scroll",positionCommandMenu,{signal:signal})}documentApi(){const api=this.config&&this.config.bindings||window.wasmBindings;return api&&typeof api.open_document==="function"&&typeof api.apply_edit==="function"?api:null}requireDocumentApi(){const api=this.documentApi();if(!api)throw new Error("The WASM document API is not available");return api}ghostHistoryApi(){if(this.destroyed||this.surface.destroyed)throw new Error("Ghost history is unavailable after editor destruction");if(this.ghostHistoryConflict)throw new Error("Ghost history conflict: explicitly reopen the document to recover");if(this.ghostHistoryActive)throw new Error("A ghost history transaction is already active");const api=this.requireDocumentApi();for(const name of["begin_trim_ghost_transaction","begin_document_history_replay","finish_document_history_transaction","abort_document_history_transaction"]){if(typeof api[name]!=="function")throw new Error(`Missing native checkpoint API: ${name}`)}return api}ghostHistoryNotice(error){const message=error&&(error.message||error.error)||String(error);this.showWarning(`Ghost history refused: ${message}`,"ghost-history")}ghostHistoryRole(api){return typeof api.kg_role_snapshot_json==="function"?api.kg_role_snapshot_json():null}ghostHistoryCheckpoint(api){const s=this.surface;return{history:structuredClone(s.history),historyIndex:s.historyIndex,historyLimit:s.historyLimit,text:s.text,selection:{...s.readSelection()||s.lastSelection},lastSelection:{...s.lastSelection},decorations:structuredClone(s.decorations),nextDecorationId:s.nextDecorationId,pendingHint:s.pendingHint,pendingSource:s.pendingSource,nodes:Array.from(s.root.childNodes),role:this.ghostHistoryRole(api),layers:this.decorations?new Map(Array.from(this.decorations.layers,(([name,items])=>[name,structuredClone(items)]))):null,registryPending:this.decorations&&this.decorations.pending,ghosts:this.ghosts?{ghosts:structuredClone(this.ghosts.ghosts),modelGhosts:this.ghosts.modelGhosts,count:this.ghosts.count,refreshCount:this.ghosts.refreshCount}:null}}restoreGhostHistoryCheckpoint(saved,api){const s=this.surface;const roleChanged=this.ghostHistoryRole(api)!==saved.role;Object.assign(s,{history:saved.history,historyIndex:saved.historyIndex,historyLimit:saved.historyLimit,text:saved.text,lastSelection:saved.lastSelection,decorations:saved.decorations,nextDecorationId:saved.nextDecorationId,pendingHint:saved.pendingHint,pendingSource:saved.pendingSource});if(saved.layers){const retired=this.decorations.layers.get("lab-trim");this.decorations.layers=saved.layers;this.decorations.pending=saved.registryPending;if(roleChanged)this.decorations.layers.set("lab-trim",retired||[])}if(saved.ghosts)Object.assign(this.ghosts,saved.ghosts);if(roleChanged){s.decorations=s.decorations.filter((item=>!String(item.id).startsWith("lab-trim:")));s.render()}else s.root.replaceChildren(...saved.nodes);s.setSelectionOffsets(saved.selection.start,saved.selection.end,saved.selection.direction);s.lastSelection=saved.lastSelection}static ghostAcknowledgement(value,token,field){if(!value||value.ok!==true||value[field]!==true||value.token!==token){throw new Error(`Native ${field} acknowledgement is missing or invalid`)}}runGhostHistoryTransaction(api,begin,prepare){const saved=this.ghostHistoryCheckpoint(api);let started;let returned=false;this.ghostHistoryActive=true;try{started=begin();returned=true;if(started===null)return null;if(!started||started.ok!==true||typeof started.token!=="string"||!started.token){throw new Error("Native begin acknowledgement is invalid")}prepare(started,saved);if(api.document_body()!==this.surface.getText())throw new Error("Tentative surface/native body mismatch");MarkdownEditor.ghostAcknowledgement(api.finish_document_history_transaction(started.token),started.token,"finalized");return started}catch(error){if(started&&typeof started.token==="string"){let retired=false;try{MarkdownEditor.ghostAcknowledgement(api.abort_document_history_transaction(started.token),started.token,"rolledBack");retired=true;this.restoreGhostHistoryCheckpoint(saved,api)}catch(abortError){this.ghostHistoryConflict={token:retired?null:started.token,nativeRetired:retired};throw new Error(`Ghost history conflict; ${retired?"native rollback acknowledged but surface restoration failed":"rollback not acknowledged"}. Explicitly reopen to recover. ${abortError.error||abortError.message||abortError}`)}}else if(returned){this.ghostHistoryConflict={token:null,nativeRetired:false}}throw error}finally{this.ghostHistoryActive=false}}paintGhostHistory(annotations,applied=[]){const s=this.surface;for(const{edit:edit,text:text}of applied){s.decorations=EditorSurface.mapRanges(s.decorations,edit);if(this.decorations)this.decorations.mapEdit(edit);s.text=text}if(this.ghosts)this.ghosts.refresh(annotations);else if(this.decorations)this.decorations.set("ghosts",annotations.ghosts.map((g=>({id:g.id,start:g.anchor.start,end:g.anchor.end,className:"te-ghost"}))));s.render()}commitTrimGhosts(level,kept){const s=this.surface;if(!Number.isInteger(s.historyLimit)||s.historyLimit<2||s.historyIndex<0){throw new Error("History capacity must retain a base state and one ghost operation")}const api=this.ghostHistoryApi();if(!Array.isArray(kept)||kept.length>4096)throw new Error("Too many kept indices");const body=s.getText();const keptJson=JSON.stringify(kept);MarkdownEditor.ghostTransportBounds(body,body,keptJson);s.ghostHistoryAdapter=this;const result=this.runGhostHistoryTransaction(api,(()=>api.begin_trim_ghost_transaction(body,level,keptJson)),((started,saved)=>{if(!started.change||!started.annotations)throw new Error("Missing native ghost receipt/annotations");s.record("trim-ghost",s.lastSelection,{start:0,deletedText:"",insertedText:"",ghost:{change:structuredClone(started.change),undo:false}});const top=s.history[s.historyIndex];if(s.text!==body||s.historyIndex<1||!top||top.text!==body||top.edits?.length!==1||JSON.stringify(top.edits[0].ghost)!==JSON.stringify({change:started.change,undo:false})){throw new Error("Ghost receipt was not retained as an undoable history step")}this.paintGhostHistory(started.annotations);s.setSelectionOffsets(saved.selection.start,saved.selection.end,saved.selection.direction)}));if(result)this.notifyGhostHistory("insertReplacementText");return result}static ghostTransportBounds(body,target,json){const encoder=new TextEncoder;if([body,target,json].some((text=>encoder.encode(text).length>16*1024*1024))){throw new Error("Ghost transaction transport exceeds 16 MiB")}}static nativeGhostHistoryOperations(body,steps,target){if(!Array.isArray(steps)||!steps.some((step=>step&&step.ghost)))throw new Error("Ghost history metadata is missing; text-diff fallback is forbidden");if(steps.length>4096)throw new Error("Too many history operations");const allowed=["start","deletedText","insertedText","ghost","alt","move","swap","overflow"];for(const step of steps){if(Object.keys(step).some((key=>!allowed.includes(key)))||["ghost","alt","move","swap","overflow"].filter((key=>step[key])).length>1||!Number.isInteger(step.start)||step.start<0||step.start>4294967295||typeof step.deletedText!=="string"||typeof step.insertedText!=="string"){throw new Error("Invalid or conflicting ghost-group history metadata")}if(step.ghost&&(typeof step.ghost.undo!=="boolean"||step.deletedText||step.insertedText)){throw new Error("Ghost receipt must be annotation-only with an exact direction")}for(const[key,fields]of Object.entries({ghost:["change","undo"],alt:["span","before","after"],move:["start","end","to"],swap:["span","from","to"],overflow:["before","after"]})){if(step[key]&&(Object.keys(step[key]).length!==fields.length||fields.some((field=>!Object.hasOwn(step[key],field))))){throw new Error("Unknown or missing ghost-group metadata fields")}}}const applied=EditorSurface.replaySteps(body,steps,target);if(!applied)throw new Error("Ghost history exact steps do not reproduce the target body");const operations=applied.map((({edit:edit,text:text})=>{if(edit.ghost)return{kind:"ghost",body:text,...edit.ghost};const base={body:text,start:edit.start,deletedText:edit.deletedText,insertedText:edit.insertedText};if(edit.alt)return{...base,kind:"alt",span:edit.alt.span,target:edit.alt.after};if(edit.move)return{...base,kind:"move",move:edit.move};if(edit.swap)return{...base,kind:"swap",...edit.swap};if(edit.overflow)return{...base,kind:"overflow",...edit.overflow};return{...base,kind:"edit"}}));const json=JSON.stringify(operations);MarkdownEditor.ghostTransportBounds(body,target,json);return{applied:applied,json:json}}replayGhostHistory(index,source,steps){const api=this.ghostHistoryApi();const s=this.surface;const entry=s.history[index];const{applied:applied,json:json}=MarkdownEditor.nativeGhostHistoryOperations(s.getText(),steps,entry.text);const result=this.runGhostHistoryTransaction(api,(()=>api.begin_document_history_replay(s.getText(),entry.text,json)),(started=>{if(!started.annotations)throw new Error("Missing native history annotations");s.historyIndex=index;this.paintGhostHistory(started.annotations,applied);const last=applied[applied.length-1];if(source==="undo"&&last)s.setSelectionOffsets(last.edit.start+last.edit.insertedText.length);else s.setSelectionOffsets(entry.start,entry.end)}));if(!result)throw new Error("Native mixed history begin returned an invalid no-op");this.notifyGhostHistory(source==="undo"?"historyUndo":"historyRedo",result.annotations)}notifyGhostHistory(inputType,annotations=null){try{if(annotations){const count=annotations.setAside.spans.length+annotations.setAside.ghosts.length;const notice=count===0?null:`${count} ${count===1?"annotation (alternatives or ghost) is":"annotations (alternatives or ghosts) are"} out of step with the text. ${count===1?"It is":"They are"} preserved and saved with the document, and ${count===1?"is":"are"} re-attached if the text returns.`;this.reflectSetAside(count,notice)}const previous=this.suppressModelSync;this.suppressModelSync=true;try{this.surface.emitChange(null,inputType==="historyUndo"?"undo":inputType==="historyRedo"?"redo":"trim")}finally{this.suppressModelSync=previous}if(this.indicators)this.indicators.flush();this.surface.dispatchInput(inputType,null)}catch(error){this.ghostHistoryNotice(error)}}connectDocumentModel(){const api=this.documentApi();if(api)api.sync_document_body(this.surface.getText());this.surface.onChange((change=>this.mirrorEdit(change)))}mirrorEdit(change){if(this.suppressModelSync)return;if(this.ghostHistoryConflict){this.ghostHistoryNotice(new Error("Explicitly reopen before mirroring edits after a ghost history conflict"));return}const api=this.documentApi();if(!api)return;if(change.edit&&change.edit.alt&&typeof api.alt_restore==="function"){this.mirrorAltStep(api,change.edit,change.text);return}const{edit:edit,text:text}=change;if(edit.swap&&typeof api.set_active_alternative==="function"){this.mirrorSwap(api,edit.swap,text);return}if(edit.overflow&&typeof api.replay_document_overflow==="function"){this.mirrorOverflow(api,edit,text);return}let outcome;try{outcome=edit.move&&typeof api.move_document_range==="function"?api.move_document_range(edit.move.start,edit.move.end,edit.move.to):api.apply_edit(edit.start,edit.deletedLength,edit.insertedText)}catch(err){this.reflectSync(api.sync_document_body(text));return}if(outcome)this.reflectSetAside(outcome.setAside,outcome.notice,outcome.warning)}moveRange(start,end,to){const api=this.requireDocumentApi();if(typeof api.move_document_range!=="function"){throw new Error("The WASM document API cannot move text")}this.alignDocumentModel(api);const outcome=api.move_document_range(start,end,to);if(to===start||to===end)return{...outcome,moved:false,start:start,end:end};const text=this.surface.getText();const len=end-start;const lo=Math.min(start,to);const hi=Math.max(end,to);const moved=text.slice(start,end);const reordered=to>end?text.slice(end,to)+moved:moved+text.slice(to,start);const newStart=to>end?to-len:to;this.suppressModelSync=true;try{this.surface.replaceRange(lo,hi,reordered,{source:"move",move:{start:start,end:end,to:to},selectStart:newStart,selectEnd:newStart+len})}finally{this.suppressModelSync=false}if(outcome)this.reflectSetAside(outcome.setAside,outcome.notice,outcome.warning);if(this.indicators)this.indicators.flush();return{...outcome,moved:true,start:newStart,end:newStart+len}}swapAlternative(spanId,index){const api=this.requireDocumentApi();const isKg=TeKg.isKgSpan(spanId);const swapFn=isKg?api.kg_swap_alternative:api.set_active_alternative;if(typeof swapFn!=="function"){throw new Error("The WASM document API cannot swap alternatives")}this.alignDocumentModel(api);const outcome=swapFn(String(spanId),index);const edit=outcome&&outcome.edit;if(!edit)return outcome;const editEnd=edit.start+edit.deletedLength;const delta=edit.insertedText.length-edit.deletedLength;const{start:spanStart,end:spanEnd}=outcome.range;const map=o=>{if(o<=edit.start)return o;if(o>=editEnd)return o+delta;return Math.max(spanStart,Math.min(spanEnd,o))};const sel=this.surface.getSelectionOffsets();this.suppressModelSync=true;try{const tag={span:outcome.span,from:outcome.from,to:outcome.to};this.surface.replaceRange(edit.start,editEnd,edit.insertedText,{source:"swap",...isKg?{kg:tag}:{swap:tag},selectStart:map(sel.start),selectEnd:map(sel.end)})}finally{this.suppressModelSync=false}this.reflectSetAside(outcome.setAside,outcome.notice,outcome.warning);if(this.indicators)this.indicators.flush();return outcome}mirrorSwap(api,swap,text){let outcome=null;try{outcome=api.set_active_alternative(String(swap.span),swap.to)}catch(err){outcome=null}if(!outcome||api.document_body()!==text){this.reflectSync(api.sync_document_body(text));return}this.reflectSetAside(outcome.setAside,outcome.notice,outcome.warning)}loadThesaurus(json){const api=this.requireDocumentApi();if(typeof api.kg_load_thesaurus!=="function"){throw new Error("The WASM document API has no knowledge-graph support")}const summary=api.kg_load_thesaurus(typeof json==="string"?json:JSON.stringify(json));if(!this.graphRoleListenerApi)this.syncGraphRole();this.redrawRoleIndicators();return summary}clearThesaurus(){const api=this.documentApi();if(api&&typeof api.kg_clear_thesaurus==="function")api.kg_clear_thesaurus();if(!this.graphRoleListenerApi)this.syncGraphRole();this.redrawRoleIndicators()}get rolegraph(){if(this.destroyed)throw new Error("The editor is destroyed");if(this.graphClient)return this.graphClient;if(typeof window.TeRoleGraphClient!=="function")throw new Error("The graph client script is unavailable");const api=this.requireDocumentApi();for(const name of["kg_role_snapshot_json","kg_prepare_role","kg_commit_prepared_role","kg_discard_prepared_role","kg_set_role_change_listener"]){if(typeof api[name]!=="function")throw new Error("The WASM module has no browser role coordination API")}this.observeRolePublications();const options=Object.assign({},this.config.rolegraph||{},{assetBase:this.config.rolegraphAssetBase||new URL(".",document.baseURI).href,roleCoordinator:{prepare:text=>api.kg_prepare_role(text),commit:token=>{const current=JSON.parse(api.kg_role_snapshot_json());if(String(BigInt(current.generation)+1n)!==this.graphClient.generation){throw new Error("Stale graph role publication")}let result;this.graphRoleCommitInProgress=true;try{result=JSON.parse(api.kg_commit_prepared_role(token))}finally{this.graphRoleCommitInProgress=false}this.redrawRoleIndicators();return result},discard:token=>api.kg_discard_prepared_role(token)},onStateChange:state=>this.graphStateChanged(state)});const client=this.graphClient=new window.TeRoleGraphClient(options);client.attachDocument=options=>this.attachGraphDocument(options);client.detachDocument=()=>this.detachGraphDocument();this.syncGraphRole();return client}observeRolePublications(){const api=this.documentApi();if(!api||typeof api.kg_set_role_change_listener!=="function"||typeof api.kg_role_snapshot_json!=="function"||this.graphRoleListenerApi)return;api.kg_set_role_change_listener((()=>{if(this.destroyed)return;try{const snapshot=JSON.parse(api.kg_role_snapshot_json());this.root.dispatchEvent(new CustomEvent("te:role-change",{bubbles:true,detail:{editor:this,generation:snapshot.generation,loaded:snapshot.loaded===true,protectionReady:snapshot.protectionReady===true}}))}catch(_){}if(this.graphRoleCommitInProgress)return;try{this.syncGraphRole()}catch(_){}}));this.graphRoleListenerApi=api}syncGraphRole(){if(!this.graphClient||this.destroyed)return;const api=this.documentApi();const snapshot=JSON.parse(api.kg_role_snapshot_json());this.graphRoleSync=this.graphClient.syncRole(snapshot);this.graphRoleSync.catch((()=>{}))}redrawRoleIndicators(){try{if(this.indicators)this.indicators.flush()}catch(_){try{if(this.root&&!this.destroyed)this.root.dispatchEvent(new CustomEvent("te:rolegraph-error",{bubbles:true,detail:{editor:this,code:"render_failed"}}))}catch(_){}}}graphStateChanged(state){if(!this.root||this.destroyed)return;this.root.dataset.rolegraphState=state.state;this.root.dispatchEvent(new CustomEvent("te:rolegraph-state",{bubbles:true,detail:Object.assign({editor:this},state)}))}attachGraphDocument({id:id,indexSource:indexSource="markdown"}={}){if(typeof id!=="string"||!id.trim()||indexSource!=="markdown"){return Promise.reject(new Error("Graph attachment requires an id and markdown source"))}const client=this.rolegraph;this.detachGraphDocument();const attachment=this.graphAttachment={id:id,timer:null};const index=()=>{if(this.destroyed||this.graphAttachment!==attachment)return;clearTimeout(attachment.timer);attachment.timer=setTimeout((()=>{if(this.destroyed||this.graphAttachment!==attachment||client.state!=="ready"||!client.hasRole)return;client.indexDocuments([{id:id,title:id,body:this.surface.getText()}]).catch((error=>{if(!this.destroyed&&this.graphAttachment===attachment){this.root.dispatchEvent(new CustomEvent("te:rolegraph-error",{bubbles:true,detail:{editor:this,code:error.code||"worker_failed"}}))}}))}),120)};attachment.unsubscribe=this.surface.onChange(index);attachment.unsubscribeState=client.onStateChange((({state:state})=>{if(state==="ready")index()}));return(async()=>{let lane,epoch;do{lane=client.lifecycle;epoch=client.epoch;try{await lane}catch(error){if(lane===client.lifecycle)throw error}if(this.destroyed||this.graphAttachment!==attachment)throw new Error("Graph attachment cancelled")}while(lane!==client.lifecycle);if(client.destroyed||epoch!==client.epoch||client.state!=="ready"||!client.hasRole){throw new Error("Graph attachment requires a ready current role")}return client.indexDocuments([{id:id,title:id,body:this.surface.getText()}])})().catch((error=>{if(this.graphAttachment===attachment)this.detachGraphDocument();throw error}))}detachGraphDocument(){const attachment=this.graphAttachment;if(!attachment)return;clearTimeout(attachment.timer);if(attachment.unsubscribe)attachment.unsubscribe();if(attachment.unsubscribeState)attachment.unsubscribeState();this.graphAttachment=null}kgLookup(start,end){const api=this.documentApi();if(!api||typeof api.kg_lookup_selection!=="function")return null;this.alignDocumentModel(api);return api.kg_lookup_selection(start,end)}kgAlternativesForSelection(ctx=null){const sel=ctx||this.surface.getSelectionOffsets();let change=null;try{change=this.alternativeOp("kg_append",sel.start,sel.end)}catch(err){console.warn("No knowledge-graph alternatives for the selection",err);return null}return this.showAlternativesFor(sel,change)}showAlternativesFor(sel,change){const term=this.kgLookup(sel.start,sel.end);const spanId=change?change.span:term&&term.span;if(spanId&&this.altPanel&&typeof this.altPanel.open==="function"&&this.altPanel.isWriteOn()){this.altPanel.open({spanId:spanId})}return change}registerAlternativesProvider(provider){if(!provider||typeof provider.id!=="string"||!provider.id){throw new Error("An alternatives provider needs an id")}if(typeof provider.alternatives!=="function"){throw new Error("An alternatives provider needs an alternatives(context) function")}if(provider.id==="thesaurus")throw new Error('The provider id "thesaurus" is reserved');const api=this.requireDocumentApi();if(typeof api.alternatives_provider_register!=="function"){throw new Error("The WASM document API cannot register alternatives providers")}if(!this.alternativeProviders)this.alternativeProviders=new Map;const model=typeof provider.model==="string"&&provider.model?provider.model:provider.id;api.alternatives_provider_register(provider.id,model);const entry={provider:provider,model:model};const previous=this.alternativeProviders.get(provider.id);const holders=(TeProviderRefs.get(provider.id)||[]).filter((h=>h!==previous));holders.push(entry);TeProviderRefs.set(provider.id,holders);this.alternativeProviders.set(provider.id,entry);return()=>this.unregisterAlternativesProvider(provider.id,entry)}unregisterAlternativesProvider(id,entry=null){if(!this.alternativeProviders||!this.alternativeProviders.has(id))return false;if(entry&&this.alternativeProviders.get(id)!==entry)return false;const held=this.alternativeProviders.get(id);this.alternativeProviders.delete(id);const api=this.documentApi();const holders=(TeProviderRefs.get(id)||[]).filter((h=>h!==held));if(holders.length>0){TeProviderRefs.set(id,holders);const last=holders[holders.length-1];if(api)api.alternatives_provider_register(id,last.model);return true}TeProviderRefs.delete(id);if(api&&typeof api.alternatives_provider_unregister==="function"){api.alternatives_provider_unregister(id)}return true}alternativesProviderIds(){const api=this.documentApi();return api&&typeof api.alternatives_provider_ids==="function"?api.alternatives_provider_ids():[]}hasAlternativesProviders(){return!!this.alternativeProviders&&this.alternativeProviders.size>0}async alternativesForSelection(ctx=null){if(!this.hasAlternativesProviders())return this.kgAlternativesForSelection(ctx);const sel=ctx?{start:ctx.start,end:ctx.end}:this.surface.getSelectionOffsets();const body=this.surface.getText();const start=Math.min(sel.start,sel.end);const end=Math.max(sel.start,sel.end);const term=this.kgLookup(start,end);const target=term?term.range:{start:start,end:end};const context={text:body.slice(target.start,target.end),body:body,start:target.start,end:target.end};const entries=Array.from(this.alternativeProviders.values());const providers=entries;const answers=await Promise.all(providers.map((async({provider:provider,model:model})=>{try{const result=await provider.alternatives({...context});if(!Array.isArray(result))throw new Error("alternatives() must resolve to an array");return result.map((r=>typeof r==="string"?{text:r,model:model}:r&&typeof r==="object"?{...r,model:r.model||model}:r))}catch(err){console.warn(`Alternatives provider ${provider.id} failed`,err);return[]}})));if(this.destroyed||this.surface.getText()!==body){console.warn("The text changed while alternatives were being fetched; nothing appended");return null}const supplied={};providers.forEach(((entry,i)=>{if(this.alternativeProviders.get(entry.provider.id)!==entry)return;supplied[entry.provider.id]=answers[i]}));let change=null;try{change=this.alternativeOp("append_alternatives",start,end,JSON.stringify(supplied))}catch(err){console.warn("No alternatives for the selection",err);return null}return this.showAlternativesFor({start:start,end:end},change)}stashRange(start,end){const api=this.requireDocumentApi();if(typeof api.stash_document_range!=="function"){throw new Error("The WASM document API cannot stash text")}this.alignDocumentModel(api);const outcome=api.stash_document_range(Math.min(start,end),Math.max(start,end));const edit=outcome.edit;this.suppressModelSync=true;try{this.surface.replaceRange(edit.start,edit.start+edit.deletedLength,"",{source:"stash",overflow:{before:outcome.overflow.before,after:outcome.overflow.after},selectStart:edit.start,selectEnd:edit.start})}finally{this.suppressModelSync=false}this.reflectSetAside(outcome.setAside,outcome.notice,outcome.warning);if(this.indicators)this.indicators.flush();if(this.ghosts)this.ghosts.flush();return outcome}mirrorOverflow(api,edit,text){if(this.overflow)this.overflow.flush();let outcome=null;try{outcome=api.apply_edit(edit.start,edit.deletedLength,edit.insertedText)}catch(err){outcome=null}const replay=api.replay_document_overflow(edit.overflow.before,edit.overflow.after);if(!outcome||api.document_body()!==text){this.reflectSync(api.sync_document_body(text))}else{this.reflectSetAside(outcome.setAside,outcome.notice,outcome.warning)}if(replay&&replay.ok&&replay.applied===false&&this.warningKind!=="malformed"){this.showWarning("Undo put the text back on the page but left Overflow unchanged: the stashed text there was edited, "+"or text was added before it. Remove it from Overflow by hand if you no longer need it.","overflow")}}alignDocumentModel(api){if(this.ghostHistoryConflict)throw new Error("Ghost history conflict: explicitly reopen before model alignment");if(this.ghostHistoryActive){if(api.document_body()!==this.surface.getText())throw new Error("Ghost transaction body conflict; implicit resync is forbidden");return}if(this.overflow)this.overflow.flush();this.reflectSync(api.sync_document_body(this.surface.getText()))}reflectSync(synced){if(synced&&synced.changed)this.reflectSetAside(synced.unresolved,synced.notice)}reflectSetAside(count,notice,detachWarning=null){const previous=this.setAsideCount;this.setAsideCount=count;if(this.warningKind==="malformed")return;if(detachWarning){this.showWarning(detachWarning,"detached")}else if(count===0){if(this.warningKind==="set-aside"||this.warningKind==="detached")this.showWarning(null)}else if(count!==previous&¬ice){this.showWarning(notice,"set-aside")}}openDocument(source,name){const api=this.requireDocumentApi();if(this.ghostHistoryActive)throw new Error("Cannot reopen during an active ghost transaction");if(this.ghostHistoryConflict&&!this.ghostHistoryConflict.token&&!this.ghostHistoryConflict.nativeRetired){throw new Error("Native recovery token unavailable; ghost history remains blocked")}const key=typeof name==="string"?name:name&&typeof name.name==="string"?name.name:null;let opened;if(this.ghostHistoryConflict&&this.ghostHistoryConflict.token){if(typeof api.recover_document_history_transaction!=="function")throw new Error("Native conflict recovery is unavailable");const token=this.ghostHistoryConflict.token;const recovered=api.recover_document_history_transaction(token,String(source));MarkdownEditor.ghostAcknowledgement(recovered,token,"recovered");if(recovered.rolledBack!==false||!recovered.opened)throw new Error("Invalid native recovery acknowledgement");opened=recovered.opened;this.ghostHistoryConflict={token:null,nativeRetired:true}}else opened=api.open_document(String(source));if(this.ghostHistoryConflict&&opened.kind==="malformed"){throw new Error("Recovery source has malformed annotations; ghost history remains blocked")}if(key)this.documentKey=key;this.suppressModelSync=true;try{this.surface.setText(opened.body,{source:"open",selectStart:0,selectEnd:0})}finally{this.suppressModelSync=false}this.surface.resetHistory();const synced=api.sync_document_body(this.surface.getText());let warning=opened.warning||null;let kind=opened.kind||null;if(kind!=="malformed"&&synced.changed){if(synced.unresolved===0){warning=null;kind=null}else if(synced.unresolved!==opened.unresolved){warning=synced.notice;kind="set-aside"}}this.setAsideCount=synced.changed?synced.unresolved:opened.unresolved;this.showWarning(warning,kind);this.ghostHistoryConflict=null;if(this.chrome&&typeof this.chrome.documentChanged==="function"){try{this.chrome.documentChanged()}catch(err){console.error("chrome.documentChanged failed",err)}}if(this.persistence&&typeof this.persistence.documentChanged==="function")this.persistence.documentChanged(name);if(this.indicators)this.indicators.flush();if(this.ghosts)this.ghosts.flush();return opened}saveDocument(){const api=this.requireDocumentApi();this.alignDocumentModel(api);const text=api.save_document();const target=this.input||document;target.dispatchEvent(new CustomEvent("te:saved",{bubbles:true,detail:{editor:this,text:text,documentKey:this.documentKey}}));return text}exportDocument(){const api=this.requireDocumentApi();this.alignDocumentModel(api);return api.export_document()}counts(){const api=this.requireDocumentApi();this.alignDocumentModel(api);return api.document_counts()}annotations(){const api=this.requireDocumentApi();this.alignDocumentModel(api);return api.document_annotations()}alternativeOp(name,...args){const api=this.requireDocumentApi();const fn=api[`alt_${name}`];if(typeof fn!=="function")throw new Error(`The WASM document API has no alt_${name}`);this.alignDocumentModel(api);const change=fn(...args);if(!change)return null;this.applyAltChange(change);return change}applyAltChange(change){const alt={span:change.span,before:change.before||null,after:change.after||null};const edit=change.edit;const sel=this.surface.getSelectionOffsets();let start;let end;let insert;let map=o=>o;if(edit){start=edit.start;end=edit.start+edit.deletedLength;insert=edit.insertedText;const delta=insert.length-edit.deletedLength;map=o=>o<=start?o:o>=end?o+delta:Math.min(o,start+insert.length)}else{const snap=alt.after||alt.before;start=snap&&snap.anchor?Math.min(snap.anchor.start,this.surface.getText().length):0;end=start;insert=""}this.suppressModelSync=true;try{this.surface.replaceRange(start,end,insert,{source:"alternatives",alt:alt,selectStart:map(sel.start),selectEnd:map(sel.end)})}finally{this.suppressModelSync=false}this.reflectSetAside(change.setAside,change.notice,change.warning);if(this.indicators)this.indicators.flush()}mirrorAltStep(api,edit,text){let outcome=null;try{outcome=api.alt_restore(String(edit.alt.span),JSON.stringify(edit.alt.after||null),edit.start,edit.deletedLength,edit.insertedText)}catch(err){outcome=null}if(!outcome||api.document_body()!==text){this.reflectSync(api.sync_document_body(text))}else{this.reflectSetAside(outcome.setAside,outcome.notice,outcome.warning)}if(this.altPanel)this.altPanel.scheduleRefresh()}onWarning(callback){this.warningListeners.add(callback);return()=>this.warningListeners.delete(callback)}showWarning(message,kind=null){this.warning=message||null;this.warningKind=this.warning?kind:null;if(this.warning&&!this.warningElement&&this.input&&this.input.parentNode){const el=document.createElement("div");el.className="te-warning";el.setAttribute("role","status");el.setAttribute("aria-live","polite");const text=document.createElement("span");text.className="te-warning-text";const dismiss=document.createElement("button");dismiss.type="button";dismiss.className="te-warning-dismiss";dismiss.setAttribute("aria-label","Dismiss warning");dismiss.textContent="×";dismiss.addEventListener("click",(()=>this.showWarning(null)),{signal:this.abortController.signal});el.append(text,dismiss);this.input.parentNode.insertBefore(el,this.input);this.createdNodes.push(el);this.warningElement=el}if(this.warningElement){this.warningElement.hidden=!this.warning;if(this.warningKind)this.warningElement.dataset.kind=this.warningKind;else delete this.warningElement.dataset.kind;this.warningElement.querySelector(".te-warning-text").textContent=this.warning||""}for(const listener of this.warningListeners){try{listener(this.warning)}catch(err){console.error("Document warning listener failed",err)}}}showCustomDialog(){const dialog=document.createElement("sl-dialog");dialog.label="Custom Formatting";dialog.innerHTML=`\n \n \n Apply\n Cancel\n `;document.body.appendChild(dialog);this.createdNodes.push(dialog);const signal=this.abortController.signal;const[applyBtn,cancelBtn]=dialog.querySelectorAll("sl-button");const prefixInput=dialog.querySelector("#prefix-input");const suffixInput=dialog.querySelector("#suffix-input");const dispose=()=>{dialog.remove();this.createdNodes=this.createdNodes.filter((node=>node!==dialog))};const close=()=>{if(typeof dialog.hide==="function")dialog.hide();else dispose()};applyBtn.addEventListener("click",(()=>{this.wrapSelectedText(prefixInput.value||"",suffixInput.value||"");close()}),{signal:signal});cancelBtn.addEventListener("click",close,{signal:signal});dialog.addEventListener("sl-after-hide",dispose,{signal:signal});if(typeof dialog.show==="function")dialog.show();return dialog}}window.EditorSurface=EditorSurface;window.MarkdownEditor=MarkdownEditor;const initEditor=()=>{const checkElements=()=>{const app=document.getElementById("app");if(window.TE_EMBED||!app)return;const required=[".markdown-input","#formatting-toolbar","#shortcuts-list",".shortcuts-dialog","#show-help"];if(required.every((selector=>app.querySelector(selector)))){if(window.terraphimEditor&&typeof window.terraphimEditor.destroy==="function"){window.terraphimEditor.destroy()}const editor=new MarkdownEditor(Object.assign({standalone:true},window.EditorConfig||{shortcuts:[],commands:[],styles:{}}));editor.initialize(app);window.terraphimEditor=editor}else{setTimeout(checkElements,100)}};checkElements()};document.addEventListener("DOMContentLoaded",(async()=>{if(window.TE_EMBED||!document.getElementById("app"))return;if(window.EditorConfig){try{const ready=window.TerraphimRuntimeAssets?.ready;if(!ready||typeof ready.then!=="function"){throw new Error("Local runtime asset readiness is unavailable")}await ready}catch(error){console.error("Editor startup failed: local runtime assets unavailable",error);return}initEditor()}else{console.error("Editor configuration not found. Make sure config.js is loaded before editor.js")}}));window.TE_EMBED=true;const TE_SCRIPT_BASE=(()=>{const script=document.currentScript;return script&&script.src?script.src:document.baseURI})();class TeraphimEditor{static async create(element,options={}){const editor=new TeraphimEditor(element,options);await editor.mount();return editor}static loadModule(options={}){if(!TeraphimEditor.modulePromise){const glueUrl=new URL(options.glueUrl||new URL("terraphim_editor.js",TE_SCRIPT_BASE).href,document.baseURI).href;const wasmUrl=new URL(options.wasmUrl||new URL("terraphim_editor_bg.wasm",TE_SCRIPT_BASE).href,document.baseURI).href;TeraphimEditor.modulePromise=(async()=>{const glue=await import(glueUrl);await glue.default({module_or_path:wasmUrl});return glue})();TeraphimEditor.modulePromise.catch((()=>{TeraphimEditor.modulePromise=null}))}return TeraphimEditor.modulePromise}static async loadShoelace(options={}){if(!window.TerraphimRuntimeAssets)throw new Error("runtime-assets.js must load before the embed wrapper");return window.TerraphimRuntimeAssets.load(options,TE_SCRIPT_BASE)}constructor(targetElement,options={}){if(!(targetElement instanceof Element)){throw new TypeError("TeraphimEditor needs a container element")}this.targetElement=targetElement;this.options=Object.assign({},options);this.editor=null;this.container=null;this.bindings=null;this.listeners=[]}async initialize(){return this.mount()}async mount(){this.destroy();const active=TeraphimEditor.active;if(active&&active!==this){throw new Error("TeraphimEditor supports one editor per page; destroy() the existing editor first")}TeraphimEditor.active=this;try{const opts=this.options;if(opts.loadShoelace!==false)await TeraphimEditor.loadShoelace(opts);const bindings=opts.bindings||await TeraphimEditor.loadModule(opts);if(!bindings||typeof bindings.mount_editor!=="function"){throw new Error("The editor module does not export mount_editor")}this.bindings=bindings;if(!window.wasmBindings)window.wasmBindings=bindings;const container=document.createElement("div");container.className="terraphim-editor-container";this.targetElement.appendChild(container);this.container=container;bindings.mount_editor(container,typeof opts.value==="string"?opts.value:"");const standalone=opts.standalone===true;const config=Object.assign({},window.EditorConfig||{shortcuts:[],commands:[]},opts.config||{},{standalone:standalone,dirtyTitle:opts.dirtyTitle===undefined?standalone:opts.dirtyTitle!==false,bindings:bindings,rolegraphAssetBase:opts.rolegraphAssetBase||(opts.assetBase?window.TerraphimRuntimeAssets.resolveBase(opts,TE_SCRIPT_BASE):new URL(".",TE_SCRIPT_BASE).href),rolegraph:opts.rolegraph||opts.config&&opts.config.rolegraph});const editor=new MarkdownEditor(config);editor.initialize(container);if(!editor.surface){editor.destroy();throw new Error("The editor could not start in its container")}this.editor=editor;return this}catch(err){this.destroy();throw err}}destroy(){let drafts=[];for(const{type:type,wrapped:wrapped}of this.listeners)document.removeEventListener(type,wrapped);this.listeners=[];if(this.editor)drafts=this.editor.destroy()||[];this.editor=null;if(this.bindings&&typeof this.bindings.unmount_editor==="function"&&this.container){this.bindings.unmount_editor()}if(this.container)this.container.remove();this.container=null;if(TeraphimEditor.active===this)TeraphimEditor.active=null;return drafts}get markdownEditor(){return this.editor}get persistence(){return this.editor?this.editor.persistence||null:null}get rolegraph(){return this.requireEditor().rolegraph}getValue(){return this.requireEditor().surface.getText()}setValue(markdown){this.requireEditor().surface.setText(String(markdown))}openDocument(source,name){return this.requireEditor().openDocument(source,name)}saveDocument(){return this.requireEditor().saveDocument()}exportDocument(){return this.requireEditor().exportDocument()}isWriteOn(){const chrome=this.requireEditor().chrome;return!!chrome&&chrome.isWriteOn()}setWriteOn(on){const chrome=this.requireEditor().chrome;if(chrome)chrome.setMode(on?"write-on":"plain")}toolsVisible(){const chrome=this.requireEditor().chrome;return!!chrome&&chrome.toolsVisible()}setTools(on){const chrome=this.requireEditor().chrome;if(chrome)chrome.setTools(on)}on(type,callback){const wrapped=e=>{if(!this.editor)return;const fromEditor=e.detail&&e.detail.editor===this.editor;if(fromEditor||this.container&&this.container.contains(e.target))callback(e)};document.addEventListener(type,wrapped);this.listeners.push({type:type,callback:callback,wrapped:wrapped});return()=>this.off(type,callback)}off(type,callback){this.listeners=this.listeners.filter((l=>{if(l.type!==type||l.callback!==callback)return true;document.removeEventListener(type,l.wrapped);return false}))}requireEditor(){if(!this.editor)throw new Error("The editor is not mounted");return this.editor}}TeraphimEditor.active=null;TeraphimEditor.modulePromise=null;window.TeraphimEditor=TeraphimEditor; \ No newline at end of file diff --git a/static/editor/terraphim_editor.js b/static/editor/terraphim_editor.js new file mode 100644 index 0000000..a68e91d --- /dev/null +++ b/static/editor/terraphim_editor.js @@ -0,0 +1,1666 @@ +/** + * Browser binding with one owned session per instance; no DOM dependency. + */ +export class TerraphimHostSession { + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + TerraphimHostSessionFinalization.unregister(this); + return ptr; + } + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_terraphimhostsession_free(ptr, 0); + } + /** + * Commit exact host output after the native text transaction. + * @param {number} id + * @param {number} revision + * @param {string} source + * @returns {string} + */ + acknowledge(id, revision, source) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.terraphimhostsession_acknowledge(this.__wbg_ptr, id, revision, ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } + } + /** + * Open a host resource with exact source and revision. + * @param {string} resource + * @param {string} session + * @param {number} revision + * @param {string} source + */ + constructor(resource, session, revision, source) { + const ptr0 = passStringToWasm0(resource, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(session, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.terraphimhostsession_new(ptr0, len0, ptr1, len1, revision, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + this.__wbg_ptr = ret[0]; + TerraphimHostSessionFinalization.register(this, this.__wbg_ptr, this); + return this; + } + /** + * Stage a protocol command; committed source stays unchanged. + * @param {string} message + * @returns {string} + */ + propose(message) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(message, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.terraphimhostsession_propose(this.__wbg_ptr, ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } + } + /** + * Invalidate a named staged operation. + * @param {number} id + */ + reject(id) { + const ret = wasm.terraphimhostsession_reject(this.__wbg_ptr, id); + if (ret[1]) { + throw takeFromExternrefTable0(ret[0]); + } + } + /** + * Current source and render projection, encoded as protocol JSON. + * @returns {string} + */ + snapshot() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.terraphimhostsession_snapshot(this.__wbg_ptr); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } + } + /** + * Adopt actual external edits or native undo/redo. + * @param {number} revision + * @param {string} source + * @returns {string} + */ + synchronize(revision, source) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.terraphimhostsession_synchronize(this.__wbg_ptr, revision, ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } + } + /** + * Query committed authored KG synonyms, encoded as JSON. + * @param {string} span_id + * @param {string} text + * @returns {string} + */ + synonyms_for(span_id, text) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.terraphimhostsession_synonyms_for(this.__wbg_ptr, ptr0, len0, ptr1, len1); + deferred3_0 = ret[0]; + deferred3_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } + } +} +if (Symbol.dispose) TerraphimHostSession.prototype[Symbol.dispose] = TerraphimHostSession.prototype.free; + +/** + * @param {string} token + * @returns {any} + */ +export function abort_document_history_transaction(token) { + const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.abort_document_history_transaction(ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Appends alternative `text` from `source` to span `span_id`. Returns the + * change, or `null` if the span already has that text; throws on error. + * @param {string} span_id + * @param {string} text + * @param {string} source + * @returns {any} + */ +export function alt_add(span_id, text, source) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.alt_add(ptr0, len0, ptr1, len1, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * "AI alternatives for selection" through every provider, in the shape of + * the #10 panel operations so `MarkdownEditor.alternativeOp( + * 'append_alternatives', start, end, supplied)` records it as one undo step. + * `supplied` is the JSON the host's awaited providers produced (see + * [`parse_supplied`]); `""` for none. Returns the change, `null` when + * nothing new was left to add, and throws, changing nothing, when there is + * nothing to offer for the selection or another span overlaps it. + * @param {number} start + * @param {number} end + * @param {string} supplied + * @returns {any} + */ +export function alt_append_alternatives(start, end, supplied) { + const ptr0 = passStringToWasm0(supplied, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.alt_append_alternatives(start, end, ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Creates a span of `kind` (`"word"`, `"sentence"`, `"paragraph"`) over + * `start..end` (UTF-16) with its first alternative `text` from `source` + * (`"human"` or `"ai"`). Returns the change (see [`AltChange::to_json`]); + * throws, changing nothing, for an invalid or overlapping range, empty text + * or text equal to the original. + * @param {string} kind + * @param {number} start + * @param {number} end + * @param {string} text + * @param {string} source + * @returns {any} + */ +export function alt_create_span(kind, start, end, text, source) { + const ptr0 = passStringToWasm0(kind, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.alt_create_span(ptr0, len0, start, end, ptr1, len1, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Replaces the text of alternative `index` of span `span_id`. Returns the + * change (with the body edit when it was the active one), or `null` when the + * text is unchanged; throws on error. + * @param {string} span_id + * @param {number} index + * @param {string} text + * @returns {any} + */ +export function alt_edit(span_id, index, text) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.alt_edit(ptr0, len0, index, ptr1, len1); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * "AI alternatives for selection" (R-8.6, see [`kg_append`]), in the shape + * of the #10 panel operations so `MarkdownEditor.alternativeOp('kg_append', + * start, end)` records it as one undo step: returns the change (see + * `AltChange::to_json`), `null` when the span already offers every synonym, + * and throws, changing nothing, when there is no KG term at the selection + * or another span overlaps it. + * @param {number} start + * @param {number} end + * @returns {any} + */ +export function alt_kg_append(start, end) { + const ret = wasm.alt_kg_append(start, end); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Moves alternative `from` of span `span_id` to position `to`. Returns the + * change, or `null` when `from == to`; throws on error. + * @param {string} span_id + * @param {number} from + * @param {number} to + * @returns {any} + */ +export function alt_move(span_id, from, to) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.alt_move(ptr0, len0, from, to); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Removes alternative `index` of span `span_id`. Returns the change (its + * `after` is `null` when the span was removed); throws on error. + * @param {string} span_id + * @param {number} index + * @returns {any} + */ +export function alt_remove(span_id, index) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.alt_remove(ptr0, len0, index); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Replays a recorded panel operation (see [`DocumentSession::alt_restore`]): + * `target_json` is the span snapshot to put back, or `"null"` to leave the + * span out. Returns `{ detached, reattached, setAside, warning, notice, ... }`; + * throws, changing nothing, when the replay does not fit. + * @param {string} span_id + * @param {string} target_json + * @param {number} start + * @param {number} deleted_len + * @param {string} inserted + * @returns {any} + */ +export function alt_restore(span_id, target_json, start, deleted_len, inserted) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(target_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(inserted, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.alt_restore(ptr0, len0, ptr1, len1, start, deleted_len, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * The provider ids in the order they are asked, as a JSON array. + * @returns {any} + */ +export function alternatives_provider_ids() { + const ret = wasm.alternatives_provider_ids(); + return ret; +} + +/** + * Registers a host provider `id` (default model `model`) after the ones + * already there, so its answers can be supplied to + * [`alt_append_alternatives`]. Re-registering keeps its place; the + * `thesaurus` id is reserved and throws. + * @param {string} id + * @param {string} model + */ +export function alternatives_provider_register(id, model) { + const ptr0 = passStringToWasm0(id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(model, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.alternatives_provider_register(ptr0, len0, ptr1, len1); + if (ret[1]) { + throw takeFromExternrefTable0(ret[0]); + } +} + +/** + * Removes the provider `id`; `false` when there was none. + * @param {string} id + * @returns {boolean} + */ +export function alternatives_provider_unregister(id) { + const ptr0 = passStringToWasm0(id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.alternatives_provider_unregister(ptr0, len0); + return ret !== 0; +} + +/** + * Mirrors a surface edit (`start`, `deletedLength`, `insertedText`, UTF-16) + * in the model. Returns `{ detached, detachedGhosts, reattached, setAside, warning, notice }`; throws + * if the edit does not fit the model, in which case call + * [`sync_document_body`]. + * @param {number} start + * @param {number} deleted_len + * @param {string} inserted + * @returns {any} + */ +export function apply_edit(start, deleted_len, inserted) { + const ptr0 = passStringToWasm0(inserted, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.apply_edit(start, deleted_len, ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * @param {string} expected_body + * @param {string} target_body + * @param {string} operations_json + * @returns {any} + */ +export function begin_document_history_replay(expected_body, target_body, operations_json) { + const ptr0 = passStringToWasm0(expected_body, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(target_body, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(operations_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.begin_document_history_replay(ptr0, len0, ptr1, len1, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * @param {string} expected_body + * @param {string} level + * @param {string} kept_json + * @returns {any} + */ +export function begin_trim_ghost_transaction(expected_body, level, kept_json) { + const ptr0 = passStringToWasm0(expected_body, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(level, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(kept_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ret = wasm.begin_trim_ghost_transaction(ptr0, len0, ptr1, len1, ptr2, len2); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * `{ spans, ghosts, overflow, setAside: { spans, ghosts }, preservedBlock, + * kg }` of the current document, in UTF-16 offsets. `kg` holds the derived + * knowledge-graph spans (issue #13, see [`crate::kg::KgSpan::to_json`]); + * they are never saved. + * @returns {any} + */ +export function document_annotations() { + const ret = wasm.document_annotations(); + return ret; +} + +/** + * The body as the model holds it (should always equal the surface text). + * @returns {string} + */ +export function document_body() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.document_body(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * `{ words, chars }` of the body, ghosted text included. + * @returns {any} + */ +export function document_counts() { + const ret = wasm.document_counts(); + return ret; +} + +/** + * The overflow (stash) text of the current document. + * @returns {string} + */ +export function document_overflow() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.document_overflow(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * Query the current browser document's scoped authored KG synonym concept. + * @param {string} span_id + * @param {string} text + * @returns {string} + */ +export function document_synonyms(span_id, text) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.document_synonyms(ptr0, len0, ptr1, len1); + deferred3_0 = ret[0]; + deferred3_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** + * The current document as clean Markdown: active alternatives, no ghosted + * text, no overflow, no block. + * @returns {string} + */ +export function export_document() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.export_document(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * @param {string} token + * @returns {any} + */ +export function finish_document_history_transaction(token) { + const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.finish_document_history_transaction(ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Render a pending (debounced) preview immediately. + * + * Call this before reading the preview for save, export or tests. Returns + * `true` if a render was pending and has now run, `false` if the preview was + * already up to date (or no editor is initialised). From JavaScript in the + * Trunk build this is `window.wasmBindings.flush_preview()`. + * @returns {boolean} + */ +export function flush_preview() { + const ret = wasm.flush_preview(); + return ret !== 0; +} + +/** + * Ghosts `start..end` (UTF-16 offsets into the body) in the current + * document. Returns `{ ok: true, id, annotations }` with the updated + * annotations (as [`document_annotations`]), or `{ ok: false, error, kind }` + * with the document unchanged. Never throws. + * @param {number} start + * @param {number} end + * @returns {any} + */ +export function ghost_range(start, end) { + const ret = wasm.ghost_range(start, end); + return ret; +} + +/** + * LF source for a DOM presentation, with rebased annotations and untouched + * malformed block bytes. Native persisted source remains owned by its host. + * + * # Errors + * Refuses overflowing annotation coordinates. + * @param {string} source + * @returns {string} + */ +export function host_presentation_source(source) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.host_presentation_source(ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** + * Forgets the thesaurus: no KG spans until the next load. + */ +export function kg_clear_thesaurus() { + wasm.kg_clear_thesaurus(); +} + +/** + * Publish a prepared role and return its summary as JSON text. + * @param {string} token + * @returns {string} + */ +export function kg_commit_prepared_role(token) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.kg_commit_prepared_role(ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** + * Discard a prepared role; active state is untouched. + * @param {string} token + */ +export function kg_discard_prepared_role(token) { + const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + wasm.kg_discard_prepared_role(ptr0, len0); +} + +/** + * The derived KG spans of the open document (see [`KgSpan::to_json`]). + * `document_annotations` returns the same list as its `kg` field. + * @returns {any} + */ +export function kg_document_spans() { + const ret = wasm.kg_document_spans(); + return ret; +} + +/** + * Loads a thesaurus JSON for KG alternatives, replacing the current one. + * Returns `{ name, concepts, skipped }`; throws (keeping the previous + * thesaurus) if the JSON is not a thesaurus. + * @param {string} json + * @returns {any} + */ +export function kg_load_thesaurus(json) { + const ptr0 = passStringToWasm0(json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.kg_load_thesaurus(ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * The KG term at the selection `start..end` with its alternatives, or + * `null` (see [`kg_lookup`]). Read-only. + * @param {number} start + * @param {number} end + * @returns {any} + */ +export function kg_lookup_selection(start, end) { + const ret = wasm.kg_lookup_selection(start, end); + return ret; +} + +/** + * Prepare local role JSON without replacing the current KG engine. + * @param {string} json + * @returns {string} + */ +export function kg_prepare_role(json) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.kg_prepare_role(ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** + * Read-only active role snapshot for the Worker coordinator. + * @returns {string} + */ +export function kg_role_snapshot_json() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.kg_role_snapshot_json(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * Replace the single opt-in browser role observer; `None` unregisters it. + * Called synchronously, with no arguments, only after successful load, clear, + * or prepared commit. Read the authoritative snapshot inside the callback. + * @param {Function | null} [listener] + */ +export function kg_set_role_change_listener(listener) { + wasm.kg_set_role_change_listener(isLikeNone(listener) ? 0 : addToExternrefTable0(listener)); +} + +/** + * Makes alternative `index` of KG span `span_id` the text. Returns the same + * shape as `set_active_alternative` (`{ span, from, to, edit, range, ... }`, + * `edit` the exact UTF-16 change made to the body, or `null`); throws, + * changing nothing, for an unknown span, an invalid index or an edit that + * would change a block span's text. + * @param {string} span_id + * @param {number} index + * @returns {any} + */ +export function kg_swap_alternative(span_id, index) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.kg_swap_alternative(ptr0, len0, index); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * `[{ id, label }]` of the six Lab actions, in popover order. + * @returns {any} + */ +export function lab_actions() { + const ret = wasm.lab_actions(); + return ret; +} + +/** + * Runs Lab action `action` (an id from [`lab_actions`]) over the current + * document body and returns `[{ kind, start, end, score, reason, proposal }]` + * in UTF-16 offsets. Throws for an unknown action id. Never changes text. + * @param {string} action + * @returns {any} + */ +export function lab_mark(action) { + const ptr0 = passStringToWasm0(action, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.lab_mark(ptr0, len0); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Render the editor markup into `root` with `initial` as the open document + * and wire the live preview to the surface inside `root` (issue #77). + * + * This is the entry point of the embeddable wrapper; [`run`] uses it for + * `#app`. Every lookup is scoped to `root`, so markup elsewhere on the page + * is never touched. `root` gains the `te-app` class, which the Write_On + * layout rules key on. There is one document session and one preview per + * module instance, so mounting replaces the previous editor: one editor + * per page (see docs/design/embedding.md). + * @param {Element} root + * @param {string} initial + */ +export function mount_editor(root, initial) { + const ptr0 = passStringToWasm0(initial, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.mount_editor(root, ptr0, len0); + if (ret[1]) { + throw takeFromExternrefTable0(ret[0]); + } +} + +/** + * Moves the body text `start..end` to `to` (UTF-16 code units; `to` is an + * insertion point in the pre-move body, outside the range), carrying the + * spans and ghosts inside the range. Returns + * `{ detached, detachedGhosts, reattached, setAside, warning, notice }` like [`apply_edit`]; + * throws (changing nothing) if the range or destination is invalid or the + * move would split a span or ghost. + * + * `MarkdownEditor.moveRange` calls this and then updates the editing surface + * without mirroring the change through [`apply_edit`]. + * @param {number} start + * @param {number} end + * @param {number} to + * @returns {any} + */ +export function move_document_range(start, end, to) { + const ret = wasm.move_document_range(start, end, to); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Opens a `.md` source as the current document and returns + * `{ body, warning, unresolved }`: put `body` on the editing surface and + * show `warning` (a string or `null`) as a non-blocking notice. + * @param {string} source + * @returns {any} + */ +export function open_document(source) { + const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.open_document(ptr0, len0); + return ret; +} + +/** + * The text a pull back (R-6.4, a copy) inserts at `at` (UTF-16) and where: + * the overflow range `from..to` (UTF-16; when empty, the line at `from`), + * separated as terraphim-lsp separates it. Returns `{ start, text }` and + * changes nothing; throws when there is nothing to pull back or for an + * invalid offset or range. `TeOverflowPanel.pullBack` inserts `text` at + * `start` through the normal edit path. + * @param {number} at + * @param {number} from + * @param {number} to + * @returns {any} + */ +export function overflow_pull_back_text(at, from, to) { + const ret = wasm.overflow_pull_back_text(at, from, to); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Synchronous model preflight for one issued receipt, before JS cursor/surface + * movement. Whole mixed replay groups require controller-level clone preflight. + * @param {string} change_json + * @param {boolean} undo + */ +export function preflight_document_ghost_change(change_json, undo) { + const ptr0 = passStringToWasm0(change_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.preflight_document_ghost_change(ptr0, len0, undo); + if (ret[1]) { + throw takeFromExternrefTable0(ret[0]); + } +} + +/** + * Current preview debounce delay in milliseconds. + * @returns {number} + */ +export function preview_delay() { + const ret = wasm.preview_delay(); + return ret >>> 0; +} + +/** + * Whether a debounced preview render is scheduled but has not yet run. + * @returns {boolean} + */ +export function preview_pending() { + const ret = wasm.preview_pending(); + return ret !== 0; +} + +/** + * Number of preview renders triggered by edits since the last [`run`]. + * The initial render on load is part of the template and is not counted. + * Intended for diagnostics and tests. + * @returns {number} + */ +export function preview_render_count() { + const ret = wasm.preview_render_count(); + return ret >>> 0; +} + +/** + * @param {string} token + * @param {string} source + * @returns {any} + */ +export function recover_document_history_transaction(token, source) { + const ptr0 = passStringToWasm0(token, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.recover_document_history_transaction(ptr0, len0, ptr1, len1); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Convert Markdown to HTML with the `markdown` crate. + * + * Exported so that JavaScript (and the browser benchmarks) use exactly the + * same conversion as the live preview. + * @param {string} input + * @returns {string} + */ +export function render_markdown(input) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(input, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.render_markdown(ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** + * Replays an issued receipt, never arbitrary promotion ranges. Protected + * promotion is wired by Lab separately, not exposed as a JS bypass here. + * @param {string} change_json + * @param {boolean} undo + * @returns {any} + */ +export function replay_document_ghost_change(change_json, undo) { + const ptr0 = passStringToWasm0(change_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.replay_document_ghost_change(ptr0, len0, undo); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Replays a recorded overflow change `from` -> `to` (undo or redo of a + * stash) onto the current overflow, keeping text typed in the panel since + * (see [`rebase_overflow`]). Returns `{ ok: true, overflow, applied }` + * (`applied: false` when an undo could not find the stashed text intact at + * its recorded position and left the overflow unchanged) or + * `{ ok: false, error, kind }`. Never throws. + * @param {string} from + * @param {string} to + * @returns {any} + */ +export function replay_document_overflow(from, to) { + const ptr0 = passStringToWasm0(from, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(to, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.replay_document_overflow(ptr0, len0, ptr1, len1); + return ret; +} + +/** + * Revives `start..end` (UTF-16 offsets into the body) in the current + * document. Returns `{ ok: true, changed, annotations }`, or + * `{ ok: false, error, kind }` with the document unchanged. Never throws. + * @param {number} start + * @param {number} end + * @returns {any} + */ +export function revive_range(start, end) { + const ret = wasm.revive_range(start, end); + return ret; +} + +/** + * Render the full-page editor, with the welcome text, into `#app`. + * + * Errors when the page has no element with id `app`. + */ +export function run() { + const ret = wasm.run(); + if (ret[1]) { + throw takeFromExternrefTable0(ret[0]); + } +} + +/** + * The current document as `.md` text: body plus annotation block, set-aside + * annotations included. + * @returns {string} + */ +export function save_document() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.save_document(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * Makes alternative `index` of span `span_id` active (R-2.4): the span's + * text is swapped and a preceding "a"/"an" fixed up (R-2.6) in the model. + * Returns `{ span, from, to, edit, range, detached, reattached, setAside, + * warning, notice }`, where `edit` (`{ start, deletedLength, deletedText, + * insertedText }` in UTF-16 code units, or `null` when `index` was already + * active) is the exact change made to the body and `range` (`{ start, end }`) + * is the span's text afterwards. Throws, changing nothing, + * for an unknown span, an invalid index or a stale anchor. + * + * `MarkdownEditor.swapAlternative` calls this and then applies `edit` to + * the editing surface without mirroring it through [`apply_edit`]. + * @param {string} span_id + * @param {number} index + * @returns {any} + */ +export function set_active_alternative(span_id, index) { + const ptr0 = passStringToWasm0(span_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.set_active_alternative(ptr0, len0, index); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Replaces the overflow text (what the Overflow panel holds). Returns + * `{ ok: true, overflow }`, or `{ ok: false, error, kind }` (kind + * `"preserved-block"`) with nothing changed. Never throws. + * @param {string} text + * @returns {any} + */ +export function set_document_overflow(text) { + const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.set_document_overflow(ptr0, len0); + return ret; +} + +/** + * Set the preview debounce delay in milliseconds; `0` renders synchronously + * on every `input`. Applies to renders scheduled after the call; a render + * already pending keeps its original deadline. [`run`] resets the delay to + * [`DEFAULT_PREVIEW_DELAY_MS`]. Does nothing if no editor is initialised. + * @param {number} ms + */ +export function set_preview_delay(ms) { + wasm.set_preview_delay(ms); +} + +/** + * Module start: render the full-page editor into `#app` (the Trunk build). + * + * Does nothing on a page that embeds the editor through the + * `TeraphimEditor` wrapper (`public/js/terraphim-editor.js`): the wrapper + * sets `window.TE_EMBED = true` before it instantiates the module and then + * calls [`mount_editor`] on its own container, so a host page that happens + * to have an element with id `app` is never taken over. A page without + * `#app` is also left alone, so instantiating the module never fails for + * want of one (a failing start function would reject the glue's `init()`). + */ +export function start() { + wasm.start(); +} + +/** + * Stashes the body text `start..end` (UTF-16) in the overflow: removed + * from the body through the normal edit path and appended to the overflow + * (R-6.3). Returns `{ edit, overflow: { before, after }, detached, + * detachedGhosts, reattached, setAside, warning, notice }`; throws, + * changing nothing, for an empty or invalid range or while a malformed + * block is preserved. + * + * `MarkdownEditor.stashRange` calls this and then applies `edit` to the + * editing surface without mirroring it through [`apply_edit`]. + * @param {number} start + * @param {number} end + * @returns {any} + */ +export function stash_document_range(start, end) { + const ret = wasm.stash_document_range(start, end); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Replaces the model body with `text` if it differs and re-anchors; returns + * `{ changed, unresolved, notice }` (`notice` as for [`apply_edit`]). + * @param {string} text + * @returns {any} + */ +export function sync_document_body(text) { + const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.sync_document_body(ptr0, len0); + return ret; +} + +/** + * Explicit-selection kind inference through shared Rust boundaries/counter. + * @param {string} text + * @returns {string} + */ +export function text_infer_kind(text) { + let deferred2_0; + let deferred2_1; + try { + const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.text_infer_kind(ptr0, len0); + deferred2_0 = ret[0]; + deferred2_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * Shared automatic targeting; coordinates and return object are UTF-16. + * @param {string} text + * @param {number} utf16 + * @param {string} kind + * @returns {any} + */ +export function text_range_at(text, utf16, kind) { + const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(kind, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.text_range_at(ptr0, len0, utf16, ptr1, len1); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * The typed edits "Make the cuts" applies at `level` with `kept` kept: + * `{ text, edits: [{ start, end, insert, kind }] }` on the current body's + * UTF-16 offsets. Throws for a stale plan; never changes text itself. + * @param {string} level + * @param {string} kept + * @returns {any} + */ +export function trim_make_cuts(level, kept) { + const ptr0 = passStringToWasm0(level, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(kept, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.trim_make_cuts(ptr0, len0, ptr1, len1); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Computes the trim plan for the current document body and returns + * `{ totalWords, levels, cuts }` (UTF-16 offsets). Align the model with the + * surface first. Never changes text. + * @returns {any} + */ +export function trim_plan_json() { + const ret = wasm.trim_plan_json(); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * The status card for trim level `level` (`"slight"`, ...) with the cut + * ids in `kept` (a JSON array) kept, plus the active pieces to fade. Throws + * for an unknown level, bad ids, no plan or a stale plan. + * @param {string} level + * @param {string} kept + * @returns {any} + */ +export function trim_status(level, kept) { + const ptr0 = passStringToWasm0(level, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(kept, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ret = wasm.trim_status(ptr0, len0, ptr1, len1); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} + +/** + * Forget the editor wired by the last [`mount_editor`]: cancel a pending + * preview render and drop the references to its nodes, so a destroyed + * embed leaves no timer behind. The surface's `input` listener stays on the + * (removed) surface and does nothing once the state is gone. + */ +export function unmount_editor() { + wasm.unmount_editor(); +} +function __wbg_get_imports() { + const import0 = { + __proto__: null, + __wbg___wbindgen_is_falsy_16bd49b68658263e: function(arg0) { + const ret = !arg0; + return ret; + }, + __wbg___wbindgen_is_undefined_8865fb403f8fe9d8: function(arg0) { + const ret = arg0 === undefined; + return ret; + }, + __wbg___wbindgen_rethrow_cb2e88c6b2a16733: function(arg0) { + throw arg0; + }, + __wbg___wbindgen_throw_41e9ee4f547fc59a: function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }, + __wbg__wbg_cb_unref_dcc1a90847f04c41: function(arg0) { + arg0._wbg_cb_unref(); + }, + __wbg_addEventListener_4d0db17c671ea324: function() { return handleError(function (arg0, arg1, arg2, arg3) { + arg0.addEventListener(getStringFromWasm0(arg1, arg2), arg3); + }, arguments); }, + __wbg_add_9ceab44da1353b80: function() { return handleError(function (arg0, arg1, arg2) { + arg0.add(getStringFromWasm0(arg1, arg2)); + }, arguments); }, + __wbg_call_6137034ef55c9d0f: function() { return handleError(function (arg0, arg1) { + const ret = arg0.call(arg1); + return ret; + }, arguments); }, + __wbg_classList_8a097a4837412bf6: function(arg0) { + const ret = arg0.classList; + return ret; + }, + __wbg_clearTimeout_c7bc4c3c7a7774af: function(arg0, arg1) { + arg0.clearTimeout(arg1); + }, + __wbg_document_9854e03c05fc8834: function(arg0) { + const ret = arg0.document; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_error_757e9472f8410341: function(arg0, arg1) { + let deferred0_0; + let deferred0_1; + try { + deferred0_0 = arg0; + deferred0_1 = arg1; + console.error(getStringFromWasm0(arg0, arg1)); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + } + }, + __wbg_error_c9cf3fc2064683a9: function(arg0) { + console.error(arg0); + }, + __wbg_getElementById_cc94972b404e4eaa: function(arg0, arg1, arg2) { + const ret = arg0.getElementById(getStringFromWasm0(arg1, arg2)); + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_getRandomValues_a608c4436c19407a: function() { return handleError(function (arg0, arg1) { + globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1)); + }, arguments); }, + __wbg_get_31af05bd4842a84f: function() { return handleError(function (arg0, arg1) { + const ret = Reflect.get(arg0, arg1); + return ret; + }, arguments); }, + __wbg_instanceof_Window_82d71df4eddf88bc: function(arg0) { + let result; + try { + result = arg0 instanceof Window; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }, + __wbg_new_227d7c05414eb861: function() { + const ret = new Error(); + return ret; + }, + __wbg_parse_0fc53dead14b3b42: function() { return handleError(function (arg0, arg1) { + const ret = JSON.parse(getStringFromWasm0(arg0, arg1)); + return ret; + }, arguments); }, + __wbg_querySelector_49877e2a9e3f670b: function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.querySelector(getStringFromWasm0(arg1, arg2)); + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, arguments); }, + __wbg_setTimeout_db7bbc18a17e152a: function() { return handleError(function (arg0, arg1, arg2) { + const ret = arg0.setTimeout(arg1, arg2); + return ret; + }, arguments); }, + __wbg_set_innerHTML_7af59a832a09a074: function(arg0, arg1, arg2) { + arg0.innerHTML = getStringFromWasm0(arg1, arg2); + }, + __wbg_stack_3b0d974bbf31e44f: function(arg0, arg1) { + const ret = arg1.stack; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }, + __wbg_static_accessor_GLOBAL_266715b9d96ba635: function() { + const ret = typeof global === 'undefined' ? null : global; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_static_accessor_GLOBAL_THIS_10fb7dc1ae063179: function() { + const ret = typeof globalThis === 'undefined' ? null : globalThis; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_static_accessor_SELF_0b583911f537483a: function() { + const ret = typeof self === 'undefined' ? null : self; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_static_accessor_WINDOW_d7f903d1508cbdc4: function() { + const ret = typeof window === 'undefined' ? null : window; + return isLikeNone(ret) ? 0 : addToExternrefTable0(ret); + }, + __wbg_textContent_acd42c63564a445e: function(arg0, arg1) { + const ret = arg1.textContent; + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); + }, + __wbindgen_generic_0000000000000001: function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 86, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke___web_sys_aad0de371f77e982___features__gen_Event__Event______true_); + return ret; + }, + __wbindgen_generic_0000000000000002: function(arg0, arg1) { + // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 88, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`. + const ret = makeMutClosure(arg0, arg1, wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke_______true_); + return ret; + }, + __wbindgen_generic_0000000000000003: function(arg0, arg1) { + // Cast intrinsic for `Ref(String) -> Externref`. + const ret = getStringFromWasm0(arg0, arg1); + return ret; + }, + __wbindgen_init_externref_table: function() { + const table = wasm.__wbindgen_externrefs; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + }, + }; + return { + __proto__: null, + "./terraphim-editor_bg.js": import0, + }; +} + +function wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke_______true_(arg0, arg1) { + wasm.wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke_______true_(arg0, arg1); +} + +function wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke___web_sys_aad0de371f77e982___features__gen_Event__Event______true_(arg0, arg1, arg2) { + wasm.wasm_bindgen_b0ba9b6703635be___convert__closures_____invoke___web_sys_aad0de371f77e982___features__gen_Event__Event______true_(arg0, arg1, arg2); +} + +const TerraphimHostSessionFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_terraphimhostsession_free(ptr, 1)); + +function addToExternrefTable0(obj) { + const idx = wasm.__externref_table_alloc(); + wasm.__wbindgen_externrefs.set(idx, obj); + return idx; +} + +const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(state => wasm.__wbindgen_destroy_closure(state.a, state.b)); + +function getArrayU8FromWasm0(ptr, len) { + ptr = ptr >>> 0; + return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len); +} + +let cachedDataViewMemory0 = null; +function getDataViewMemory0() { + if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { + cachedDataViewMemory0 = new DataView(wasm.memory.buffer); + } + return cachedDataViewMemory0; +} + +function getStringFromWasm0(ptr, len) { + return decodeText(ptr >>> 0, len); +} + +let cachedUint8ArrayMemory0 = null; +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + const idx = addToExternrefTable0(e); + wasm.__wbindgen_exn_store(idx); + } +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +function makeMutClosure(arg0, arg1, f) { + const state = { a: arg0, b: arg1, cnt: 1 }; + const real = (...args) => { + + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try { + return f(a, state.b, ...args); + } finally { + state.a = a; + real._wbg_cb_unref(); + } + }; + real._wbg_cb_unref = () => { + if (--state.cnt === 0) { + wasm.__wbindgen_destroy_closure(state.a, state.b); + state.a = 0; + CLOSURE_DTORS.unregister(state); + } + }; + CLOSURE_DTORS.register(real, state, state); + return real; +} + +function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = cachedTextEncoder.encodeInto(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function takeFromExternrefTable0(idx) { + const value = wasm.__wbindgen_externrefs.get(idx); + wasm.__externref_table_dealloc(idx); + return value; +} + +let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); +cachedTextDecoder.decode(); +const MAX_SAFARI_DECODE_BYTES = 2146435072; +let numBytesDecoded = 0; +function decodeText(ptr, len) { + numBytesDecoded += len; + if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) { + cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + cachedTextDecoder.decode(); + numBytesDecoded = len; + } + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + +const cachedTextEncoder = new TextEncoder(); + +if (!('encodeInto' in cachedTextEncoder)) { + cachedTextEncoder.encodeInto = function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; + }; +} + +let WASM_VECTOR_LEN = 0; + +let wasmModule, wasmInstance, wasm; +function __wbg_finalize_init(instance, module) { + wasmInstance = instance; + wasm = instance.exports; + wasmModule = module; + cachedDataViewMemory0 = null; + cachedUint8ArrayMemory0 = null; + wasm.__wbindgen_start(); + return wasm; +} + +async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (!module.ok) { + throw new Error(`failed to fetch Wasm: ${module.status} ${module.statusText} fetching '${module.url}'`); + } + + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + } catch (e) { + const validResponse = expectedResponseType(module.type); + + if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { throw e; } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + } else { + return instance; + } + } + + function expectedResponseType(type) { + switch (type) { + case 'basic': case 'cors': case 'default': return true; + } + return false; + } +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + + if (module !== undefined) { + if (Object.getPrototypeOf(module) === Object.prototype) { + ({module} = module) + } else { + console.warn('using deprecated parameters for `initSync()`; pass a single object instead') + } + } + + const imports = __wbg_get_imports(); + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + const instance = new WebAssembly.Instance(module, imports); + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(module_or_path) { + if (wasm !== undefined) return wasm; + + + if (module_or_path !== undefined) { + if (Object.getPrototypeOf(module_or_path) === Object.prototype) { + ({module_or_path} = module_or_path) + } else { + console.warn('using deprecated parameters for the initialization function; pass a single object instead') + } + } + + if (module_or_path === undefined) { + module_or_path = new URL('terraphim-editor_bg.wasm', import.meta.url); + } + const imports = __wbg_get_imports(); + + if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { + module_or_path = fetch(module_or_path); + } + + const { instance, module } = await __wbg_load(await module_or_path, imports); + + return __wbg_finalize_init(instance, module); +} + +export { initSync, __wbg_init as default }; diff --git a/static/editor/terraphim_editor_bg.wasm b/static/editor/terraphim_editor_bg.wasm new file mode 100644 index 0000000..25f9c9c Binary files /dev/null and b/static/editor/terraphim_editor_bg.wasm differ diff --git a/static/editor/tokens-36540e1e0e83eb54.css b/static/editor/tokens-36540e1e0e83eb54.css new file mode 100644 index 0000000..1b68601 --- /dev/null +++ b/static/editor/tokens-36540e1e0e83eb54.css @@ -0,0 +1,113 @@ +/* + * terraphim-editor design tokens and Write_On theme + * ================================================== + * + * Single source of truth for the Write_On visual language + * (docs/requirements/alternative-control.md, section 10 and R-7.1). + * All later UI issues (alternatives panel, ghosting, overflow panel, + * context menu, Lab popover, corner chrome) must consume these custom + * properties rather than hard-coding colours, radii or fonts. + * Full documentation, contrast figures and rationale: docs/design/tokens.md + * + * Values come from the spec text; sampling from the source demo frames + * is deferred (the frames are not available in the repository). + * + * Tokens (defined once, on :root, inert until consumed): + * --te-color-bg #0a0d1c page background + * --te-color-text #e8d9c4 body text, warm cream + * --te-color-text-dim #8f8781 dim UI text (counts, shortcut hints); solid + * --te-color-accent #8c86e6 lavender accent (active tab, lit dot, save icon) + * --te-color-accent-soft rgba(140, 134, 230, 0.4) underline / dot (accent at 40%) + * --te-color-panel #11142a side panels (alternatives, overflow) + * --te-color-panel-border #262a4a 1px lighter panel border + * --te-color-popover #171a2e context menu and Lab popover + * --te-color-popover-hover #1f2340 hovered row in menus + * --te-radius-popover 8px + * --te-font-mono monospace stack for body and UI + * --te-font-display cursive stack for panel titles + * --te-font-size-body 1.0625rem + * --te-line-height 1.75 + * --te-ghost-opacity 0.1 ghosted spans (R-5.1) + * --te-measure 70ch text column width (R-7.1) + * --te-page-margin-top generous top margin (R-7.1) + * + * Scoping: the Write_On theme and layout apply only to the editor root + * (.te-app: #app on the full page, the embed container otherwise) and its + * descendants, when it carries data-mode="write-on" (or the class + * "write-on"). The plain editor is unaffected. The chrome + * (public/js/chrome.js) sets the attribute on the editor root and, on the + * full page only, on , which gets the page background; a host + * element with the same attribute is never styled (issue #77). The custom + * properties live on :root, so chrome mounted outside the root (selection + * menu, persistence notice and dialogs) reads them too. To preview, set + *
+ * by hand or in the browser devtools. + * + * Expected hooks inside the scope: + * .te-surface the editable writing surface (contenteditable) + * .markdown-preview the rendered preview + */ + +:root { + --te-color-bg: #0a0d1c; + --te-color-text: #e8d9c4; + --te-color-text-dim: #8f8781; + --te-color-accent: #8c86e6; + --te-color-accent-soft: rgba(140, 134, 230, 0.4); + --te-color-panel: #11142a; + --te-color-panel-border: #262a4a; + --te-color-popover: #171a2e; + --te-color-popover-hover: #1f2340; + --te-radius-popover: 8px; + --te-font-mono: "JetBrains Mono", "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; + --te-font-display: "Caveat", "Segoe Script", "Bradley Hand", "Brush Script MT", cursive; + --te-font-size-body: 1.0625rem; + --te-line-height: 1.75; + --te-ghost-opacity: 0.1; + --te-measure: 70ch; + --te-page-margin-top: clamp(3rem, 12vh, 8rem); +} + +/* ---- Write_On theme (opt-in scope) ---- */ + +/* The editor root (.te-app) carries the theme; the full page also sets the + attribute on for its own background (chrome.pageMode). Plain mode + wears the same palette and column, as the video's does from 0:00 to 0:05 + (issue #107); only the Markdown tools view (data-tools="on": toolbar and + preview) keeps the light Shoelace look. */ +.te-app[data-mode="write-on"], +.te-app.write-on, +body[data-mode="write-on"], +.te-app:not([data-mode="write-on"]):not([data-tools="on"]) { + min-height: 100vh; + background-color: var(--te-color-bg); + color: var(--te-color-text); + font-family: var(--te-font-mono); + font-size: var(--te-font-size-body); + line-height: var(--te-line-height); + color-scheme: dark; +} + +/* ---- Write_On layout (R-7.1): centred ~70ch column, generous top margin ---- */ + +.te-app[data-mode="write-on"] .te-surface, +.te-app[data-mode="write-on"] .markdown-preview, +.te-app.write-on .te-surface, +.te-app.write-on .markdown-preview, +.te-app:not([data-mode="write-on"]):not([data-tools="on"]) .te-surface { + box-sizing: content-box; + max-width: var(--te-measure); + margin-inline: auto; + padding-top: var(--te-page-margin-top); + background-color: transparent; + color: inherit; + font-family: var(--te-font-mono); + line-height: var(--te-line-height); + caret-color: var(--te-color-accent); +} + +.te-app[data-mode="write-on"] ::selection, +.te-app.write-on ::selection, +.te-app:not([data-mode="write-on"]):not([data-tools="on"]) ::selection { + background-color: var(--te-color-accent-soft); +} diff --git a/static/editor/trim-a64aceacb4246fba.css b/static/editor/trim-a64aceacb4246fba.css new file mode 100644 index 0000000..2687e08 --- /dev/null +++ b/static/editor/trim-a64aceacb4246fba.css @@ -0,0 +1,276 @@ +/* + * Trim levels, the faded preview and the status card (issue #15, spec R-8.3 + * to R-8.5). Consumes public/css/tokens.css only; nothing is hard-coded + * except the shadow (as in lab.css and selection-menu.css). + * + * The preview uses the ghost treatment (R-5.1: the fade on the text colour, + * `--te-ghost-opacity`) under its own class, `te-trim-faded`, so a trim + * preview is never confused with a user ghost (`te-ghost`) in the DOM or in + * the model. The card's signature is a one-line meter: the document as a + * bar, the part that would go drawn with the same fade as the faded words. + */ + +/* ---- Faded preview (R-8.4) ---- */ + +.te-trim-faded { + color: color-mix(in srgb, currentColor calc(var(--te-ghost-opacity) * 100%), transparent); +} + +@supports not (color: color-mix(in srgb, red 50%, transparent)) { + .te-trim-faded { + opacity: var(--te-ghost-opacity); + } +} + +/* Clickable to keep only while the card is shown. */ +[data-te-trimming] .te-trim-faded { + cursor: pointer; +} + +[data-te-trimming] .te-trim-faded:hover { + text-decoration: underline dotted var(--te-color-accent-soft) 1px; + text-underline-offset: 0.22em; +} + +/* The walk-through cursor: still faded, outlined in the accent. */ +.te-trim-current { + border-radius: 2px; + box-shadow: 0 0 0 1px var(--te-color-accent); + -webkit-box-decoration-break: clone; + box-decoration-break: clone; +} + +/* ---- Level buttons in the Lab popover (R-8.3) ---- */ + +.te-lab-trim { + align-items: center; + padding-top: 0.625rem; + border-top: 1px solid var(--te-color-panel-border); +} + +/* The radio group's name ("Trim"), for assistive technology only: the video + shows the five buttons with no label above them (4:56 to 5:30). */ +.te-trim-label { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0 0 0 0); + clip-path: inset(50%); + white-space: nowrap; + border: 0; +} + +/* One row of five equal buttons, the target under the name, as in the + video; only a window too narrow for the popover's full width wraps it. */ +.te-trim-levels { + display: flex; + flex: 1 1 100%; + flex-wrap: nowrap; + gap: 0.25rem; + min-width: 0; +} + +@media (max-width: 34rem) { + .te-trim-levels { + flex-wrap: wrap; + } +} + +.te-lab .te-trim-level { + display: inline-flex; + flex: 1 1 0; + flex-direction: column; + align-items: center; + gap: 0.125rem; + min-width: 0; + padding: 0.375rem 0.125rem; + border: 1px solid var(--te-color-panel-border); + border-radius: calc(var(--te-radius-popover) - 2px); + color: var(--te-color-text); + font-size: 0.6875rem; + white-space: nowrap; +} + +.te-lab .te-trim-level:hover { + background: var(--te-color-popover-hover); +} + +.te-lab .te-trim-level[aria-checked="true"] { + border-color: var(--te-color-accent); + color: var(--te-color-accent); +} + +.te-trim-level-target { + color: var(--te-color-text-dim); + font-size: 0.625rem; + font-variant-numeric: tabular-nums; +} + +/* ---- Status card (R-8.4), bottom-centre ---- */ + +.te-trim-card { + position: fixed; + left: 50%; + bottom: 4rem; + z-index: 30; + box-sizing: border-box; + width: min(28rem, calc(100vw - 2rem)); + padding: 0.875rem 1rem 0.75rem; + transform: translateX(-50%); + border: 1px solid var(--te-color-panel-border); + border-radius: var(--te-radius-popover); + background: var(--te-color-popover); + box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55); + color: var(--te-color-text); + font-family: var(--te-font-mono); + font-size: 0.8125rem; + line-height: 1.5; + text-align: left; + outline: none; +} + +.te-trim-card[hidden], +.te-trim-walk[hidden] { + display: none; +} + +.te-trim-card:focus-visible { + box-shadow: 0 0 0 2px var(--te-color-accent), 0 18px 48px rgba(0, 0, 0, 0.55); +} + +@media (prefers-reduced-motion: no-preference) { + .te-trim-card:not([hidden]) { + animation: te-trim-rise 160ms ease-out; + } +} + +@keyframes te-trim-rise { + from { opacity: 0; transform: translate(-50%, 6px); } + to { opacity: 1; transform: translate(-50%, 0); } +} + +.te-trim-card-head { + display: flex; + flex-wrap: wrap; + align-items: baseline; + justify-content: space-between; + gap: 0.25rem 1rem; +} + +.te-trim-card-level { + font-weight: 700; +} + +.te-trim-card-count { + color: var(--te-color-accent); + font-variant-numeric: tabular-nums; +} + +.te-trim-meter { + position: relative; + height: 3px; + margin: 0.5rem 0 0.625rem; + border-radius: 2px; + background: color-mix(in srgb, var(--te-color-text) calc(var(--te-ghost-opacity) * 100%), transparent); + overflow: hidden; +} + +.te-trim-meter-keep { + position: absolute; + inset: 0 auto 0 0; + width: 100%; + background: var(--te-color-text); +} + +@media (prefers-reduced-motion: no-preference) { + .te-trim-meter-keep { + transition: width 220ms ease-out; + } +} + +.te-trim-card-hint, +.te-trim-walk-text { + margin: 0 0 0.625rem; + color: var(--te-color-text-dim); + font-size: 0.75rem; +} + +.te-trim-walk-text { + color: var(--te-color-text); + overflow-wrap: anywhere; +} + +.te-trim-actions, +.te-trim-walk-buttons { + display: flex; + flex-wrap: wrap; + gap: 0.375rem; +} + +.te-trim-walk-buttons { + margin-bottom: 0.625rem; +} + +.te-trim-card button { + display: inline-flex; + align-items: center; + gap: 0.5ch; + margin: 0; + padding: 0.25rem 0.75rem; + border: 1px solid var(--te-color-panel-border); + border-radius: 999px; + background: none; + color: var(--te-color-text); + font: inherit; + font-size: 0.75rem; + cursor: pointer; +} + +.te-trim-card button i { + color: var(--te-color-text-dim); + font-size: 0.6875rem; +} + +.te-trim-card button:hover:not(:disabled) { + background: var(--te-color-popover-hover); +} + +.te-trim-card button:focus-visible { + outline: 2px solid var(--te-color-accent); + outline-offset: 1px; +} + +.te-trim-card button:disabled { + color: var(--te-color-text-dim); + cursor: default; +} + +.te-trim-card .te-trim-cut { + border-color: var(--te-color-accent); + color: var(--te-color-accent); +} + +.te-trim-card .te-trim-cut i { + color: var(--te-color-accent); +} + +.te-trim-card .te-trim-done { + margin-left: auto; +} + +/* Visually hidden live region for messages while the card is hidden. */ +.te-trim-live { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0 0 0 0); + clip-path: inset(50%); + white-space: nowrap; + border: 0; +} diff --git a/static/editor/vendor/fontawesome/6.5.2/LICENSE.txt b/static/editor/vendor/fontawesome/6.5.2/LICENSE.txt new file mode 100644 index 0000000..e69c5e3 --- /dev/null +++ b/static/editor/vendor/fontawesome/6.5.2/LICENSE.txt @@ -0,0 +1,165 @@ +Fonticons, Inc. (https://fontawesome.com) + +-------------------------------------------------------------------------------- + +Font Awesome Free License + +Font Awesome Free is free, open source, and GPL friendly. You can use it for +commercial projects, open source projects, or really almost whatever you want. +Full Font Awesome Free license: https://fontawesome.com/license/free. + +-------------------------------------------------------------------------------- + +# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/) + +The Font Awesome Free download is licensed under a Creative Commons +Attribution 4.0 International License and applies to all icons packaged +as SVG and JS file types. + +-------------------------------------------------------------------------------- + +# Fonts: SIL OFL 1.1 License + +In the Font Awesome Free download, the SIL OFL license applies to all icons +packaged as web and desktop font files. + +Copyright (c) 2024 Fonticons, Inc. (https://fontawesome.com) +with Reserved Font Name: "Font Awesome". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +SIL OPEN FONT LICENSE +Version 1.1 - 26 February 2007 + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting — in part or in whole — any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +-------------------------------------------------------------------------------- + +# Code: MIT License (https://opensource.org/licenses/MIT) + +In the Font Awesome Free download, the MIT license applies to all non-font and +non-icon files. + +Copyright 2024 Fonticons, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------------- + +# Attribution + +Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font +Awesome Free files already contain embedded comments with sufficient +attribution, so you shouldn't need to do anything additional when using these +files normally. + +We've kept attribution comments terse, so we ask that you do not actively work +to remove them from files, especially code. They're a great way for folks to +learn about Font Awesome. + +-------------------------------------------------------------------------------- + +# Brand Icons + +All brand icons are trademarks of their respective owners. The use of these +trademarks does not indicate endorsement of the trademark holder by Font +Awesome, nor vice versa. **Please do not use brand logos for any purpose except +to represent the company, product, or service to which they refer.** diff --git a/static/editor/vendor/fontawesome/6.5.2/css/brands.min.css b/static/editor/vendor/fontawesome/6.5.2/css/brands.min.css new file mode 100644 index 0000000..3e70760 --- /dev/null +++ b/static/editor/vendor/fontawesome/6.5.2/css/brands.min.css @@ -0,0 +1,6 @@ +/*! + * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2024 Fonticons, Inc. + */ +:host,:root{--fa-style-family-brands:"Font Awesome 6 Brands";--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}.fa-brands,.fab{font-weight:400}.fa-monero:before{content:"\f3d0"}.fa-hooli:before{content:"\f427"}.fa-yelp:before{content:"\f1e9"}.fa-cc-visa:before{content:"\f1f0"}.fa-lastfm:before{content:"\f202"}.fa-shopware:before{content:"\f5b5"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-aws:before{content:"\f375"}.fa-redhat:before{content:"\f7bc"}.fa-yoast:before{content:"\f2b1"}.fa-cloudflare:before{content:"\e07d"}.fa-ups:before{content:"\f7e0"}.fa-pixiv:before{content:"\e640"}.fa-wpexplorer:before{content:"\f2de"}.fa-dyalog:before{content:"\f399"}.fa-bity:before{content:"\f37a"}.fa-stackpath:before{content:"\f842"}.fa-buysellads:before{content:"\f20d"}.fa-first-order:before{content:"\f2b0"}.fa-modx:before{content:"\f285"}.fa-guilded:before{content:"\e07e"}.fa-vnv:before{content:"\f40b"}.fa-js-square:before,.fa-square-js:before{content:"\f3b9"}.fa-microsoft:before{content:"\f3ca"}.fa-qq:before{content:"\f1d6"}.fa-orcid:before{content:"\f8d2"}.fa-java:before{content:"\f4e4"}.fa-invision:before{content:"\f7b0"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-centercode:before{content:"\f380"}.fa-glide-g:before{content:"\f2a6"}.fa-drupal:before{content:"\f1a9"}.fa-jxl:before{content:"\e67b"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-unity:before{content:"\e049"}.fa-whmcs:before{content:"\f40d"}.fa-rocketchat:before{content:"\f3e8"}.fa-vk:before{content:"\f189"}.fa-untappd:before{content:"\f405"}.fa-mailchimp:before{content:"\f59e"}.fa-css3-alt:before{content:"\f38b"}.fa-reddit-square:before,.fa-square-reddit:before{content:"\f1a2"}.fa-vimeo-v:before{content:"\f27d"}.fa-contao:before{content:"\f26d"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-deskpro:before{content:"\f38f"}.fa-brave:before{content:"\e63c"}.fa-sistrix:before{content:"\f3ee"}.fa-instagram-square:before,.fa-square-instagram:before{content:"\e055"}.fa-battle-net:before{content:"\f835"}.fa-the-red-yeti:before{content:"\f69d"}.fa-hacker-news-square:before,.fa-square-hacker-news:before{content:"\f3af"}.fa-edge:before{content:"\f282"}.fa-threads:before{content:"\e618"}.fa-napster:before{content:"\f3d2"}.fa-snapchat-square:before,.fa-square-snapchat:before{content:"\f2ad"}.fa-google-plus-g:before{content:"\f0d5"}.fa-artstation:before{content:"\f77a"}.fa-markdown:before{content:"\f60f"}.fa-sourcetree:before{content:"\f7d3"}.fa-google-plus:before{content:"\f2b3"}.fa-diaspora:before{content:"\f791"}.fa-foursquare:before{content:"\f180"}.fa-stack-overflow:before{content:"\f16c"}.fa-github-alt:before{content:"\f113"}.fa-phoenix-squadron:before{content:"\f511"}.fa-pagelines:before{content:"\f18c"}.fa-algolia:before{content:"\f36c"}.fa-red-river:before{content:"\f3e3"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-safari:before{content:"\f267"}.fa-google:before{content:"\f1a0"}.fa-font-awesome-alt:before,.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-atlassian:before{content:"\f77b"}.fa-linkedin-in:before{content:"\f0e1"}.fa-digital-ocean:before{content:"\f391"}.fa-nimblr:before{content:"\f5a8"}.fa-chromecast:before{content:"\f838"}.fa-evernote:before{content:"\f839"}.fa-hacker-news:before{content:"\f1d4"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-adversal:before{content:"\f36a"}.fa-creative-commons:before{content:"\f25e"}.fa-watchman-monitoring:before{content:"\e087"}.fa-fonticons:before{content:"\f280"}.fa-weixin:before{content:"\f1d7"}.fa-shirtsinbulk:before{content:"\f214"}.fa-codepen:before{content:"\f1cb"}.fa-git-alt:before{content:"\f841"}.fa-lyft:before{content:"\f3c3"}.fa-rev:before{content:"\f5b2"}.fa-windows:before{content:"\f17a"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-square-viadeo:before,.fa-viadeo-square:before{content:"\f2aa"}.fa-meetup:before{content:"\f2e0"}.fa-centos:before{content:"\f789"}.fa-adn:before{content:"\f170"}.fa-cloudsmith:before{content:"\f384"}.fa-opensuse:before{content:"\e62b"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-dribbble-square:before,.fa-square-dribbble:before{content:"\f397"}.fa-codiepie:before{content:"\f284"}.fa-node:before{content:"\f419"}.fa-mix:before{content:"\f3cb"}.fa-steam:before{content:"\f1b6"}.fa-cc-apple-pay:before{content:"\f416"}.fa-scribd:before{content:"\f28a"}.fa-debian:before{content:"\e60b"}.fa-openid:before{content:"\f19b"}.fa-instalod:before{content:"\e081"}.fa-expeditedssl:before{content:"\f23e"}.fa-sellcast:before{content:"\f2da"}.fa-square-twitter:before,.fa-twitter-square:before{content:"\f081"}.fa-r-project:before{content:"\f4f7"}.fa-delicious:before{content:"\f1a5"}.fa-freebsd:before{content:"\f3a4"}.fa-vuejs:before{content:"\f41f"}.fa-accusoft:before{content:"\f369"}.fa-ioxhost:before{content:"\f208"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-app-store:before{content:"\f36f"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-itunes-note:before{content:"\f3b5"}.fa-golang:before{content:"\e40f"}.fa-kickstarter:before,.fa-square-kickstarter:before{content:"\f3bb"}.fa-grav:before{content:"\f2d6"}.fa-weibo:before{content:"\f18a"}.fa-uncharted:before{content:"\e084"}.fa-firstdraft:before{content:"\f3a1"}.fa-square-youtube:before,.fa-youtube-square:before{content:"\f431"}.fa-wikipedia-w:before{content:"\f266"}.fa-rendact:before,.fa-wpressr:before{content:"\f3e4"}.fa-angellist:before{content:"\f209"}.fa-galactic-republic:before{content:"\f50c"}.fa-nfc-directional:before{content:"\e530"}.fa-skype:before{content:"\f17e"}.fa-joget:before{content:"\f3b7"}.fa-fedora:before{content:"\f798"}.fa-stripe-s:before{content:"\f42a"}.fa-meta:before{content:"\e49b"}.fa-laravel:before{content:"\f3bd"}.fa-hotjar:before{content:"\f3b1"}.fa-bluetooth-b:before{content:"\f294"}.fa-square-letterboxd:before{content:"\e62e"}.fa-sticker-mule:before{content:"\f3f7"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-hips:before{content:"\f452"}.fa-behance:before{content:"\f1b4"}.fa-reddit:before{content:"\f1a1"}.fa-discord:before{content:"\f392"}.fa-chrome:before{content:"\f268"}.fa-app-store-ios:before{content:"\f370"}.fa-cc-discover:before{content:"\f1f2"}.fa-wpbeginner:before{content:"\f297"}.fa-confluence:before{content:"\f78d"}.fa-shoelace:before{content:"\e60c"}.fa-mdb:before{content:"\f8ca"}.fa-dochub:before{content:"\f394"}.fa-accessible-icon:before{content:"\f368"}.fa-ebay:before{content:"\f4f4"}.fa-amazon:before{content:"\f270"}.fa-unsplash:before{content:"\e07c"}.fa-yarn:before{content:"\f7e3"}.fa-square-steam:before,.fa-steam-square:before{content:"\f1b7"}.fa-500px:before{content:"\f26e"}.fa-square-vimeo:before,.fa-vimeo-square:before{content:"\f194"}.fa-asymmetrik:before{content:"\f372"}.fa-font-awesome-flag:before,.fa-font-awesome-logo-full:before,.fa-font-awesome:before{content:"\f2b4"}.fa-gratipay:before{content:"\f184"}.fa-apple:before{content:"\f179"}.fa-hive:before{content:"\e07f"}.fa-gitkraken:before{content:"\f3a6"}.fa-keybase:before{content:"\f4f5"}.fa-apple-pay:before{content:"\f415"}.fa-padlet:before{content:"\e4a0"}.fa-amazon-pay:before{content:"\f42c"}.fa-github-square:before,.fa-square-github:before{content:"\f092"}.fa-stumbleupon:before{content:"\f1a4"}.fa-fedex:before{content:"\f797"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-shopify:before{content:"\e057"}.fa-neos:before{content:"\f612"}.fa-square-threads:before{content:"\e619"}.fa-hackerrank:before{content:"\f5f7"}.fa-researchgate:before{content:"\f4f8"}.fa-swift:before{content:"\f8e1"}.fa-angular:before{content:"\f420"}.fa-speakap:before{content:"\f3f3"}.fa-angrycreative:before{content:"\f36e"}.fa-y-combinator:before{content:"\f23b"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-google-scholar:before{content:"\e63b"}.fa-gitlab-square:before,.fa-square-gitlab:before{content:"\e5ae"}.fa-studiovinari:before{content:"\f3f8"}.fa-pied-piper:before{content:"\f2ae"}.fa-wordpress:before{content:"\f19a"}.fa-product-hunt:before{content:"\f288"}.fa-firefox:before{content:"\f269"}.fa-linode:before{content:"\f2b8"}.fa-goodreads:before{content:"\f3a8"}.fa-odnoklassniki-square:before,.fa-square-odnoklassniki:before{content:"\f264"}.fa-jsfiddle:before{content:"\f1cc"}.fa-sith:before{content:"\f512"}.fa-themeisle:before{content:"\f2b2"}.fa-page4:before{content:"\f3d7"}.fa-hashnode:before{content:"\e499"}.fa-react:before{content:"\f41b"}.fa-cc-paypal:before{content:"\f1f4"}.fa-squarespace:before{content:"\f5be"}.fa-cc-stripe:before{content:"\f1f5"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-bitcoin:before{content:"\f379"}.fa-keycdn:before{content:"\f3ba"}.fa-opera:before{content:"\f26a"}.fa-itch-io:before{content:"\f83a"}.fa-umbraco:before{content:"\f8e8"}.fa-galactic-senate:before{content:"\f50d"}.fa-ubuntu:before{content:"\f7df"}.fa-draft2digital:before{content:"\f396"}.fa-stripe:before{content:"\f429"}.fa-houzz:before{content:"\f27c"}.fa-gg:before{content:"\f260"}.fa-dhl:before{content:"\f790"}.fa-pinterest-square:before,.fa-square-pinterest:before{content:"\f0d3"}.fa-xing:before{content:"\f168"}.fa-blackberry:before{content:"\f37b"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-playstation:before{content:"\f3df"}.fa-quinscape:before{content:"\f459"}.fa-less:before{content:"\f41d"}.fa-blogger-b:before{content:"\f37d"}.fa-opencart:before{content:"\f23d"}.fa-vine:before{content:"\f1ca"}.fa-signal-messenger:before{content:"\e663"}.fa-paypal:before{content:"\f1ed"}.fa-gitlab:before{content:"\f296"}.fa-typo3:before{content:"\f42b"}.fa-reddit-alien:before{content:"\f281"}.fa-yahoo:before{content:"\f19e"}.fa-dailymotion:before{content:"\e052"}.fa-affiliatetheme:before{content:"\f36b"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-bootstrap:before{content:"\f836"}.fa-odnoklassniki:before{content:"\f263"}.fa-nfc-symbol:before{content:"\e531"}.fa-mintbit:before{content:"\e62f"}.fa-ethereum:before{content:"\f42e"}.fa-speaker-deck:before{content:"\f83c"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-patreon:before{content:"\f3d9"}.fa-avianex:before{content:"\f374"}.fa-ello:before{content:"\f5f1"}.fa-gofore:before{content:"\f3a7"}.fa-bimobject:before{content:"\f378"}.fa-brave-reverse:before{content:"\e63d"}.fa-facebook-f:before{content:"\f39e"}.fa-google-plus-square:before,.fa-square-google-plus:before{content:"\f0d4"}.fa-web-awesome:before{content:"\e682"}.fa-mandalorian:before{content:"\f50f"}.fa-first-order-alt:before{content:"\f50a"}.fa-osi:before{content:"\f41a"}.fa-google-wallet:before{content:"\f1ee"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-periscope:before{content:"\f3da"}.fa-fulcrum:before{content:"\f50b"}.fa-cloudscale:before{content:"\f383"}.fa-forumbee:before{content:"\f211"}.fa-mizuni:before{content:"\f3cc"}.fa-schlix:before{content:"\f3ea"}.fa-square-xing:before,.fa-xing-square:before{content:"\f169"}.fa-bandcamp:before{content:"\f2d5"}.fa-wpforms:before{content:"\f298"}.fa-cloudversify:before{content:"\f385"}.fa-usps:before{content:"\f7e1"}.fa-megaport:before{content:"\f5a3"}.fa-magento:before{content:"\f3c4"}.fa-spotify:before{content:"\f1bc"}.fa-optin-monster:before{content:"\f23c"}.fa-fly:before{content:"\f417"}.fa-aviato:before{content:"\f421"}.fa-itunes:before{content:"\f3b4"}.fa-cuttlefish:before{content:"\f38c"}.fa-blogger:before{content:"\f37c"}.fa-flickr:before{content:"\f16e"}.fa-viber:before{content:"\f409"}.fa-soundcloud:before{content:"\f1be"}.fa-digg:before{content:"\f1a6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-letterboxd:before{content:"\e62d"}.fa-symfony:before{content:"\f83d"}.fa-maxcdn:before{content:"\f136"}.fa-etsy:before{content:"\f2d7"}.fa-facebook-messenger:before{content:"\f39f"}.fa-audible:before{content:"\f373"}.fa-think-peaks:before{content:"\f731"}.fa-bilibili:before{content:"\e3d9"}.fa-erlang:before{content:"\f39d"}.fa-x-twitter:before{content:"\e61b"}.fa-cotton-bureau:before{content:"\f89e"}.fa-dashcube:before{content:"\f210"}.fa-42-group:before,.fa-innosoft:before{content:"\e080"}.fa-stack-exchange:before{content:"\f18d"}.fa-elementor:before{content:"\f430"}.fa-pied-piper-square:before,.fa-square-pied-piper:before{content:"\e01e"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-palfed:before{content:"\f3d8"}.fa-superpowers:before{content:"\f2dd"}.fa-resolving:before{content:"\f3e7"}.fa-xbox:before{content:"\f412"}.fa-square-web-awesome-stroke:before{content:"\e684"}.fa-searchengin:before{content:"\f3eb"}.fa-tiktok:before{content:"\e07b"}.fa-facebook-square:before,.fa-square-facebook:before{content:"\f082"}.fa-renren:before{content:"\f18b"}.fa-linux:before{content:"\f17c"}.fa-glide:before{content:"\f2a5"}.fa-linkedin:before{content:"\f08c"}.fa-hubspot:before{content:"\f3b2"}.fa-deploydog:before{content:"\f38e"}.fa-twitch:before{content:"\f1e8"}.fa-ravelry:before{content:"\f2d9"}.fa-mixer:before{content:"\e056"}.fa-lastfm-square:before,.fa-square-lastfm:before{content:"\f203"}.fa-vimeo:before{content:"\f40a"}.fa-mendeley:before{content:"\f7b3"}.fa-uniregistry:before{content:"\f404"}.fa-figma:before{content:"\f799"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-dropbox:before{content:"\f16b"}.fa-instagram:before{content:"\f16d"}.fa-cmplid:before{content:"\e360"}.fa-upwork:before{content:"\e641"}.fa-facebook:before{content:"\f09a"}.fa-gripfire:before{content:"\f3ac"}.fa-jedi-order:before{content:"\f50e"}.fa-uikit:before{content:"\f403"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-phabricator:before{content:"\f3db"}.fa-ussunnah:before{content:"\f407"}.fa-earlybirds:before{content:"\f39a"}.fa-trade-federation:before{content:"\f513"}.fa-autoprefixer:before{content:"\f41c"}.fa-whatsapp:before{content:"\f232"}.fa-square-upwork:before{content:"\e67c"}.fa-slideshare:before{content:"\f1e7"}.fa-google-play:before{content:"\f3ab"}.fa-viadeo:before{content:"\f2a9"}.fa-line:before{content:"\f3c0"}.fa-google-drive:before{content:"\f3aa"}.fa-servicestack:before{content:"\f3ec"}.fa-simplybuilt:before{content:"\f215"}.fa-bitbucket:before{content:"\f171"}.fa-imdb:before{content:"\f2d8"}.fa-deezer:before{content:"\e077"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-jira:before{content:"\f7b1"}.fa-docker:before{content:"\f395"}.fa-screenpal:before{content:"\e570"}.fa-bluetooth:before{content:"\f293"}.fa-gitter:before{content:"\f426"}.fa-d-and-d:before{content:"\f38d"}.fa-microblog:before{content:"\e01a"}.fa-cc-diners-club:before{content:"\f24c"}.fa-gg-circle:before{content:"\f261"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-yandex:before{content:"\f413"}.fa-readme:before{content:"\f4d5"}.fa-html5:before{content:"\f13b"}.fa-sellsy:before{content:"\f213"}.fa-square-web-awesome:before{content:"\e683"}.fa-sass:before{content:"\f41e"}.fa-wirsindhandwerk:before,.fa-wsh:before{content:"\e2d0"}.fa-buromobelexperte:before{content:"\f37f"}.fa-salesforce:before{content:"\f83b"}.fa-octopus-deploy:before{content:"\e082"}.fa-medapps:before{content:"\f3c6"}.fa-ns8:before{content:"\f3d5"}.fa-pinterest-p:before{content:"\f231"}.fa-apper:before{content:"\f371"}.fa-fort-awesome:before{content:"\f286"}.fa-waze:before{content:"\f83f"}.fa-bluesky:before{content:"\e671"}.fa-cc-jcb:before{content:"\f24b"}.fa-snapchat-ghost:before,.fa-snapchat:before{content:"\f2ab"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-rust:before{content:"\e07a"}.fa-wix:before{content:"\f5cf"}.fa-behance-square:before,.fa-square-behance:before{content:"\f1b5"}.fa-supple:before{content:"\f3f9"}.fa-webflow:before{content:"\e65c"}.fa-rebel:before{content:"\f1d0"}.fa-css3:before{content:"\f13c"}.fa-staylinked:before{content:"\f3f5"}.fa-kaggle:before{content:"\f5fa"}.fa-space-awesome:before{content:"\e5ac"}.fa-deviantart:before{content:"\f1bd"}.fa-cpanel:before{content:"\f388"}.fa-goodreads-g:before{content:"\f3a9"}.fa-git-square:before,.fa-square-git:before{content:"\f1d2"}.fa-square-tumblr:before,.fa-tumblr-square:before{content:"\f174"}.fa-trello:before{content:"\f181"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-get-pocket:before{content:"\f265"}.fa-perbyte:before{content:"\e083"}.fa-grunt:before{content:"\f3ad"}.fa-weebly:before{content:"\f5cc"}.fa-connectdevelop:before{content:"\f20e"}.fa-leanpub:before{content:"\f212"}.fa-black-tie:before{content:"\f27e"}.fa-themeco:before{content:"\f5c6"}.fa-python:before{content:"\f3e2"}.fa-android:before{content:"\f17b"}.fa-bots:before{content:"\e340"}.fa-free-code-camp:before{content:"\f2c5"}.fa-hornbill:before{content:"\f592"}.fa-js:before{content:"\f3b8"}.fa-ideal:before{content:"\e013"}.fa-git:before{content:"\f1d3"}.fa-dev:before{content:"\f6cc"}.fa-sketch:before{content:"\f7c6"}.fa-yandex-international:before{content:"\f414"}.fa-cc-amex:before{content:"\f1f3"}.fa-uber:before{content:"\f402"}.fa-github:before{content:"\f09b"}.fa-php:before{content:"\f457"}.fa-alipay:before{content:"\f642"}.fa-youtube:before{content:"\f167"}.fa-skyatlas:before{content:"\f216"}.fa-firefox-browser:before{content:"\e007"}.fa-replyd:before{content:"\f3e6"}.fa-suse:before{content:"\f7d6"}.fa-jenkins:before{content:"\f3b6"}.fa-twitter:before{content:"\f099"}.fa-rockrms:before{content:"\f3e9"}.fa-pinterest:before{content:"\f0d2"}.fa-buffer:before{content:"\f837"}.fa-npm:before{content:"\f3d4"}.fa-yammer:before{content:"\f840"}.fa-btc:before{content:"\f15a"}.fa-dribbble:before{content:"\f17d"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-internet-explorer:before{content:"\f26b"}.fa-stubber:before{content:"\e5c7"}.fa-telegram-plane:before,.fa-telegram:before{content:"\f2c6"}.fa-old-republic:before{content:"\f510"}.fa-odysee:before{content:"\e5c6"}.fa-square-whatsapp:before,.fa-whatsapp-square:before{content:"\f40c"}.fa-node-js:before{content:"\f3d3"}.fa-edge-legacy:before{content:"\e078"}.fa-slack-hash:before,.fa-slack:before{content:"\f198"}.fa-medrt:before{content:"\f3c8"}.fa-usb:before{content:"\f287"}.fa-tumblr:before{content:"\f173"}.fa-vaadin:before{content:"\f408"}.fa-quora:before{content:"\f2c4"}.fa-square-x-twitter:before{content:"\e61a"}.fa-reacteurope:before{content:"\f75d"}.fa-medium-m:before,.fa-medium:before{content:"\f23a"}.fa-amilia:before{content:"\f36d"}.fa-mixcloud:before{content:"\f289"}.fa-flipboard:before{content:"\f44d"}.fa-viacoin:before{content:"\f237"}.fa-critical-role:before{content:"\f6c9"}.fa-sitrox:before{content:"\e44a"}.fa-discourse:before{content:"\f393"}.fa-joomla:before{content:"\f1aa"}.fa-mastodon:before{content:"\f4f6"}.fa-airbnb:before{content:"\f834"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-buy-n-large:before{content:"\f8a6"}.fa-gulp:before{content:"\f3ae"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-strava:before{content:"\f428"}.fa-ember:before{content:"\f423"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-teamspeak:before{content:"\f4f9"}.fa-pushed:before{content:"\f3e1"}.fa-wordpress-simple:before{content:"\f411"}.fa-nutritionix:before{content:"\f3d6"}.fa-wodu:before{content:"\e088"}.fa-google-pay:before{content:"\e079"}.fa-intercom:before{content:"\f7af"}.fa-zhihu:before{content:"\f63f"}.fa-korvue:before{content:"\f42f"}.fa-pix:before{content:"\e43a"}.fa-steam-symbol:before{content:"\f3f6"} \ No newline at end of file diff --git a/static/editor/vendor/fontawesome/6.5.2/css/fontawesome.min.css b/static/editor/vendor/fontawesome/6.5.2/css/fontawesome.min.css new file mode 100644 index 0000000..7e1c254 --- /dev/null +++ b/static/editor/vendor/fontawesome/6.5.2/css/fontawesome.min.css @@ -0,0 +1,9 @@ +/*! + * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2024 Fonticons, Inc. + */ +.fa{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.fa-brands,.fa-classic,.fa-regular,.fa-sharp,.fa-solid,.fab,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fa-classic,.fa-regular,.fa-solid,.far,.fas{font-family:"Font Awesome 6 Free"}.fa-brands,.fab{font-family:"Font Awesome 6 Brands"}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-transition-delay:0s;transition-delay:0s;-webkit-transition-duration:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,0));transform:rotate(var(--fa-rotate-angle,0))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)} + +.fa-0:before{content:"\30"}.fa-1:before{content:"\31"}.fa-2:before{content:"\32"}.fa-3:before{content:"\33"}.fa-4:before{content:"\34"}.fa-5:before{content:"\35"}.fa-6:before{content:"\36"}.fa-7:before{content:"\37"}.fa-8:before{content:"\38"}.fa-9:before{content:"\39"}.fa-fill-drip:before{content:"\f576"}.fa-arrows-to-circle:before{content:"\e4bd"}.fa-chevron-circle-right:before,.fa-circle-chevron-right:before{content:"\f138"}.fa-at:before{content:"\40"}.fa-trash-alt:before,.fa-trash-can:before{content:"\f2ed"}.fa-text-height:before{content:"\f034"}.fa-user-times:before,.fa-user-xmark:before{content:"\f235"}.fa-stethoscope:before{content:"\f0f1"}.fa-comment-alt:before,.fa-message:before{content:"\f27a"}.fa-info:before{content:"\f129"}.fa-compress-alt:before,.fa-down-left-and-up-right-to-center:before{content:"\f422"}.fa-explosion:before{content:"\e4e9"}.fa-file-alt:before,.fa-file-lines:before,.fa-file-text:before{content:"\f15c"}.fa-wave-square:before{content:"\f83e"}.fa-ring:before{content:"\f70b"}.fa-building-un:before{content:"\e4d9"}.fa-dice-three:before{content:"\f527"}.fa-calendar-alt:before,.fa-calendar-days:before{content:"\f073"}.fa-anchor-circle-check:before{content:"\e4aa"}.fa-building-circle-arrow-right:before{content:"\e4d1"}.fa-volleyball-ball:before,.fa-volleyball:before{content:"\f45f"}.fa-arrows-up-to-line:before{content:"\e4c2"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-circle-minus:before,.fa-minus-circle:before{content:"\f056"}.fa-door-open:before{content:"\f52b"}.fa-right-from-bracket:before,.fa-sign-out-alt:before{content:"\f2f5"}.fa-atom:before{content:"\f5d2"}.fa-soap:before{content:"\e06e"}.fa-heart-music-camera-bolt:before,.fa-icons:before{content:"\f86d"}.fa-microphone-alt-slash:before,.fa-microphone-lines-slash:before{content:"\f539"}.fa-bridge-circle-check:before{content:"\e4c9"}.fa-pump-medical:before{content:"\e06a"}.fa-fingerprint:before{content:"\f577"}.fa-hand-point-right:before{content:"\f0a4"}.fa-magnifying-glass-location:before,.fa-search-location:before{content:"\f689"}.fa-forward-step:before,.fa-step-forward:before{content:"\f051"}.fa-face-smile-beam:before,.fa-smile-beam:before{content:"\f5b8"}.fa-flag-checkered:before{content:"\f11e"}.fa-football-ball:before,.fa-football:before{content:"\f44e"}.fa-school-circle-exclamation:before{content:"\e56c"}.fa-crop:before{content:"\f125"}.fa-angle-double-down:before,.fa-angles-down:before{content:"\f103"}.fa-users-rectangle:before{content:"\e594"}.fa-people-roof:before{content:"\e537"}.fa-people-line:before{content:"\e534"}.fa-beer-mug-empty:before,.fa-beer:before{content:"\f0fc"}.fa-diagram-predecessor:before{content:"\e477"}.fa-arrow-up-long:before,.fa-long-arrow-up:before{content:"\f176"}.fa-burn:before,.fa-fire-flame-simple:before{content:"\f46a"}.fa-male:before,.fa-person:before{content:"\f183"}.fa-laptop:before{content:"\f109"}.fa-file-csv:before{content:"\f6dd"}.fa-menorah:before{content:"\f676"}.fa-truck-plane:before{content:"\e58f"}.fa-record-vinyl:before{content:"\f8d9"}.fa-face-grin-stars:before,.fa-grin-stars:before{content:"\f587"}.fa-bong:before{content:"\f55c"}.fa-pastafarianism:before,.fa-spaghetti-monster-flying:before{content:"\f67b"}.fa-arrow-down-up-across-line:before{content:"\e4af"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f2e5"}.fa-jar-wheat:before{content:"\e517"}.fa-envelopes-bulk:before,.fa-mail-bulk:before{content:"\f674"}.fa-file-circle-exclamation:before{content:"\e4eb"}.fa-circle-h:before,.fa-hospital-symbol:before{content:"\f47e"}.fa-pager:before{content:"\f815"}.fa-address-book:before,.fa-contact-book:before{content:"\f2b9"}.fa-strikethrough:before{content:"\f0cc"}.fa-k:before{content:"\4b"}.fa-landmark-flag:before{content:"\e51c"}.fa-pencil-alt:before,.fa-pencil:before{content:"\f303"}.fa-backward:before{content:"\f04a"}.fa-caret-right:before{content:"\f0da"}.fa-comments:before{content:"\f086"}.fa-file-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-code-pull-request:before{content:"\e13c"}.fa-clipboard-list:before{content:"\f46d"}.fa-truck-loading:before,.fa-truck-ramp-box:before{content:"\f4de"}.fa-user-check:before{content:"\f4fc"}.fa-vial-virus:before{content:"\e597"}.fa-sheet-plastic:before{content:"\e571"}.fa-blog:before{content:"\f781"}.fa-user-ninja:before{content:"\f504"}.fa-person-arrow-up-from-line:before{content:"\e539"}.fa-scroll-torah:before,.fa-torah:before{content:"\f6a0"}.fa-broom-ball:before,.fa-quidditch-broom-ball:before,.fa-quidditch:before{content:"\f458"}.fa-toggle-off:before{content:"\f204"}.fa-archive:before,.fa-box-archive:before{content:"\f187"}.fa-person-drowning:before{content:"\e545"}.fa-arrow-down-9-1:before,.fa-sort-numeric-desc:before,.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-face-grin-tongue-squint:before,.fa-grin-tongue-squint:before{content:"\f58a"}.fa-spray-can:before{content:"\f5bd"}.fa-truck-monster:before{content:"\f63b"}.fa-w:before{content:"\57"}.fa-earth-africa:before,.fa-globe-africa:before{content:"\f57c"}.fa-rainbow:before{content:"\f75b"}.fa-circle-notch:before{content:"\f1ce"}.fa-tablet-alt:before,.fa-tablet-screen-button:before{content:"\f3fa"}.fa-paw:before{content:"\f1b0"}.fa-cloud:before{content:"\f0c2"}.fa-trowel-bricks:before{content:"\e58a"}.fa-face-flushed:before,.fa-flushed:before{content:"\f579"}.fa-hospital-user:before{content:"\f80d"}.fa-tent-arrow-left-right:before{content:"\e57f"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-binoculars:before{content:"\f1e5"}.fa-microphone-slash:before{content:"\f131"}.fa-box-tissue:before{content:"\e05b"}.fa-motorcycle:before{content:"\f21c"}.fa-bell-concierge:before,.fa-concierge-bell:before{content:"\f562"}.fa-pen-ruler:before,.fa-pencil-ruler:before{content:"\f5ae"}.fa-people-arrows-left-right:before,.fa-people-arrows:before{content:"\e068"}.fa-mars-and-venus-burst:before{content:"\e523"}.fa-caret-square-right:before,.fa-square-caret-right:before{content:"\f152"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-sun-plant-wilt:before{content:"\e57a"}.fa-toilets-portable:before{content:"\e584"}.fa-hockey-puck:before{content:"\f453"}.fa-table:before{content:"\f0ce"}.fa-magnifying-glass-arrow-right:before{content:"\e521"}.fa-digital-tachograph:before,.fa-tachograph-digital:before{content:"\f566"}.fa-users-slash:before{content:"\e073"}.fa-clover:before{content:"\e139"}.fa-mail-reply:before,.fa-reply:before{content:"\f3e5"}.fa-star-and-crescent:before{content:"\f699"}.fa-house-fire:before{content:"\e50c"}.fa-minus-square:before,.fa-square-minus:before{content:"\f146"}.fa-helicopter:before{content:"\f533"}.fa-compass:before{content:"\f14e"}.fa-caret-square-down:before,.fa-square-caret-down:before{content:"\f150"}.fa-file-circle-question:before{content:"\e4ef"}.fa-laptop-code:before{content:"\f5fc"}.fa-swatchbook:before{content:"\f5c3"}.fa-prescription-bottle:before{content:"\f485"}.fa-bars:before,.fa-navicon:before{content:"\f0c9"}.fa-people-group:before{content:"\e533"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-heart-broken:before,.fa-heart-crack:before{content:"\f7a9"}.fa-external-link-square-alt:before,.fa-square-up-right:before{content:"\f360"}.fa-face-kiss-beam:before,.fa-kiss-beam:before{content:"\f597"}.fa-film:before{content:"\f008"}.fa-ruler-horizontal:before{content:"\f547"}.fa-people-robbery:before{content:"\e536"}.fa-lightbulb:before{content:"\f0eb"}.fa-caret-left:before{content:"\f0d9"}.fa-circle-exclamation:before,.fa-exclamation-circle:before{content:"\f06a"}.fa-school-circle-xmark:before{content:"\e56d"}.fa-arrow-right-from-bracket:before,.fa-sign-out:before{content:"\f08b"}.fa-chevron-circle-down:before,.fa-circle-chevron-down:before{content:"\f13a"}.fa-unlock-alt:before,.fa-unlock-keyhole:before{content:"\f13e"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-headphones-alt:before,.fa-headphones-simple:before{content:"\f58f"}.fa-sitemap:before{content:"\f0e8"}.fa-circle-dollar-to-slot:before,.fa-donate:before{content:"\f4b9"}.fa-memory:before{content:"\f538"}.fa-road-spikes:before{content:"\e568"}.fa-fire-burner:before{content:"\e4f1"}.fa-flag:before{content:"\f024"}.fa-hanukiah:before{content:"\f6e6"}.fa-feather:before{content:"\f52d"}.fa-volume-down:before,.fa-volume-low:before{content:"\f027"}.fa-comment-slash:before{content:"\f4b3"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-compress:before{content:"\f066"}.fa-wheat-alt:before,.fa-wheat-awn:before{content:"\e2cd"}.fa-ankh:before{content:"\f644"}.fa-hands-holding-child:before{content:"\e4fa"}.fa-asterisk:before{content:"\2a"}.fa-check-square:before,.fa-square-check:before{content:"\f14a"}.fa-peseta-sign:before{content:"\e221"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-ghost:before{content:"\f6e2"}.fa-list-squares:before,.fa-list:before{content:"\f03a"}.fa-phone-square-alt:before,.fa-square-phone-flip:before{content:"\f87b"}.fa-cart-plus:before{content:"\f217"}.fa-gamepad:before{content:"\f11b"}.fa-circle-dot:before,.fa-dot-circle:before{content:"\f192"}.fa-dizzy:before,.fa-face-dizzy:before{content:"\f567"}.fa-egg:before{content:"\f7fb"}.fa-house-medical-circle-xmark:before{content:"\e513"}.fa-campground:before{content:"\f6bb"}.fa-folder-plus:before{content:"\f65e"}.fa-futbol-ball:before,.fa-futbol:before,.fa-soccer-ball:before{content:"\f1e3"}.fa-paint-brush:before,.fa-paintbrush:before{content:"\f1fc"}.fa-lock:before{content:"\f023"}.fa-gas-pump:before{content:"\f52f"}.fa-hot-tub-person:before,.fa-hot-tub:before{content:"\f593"}.fa-map-location:before,.fa-map-marked:before{content:"\f59f"}.fa-house-flood-water:before{content:"\e50e"}.fa-tree:before{content:"\f1bb"}.fa-bridge-lock:before{content:"\e4cc"}.fa-sack-dollar:before{content:"\f81d"}.fa-edit:before,.fa-pen-to-square:before{content:"\f044"}.fa-car-side:before{content:"\f5e4"}.fa-share-alt:before,.fa-share-nodes:before{content:"\f1e0"}.fa-heart-circle-minus:before{content:"\e4ff"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-microscope:before{content:"\f610"}.fa-sink:before{content:"\e06d"}.fa-bag-shopping:before,.fa-shopping-bag:before{content:"\f290"}.fa-arrow-down-z-a:before,.fa-sort-alpha-desc:before,.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-mitten:before{content:"\f7b5"}.fa-person-rays:before{content:"\e54d"}.fa-users:before{content:"\f0c0"}.fa-eye-slash:before{content:"\f070"}.fa-flask-vial:before{content:"\e4f3"}.fa-hand-paper:before,.fa-hand:before{content:"\f256"}.fa-om:before{content:"\f679"}.fa-worm:before{content:"\e599"}.fa-house-circle-xmark:before{content:"\e50b"}.fa-plug:before{content:"\f1e6"}.fa-chevron-up:before{content:"\f077"}.fa-hand-spock:before{content:"\f259"}.fa-stopwatch:before{content:"\f2f2"}.fa-face-kiss:before,.fa-kiss:before{content:"\f596"}.fa-bridge-circle-xmark:before{content:"\e4cb"}.fa-face-grin-tongue:before,.fa-grin-tongue:before{content:"\f589"}.fa-chess-bishop:before{content:"\f43a"}.fa-face-grin-wink:before,.fa-grin-wink:before{content:"\f58c"}.fa-deaf:before,.fa-deafness:before,.fa-ear-deaf:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-road-circle-check:before{content:"\e564"}.fa-dice-five:before{content:"\f523"}.fa-rss-square:before,.fa-square-rss:before{content:"\f143"}.fa-land-mine-on:before{content:"\e51b"}.fa-i-cursor:before{content:"\f246"}.fa-stamp:before{content:"\f5bf"}.fa-stairs:before{content:"\e289"}.fa-i:before{content:"\49"}.fa-hryvnia-sign:before,.fa-hryvnia:before{content:"\f6f2"}.fa-pills:before{content:"\f484"}.fa-face-grin-wide:before,.fa-grin-alt:before{content:"\f581"}.fa-tooth:before{content:"\f5c9"}.fa-v:before{content:"\56"}.fa-bangladeshi-taka-sign:before{content:"\e2e6"}.fa-bicycle:before{content:"\f206"}.fa-rod-asclepius:before,.fa-rod-snake:before,.fa-staff-aesculapius:before,.fa-staff-snake:before{content:"\e579"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-ambulance:before,.fa-truck-medical:before{content:"\f0f9"}.fa-wheat-awn-circle-exclamation:before{content:"\e598"}.fa-snowman:before{content:"\f7d0"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-road-barrier:before{content:"\e562"}.fa-school:before{content:"\f549"}.fa-igloo:before{content:"\f7ae"}.fa-joint:before{content:"\f595"}.fa-angle-right:before{content:"\f105"}.fa-horse:before{content:"\f6f0"}.fa-q:before{content:"\51"}.fa-g:before{content:"\47"}.fa-notes-medical:before{content:"\f481"}.fa-temperature-2:before,.fa-temperature-half:before,.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-dong-sign:before{content:"\e169"}.fa-capsules:before{content:"\f46b"}.fa-poo-bolt:before,.fa-poo-storm:before{content:"\f75a"}.fa-face-frown-open:before,.fa-frown-open:before{content:"\f57a"}.fa-hand-point-up:before{content:"\f0a6"}.fa-money-bill:before{content:"\f0d6"}.fa-bookmark:before{content:"\f02e"}.fa-align-justify:before{content:"\f039"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-helmet-un:before{content:"\e503"}.fa-bullseye:before{content:"\f140"}.fa-bacon:before{content:"\f7e5"}.fa-hand-point-down:before{content:"\f0a7"}.fa-arrow-up-from-bracket:before{content:"\e09a"}.fa-folder-blank:before,.fa-folder:before{content:"\f07b"}.fa-file-medical-alt:before,.fa-file-waveform:before{content:"\f478"}.fa-radiation:before{content:"\f7b9"}.fa-chart-simple:before{content:"\e473"}.fa-mars-stroke:before{content:"\f229"}.fa-vial:before{content:"\f492"}.fa-dashboard:before,.fa-gauge-med:before,.fa-gauge:before,.fa-tachometer-alt-average:before{content:"\f624"}.fa-magic-wand-sparkles:before,.fa-wand-magic-sparkles:before{content:"\e2ca"}.fa-e:before{content:"\45"}.fa-pen-alt:before,.fa-pen-clip:before{content:"\f305"}.fa-bridge-circle-exclamation:before{content:"\e4ca"}.fa-user:before{content:"\f007"}.fa-school-circle-check:before{content:"\e56b"}.fa-dumpster:before{content:"\f793"}.fa-shuttle-van:before,.fa-van-shuttle:before{content:"\f5b6"}.fa-building-user:before{content:"\e4da"}.fa-caret-square-left:before,.fa-square-caret-left:before{content:"\f191"}.fa-highlighter:before{content:"\f591"}.fa-key:before{content:"\f084"}.fa-bullhorn:before{content:"\f0a1"}.fa-globe:before{content:"\f0ac"}.fa-synagogue:before{content:"\f69b"}.fa-person-half-dress:before{content:"\e548"}.fa-road-bridge:before{content:"\e563"}.fa-location-arrow:before{content:"\f124"}.fa-c:before{content:"\43"}.fa-tablet-button:before{content:"\f10a"}.fa-building-lock:before{content:"\e4d6"}.fa-pizza-slice:before{content:"\f818"}.fa-money-bill-wave:before{content:"\f53a"}.fa-area-chart:before,.fa-chart-area:before{content:"\f1fe"}.fa-house-flag:before{content:"\e50d"}.fa-person-circle-minus:before{content:"\e540"}.fa-ban:before,.fa-cancel:before{content:"\f05e"}.fa-camera-rotate:before{content:"\e0d8"}.fa-air-freshener:before,.fa-spray-can-sparkles:before{content:"\f5d0"}.fa-star:before{content:"\f005"}.fa-repeat:before{content:"\f363"}.fa-cross:before{content:"\f654"}.fa-box:before{content:"\f466"}.fa-venus-mars:before{content:"\f228"}.fa-arrow-pointer:before,.fa-mouse-pointer:before{content:"\f245"}.fa-expand-arrows-alt:before,.fa-maximize:before{content:"\f31e"}.fa-charging-station:before{content:"\f5e7"}.fa-shapes:before,.fa-triangle-circle-square:before{content:"\f61f"}.fa-random:before,.fa-shuffle:before{content:"\f074"}.fa-person-running:before,.fa-running:before{content:"\f70c"}.fa-mobile-retro:before{content:"\e527"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-spider:before{content:"\f717"}.fa-hands-bound:before{content:"\e4f9"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-plane-circle-exclamation:before{content:"\e556"}.fa-x-ray:before{content:"\f497"}.fa-spell-check:before{content:"\f891"}.fa-slash:before{content:"\f715"}.fa-computer-mouse:before,.fa-mouse:before{content:"\f8cc"}.fa-arrow-right-to-bracket:before,.fa-sign-in:before{content:"\f090"}.fa-shop-slash:before,.fa-store-alt-slash:before{content:"\e070"}.fa-server:before{content:"\f233"}.fa-virus-covid-slash:before{content:"\e4a9"}.fa-shop-lock:before{content:"\e4a5"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-blender-phone:before{content:"\f6b6"}.fa-building-wheat:before{content:"\e4db"}.fa-person-breastfeeding:before{content:"\e53a"}.fa-right-to-bracket:before,.fa-sign-in-alt:before{content:"\f2f6"}.fa-venus:before{content:"\f221"}.fa-passport:before{content:"\f5ab"}.fa-heart-pulse:before,.fa-heartbeat:before{content:"\f21e"}.fa-people-carry-box:before,.fa-people-carry:before{content:"\f4ce"}.fa-temperature-high:before{content:"\f769"}.fa-microchip:before{content:"\f2db"}.fa-crown:before{content:"\f521"}.fa-weight-hanging:before{content:"\f5cd"}.fa-xmarks-lines:before{content:"\e59a"}.fa-file-prescription:before{content:"\f572"}.fa-weight-scale:before,.fa-weight:before{content:"\f496"}.fa-user-friends:before,.fa-user-group:before{content:"\f500"}.fa-arrow-up-a-z:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-chess-knight:before{content:"\f441"}.fa-face-laugh-squint:before,.fa-laugh-squint:before{content:"\f59b"}.fa-wheelchair:before{content:"\f193"}.fa-arrow-circle-up:before,.fa-circle-arrow-up:before{content:"\f0aa"}.fa-toggle-on:before{content:"\f205"}.fa-person-walking:before,.fa-walking:before{content:"\f554"}.fa-l:before{content:"\4c"}.fa-fire:before{content:"\f06d"}.fa-bed-pulse:before,.fa-procedures:before{content:"\f487"}.fa-shuttle-space:before,.fa-space-shuttle:before{content:"\f197"}.fa-face-laugh:before,.fa-laugh:before{content:"\f599"}.fa-folder-open:before{content:"\f07c"}.fa-heart-circle-plus:before{content:"\e500"}.fa-code-fork:before{content:"\e13b"}.fa-city:before{content:"\f64f"}.fa-microphone-alt:before,.fa-microphone-lines:before{content:"\f3c9"}.fa-pepper-hot:before{content:"\f816"}.fa-unlock:before{content:"\f09c"}.fa-colon-sign:before{content:"\e140"}.fa-headset:before{content:"\f590"}.fa-store-slash:before{content:"\e071"}.fa-road-circle-xmark:before{content:"\e566"}.fa-user-minus:before{content:"\f503"}.fa-mars-stroke-up:before,.fa-mars-stroke-v:before{content:"\f22a"}.fa-champagne-glasses:before,.fa-glass-cheers:before{content:"\f79f"}.fa-clipboard:before{content:"\f328"}.fa-house-circle-exclamation:before{content:"\e50a"}.fa-file-arrow-up:before,.fa-file-upload:before{content:"\f574"}.fa-wifi-3:before,.fa-wifi-strong:before,.fa-wifi:before{content:"\f1eb"}.fa-bath:before,.fa-bathtub:before{content:"\f2cd"}.fa-underline:before{content:"\f0cd"}.fa-user-edit:before,.fa-user-pen:before{content:"\f4ff"}.fa-signature:before{content:"\f5b7"}.fa-stroopwafel:before{content:"\f551"}.fa-bold:before{content:"\f032"}.fa-anchor-lock:before{content:"\e4ad"}.fa-building-ngo:before{content:"\e4d7"}.fa-manat-sign:before{content:"\e1d5"}.fa-not-equal:before{content:"\f53e"}.fa-border-style:before,.fa-border-top-left:before{content:"\f853"}.fa-map-location-dot:before,.fa-map-marked-alt:before{content:"\f5a0"}.fa-jedi:before{content:"\f669"}.fa-poll:before,.fa-square-poll-vertical:before{content:"\f681"}.fa-mug-hot:before{content:"\f7b6"}.fa-battery-car:before,.fa-car-battery:before{content:"\f5df"}.fa-gift:before{content:"\f06b"}.fa-dice-two:before{content:"\f528"}.fa-chess-queen:before{content:"\f445"}.fa-glasses:before{content:"\f530"}.fa-chess-board:before{content:"\f43c"}.fa-building-circle-check:before{content:"\e4d2"}.fa-person-chalkboard:before{content:"\e53d"}.fa-mars-stroke-h:before,.fa-mars-stroke-right:before{content:"\f22b"}.fa-hand-back-fist:before,.fa-hand-rock:before{content:"\f255"}.fa-caret-square-up:before,.fa-square-caret-up:before{content:"\f151"}.fa-cloud-showers-water:before{content:"\e4e4"}.fa-bar-chart:before,.fa-chart-bar:before{content:"\f080"}.fa-hands-bubbles:before,.fa-hands-wash:before{content:"\e05e"}.fa-less-than-equal:before{content:"\f537"}.fa-train:before{content:"\f238"}.fa-eye-low-vision:before,.fa-low-vision:before{content:"\f2a8"}.fa-crow:before{content:"\f520"}.fa-sailboat:before{content:"\e445"}.fa-window-restore:before{content:"\f2d2"}.fa-plus-square:before,.fa-square-plus:before{content:"\f0fe"}.fa-torii-gate:before{content:"\f6a1"}.fa-frog:before{content:"\f52e"}.fa-bucket:before{content:"\e4cf"}.fa-image:before{content:"\f03e"}.fa-microphone:before{content:"\f130"}.fa-cow:before{content:"\f6c8"}.fa-caret-up:before{content:"\f0d8"}.fa-screwdriver:before{content:"\f54a"}.fa-folder-closed:before{content:"\e185"}.fa-house-tsunami:before{content:"\e515"}.fa-square-nfi:before{content:"\e576"}.fa-arrow-up-from-ground-water:before{content:"\e4b5"}.fa-glass-martini-alt:before,.fa-martini-glass:before{content:"\f57b"}.fa-rotate-back:before,.fa-rotate-backward:before,.fa-rotate-left:before,.fa-undo-alt:before{content:"\f2ea"}.fa-columns:before,.fa-table-columns:before{content:"\f0db"}.fa-lemon:before{content:"\f094"}.fa-head-side-mask:before{content:"\e063"}.fa-handshake:before{content:"\f2b5"}.fa-gem:before{content:"\f3a5"}.fa-dolly-box:before,.fa-dolly:before{content:"\f472"}.fa-smoking:before{content:"\f48d"}.fa-compress-arrows-alt:before,.fa-minimize:before{content:"\f78c"}.fa-monument:before{content:"\f5a6"}.fa-snowplow:before{content:"\f7d2"}.fa-angle-double-right:before,.fa-angles-right:before{content:"\f101"}.fa-cannabis:before{content:"\f55f"}.fa-circle-play:before,.fa-play-circle:before{content:"\f144"}.fa-tablets:before{content:"\f490"}.fa-ethernet:before{content:"\f796"}.fa-eur:before,.fa-euro-sign:before,.fa-euro:before{content:"\f153"}.fa-chair:before{content:"\f6c0"}.fa-check-circle:before,.fa-circle-check:before{content:"\f058"}.fa-circle-stop:before,.fa-stop-circle:before{content:"\f28d"}.fa-compass-drafting:before,.fa-drafting-compass:before{content:"\f568"}.fa-plate-wheat:before{content:"\e55a"}.fa-icicles:before{content:"\f7ad"}.fa-person-shelter:before{content:"\e54f"}.fa-neuter:before{content:"\f22c"}.fa-id-badge:before{content:"\f2c1"}.fa-marker:before{content:"\f5a1"}.fa-face-laugh-beam:before,.fa-laugh-beam:before{content:"\f59a"}.fa-helicopter-symbol:before{content:"\e502"}.fa-universal-access:before{content:"\f29a"}.fa-chevron-circle-up:before,.fa-circle-chevron-up:before{content:"\f139"}.fa-lari-sign:before{content:"\e1c8"}.fa-volcano:before{content:"\f770"}.fa-person-walking-dashed-line-arrow-right:before{content:"\e553"}.fa-gbp:before,.fa-pound-sign:before,.fa-sterling-sign:before{content:"\f154"}.fa-viruses:before{content:"\e076"}.fa-square-person-confined:before{content:"\e577"}.fa-user-tie:before{content:"\f508"}.fa-arrow-down-long:before,.fa-long-arrow-down:before{content:"\f175"}.fa-tent-arrow-down-to-line:before{content:"\e57e"}.fa-certificate:before{content:"\f0a3"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-suitcase:before{content:"\f0f2"}.fa-person-skating:before,.fa-skating:before{content:"\f7c5"}.fa-filter-circle-dollar:before,.fa-funnel-dollar:before{content:"\f662"}.fa-camera-retro:before{content:"\f083"}.fa-arrow-circle-down:before,.fa-circle-arrow-down:before{content:"\f0ab"}.fa-arrow-right-to-file:before,.fa-file-import:before{content:"\f56f"}.fa-external-link-square:before,.fa-square-arrow-up-right:before{content:"\f14c"}.fa-box-open:before{content:"\f49e"}.fa-scroll:before{content:"\f70e"}.fa-spa:before{content:"\f5bb"}.fa-location-pin-lock:before{content:"\e51f"}.fa-pause:before{content:"\f04c"}.fa-hill-avalanche:before{content:"\e507"}.fa-temperature-0:before,.fa-temperature-empty:before,.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-bomb:before{content:"\f1e2"}.fa-registered:before{content:"\f25d"}.fa-address-card:before,.fa-contact-card:before,.fa-vcard:before{content:"\f2bb"}.fa-balance-scale-right:before,.fa-scale-unbalanced-flip:before{content:"\f516"}.fa-subscript:before{content:"\f12c"}.fa-diamond-turn-right:before,.fa-directions:before{content:"\f5eb"}.fa-burst:before{content:"\e4dc"}.fa-house-laptop:before,.fa-laptop-house:before{content:"\e066"}.fa-face-tired:before,.fa-tired:before{content:"\f5c8"}.fa-money-bills:before{content:"\e1f3"}.fa-smog:before{content:"\f75f"}.fa-crutch:before{content:"\f7f7"}.fa-cloud-arrow-up:before,.fa-cloud-upload-alt:before,.fa-cloud-upload:before{content:"\f0ee"}.fa-palette:before{content:"\f53f"}.fa-arrows-turn-right:before{content:"\e4c0"}.fa-vest:before{content:"\e085"}.fa-ferry:before{content:"\e4ea"}.fa-arrows-down-to-people:before{content:"\e4b9"}.fa-seedling:before,.fa-sprout:before{content:"\f4d8"}.fa-arrows-alt-h:before,.fa-left-right:before{content:"\f337"}.fa-boxes-packing:before{content:"\e4c7"}.fa-arrow-circle-left:before,.fa-circle-arrow-left:before{content:"\f0a8"}.fa-group-arrows-rotate:before{content:"\e4f6"}.fa-bowl-food:before{content:"\e4c6"}.fa-candy-cane:before{content:"\f786"}.fa-arrow-down-wide-short:before,.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-cloud-bolt:before,.fa-thunderstorm:before{content:"\f76c"}.fa-remove-format:before,.fa-text-slash:before{content:"\f87d"}.fa-face-smile-wink:before,.fa-smile-wink:before{content:"\f4da"}.fa-file-word:before{content:"\f1c2"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-arrows-h:before,.fa-arrows-left-right:before{content:"\f07e"}.fa-house-lock:before{content:"\e510"}.fa-cloud-arrow-down:before,.fa-cloud-download-alt:before,.fa-cloud-download:before{content:"\f0ed"}.fa-children:before{content:"\e4e1"}.fa-blackboard:before,.fa-chalkboard:before{content:"\f51b"}.fa-user-alt-slash:before,.fa-user-large-slash:before{content:"\f4fa"}.fa-envelope-open:before{content:"\f2b6"}.fa-handshake-alt-slash:before,.fa-handshake-simple-slash:before{content:"\e05f"}.fa-mattress-pillow:before{content:"\e525"}.fa-guarani-sign:before{content:"\e19a"}.fa-arrows-rotate:before,.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-fire-extinguisher:before{content:"\f134"}.fa-cruzeiro-sign:before{content:"\e152"}.fa-greater-than-equal:before{content:"\f532"}.fa-shield-alt:before,.fa-shield-halved:before{content:"\f3ed"}.fa-atlas:before,.fa-book-atlas:before{content:"\f558"}.fa-virus:before{content:"\e074"}.fa-envelope-circle-check:before{content:"\e4e8"}.fa-layer-group:before{content:"\f5fd"}.fa-arrows-to-dot:before{content:"\e4be"}.fa-archway:before{content:"\f557"}.fa-heart-circle-check:before{content:"\e4fd"}.fa-house-chimney-crack:before,.fa-house-damage:before{content:"\f6f1"}.fa-file-archive:before,.fa-file-zipper:before{content:"\f1c6"}.fa-square:before{content:"\f0c8"}.fa-glass-martini:before,.fa-martini-glass-empty:before{content:"\f000"}.fa-couch:before{content:"\f4b8"}.fa-cedi-sign:before{content:"\e0df"}.fa-italic:before{content:"\f033"}.fa-table-cells-column-lock:before{content:"\e678"}.fa-church:before{content:"\f51d"}.fa-comments-dollar:before{content:"\f653"}.fa-democrat:before{content:"\f747"}.fa-z:before{content:"\5a"}.fa-person-skiing:before,.fa-skiing:before{content:"\f7c9"}.fa-road-lock:before{content:"\e567"}.fa-a:before{content:"\41"}.fa-temperature-arrow-down:before,.fa-temperature-down:before{content:"\e03f"}.fa-feather-alt:before,.fa-feather-pointed:before{content:"\f56b"}.fa-p:before{content:"\50"}.fa-snowflake:before{content:"\f2dc"}.fa-newspaper:before{content:"\f1ea"}.fa-ad:before,.fa-rectangle-ad:before{content:"\f641"}.fa-arrow-circle-right:before,.fa-circle-arrow-right:before{content:"\f0a9"}.fa-filter-circle-xmark:before{content:"\e17b"}.fa-locust:before{content:"\e520"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-list-1-2:before,.fa-list-numeric:before,.fa-list-ol:before{content:"\f0cb"}.fa-person-dress-burst:before{content:"\e544"}.fa-money-check-alt:before,.fa-money-check-dollar:before{content:"\f53d"}.fa-vector-square:before{content:"\f5cb"}.fa-bread-slice:before{content:"\f7ec"}.fa-language:before{content:"\f1ab"}.fa-face-kiss-wink-heart:before,.fa-kiss-wink-heart:before{content:"\f598"}.fa-filter:before{content:"\f0b0"}.fa-question:before{content:"\3f"}.fa-file-signature:before{content:"\f573"}.fa-arrows-alt:before,.fa-up-down-left-right:before{content:"\f0b2"}.fa-house-chimney-user:before{content:"\e065"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-puzzle-piece:before{content:"\f12e"}.fa-money-check:before{content:"\f53c"}.fa-star-half-alt:before,.fa-star-half-stroke:before{content:"\f5c0"}.fa-code:before{content:"\f121"}.fa-glass-whiskey:before,.fa-whiskey-glass:before{content:"\f7a0"}.fa-building-circle-exclamation:before{content:"\e4d3"}.fa-magnifying-glass-chart:before{content:"\e522"}.fa-arrow-up-right-from-square:before,.fa-external-link:before{content:"\f08e"}.fa-cubes-stacked:before{content:"\e4e6"}.fa-krw:before,.fa-won-sign:before,.fa-won:before{content:"\f159"}.fa-virus-covid:before{content:"\e4a8"}.fa-austral-sign:before{content:"\e0a9"}.fa-f:before{content:"\46"}.fa-leaf:before{content:"\f06c"}.fa-road:before{content:"\f018"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-person-circle-plus:before{content:"\e541"}.fa-chart-pie:before,.fa-pie-chart:before{content:"\f200"}.fa-bolt-lightning:before{content:"\e0b7"}.fa-sack-xmark:before{content:"\e56a"}.fa-file-excel:before{content:"\f1c3"}.fa-file-contract:before{content:"\f56c"}.fa-fish-fins:before{content:"\e4f2"}.fa-building-flag:before{content:"\e4d5"}.fa-face-grin-beam:before,.fa-grin-beam:before{content:"\f582"}.fa-object-ungroup:before{content:"\f248"}.fa-poop:before{content:"\f619"}.fa-location-pin:before,.fa-map-marker:before{content:"\f041"}.fa-kaaba:before{content:"\f66b"}.fa-toilet-paper:before{content:"\f71e"}.fa-hard-hat:before,.fa-hat-hard:before,.fa-helmet-safety:before{content:"\f807"}.fa-eject:before{content:"\f052"}.fa-arrow-alt-circle-right:before,.fa-circle-right:before{content:"\f35a"}.fa-plane-circle-check:before{content:"\e555"}.fa-face-rolling-eyes:before,.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-object-group:before{content:"\f247"}.fa-chart-line:before,.fa-line-chart:before{content:"\f201"}.fa-mask-ventilator:before{content:"\e524"}.fa-arrow-right:before{content:"\f061"}.fa-map-signs:before,.fa-signs-post:before{content:"\f277"}.fa-cash-register:before{content:"\f788"}.fa-person-circle-question:before{content:"\e542"}.fa-h:before{content:"\48"}.fa-tarp:before{content:"\e57b"}.fa-screwdriver-wrench:before,.fa-tools:before{content:"\f7d9"}.fa-arrows-to-eye:before{content:"\e4bf"}.fa-plug-circle-bolt:before{content:"\e55b"}.fa-heart:before{content:"\f004"}.fa-mars-and-venus:before{content:"\f224"}.fa-home-user:before,.fa-house-user:before{content:"\e1b0"}.fa-dumpster-fire:before{content:"\f794"}.fa-house-crack:before{content:"\e3b1"}.fa-cocktail:before,.fa-martini-glass-citrus:before{content:"\f561"}.fa-face-surprise:before,.fa-surprise:before{content:"\f5c2"}.fa-bottle-water:before{content:"\e4c5"}.fa-circle-pause:before,.fa-pause-circle:before{content:"\f28b"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-apple-alt:before,.fa-apple-whole:before{content:"\f5d1"}.fa-kitchen-set:before{content:"\e51a"}.fa-r:before{content:"\52"}.fa-temperature-1:before,.fa-temperature-quarter:before,.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-cube:before{content:"\f1b2"}.fa-bitcoin-sign:before{content:"\e0b4"}.fa-shield-dog:before{content:"\e573"}.fa-solar-panel:before{content:"\f5ba"}.fa-lock-open:before{content:"\f3c1"}.fa-elevator:before{content:"\e16d"}.fa-money-bill-transfer:before{content:"\e528"}.fa-money-bill-trend-up:before{content:"\e529"}.fa-house-flood-water-circle-arrow-right:before{content:"\e50f"}.fa-poll-h:before,.fa-square-poll-horizontal:before{content:"\f682"}.fa-circle:before{content:"\f111"}.fa-backward-fast:before,.fa-fast-backward:before{content:"\f049"}.fa-recycle:before{content:"\f1b8"}.fa-user-astronaut:before{content:"\f4fb"}.fa-plane-slash:before{content:"\e069"}.fa-trademark:before{content:"\f25c"}.fa-basketball-ball:before,.fa-basketball:before{content:"\f434"}.fa-satellite-dish:before{content:"\f7c0"}.fa-arrow-alt-circle-up:before,.fa-circle-up:before{content:"\f35b"}.fa-mobile-alt:before,.fa-mobile-screen-button:before{content:"\f3cd"}.fa-volume-high:before,.fa-volume-up:before{content:"\f028"}.fa-users-rays:before{content:"\e593"}.fa-wallet:before{content:"\f555"}.fa-clipboard-check:before{content:"\f46c"}.fa-file-audio:before{content:"\f1c7"}.fa-burger:before,.fa-hamburger:before{content:"\f805"}.fa-wrench:before{content:"\f0ad"}.fa-bugs:before{content:"\e4d0"}.fa-rupee-sign:before,.fa-rupee:before{content:"\f156"}.fa-file-image:before{content:"\f1c5"}.fa-circle-question:before,.fa-question-circle:before{content:"\f059"}.fa-plane-departure:before{content:"\f5b0"}.fa-handshake-slash:before{content:"\e060"}.fa-book-bookmark:before{content:"\e0bb"}.fa-code-branch:before{content:"\f126"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-bridge:before{content:"\e4c8"}.fa-phone-alt:before,.fa-phone-flip:before{content:"\f879"}.fa-truck-front:before{content:"\e2b7"}.fa-cat:before{content:"\f6be"}.fa-anchor-circle-exclamation:before{content:"\e4ab"}.fa-truck-field:before{content:"\e58d"}.fa-route:before{content:"\f4d7"}.fa-clipboard-question:before{content:"\e4e3"}.fa-panorama:before{content:"\e209"}.fa-comment-medical:before{content:"\f7f5"}.fa-teeth-open:before{content:"\f62f"}.fa-file-circle-minus:before{content:"\e4ed"}.fa-tags:before{content:"\f02c"}.fa-wine-glass:before{content:"\f4e3"}.fa-fast-forward:before,.fa-forward-fast:before{content:"\f050"}.fa-face-meh-blank:before,.fa-meh-blank:before{content:"\f5a4"}.fa-parking:before,.fa-square-parking:before{content:"\f540"}.fa-house-signal:before{content:"\e012"}.fa-bars-progress:before,.fa-tasks-alt:before{content:"\f828"}.fa-faucet-drip:before{content:"\e006"}.fa-cart-flatbed:before,.fa-dolly-flatbed:before{content:"\f474"}.fa-ban-smoking:before,.fa-smoking-ban:before{content:"\f54d"}.fa-terminal:before{content:"\f120"}.fa-mobile-button:before{content:"\f10b"}.fa-house-medical-flag:before{content:"\e514"}.fa-basket-shopping:before,.fa-shopping-basket:before{content:"\f291"}.fa-tape:before{content:"\f4db"}.fa-bus-alt:before,.fa-bus-simple:before{content:"\f55e"}.fa-eye:before{content:"\f06e"}.fa-face-sad-cry:before,.fa-sad-cry:before{content:"\f5b3"}.fa-audio-description:before{content:"\f29e"}.fa-person-military-to-person:before{content:"\e54c"}.fa-file-shield:before{content:"\e4f0"}.fa-user-slash:before{content:"\f506"}.fa-pen:before{content:"\f304"}.fa-tower-observation:before{content:"\e586"}.fa-file-code:before{content:"\f1c9"}.fa-signal-5:before,.fa-signal-perfect:before,.fa-signal:before{content:"\f012"}.fa-bus:before{content:"\f207"}.fa-heart-circle-xmark:before{content:"\e501"}.fa-home-lg:before,.fa-house-chimney:before{content:"\e3af"}.fa-window-maximize:before{content:"\f2d0"}.fa-face-frown:before,.fa-frown:before{content:"\f119"}.fa-prescription:before{content:"\f5b1"}.fa-shop:before,.fa-store-alt:before{content:"\f54f"}.fa-floppy-disk:before,.fa-save:before{content:"\f0c7"}.fa-vihara:before{content:"\f6a7"}.fa-balance-scale-left:before,.fa-scale-unbalanced:before{content:"\f515"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-comment-dots:before,.fa-commenting:before{content:"\f4ad"}.fa-plant-wilt:before{content:"\e5aa"}.fa-diamond:before{content:"\f219"}.fa-face-grin-squint:before,.fa-grin-squint:before{content:"\f585"}.fa-hand-holding-dollar:before,.fa-hand-holding-usd:before{content:"\f4c0"}.fa-bacterium:before{content:"\e05a"}.fa-hand-pointer:before{content:"\f25a"}.fa-drum-steelpan:before{content:"\f56a"}.fa-hand-scissors:before{content:"\f257"}.fa-hands-praying:before,.fa-praying-hands:before{content:"\f684"}.fa-arrow-right-rotate:before,.fa-arrow-rotate-forward:before,.fa-arrow-rotate-right:before,.fa-redo:before{content:"\f01e"}.fa-biohazard:before{content:"\f780"}.fa-location-crosshairs:before,.fa-location:before{content:"\f601"}.fa-mars-double:before{content:"\f227"}.fa-child-dress:before{content:"\e59c"}.fa-users-between-lines:before{content:"\e591"}.fa-lungs-virus:before{content:"\e067"}.fa-face-grin-tears:before,.fa-grin-tears:before{content:"\f588"}.fa-phone:before{content:"\f095"}.fa-calendar-times:before,.fa-calendar-xmark:before{content:"\f273"}.fa-child-reaching:before{content:"\e59d"}.fa-head-side-virus:before{content:"\e064"}.fa-user-cog:before,.fa-user-gear:before{content:"\f4fe"}.fa-arrow-up-1-9:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-door-closed:before{content:"\f52a"}.fa-shield-virus:before{content:"\e06c"}.fa-dice-six:before{content:"\f526"}.fa-mosquito-net:before{content:"\e52c"}.fa-bridge-water:before{content:"\e4ce"}.fa-person-booth:before{content:"\f756"}.fa-text-width:before{content:"\f035"}.fa-hat-wizard:before{content:"\f6e8"}.fa-pen-fancy:before{content:"\f5ac"}.fa-digging:before,.fa-person-digging:before{content:"\f85e"}.fa-trash:before{content:"\f1f8"}.fa-gauge-simple-med:before,.fa-gauge-simple:before,.fa-tachometer-average:before{content:"\f629"}.fa-book-medical:before{content:"\f7e6"}.fa-poo:before{content:"\f2fe"}.fa-quote-right-alt:before,.fa-quote-right:before{content:"\f10e"}.fa-shirt:before,.fa-t-shirt:before,.fa-tshirt:before{content:"\f553"}.fa-cubes:before{content:"\f1b3"}.fa-divide:before{content:"\f529"}.fa-tenge-sign:before,.fa-tenge:before{content:"\f7d7"}.fa-headphones:before{content:"\f025"}.fa-hands-holding:before{content:"\f4c2"}.fa-hands-clapping:before{content:"\e1a8"}.fa-republican:before{content:"\f75e"}.fa-arrow-left:before{content:"\f060"}.fa-person-circle-xmark:before{content:"\e543"}.fa-ruler:before{content:"\f545"}.fa-align-left:before{content:"\f036"}.fa-dice-d6:before{content:"\f6d1"}.fa-restroom:before{content:"\f7bd"}.fa-j:before{content:"\4a"}.fa-users-viewfinder:before{content:"\e595"}.fa-file-video:before{content:"\f1c8"}.fa-external-link-alt:before,.fa-up-right-from-square:before{content:"\f35d"}.fa-table-cells:before,.fa-th:before{content:"\f00a"}.fa-file-pdf:before{content:"\f1c1"}.fa-bible:before,.fa-book-bible:before{content:"\f647"}.fa-o:before{content:"\4f"}.fa-medkit:before,.fa-suitcase-medical:before{content:"\f0fa"}.fa-user-secret:before{content:"\f21b"}.fa-otter:before{content:"\f700"}.fa-female:before,.fa-person-dress:before{content:"\f182"}.fa-comment-dollar:before{content:"\f651"}.fa-briefcase-clock:before,.fa-business-time:before{content:"\f64a"}.fa-table-cells-large:before,.fa-th-large:before{content:"\f009"}.fa-book-tanakh:before,.fa-tanakh:before{content:"\f827"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-clipboard-user:before{content:"\f7f3"}.fa-child:before{content:"\f1ae"}.fa-lira-sign:before{content:"\f195"}.fa-satellite:before{content:"\f7bf"}.fa-plane-lock:before{content:"\e558"}.fa-tag:before{content:"\f02b"}.fa-comment:before{content:"\f075"}.fa-birthday-cake:before,.fa-cake-candles:before,.fa-cake:before{content:"\f1fd"}.fa-envelope:before{content:"\f0e0"}.fa-angle-double-up:before,.fa-angles-up:before{content:"\f102"}.fa-paperclip:before{content:"\f0c6"}.fa-arrow-right-to-city:before{content:"\e4b3"}.fa-ribbon:before{content:"\f4d6"}.fa-lungs:before{content:"\f604"}.fa-arrow-up-9-1:before,.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-litecoin-sign:before{content:"\e1d3"}.fa-border-none:before{content:"\f850"}.fa-circle-nodes:before{content:"\e4e2"}.fa-parachute-box:before{content:"\f4cd"}.fa-indent:before{content:"\f03c"}.fa-truck-field-un:before{content:"\e58e"}.fa-hourglass-empty:before,.fa-hourglass:before{content:"\f254"}.fa-mountain:before{content:"\f6fc"}.fa-user-doctor:before,.fa-user-md:before{content:"\f0f0"}.fa-circle-info:before,.fa-info-circle:before{content:"\f05a"}.fa-cloud-meatball:before{content:"\f73b"}.fa-camera-alt:before,.fa-camera:before{content:"\f030"}.fa-square-virus:before{content:"\e578"}.fa-meteor:before{content:"\f753"}.fa-car-on:before{content:"\e4dd"}.fa-sleigh:before{content:"\f7cc"}.fa-arrow-down-1-9:before,.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-hand-holding-droplet:before,.fa-hand-holding-water:before{content:"\f4c1"}.fa-water:before{content:"\f773"}.fa-calendar-check:before{content:"\f274"}.fa-braille:before{content:"\f2a1"}.fa-prescription-bottle-alt:before,.fa-prescription-bottle-medical:before{content:"\f486"}.fa-landmark:before{content:"\f66f"}.fa-truck:before{content:"\f0d1"}.fa-crosshairs:before{content:"\f05b"}.fa-person-cane:before{content:"\e53c"}.fa-tent:before{content:"\e57d"}.fa-vest-patches:before{content:"\e086"}.fa-check-double:before{content:"\f560"}.fa-arrow-down-a-z:before,.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-money-bill-wheat:before{content:"\e52a"}.fa-cookie:before{content:"\f563"}.fa-arrow-left-rotate:before,.fa-arrow-rotate-back:before,.fa-arrow-rotate-backward:before,.fa-arrow-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-hard-drive:before,.fa-hdd:before{content:"\f0a0"}.fa-face-grin-squint-tears:before,.fa-grin-squint-tears:before{content:"\f586"}.fa-dumbbell:before{content:"\f44b"}.fa-list-alt:before,.fa-rectangle-list:before{content:"\f022"}.fa-tarp-droplet:before{content:"\e57c"}.fa-house-medical-circle-check:before{content:"\e511"}.fa-person-skiing-nordic:before,.fa-skiing-nordic:before{content:"\f7ca"}.fa-calendar-plus:before{content:"\f271"}.fa-plane-arrival:before{content:"\f5af"}.fa-arrow-alt-circle-left:before,.fa-circle-left:before{content:"\f359"}.fa-subway:before,.fa-train-subway:before{content:"\f239"}.fa-chart-gantt:before{content:"\e0e4"}.fa-indian-rupee-sign:before,.fa-indian-rupee:before,.fa-inr:before{content:"\e1bc"}.fa-crop-alt:before,.fa-crop-simple:before{content:"\f565"}.fa-money-bill-1:before,.fa-money-bill-alt:before{content:"\f3d1"}.fa-left-long:before,.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-dna:before{content:"\f471"}.fa-virus-slash:before{content:"\e075"}.fa-minus:before,.fa-subtract:before{content:"\f068"}.fa-chess:before{content:"\f439"}.fa-arrow-left-long:before,.fa-long-arrow-left:before{content:"\f177"}.fa-plug-circle-check:before{content:"\e55c"}.fa-street-view:before{content:"\f21d"}.fa-franc-sign:before{content:"\e18f"}.fa-volume-off:before{content:"\f026"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before,.fa-hands-american-sign-language-interpreting:before,.fa-hands-asl-interpreting:before{content:"\f2a3"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-droplet-slash:before,.fa-tint-slash:before{content:"\f5c7"}.fa-mosque:before{content:"\f678"}.fa-mosquito:before{content:"\e52b"}.fa-star-of-david:before{content:"\f69a"}.fa-person-military-rifle:before{content:"\e54b"}.fa-cart-shopping:before,.fa-shopping-cart:before{content:"\f07a"}.fa-vials:before{content:"\f493"}.fa-plug-circle-plus:before{content:"\e55f"}.fa-place-of-worship:before{content:"\f67f"}.fa-grip-vertical:before{content:"\f58e"}.fa-arrow-turn-up:before,.fa-level-up:before{content:"\f148"}.fa-u:before{content:"\55"}.fa-square-root-alt:before,.fa-square-root-variable:before{content:"\f698"}.fa-clock-four:before,.fa-clock:before{content:"\f017"}.fa-backward-step:before,.fa-step-backward:before{content:"\f048"}.fa-pallet:before{content:"\f482"}.fa-faucet:before{content:"\e005"}.fa-baseball-bat-ball:before{content:"\f432"}.fa-s:before{content:"\53"}.fa-timeline:before{content:"\e29c"}.fa-keyboard:before{content:"\f11c"}.fa-caret-down:before{content:"\f0d7"}.fa-clinic-medical:before,.fa-house-chimney-medical:before{content:"\f7f2"}.fa-temperature-3:before,.fa-temperature-three-quarters:before,.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-mobile-android-alt:before,.fa-mobile-screen:before{content:"\f3cf"}.fa-plane-up:before{content:"\e22d"}.fa-piggy-bank:before{content:"\f4d3"}.fa-battery-3:before,.fa-battery-half:before{content:"\f242"}.fa-mountain-city:before{content:"\e52e"}.fa-coins:before{content:"\f51e"}.fa-khanda:before{content:"\f66d"}.fa-sliders-h:before,.fa-sliders:before{content:"\f1de"}.fa-folder-tree:before{content:"\f802"}.fa-network-wired:before{content:"\f6ff"}.fa-map-pin:before{content:"\f276"}.fa-hamsa:before{content:"\f665"}.fa-cent-sign:before{content:"\e3f5"}.fa-flask:before{content:"\f0c3"}.fa-person-pregnant:before{content:"\e31e"}.fa-wand-sparkles:before{content:"\f72b"}.fa-ellipsis-v:before,.fa-ellipsis-vertical:before{content:"\f142"}.fa-ticket:before{content:"\f145"}.fa-power-off:before{content:"\f011"}.fa-long-arrow-alt-right:before,.fa-right-long:before{content:"\f30b"}.fa-flag-usa:before{content:"\f74d"}.fa-laptop-file:before{content:"\e51d"}.fa-teletype:before,.fa-tty:before{content:"\f1e4"}.fa-diagram-next:before{content:"\e476"}.fa-person-rifle:before{content:"\e54e"}.fa-house-medical-circle-exclamation:before{content:"\e512"}.fa-closed-captioning:before{content:"\f20a"}.fa-hiking:before,.fa-person-hiking:before{content:"\f6ec"}.fa-venus-double:before{content:"\f226"}.fa-images:before{content:"\f302"}.fa-calculator:before{content:"\f1ec"}.fa-people-pulling:before{content:"\e535"}.fa-n:before{content:"\4e"}.fa-cable-car:before,.fa-tram:before{content:"\f7da"}.fa-cloud-rain:before{content:"\f73d"}.fa-building-circle-xmark:before{content:"\e4d4"}.fa-ship:before{content:"\f21a"}.fa-arrows-down-to-line:before{content:"\e4b8"}.fa-download:before{content:"\f019"}.fa-face-grin:before,.fa-grin:before{content:"\f580"}.fa-backspace:before,.fa-delete-left:before{content:"\f55a"}.fa-eye-dropper-empty:before,.fa-eye-dropper:before,.fa-eyedropper:before{content:"\f1fb"}.fa-file-circle-check:before{content:"\e5a0"}.fa-forward:before{content:"\f04e"}.fa-mobile-android:before,.fa-mobile-phone:before,.fa-mobile:before{content:"\f3ce"}.fa-face-meh:before,.fa-meh:before{content:"\f11a"}.fa-align-center:before{content:"\f037"}.fa-book-dead:before,.fa-book-skull:before{content:"\f6b7"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-heart-circle-exclamation:before{content:"\e4fe"}.fa-home-alt:before,.fa-home-lg-alt:before,.fa-home:before,.fa-house:before{content:"\f015"}.fa-calendar-week:before{content:"\f784"}.fa-laptop-medical:before{content:"\f812"}.fa-b:before{content:"\42"}.fa-file-medical:before{content:"\f477"}.fa-dice-one:before{content:"\f525"}.fa-kiwi-bird:before{content:"\f535"}.fa-arrow-right-arrow-left:before,.fa-exchange:before{content:"\f0ec"}.fa-redo-alt:before,.fa-rotate-forward:before,.fa-rotate-right:before{content:"\f2f9"}.fa-cutlery:before,.fa-utensils:before{content:"\f2e7"}.fa-arrow-up-wide-short:before,.fa-sort-amount-up:before{content:"\f161"}.fa-mill-sign:before{content:"\e1ed"}.fa-bowl-rice:before{content:"\e2eb"}.fa-skull:before{content:"\f54c"}.fa-broadcast-tower:before,.fa-tower-broadcast:before{content:"\f519"}.fa-truck-pickup:before{content:"\f63c"}.fa-long-arrow-alt-up:before,.fa-up-long:before{content:"\f30c"}.fa-stop:before{content:"\f04d"}.fa-code-merge:before{content:"\f387"}.fa-upload:before{content:"\f093"}.fa-hurricane:before{content:"\f751"}.fa-mound:before{content:"\e52d"}.fa-toilet-portable:before{content:"\e583"}.fa-compact-disc:before{content:"\f51f"}.fa-file-arrow-down:before,.fa-file-download:before{content:"\f56d"}.fa-caravan:before{content:"\f8ff"}.fa-shield-cat:before{content:"\e572"}.fa-bolt:before,.fa-zap:before{content:"\f0e7"}.fa-glass-water:before{content:"\e4f4"}.fa-oil-well:before{content:"\e532"}.fa-vault:before{content:"\e2c5"}.fa-mars:before{content:"\f222"}.fa-toilet:before{content:"\f7d8"}.fa-plane-circle-xmark:before{content:"\e557"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen-sign:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble-sign:before,.fa-ruble:before{content:"\f158"}.fa-sun:before{content:"\f185"}.fa-guitar:before{content:"\f7a6"}.fa-face-laugh-wink:before,.fa-laugh-wink:before{content:"\f59c"}.fa-horse-head:before{content:"\f7ab"}.fa-bore-hole:before{content:"\e4c3"}.fa-industry:before{content:"\f275"}.fa-arrow-alt-circle-down:before,.fa-circle-down:before{content:"\f358"}.fa-arrows-turn-to-dots:before{content:"\e4c1"}.fa-florin-sign:before{content:"\e184"}.fa-arrow-down-short-wide:before,.fa-sort-amount-desc:before,.fa-sort-amount-down-alt:before{content:"\f884"}.fa-less-than:before{content:"\3c"}.fa-angle-down:before{content:"\f107"}.fa-car-tunnel:before{content:"\e4de"}.fa-head-side-cough:before{content:"\e061"}.fa-grip-lines:before{content:"\f7a4"}.fa-thumbs-down:before{content:"\f165"}.fa-user-lock:before{content:"\f502"}.fa-arrow-right-long:before,.fa-long-arrow-right:before{content:"\f178"}.fa-anchor-circle-xmark:before{content:"\e4ac"}.fa-ellipsis-h:before,.fa-ellipsis:before{content:"\f141"}.fa-chess-pawn:before{content:"\f443"}.fa-first-aid:before,.fa-kit-medical:before{content:"\f479"}.fa-person-through-window:before{content:"\e5a9"}.fa-toolbox:before{content:"\f552"}.fa-hands-holding-circle:before{content:"\e4fb"}.fa-bug:before{content:"\f188"}.fa-credit-card-alt:before,.fa-credit-card:before{content:"\f09d"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-hand-holding-hand:before{content:"\e4f7"}.fa-book-open-reader:before,.fa-book-reader:before{content:"\f5da"}.fa-mountain-sun:before{content:"\e52f"}.fa-arrows-left-right-to-line:before{content:"\e4ba"}.fa-dice-d20:before{content:"\f6cf"}.fa-truck-droplet:before{content:"\e58c"}.fa-file-circle-xmark:before{content:"\e5a1"}.fa-temperature-arrow-up:before,.fa-temperature-up:before{content:"\e040"}.fa-medal:before{content:"\f5a2"}.fa-bed:before{content:"\f236"}.fa-h-square:before,.fa-square-h:before{content:"\f0fd"}.fa-podcast:before{content:"\f2ce"}.fa-temperature-4:before,.fa-temperature-full:before,.fa-thermometer-4:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-bell:before{content:"\f0f3"}.fa-superscript:before{content:"\f12b"}.fa-plug-circle-xmark:before{content:"\e560"}.fa-star-of-life:before{content:"\f621"}.fa-phone-slash:before{content:"\f3dd"}.fa-paint-roller:before{content:"\f5aa"}.fa-hands-helping:before,.fa-handshake-angle:before{content:"\f4c4"}.fa-location-dot:before,.fa-map-marker-alt:before{content:"\f3c5"}.fa-file:before{content:"\f15b"}.fa-greater-than:before{content:"\3e"}.fa-person-swimming:before,.fa-swimmer:before{content:"\f5c4"}.fa-arrow-down:before{content:"\f063"}.fa-droplet:before,.fa-tint:before{content:"\f043"}.fa-eraser:before{content:"\f12d"}.fa-earth-america:before,.fa-earth-americas:before,.fa-earth:before,.fa-globe-americas:before{content:"\f57d"}.fa-person-burst:before{content:"\e53b"}.fa-dove:before{content:"\f4ba"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-socks:before{content:"\f696"}.fa-inbox:before{content:"\f01c"}.fa-section:before{content:"\e447"}.fa-gauge-high:before,.fa-tachometer-alt-fast:before,.fa-tachometer-alt:before{content:"\f625"}.fa-envelope-open-text:before{content:"\f658"}.fa-hospital-alt:before,.fa-hospital-wide:before,.fa-hospital:before{content:"\f0f8"}.fa-wine-bottle:before{content:"\f72f"}.fa-chess-rook:before{content:"\f447"}.fa-bars-staggered:before,.fa-reorder:before,.fa-stream:before{content:"\f550"}.fa-dharmachakra:before{content:"\f655"}.fa-hotdog:before{content:"\f80f"}.fa-blind:before,.fa-person-walking-with-cane:before{content:"\f29d"}.fa-drum:before{content:"\f569"}.fa-ice-cream:before{content:"\f810"}.fa-heart-circle-bolt:before{content:"\e4fc"}.fa-fax:before{content:"\f1ac"}.fa-paragraph:before{content:"\f1dd"}.fa-check-to-slot:before,.fa-vote-yea:before{content:"\f772"}.fa-star-half:before{content:"\f089"}.fa-boxes-alt:before,.fa-boxes-stacked:before,.fa-boxes:before{content:"\f468"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-assistive-listening-systems:before,.fa-ear-listen:before{content:"\f2a2"}.fa-tree-city:before{content:"\e587"}.fa-play:before{content:"\f04b"}.fa-font:before{content:"\f031"}.fa-table-cells-row-lock:before{content:"\e67a"}.fa-rupiah-sign:before{content:"\e23d"}.fa-magnifying-glass:before,.fa-search:before{content:"\f002"}.fa-ping-pong-paddle-ball:before,.fa-table-tennis-paddle-ball:before,.fa-table-tennis:before{content:"\f45d"}.fa-diagnoses:before,.fa-person-dots-from-line:before{content:"\f470"}.fa-trash-can-arrow-up:before,.fa-trash-restore-alt:before{content:"\f82a"}.fa-naira-sign:before{content:"\e1f6"}.fa-cart-arrow-down:before{content:"\f218"}.fa-walkie-talkie:before{content:"\f8ef"}.fa-file-edit:before,.fa-file-pen:before{content:"\f31c"}.fa-receipt:before{content:"\f543"}.fa-pen-square:before,.fa-pencil-square:before,.fa-square-pen:before{content:"\f14b"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-person-circle-exclamation:before{content:"\e53f"}.fa-chevron-down:before{content:"\f078"}.fa-battery-5:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-skull-crossbones:before{content:"\f714"}.fa-code-compare:before{content:"\e13a"}.fa-list-dots:before,.fa-list-ul:before{content:"\f0ca"}.fa-school-lock:before{content:"\e56f"}.fa-tower-cell:before{content:"\e585"}.fa-down-long:before,.fa-long-arrow-alt-down:before{content:"\f309"}.fa-ranking-star:before{content:"\e561"}.fa-chess-king:before{content:"\f43f"}.fa-person-harassing:before{content:"\e549"}.fa-brazilian-real-sign:before{content:"\e46c"}.fa-landmark-alt:before,.fa-landmark-dome:before{content:"\f752"}.fa-arrow-up:before{content:"\f062"}.fa-television:before,.fa-tv-alt:before,.fa-tv:before{content:"\f26c"}.fa-shrimp:before{content:"\e448"}.fa-list-check:before,.fa-tasks:before{content:"\f0ae"}.fa-jug-detergent:before{content:"\e519"}.fa-circle-user:before,.fa-user-circle:before{content:"\f2bd"}.fa-user-shield:before{content:"\f505"}.fa-wind:before{content:"\f72e"}.fa-car-burst:before,.fa-car-crash:before{content:"\f5e1"}.fa-y:before{content:"\59"}.fa-person-snowboarding:before,.fa-snowboarding:before{content:"\f7ce"}.fa-shipping-fast:before,.fa-truck-fast:before{content:"\f48b"}.fa-fish:before{content:"\f578"}.fa-user-graduate:before{content:"\f501"}.fa-adjust:before,.fa-circle-half-stroke:before{content:"\f042"}.fa-clapperboard:before{content:"\e131"}.fa-circle-radiation:before,.fa-radiation-alt:before{content:"\f7ba"}.fa-baseball-ball:before,.fa-baseball:before{content:"\f433"}.fa-jet-fighter-up:before{content:"\e518"}.fa-diagram-project:before,.fa-project-diagram:before{content:"\f542"}.fa-copy:before{content:"\f0c5"}.fa-volume-mute:before,.fa-volume-times:before,.fa-volume-xmark:before{content:"\f6a9"}.fa-hand-sparkles:before{content:"\e05d"}.fa-grip-horizontal:before,.fa-grip:before{content:"\f58d"}.fa-share-from-square:before,.fa-share-square:before{content:"\f14d"}.fa-child-combatant:before,.fa-child-rifle:before{content:"\e4e0"}.fa-gun:before{content:"\e19b"}.fa-phone-square:before,.fa-square-phone:before{content:"\f098"}.fa-add:before,.fa-plus:before{content:"\2b"}.fa-expand:before{content:"\f065"}.fa-computer:before{content:"\e4e5"}.fa-close:before,.fa-multiply:before,.fa-remove:before,.fa-times:before,.fa-xmark:before{content:"\f00d"}.fa-arrows-up-down-left-right:before,.fa-arrows:before{content:"\f047"}.fa-chalkboard-teacher:before,.fa-chalkboard-user:before{content:"\f51c"}.fa-peso-sign:before{content:"\e222"}.fa-building-shield:before{content:"\e4d8"}.fa-baby:before{content:"\f77c"}.fa-users-line:before{content:"\e592"}.fa-quote-left-alt:before,.fa-quote-left:before{content:"\f10d"}.fa-tractor:before{content:"\f722"}.fa-trash-arrow-up:before,.fa-trash-restore:before{content:"\f829"}.fa-arrow-down-up-lock:before{content:"\e4b0"}.fa-lines-leaning:before{content:"\e51e"}.fa-ruler-combined:before{content:"\f546"}.fa-copyright:before{content:"\f1f9"}.fa-equals:before{content:"\3d"}.fa-blender:before{content:"\f517"}.fa-teeth:before{content:"\f62e"}.fa-ils:before,.fa-shekel-sign:before,.fa-shekel:before,.fa-sheqel-sign:before,.fa-sheqel:before{content:"\f20b"}.fa-map:before{content:"\f279"}.fa-rocket:before{content:"\f135"}.fa-photo-film:before,.fa-photo-video:before{content:"\f87c"}.fa-folder-minus:before{content:"\f65d"}.fa-store:before{content:"\f54e"}.fa-arrow-trend-up:before{content:"\e098"}.fa-plug-circle-minus:before{content:"\e55e"}.fa-sign-hanging:before,.fa-sign:before{content:"\f4d9"}.fa-bezier-curve:before{content:"\f55b"}.fa-bell-slash:before{content:"\f1f6"}.fa-tablet-android:before,.fa-tablet:before{content:"\f3fb"}.fa-school-flag:before{content:"\e56e"}.fa-fill:before{content:"\f575"}.fa-angle-up:before{content:"\f106"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-holly-berry:before{content:"\f7aa"}.fa-chevron-left:before{content:"\f053"}.fa-bacteria:before{content:"\e059"}.fa-hand-lizard:before{content:"\f258"}.fa-notdef:before{content:"\e1fe"}.fa-disease:before{content:"\f7fa"}.fa-briefcase-medical:before{content:"\f469"}.fa-genderless:before{content:"\f22d"}.fa-chevron-right:before{content:"\f054"}.fa-retweet:before{content:"\f079"}.fa-car-alt:before,.fa-car-rear:before{content:"\f5de"}.fa-pump-soap:before{content:"\e06b"}.fa-video-slash:before{content:"\f4e2"}.fa-battery-2:before,.fa-battery-quarter:before{content:"\f243"}.fa-radio:before{content:"\f8d7"}.fa-baby-carriage:before,.fa-carriage-baby:before{content:"\f77d"}.fa-traffic-light:before{content:"\f637"}.fa-thermometer:before{content:"\f491"}.fa-vr-cardboard:before{content:"\f729"}.fa-hand-middle-finger:before{content:"\f806"}.fa-percent:before,.fa-percentage:before{content:"\25"}.fa-truck-moving:before{content:"\f4df"}.fa-glass-water-droplet:before{content:"\e4f5"}.fa-display:before{content:"\e163"}.fa-face-smile:before,.fa-smile:before{content:"\f118"}.fa-thumb-tack:before,.fa-thumbtack:before{content:"\f08d"}.fa-trophy:before{content:"\f091"}.fa-person-praying:before,.fa-pray:before{content:"\f683"}.fa-hammer:before{content:"\f6e3"}.fa-hand-peace:before{content:"\f25b"}.fa-rotate:before,.fa-sync-alt:before{content:"\f2f1"}.fa-spinner:before{content:"\f110"}.fa-robot:before{content:"\f544"}.fa-peace:before{content:"\f67c"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-warehouse:before{content:"\f494"}.fa-arrow-up-right-dots:before{content:"\e4b7"}.fa-splotch:before{content:"\f5bc"}.fa-face-grin-hearts:before,.fa-grin-hearts:before{content:"\f584"}.fa-dice-four:before{content:"\f524"}.fa-sim-card:before{content:"\f7c4"}.fa-transgender-alt:before,.fa-transgender:before{content:"\f225"}.fa-mercury:before{content:"\f223"}.fa-arrow-turn-down:before,.fa-level-down:before{content:"\f149"}.fa-person-falling-burst:before{content:"\e547"}.fa-award:before{content:"\f559"}.fa-ticket-alt:before,.fa-ticket-simple:before{content:"\f3ff"}.fa-building:before{content:"\f1ad"}.fa-angle-double-left:before,.fa-angles-left:before{content:"\f100"}.fa-qrcode:before{content:"\f029"}.fa-clock-rotate-left:before,.fa-history:before{content:"\f1da"}.fa-face-grin-beam-sweat:before,.fa-grin-beam-sweat:before{content:"\f583"}.fa-arrow-right-from-file:before,.fa-file-export:before{content:"\f56e"}.fa-shield-blank:before,.fa-shield:before{content:"\f132"}.fa-arrow-up-short-wide:before,.fa-sort-amount-up-alt:before{content:"\f885"}.fa-house-medical:before{content:"\e3b2"}.fa-golf-ball-tee:before,.fa-golf-ball:before{content:"\f450"}.fa-chevron-circle-left:before,.fa-circle-chevron-left:before{content:"\f137"}.fa-house-chimney-window:before{content:"\e00d"}.fa-pen-nib:before{content:"\f5ad"}.fa-tent-arrow-turn-left:before{content:"\e580"}.fa-tents:before{content:"\e582"}.fa-magic:before,.fa-wand-magic:before{content:"\f0d0"}.fa-dog:before{content:"\f6d3"}.fa-carrot:before{content:"\f787"}.fa-moon:before{content:"\f186"}.fa-wine-glass-alt:before,.fa-wine-glass-empty:before{content:"\f5ce"}.fa-cheese:before{content:"\f7ef"}.fa-yin-yang:before{content:"\f6ad"}.fa-music:before{content:"\f001"}.fa-code-commit:before{content:"\f386"}.fa-temperature-low:before{content:"\f76b"}.fa-biking:before,.fa-person-biking:before{content:"\f84a"}.fa-broom:before{content:"\f51a"}.fa-shield-heart:before{content:"\e574"}.fa-gopuram:before{content:"\f664"}.fa-earth-oceania:before,.fa-globe-oceania:before{content:"\e47b"}.fa-square-xmark:before,.fa-times-square:before,.fa-xmark-square:before{content:"\f2d3"}.fa-hashtag:before{content:"\23"}.fa-expand-alt:before,.fa-up-right-and-down-left-from-center:before{content:"\f424"}.fa-oil-can:before{content:"\f613"}.fa-t:before{content:"\54"}.fa-hippo:before{content:"\f6ed"}.fa-chart-column:before{content:"\e0e3"}.fa-infinity:before{content:"\f534"}.fa-vial-circle-check:before{content:"\e596"}.fa-person-arrow-down-to-line:before{content:"\e538"}.fa-voicemail:before{content:"\f897"}.fa-fan:before{content:"\f863"}.fa-person-walking-luggage:before{content:"\e554"}.fa-arrows-alt-v:before,.fa-up-down:before{content:"\f338"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-calendar:before{content:"\f133"}.fa-trailer:before{content:"\e041"}.fa-bahai:before,.fa-haykal:before{content:"\f666"}.fa-sd-card:before{content:"\f7c2"}.fa-dragon:before{content:"\f6d5"}.fa-shoe-prints:before{content:"\f54b"}.fa-circle-plus:before,.fa-plus-circle:before{content:"\f055"}.fa-face-grin-tongue-wink:before,.fa-grin-tongue-wink:before{content:"\f58b"}.fa-hand-holding:before{content:"\f4bd"}.fa-plug-circle-exclamation:before{content:"\e55d"}.fa-chain-broken:before,.fa-chain-slash:before,.fa-link-slash:before,.fa-unlink:before{content:"\f127"}.fa-clone:before{content:"\f24d"}.fa-person-walking-arrow-loop-left:before{content:"\e551"}.fa-arrow-up-z-a:before,.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-fire-alt:before,.fa-fire-flame-curved:before{content:"\f7e4"}.fa-tornado:before{content:"\f76f"}.fa-file-circle-plus:before{content:"\e494"}.fa-book-quran:before,.fa-quran:before{content:"\f687"}.fa-anchor:before{content:"\f13d"}.fa-border-all:before{content:"\f84c"}.fa-angry:before,.fa-face-angry:before{content:"\f556"}.fa-cookie-bite:before{content:"\f564"}.fa-arrow-trend-down:before{content:"\e097"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-draw-polygon:before{content:"\f5ee"}.fa-balance-scale:before,.fa-scale-balanced:before{content:"\f24e"}.fa-gauge-simple-high:before,.fa-tachometer-fast:before,.fa-tachometer:before{content:"\f62a"}.fa-shower:before{content:"\f2cc"}.fa-desktop-alt:before,.fa-desktop:before{content:"\f390"}.fa-m:before{content:"\4d"}.fa-table-list:before,.fa-th-list:before{content:"\f00b"}.fa-comment-sms:before,.fa-sms:before{content:"\f7cd"}.fa-book:before{content:"\f02d"}.fa-user-plus:before{content:"\f234"}.fa-check:before{content:"\f00c"}.fa-battery-4:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-house-circle-check:before{content:"\e509"}.fa-angle-left:before{content:"\f104"}.fa-diagram-successor:before{content:"\e47a"}.fa-truck-arrow-right:before{content:"\e58b"}.fa-arrows-split-up-and-left:before{content:"\e4bc"}.fa-fist-raised:before,.fa-hand-fist:before{content:"\f6de"}.fa-cloud-moon:before{content:"\f6c3"}.fa-briefcase:before{content:"\f0b1"}.fa-person-falling:before{content:"\e546"}.fa-image-portrait:before,.fa-portrait:before{content:"\f3e0"}.fa-user-tag:before{content:"\f507"}.fa-rug:before{content:"\e569"}.fa-earth-europe:before,.fa-globe-europe:before{content:"\f7a2"}.fa-cart-flatbed-suitcase:before,.fa-luggage-cart:before{content:"\f59d"}.fa-rectangle-times:before,.fa-rectangle-xmark:before,.fa-times-rectangle:before,.fa-window-close:before{content:"\f410"}.fa-baht-sign:before{content:"\e0ac"}.fa-book-open:before{content:"\f518"}.fa-book-journal-whills:before,.fa-journal-whills:before{content:"\f66a"}.fa-handcuffs:before{content:"\e4f8"}.fa-exclamation-triangle:before,.fa-triangle-exclamation:before,.fa-warning:before{content:"\f071"}.fa-database:before{content:"\f1c0"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-bottle-droplet:before{content:"\e4c4"}.fa-mask-face:before{content:"\e1d7"}.fa-hill-rockslide:before{content:"\e508"}.fa-exchange-alt:before,.fa-right-left:before{content:"\f362"}.fa-paper-plane:before{content:"\f1d8"}.fa-road-circle-exclamation:before{content:"\e565"}.fa-dungeon:before{content:"\f6d9"}.fa-align-right:before{content:"\f038"}.fa-money-bill-1-wave:before,.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-life-ring:before{content:"\f1cd"}.fa-hands:before,.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-calendar-day:before{content:"\f783"}.fa-ladder-water:before,.fa-swimming-pool:before,.fa-water-ladder:before{content:"\f5c5"}.fa-arrows-up-down:before,.fa-arrows-v:before{content:"\f07d"}.fa-face-grimace:before,.fa-grimace:before{content:"\f57f"}.fa-wheelchair-alt:before,.fa-wheelchair-move:before{content:"\e2ce"}.fa-level-down-alt:before,.fa-turn-down:before{content:"\f3be"}.fa-person-walking-arrow-right:before{content:"\e552"}.fa-envelope-square:before,.fa-square-envelope:before{content:"\f199"}.fa-dice:before{content:"\f522"}.fa-bowling-ball:before{content:"\f436"}.fa-brain:before{content:"\f5dc"}.fa-band-aid:before,.fa-bandage:before{content:"\f462"}.fa-calendar-minus:before{content:"\f272"}.fa-circle-xmark:before,.fa-times-circle:before,.fa-xmark-circle:before{content:"\f057"}.fa-gifts:before{content:"\f79c"}.fa-hotel:before{content:"\f594"}.fa-earth-asia:before,.fa-globe-asia:before{content:"\f57e"}.fa-id-card-alt:before,.fa-id-card-clip:before{content:"\f47f"}.fa-magnifying-glass-plus:before,.fa-search-plus:before{content:"\f00e"}.fa-thumbs-up:before{content:"\f164"}.fa-user-clock:before{content:"\f4fd"}.fa-allergies:before,.fa-hand-dots:before{content:"\f461"}.fa-file-invoice:before{content:"\f570"}.fa-window-minimize:before{content:"\f2d1"}.fa-coffee:before,.fa-mug-saucer:before{content:"\f0f4"}.fa-brush:before{content:"\f55d"}.fa-mask:before{content:"\f6fa"}.fa-magnifying-glass-minus:before,.fa-search-minus:before{content:"\f010"}.fa-ruler-vertical:before{content:"\f548"}.fa-user-alt:before,.fa-user-large:before{content:"\f406"}.fa-train-tram:before{content:"\e5b4"}.fa-user-nurse:before{content:"\f82f"}.fa-syringe:before{content:"\f48e"}.fa-cloud-sun:before{content:"\f6c4"}.fa-stopwatch-20:before{content:"\e06f"}.fa-square-full:before{content:"\f45c"}.fa-magnet:before{content:"\f076"}.fa-jar:before{content:"\e516"}.fa-note-sticky:before,.fa-sticky-note:before{content:"\f249"}.fa-bug-slash:before{content:"\e490"}.fa-arrow-up-from-water-pump:before{content:"\e4b6"}.fa-bone:before{content:"\f5d7"}.fa-user-injured:before{content:"\f728"}.fa-face-sad-tear:before,.fa-sad-tear:before{content:"\f5b4"}.fa-plane:before{content:"\f072"}.fa-tent-arrows-down:before{content:"\e581"}.fa-exclamation:before{content:"\21"}.fa-arrows-spin:before{content:"\e4bb"}.fa-print:before{content:"\f02f"}.fa-try:before,.fa-turkish-lira-sign:before,.fa-turkish-lira:before{content:"\e2bb"}.fa-dollar-sign:before,.fa-dollar:before,.fa-usd:before{content:"\24"}.fa-x:before{content:"\58"}.fa-magnifying-glass-dollar:before,.fa-search-dollar:before{content:"\f688"}.fa-users-cog:before,.fa-users-gear:before{content:"\f509"}.fa-person-military-pointing:before{content:"\e54a"}.fa-bank:before,.fa-building-columns:before,.fa-institution:before,.fa-museum:before,.fa-university:before{content:"\f19c"}.fa-umbrella:before{content:"\f0e9"}.fa-trowel:before{content:"\e589"}.fa-d:before{content:"\44"}.fa-stapler:before{content:"\e5af"}.fa-masks-theater:before,.fa-theater-masks:before{content:"\f630"}.fa-kip-sign:before{content:"\e1c4"}.fa-hand-point-left:before{content:"\f0a5"}.fa-handshake-alt:before,.fa-handshake-simple:before{content:"\f4c6"}.fa-fighter-jet:before,.fa-jet-fighter:before{content:"\f0fb"}.fa-share-alt-square:before,.fa-square-share-nodes:before{content:"\f1e1"}.fa-barcode:before{content:"\f02a"}.fa-plus-minus:before{content:"\e43c"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-person-circle-check:before{content:"\e53e"}.fa-level-up-alt:before,.fa-turn-up:before{content:"\f3bf"} +.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0} \ No newline at end of file diff --git a/static/editor/vendor/fontawesome/6.5.2/css/regular.min.css b/static/editor/vendor/fontawesome/6.5.2/css/regular.min.css new file mode 100644 index 0000000..7f1cb00 --- /dev/null +++ b/static/editor/vendor/fontawesome/6.5.2/css/regular.min.css @@ -0,0 +1,6 @@ +/*! + * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2024 Fonticons, Inc. + */ +:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400} \ No newline at end of file diff --git a/static/editor/vendor/fontawesome/6.5.2/css/solid.min.css b/static/editor/vendor/fontawesome/6.5.2/css/solid.min.css new file mode 100644 index 0000000..e7d97d2 --- /dev/null +++ b/static/editor/vendor/fontawesome/6.5.2/css/solid.min.css @@ -0,0 +1,6 @@ +/*! + * Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2024 Fonticons, Inc. + */ +:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900} \ No newline at end of file diff --git a/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-brands-400.ttf b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-brands-400.ttf new file mode 100644 index 0000000..1fbb1f7 Binary files /dev/null and b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-brands-400.ttf differ diff --git a/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-brands-400.woff2 b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-brands-400.woff2 new file mode 100644 index 0000000..5d28021 Binary files /dev/null and b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-brands-400.woff2 differ diff --git a/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-regular-400.ttf b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-regular-400.ttf new file mode 100644 index 0000000..549d68d Binary files /dev/null and b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-regular-400.ttf differ diff --git a/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-regular-400.woff2 b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-regular-400.woff2 new file mode 100644 index 0000000..18400d7 Binary files /dev/null and b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-regular-400.woff2 differ diff --git a/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-solid-900.ttf b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-solid-900.ttf new file mode 100644 index 0000000..bb2a869 Binary files /dev/null and b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-solid-900.ttf differ diff --git a/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-solid-900.woff2 b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-solid-900.woff2 new file mode 100644 index 0000000..758dd4f Binary files /dev/null and b/static/editor/vendor/fontawesome/6.5.2/webfonts/fa-solid-900.woff2 differ diff --git a/static/editor/vendor/manifest.json b/static/editor/vendor/manifest.json new file mode 100644 index 0000000..acdd878 --- /dev/null +++ b/static/editor/vendor/manifest.json @@ -0,0 +1,1077 @@ +{ + "schema": 1, + "upstreamFileCount": 98, + "noticeFileCount": 9, + "files": [ + { + "path": "fontawesome/6.5.2/LICENSE.txt", + "bytes": 7427, + "sha256": "9b914eae88817d63b576eab5aafde7068c7a1abae125d7cdfb034f1da43a9afc", + "source": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", + "archiveSha256": "1622d514686c299db3ffe9405c4f124778ce812658f66282d4524247e66fff9c", + "member": "package/LICENSE.txt", + "package": "fontawesome", + "version": "6.5.2" + }, + { + "path": "fontawesome/6.5.2/css/brands.min.css", + "bytes": 19582, + "sha256": "e10425ad768bc98ff1fb272a0ac8420f9d1ba22f0612c08ff1010c95080ffe7e", + "source": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", + "archiveSha256": "1622d514686c299db3ffe9405c4f124778ce812658f66282d4524247e66fff9c", + "member": "package/css/brands.min.css", + "package": "fontawesome", + "version": "6.5.2" + }, + { + "path": "fontawesome/6.5.2/css/fontawesome.min.css", + "bytes": 80888, + "sha256": "137b1cf3cea9a8adb7884343a9a5ddddf4280f59153f74dc782fb7f7bf0d0519", + "source": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", + "archiveSha256": "1622d514686c299db3ffe9405c4f124778ce812658f66282d4524247e66fff9c", + "member": "package/css/fontawesome.min.css", + "package": "fontawesome", + "version": "6.5.2" + }, + { + "path": "fontawesome/6.5.2/css/regular.min.css", + "bytes": 580, + "sha256": "6f4f16d58da1c82c0c3a3436e021a3d39b4742f741192c546e73e947eacfd92f", + "source": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", + "archiveSha256": "1622d514686c299db3ffe9405c4f124778ce812658f66282d4524247e66fff9c", + "member": "package/css/regular.min.css", + "package": "fontawesome", + "version": "6.5.2" + }, + { + "path": "fontawesome/6.5.2/css/solid.min.css", + "bytes": 572, + "sha256": "e65dc5b48fb5f39b142360c57c3a215744c94e56c755c929cc3e88fe12aab4d3", + "source": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", + "archiveSha256": "1622d514686c299db3ffe9405c4f124778ce812658f66282d4524247e66fff9c", + "member": "package/css/solid.min.css", + "package": "fontawesome", + "version": "6.5.2" + }, + { + "path": "fontawesome/6.5.2/webfonts/fa-brands-400.ttf", + "bytes": 209128, + "sha256": "e28096fa75a96ac77020155ea3a6dd7312983e84115366d4cf49a0c312ec6d51", + "source": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", + "archiveSha256": "1622d514686c299db3ffe9405c4f124778ce812658f66282d4524247e66fff9c", + "member": "package/webfonts/fa-brands-400.ttf", + "package": "fontawesome", + "version": "6.5.2" + }, + { + "path": "fontawesome/6.5.2/webfonts/fa-brands-400.woff2", + "bytes": 117852, + "sha256": "232c6f6a7678304f9efaa26f30b1610debc2ba9f4cd636b5e6751c8d73761b92", + "source": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", + "archiveSha256": "1622d514686c299db3ffe9405c4f124778ce812658f66282d4524247e66fff9c", + "member": "package/webfonts/fa-brands-400.woff2", + "package": "fontawesome", + "version": "6.5.2" + }, + { + "path": "fontawesome/6.5.2/webfonts/fa-regular-400.ttf", + "bytes": 67860, + "sha256": "9174757efc83e072436e873c22be1663d3c103b0a16d7fb73569af4918d4d351", + "source": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", + "archiveSha256": "1622d514686c299db3ffe9405c4f124778ce812658f66282d4524247e66fff9c", + "member": "package/webfonts/fa-regular-400.ttf", + "package": "fontawesome", + "version": "6.5.2" + }, + { + "path": "fontawesome/6.5.2/webfonts/fa-regular-400.woff2", + "bytes": 25392, + "sha256": "c27da6f833431da5aa295c44540bfac0fd8270ba6a3c4346427006d8a7b34b76", + "source": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", + "archiveSha256": "1622d514686c299db3ffe9405c4f124778ce812658f66282d4524247e66fff9c", + "member": "package/webfonts/fa-regular-400.woff2", + "package": "fontawesome", + "version": "6.5.2" + }, + { + "path": "fontawesome/6.5.2/webfonts/fa-solid-900.ttf", + "bytes": 420332, + "sha256": "b4990d0d0c5f5d38d62e936eea120674e584c7eea8dcee38a975c0cf9a37539b", + "source": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", + "archiveSha256": "1622d514686c299db3ffe9405c4f124778ce812658f66282d4524247e66fff9c", + "member": "package/webfonts/fa-solid-900.ttf", + "package": "fontawesome", + "version": "6.5.2" + }, + { + "path": "fontawesome/6.5.2/webfonts/fa-solid-900.woff2", + "bytes": 156400, + "sha256": "ae17c16afbea216707b2203ea1cf9bdb45b9bfe47d0f4ae3258ddbc6294dd02f", + "source": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", + "archiveSha256": "1622d514686c299db3ffe9405c4f124778ce812658f66282d4524247e66fff9c", + "member": "package/webfonts/fa-solid-900.woff2", + "package": "fontawesome", + "version": "6.5.2" + }, + { + "path": "shoelace/2.12.0/LICENSE.md", + "bytes": 1065, + "sha256": "ac9ffacbfc877b6f4253a481b3c0edbad0c019ac3506a3b51415b336f4a0c054", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/LICENSE.md", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/assets/icons/LICENSE", + "bytes": 1093, + "sha256": "0623348b57e817b6897c41b6065380b599d5befe2839d83270566c605e430441", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/assets/icons/LICENSE", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/assets/icons/code.svg", + "bytes": 362, + "sha256": "e66b460f5fd8ca4327d155aa79510cb114d11c50d1b62d25c7520ad1baa02e1b", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/assets/icons/code.svg", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/assets/icons/link.svg", + "bytes": 403, + "sha256": "0634bc395f62df3ff42772c56af5c07bb6eba6ab23a70613ad0f255737e6511f", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/assets/icons/link.svg", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/assets/icons/question-circle.svg", + "bytes": 719, + "sha256": "b9408c681dfb0002a76f91c9ac864b7127ae52428aa81eca188985504a61d7e0", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/assets/icons/question-circle.svg", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/assets/icons/type-bold.svg", + "bytes": 470, + "sha256": "8e7adb4a29d9fd60445e2e9d96461ddc1212ef14bfd76f41ffb951fa9968eade", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/assets/icons/type-bold.svg", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/assets/icons/type-h1.svg", + "bytes": 263, + "sha256": "9a211647fe7df5c57c0b86e6625cd9c661e3df540f646c30465890aece8e1207", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/assets/icons/type-h1.svg", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/assets/icons/type-h2.svg", + "bytes": 471, + "sha256": "0a5ce744585975247d8784149d3807ad57f1cbf834a34a5ab8e394a8660ba934", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/assets/icons/type-h2.svg", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/assets/icons/type-h3.svg", + "bytes": 636, + "sha256": "c588ec487945d9306c1005196ed7f84bd314633423171559f0aefa6b18c97211", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/assets/icons/type-h3.svg", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/assets/icons/type-italic.svg", + "bytes": 351, + "sha256": "29722685928ea226f897ebb7784132e810e976ffb61e21f625d875467f853153", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/assets/icons/type-italic.svg", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/assets/icons/type-underline.svg", + "bytes": 334, + "sha256": "e1038bb0001fe6f8e4e7f250bd497b71df555a093a1970aa99be189a83a3089e", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/assets/icons/type-underline.svg", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.24R5WE5A.js", + "bytes": 428, + "sha256": "d820009490ad7b49fb616d2eac3eab89a9159c7e3d29796e522b6b903fa01be1", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.24R5WE5A.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.27ILGUWR.js", + "bytes": 46508, + "sha256": "c7a8c01697a327f9a24f20a80892eba4289b275114876996b7468d95a889ad94", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.27ILGUWR.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.2AIXAFCV.js", + "bytes": 15285, + "sha256": "243637209fe98b86324a9e50bc4b119786f749087a83f769fa5d35540e9cee8c", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.2AIXAFCV.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.2SZPKEN5.js", + "bytes": 8094, + "sha256": "a06102043565da0ed6dbff17f50fdacadbb9cae0cf43f1904535101fc8938413", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.2SZPKEN5.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.2X5EERK2.js", + "bytes": 8328, + "sha256": "b08f0ec5efc3540bab614d7b5f6c4ccbdbf2ef9b517cf1e3ddf7d204d223aaa2", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.2X5EERK2.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.3GITBIC4.js", + "bytes": 1304, + "sha256": "65f42f91445f0b5123f13e695f2973cbeb3a643a10dccb86baa620de0f0a32ef", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.3GITBIC4.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.3TFKS637.js", + "bytes": 7807, + "sha256": "14f82f632089ec1a68784a8aa36aec1e4a713623749ad880fbbd0119f78da043", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.3TFKS637.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.3Y6SB6QS.js", + "bytes": 894, + "sha256": "f1cea1705e0c978806be53c89f206e9c4be9cac5348d77fae76b8d759ff2a9c9", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.3Y6SB6QS.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.43GPPE74.js", + "bytes": 54865, + "sha256": "bb0054a347cbb37a414c64621c5151385e305c5a51451b9346ecd636dfde9c1b", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.43GPPE74.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.4S3RCQCG.js", + "bytes": 6871, + "sha256": "822f662c32036ec0fd981cee20a7726a5d21c160a57dcc5597bddcc639349a98", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.4S3RCQCG.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.52GJFLW5.js", + "bytes": 1492, + "sha256": "1c85cb5aba7188eb4a8587a8076781f49a5819be22740985e9b0c31e3fd8e0a3", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.52GJFLW5.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.52OC5JIK.js", + "bytes": 161, + "sha256": "8e6b5b64330fa64475326259787126ac9a69fd19e63e0d872461ad8f42d00f79", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.52OC5JIK.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.6LJMKLWZ.js", + "bytes": 1233, + "sha256": "14a4528f459174f043fc09a0c85870a3a13c622e99db52a52f653cc12cc49de3", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.6LJMKLWZ.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.6U4GOUZA.js", + "bytes": 169, + "sha256": "68684064e5e3af813bcf7022ce4cfe32f9169cc87de96cd5a6541c51a2904813", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.6U4GOUZA.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.7G6CFCYN.js", + "bytes": 177, + "sha256": "008d0d801c35fdea8829b0a18af142786bac4aa5403d8d2450140f0523a57363", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.7G6CFCYN.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.7I3HGH5I.js", + "bytes": 6416, + "sha256": "5ff9e71578f1148596cd76ff6518f4a9350c3a78dcdc7ec83a1f968bb575e113", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.7I3HGH5I.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.ABG77QVN.js", + "bytes": 2744, + "sha256": "eb49eecff1bdad654b2ccd12a899c841708f42b4941d06a3a588c6e24fb2ae5c", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.ABG77QVN.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.B3ILE7LN.js", + "bytes": 4459, + "sha256": "0fb77ecfeabdd945703ef57a818c219a87889d5409f0287e9e8d3c6be3241d48", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.B3ILE7LN.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.B4BZKR24.js", + "bytes": 320, + "sha256": "8bdb8b4bf886773489e9b8436aaf39a63de270073002ff62341649d857ea27cd", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.B4BZKR24.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.BXWBYEBW.js", + "bytes": 15937, + "sha256": "fe753faaca5c419872f5816aebc4af4afa97ded7380ec52675cf0908929652f9", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.BXWBYEBW.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.DOYC4G7X.js", + "bytes": 1475, + "sha256": "cbccc5fca4cded950114904fb9db9471eb581c03eea6242c0c8bc47bbd56ee9a", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.DOYC4G7X.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.E7LAXGG4.js", + "bytes": 185, + "sha256": "4a6f1bf858fb8735a9fde90e6c7ba7a1aff940d5d1240f3701802cb504208cd2", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.E7LAXGG4.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.ESELY2US.js", + "bytes": 962, + "sha256": "c4097a6aa14847ee845e5f0cfe35a3a4291962c383d08d3523e3056878af9720", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.ESELY2US.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.FDFOFJDR.js", + "bytes": 1265, + "sha256": "fb369159ef9c20b824925cee0f9978ffdb3a6c521d2f07a8fc135291d535a177", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.FDFOFJDR.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.FZX37GM3.js", + "bytes": 3884, + "sha256": "2cbb7c5c4a53cb51a470e8d569ea78933aeaf5d5ed17e5380059fb70aa1fab9c", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.FZX37GM3.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.G2QSU2BF.js", + "bytes": 750, + "sha256": "7828cb2a3a091d166721f1080ea93372f7dda91dc08f2f85878b5f87d58cb52f", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.G2QSU2BF.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.GL3UMKIL.js", + "bytes": 1457, + "sha256": "ebb9e48d84cb4d0a9dffe8c8f6e63bb4c59d0e8555710fb7adf5e34052edb226", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.GL3UMKIL.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.HAL7R4WT.js", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.HAL7R4WT.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.HF7GESMZ.js", + "bytes": 285, + "sha256": "4f1d59a3fb42615764c821c9f633ef85fd2ec5e7701355c82ea22eac263950a4", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.HF7GESMZ.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.HPQ3PGLN.js", + "bytes": 1164, + "sha256": "bffc0c50bd516e7403b794eb9af5e894775bc1a13bd2b5ecc66ddbed1b53f31b", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.HPQ3PGLN.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.HQAAUCQS.js", + "bytes": 384, + "sha256": "e0c9a4907115439faf7167b42a66b711858b930809afbe5ef3d04ff733811b98", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.HQAAUCQS.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.HTWHAKXT.js", + "bytes": 1317, + "sha256": "86ba3477e5fff6f7844ac3343b2b2812ca299c7f537d6904855a112111f90ac3", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.HTWHAKXT.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.I5LOVG5T.js", + "bytes": 214, + "sha256": "637e9bc9f021553298c96430c3057a266f3cf4d6746b687a4d5057bd4342acfd", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.I5LOVG5T.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.JJNPZ52S.js", + "bytes": 674, + "sha256": "e869c821cc70ece9f594c1cf6cb2d8458ee6de5b33d2f7c4abb75544aa1cb29d", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.JJNPZ52S.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.M2U2QT2K.js", + "bytes": 324, + "sha256": "7c7463cbf1286d791c43b09b02ddf87828b07f2d2156454e7c59edf593641f91", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.M2U2QT2K.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.MKSGYDJ7.js", + "bytes": 1307, + "sha256": "8ec92f56da768dd469e9c4e1b2d84bf305598eeb487b5172f54f390973c4ebb0", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.MKSGYDJ7.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.MOUXRZGR.js", + "bytes": 185, + "sha256": "4b6060f74725dfa6d2e00708eeb68622e80032c29e8a29690cd32c3e7cf43b16", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.MOUXRZGR.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.MTYRJPGI.js", + "bytes": 2761, + "sha256": "66a0947074c14ad59db09bca8d072469ef928736c3705cc7deeb98e029b5324d", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.MTYRJPGI.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.NEZCQBYJ.js", + "bytes": 3140, + "sha256": "89adf06f2beac497d981a3fc37b45698bb90fb349a72938ba4a60ad7cd22d959", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.NEZCQBYJ.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.NH3SRVOC.js", + "bytes": 319, + "sha256": "76b91e0a54ce01582a347456246f7433e936029ab1f9e80a87ced715864c4fa9", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.NH3SRVOC.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.NYIIDP5N.js", + "bytes": 1698, + "sha256": "32772b78bca61210992bc3a593669ce2621d213e72b0b382e60cc09ddd87a539", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.NYIIDP5N.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.O27EHOBW.js", + "bytes": 3566, + "sha256": "1a9aeba601f49a8ddb7507d1be6dac6ada4757edd7a69c9252f3421040854476", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.O27EHOBW.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.OQXQSIH6.js", + "bytes": 10452, + "sha256": "f72e99e832ab8262e441cd7fc4aef6cae99dfa32e7aa2d663184dcde34e5be0d", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.OQXQSIH6.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.OZPGMRHW.js", + "bytes": 1790, + "sha256": "3ba2b5e16c8626a2ed2df8f71152bcc0c95df1b1903093bbfab76dae8082c65a", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.OZPGMRHW.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.P7ZG6EMR.js", + "bytes": 274, + "sha256": "11da9e127aa1b30b0969bf5d90bfbeb62e0c6e35c7e0c165d35d7171358b06d5", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.P7ZG6EMR.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.PCNTRSPK.js", + "bytes": 683, + "sha256": "d50534e993927bedc5b34e6d1561e767e7b8f72fa6357e62bfcb7b954ccc0e21", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.PCNTRSPK.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.PNPJ7KDG.js", + "bytes": 337, + "sha256": "0cdc32350809cc64b71ecca105bd7ea95f177ca11a7e65f0a8cdb70202f8bfde", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.PNPJ7KDG.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.QOTN4UPW.js", + "bytes": 8460, + "sha256": "f0a57a7554f90327e24beea70b9d933fe294549d9c3a3352051bb5403bf27bae", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.QOTN4UPW.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.QPSNFEB2.js", + "bytes": 1089, + "sha256": "9363a2487d32bff55b2ac56dcb57bfe1c07630c74d35171cd00ab492d16a3a47", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.QPSNFEB2.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.RBTTBYTX.js", + "bytes": 1739, + "sha256": "6be2e7bdfc5e93af07b81a78bd188f24e22e7b3f0f7597626a924e17bb30601a", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.RBTTBYTX.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.RIBKWZXP.js", + "bytes": 993, + "sha256": "1f1d6889fc039c40fbd5ef439b79c05fba241aa1e221619f1d6e8ee7042ded6b", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.RIBKWZXP.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.RK73WSZS.js", + "bytes": 2178, + "sha256": "06df6b1b1b699b5a462620b1aeacad9bef7ca4706d3d0fe146292d7316bc3357", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.RK73WSZS.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.TDLFM7SW.js", + "bytes": 533, + "sha256": "a1d916205572ca7dea93311989aac9706f7d66147bb7619ce8104107fc1ab5cc", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.TDLFM7SW.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.WAIE6M2P.js", + "bytes": 877, + "sha256": "047a6b9e403b0e2c6e7986dedfee63031379cb31ec23a2a08bf5b5ca1fa4cace", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.WAIE6M2P.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.WFFSQ5E5.js", + "bytes": 169, + "sha256": "408c5d6195fcd0e23b8d97797d96289a2cff9b298ca6d37680243b564121d1d0", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.WFFSQ5E5.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.XAOA43RZ.js", + "bytes": 911, + "sha256": "223cef3722caa4e3fb6efb90d5d4bc67ec30d51683e6df6e163a1c29884f72ce", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.XAOA43RZ.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.XQ4KBI5G.js", + "bytes": 1856, + "sha256": "6b8ea2bf689231baa347d7c13c65e018f0876cbfc73b2f32d217dd7c5b070899", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.XQ4KBI5G.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.XXIXGO5R.js", + "bytes": 7030, + "sha256": "1ed2486116dcc58a15b579402169f054027b1e7bef36ee258d98014976656e03", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.XXIXGO5R.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.XZMYMJVO.js", + "bytes": 2168, + "sha256": "b4f349ee02e854ba3dd6888bdbda3248c91da52772de86261d630b22c1f865e4", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.XZMYMJVO.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.YCHBWCKL.js", + "bytes": 2406, + "sha256": "bd70677c7a805a7f0af02c88b2c217f96ae6391679b04938252238efaaf4710f", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.YCHBWCKL.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.YGMDV7QR.js", + "bytes": 177, + "sha256": "f81b5a698455691fc798d0c778899017d2935cb288692f0ccb837b899bf46243", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.YGMDV7QR.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.YPBP7XEQ.js", + "bytes": 222, + "sha256": "65de5249054ea64c13504137a773c08a3ecedf2f6c9762bf9ca8e670b762a20a", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.YPBP7XEQ.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.YZETUBD6.js", + "bytes": 3115, + "sha256": "78fb7c2ef495d0c74629aa6aee0ae3592bbd4c5103d6d37bc179b7695f2f74b3", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.YZETUBD6.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/chunks/chunk.ZL53POKZ.js", + "bytes": 1004, + "sha256": "1f0c820ee81e8cd783cefcba1b28f075b64014f72932823c134cfbd45aee61c1", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/chunks/chunk.ZL53POKZ.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/components/badge/badge.js", + "bytes": 433, + "sha256": "d8ee2039ef00280a52f17661d088647e328c677e090830381f3749fa343eccd9", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/components/badge/badge.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/components/button-group/button-group.js", + "bytes": 365, + "sha256": "d40c5473fa0a54722f6bbb5442339e9ae7fc10b34a8bc7d5bc5ac6138713ed31", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/components/button-group/button-group.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/components/button/button.js", + "bytes": 1132, + "sha256": "d2c4baaf63d05b25d7e5d9950ae2a4cecdedff13f484387767dffa937beaf3c9", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/components/button/button.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/components/dialog/dialog.js", + "bytes": 1337, + "sha256": "62df6c895cd87fc0c7731b094695ed80b62a23fb13b895b124fcb9f1d9154ea9", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/components/dialog/dialog.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/components/divider/divider.js", + "bytes": 396, + "sha256": "79e2fd331684c7fc545ce40cfdf9bcc92f3e26ec40566a08703da4c9dde8ad6a", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/components/divider/divider.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/components/icon/icon.js", + "bytes": 595, + "sha256": "b2eb2f56da87f532693ad0bce20a9c258d19964a37f4ebc51db6893cf306c636", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/components/icon/icon.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/components/input/input.js", + "bytes": 1130, + "sha256": "084441c5b23b3842cc405fbc5e9fa54676f558b6a3bba5884582c667d8b057c3", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/components/input/input.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/components/split-panel/split-panel.js", + "bytes": 650, + "sha256": "e1780bedaa9c3b17612eae9560c39f448fd03c1f224c953fdc4c6df44ad38ae4", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/components/split-panel/split-panel.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/components/tooltip/tooltip.js", + "bytes": 806, + "sha256": "7e435dd2b5b7aa59e00e2967b90c8a9db61df5de781d55ec7f6b689007499d5e", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/components/tooltip/tooltip.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/themes/light.css", + "bytes": 19286, + "sha256": "7bc8fb762a0f357d171df3126797e53cc0210d798080472fac6f72093acaf708", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/themes/light.css", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/utilities/base-path.js", + "bytes": 155, + "sha256": "81b967daca7f541a910bdfabf297dbb0ec3d7fbf4588a9fb8bb8bae1b2ae28e1", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/utilities/base-path.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "path": "shoelace/2.12.0/cdn/utilities/icon-library.js", + "bytes": 343, + "sha256": "dbbfd83ee038e1c5d94f7107e5117fdf835523cbf595bcc87c7ec21a76157b19", + "source": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.12.0.tgz", + "archiveSha256": "76ae69764177f9fac0628bc59caa243b6897ef1536e265a381b3c31de1996f52", + "member": "package/cdn/utilities/icon-library.js", + "package": "shoelace", + "version": "2.12.0" + }, + { + "package": "composed-offset-position", + "version": "0.0.4", + "source": "https://registry.npmjs.org/composed-offset-position/-/composed-offset-position-0.0.4.tgz", + "archiveSha256": "8a2a090824eb3f1c08d880b1a1f8b83a90b6a09e4551ce0acbfedea29345afe8", + "path": "shoelace/2.12.0/third-party-notices/composed-offset-position/LICENSE.md", + "bytes": 1094, + "sha256": "086c588bc8fd118a6a316d4701cf46bba36aa934f94fcfd933a35127c24627e4", + "member": "package/LICENSE.md" + }, + { + "package": "@floating-ui/core", + "version": "1.5.0", + "source": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", + "archiveSha256": "36328fec6f0b2f120c60e8c82a0eaeb1322483ca5fb622c9c1dd6bea2e789ea6", + "path": "shoelace/2.12.0/third-party-notices/floating-ui_core/LICENSE", + "bytes": 1089, + "sha256": "0e4c9a9b6c71019cbbea3bdc20b01223110a9035700f9c960c8fcbf78c2325ce", + "member": "package/LICENSE" + }, + { + "package": "@floating-ui/dom", + "version": "1.5.3", + "source": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "archiveSha256": "c28af40d7c38bcf7f64ce7f82f148c2158f2bbe53ee4f2adb977849dfd63c1b3", + "path": "shoelace/2.12.0/third-party-notices/floating-ui_dom/LICENSE", + "bytes": 1089, + "sha256": "0e4c9a9b6c71019cbbea3bdc20b01223110a9035700f9c960c8fcbf78c2325ce", + "member": "package/LICENSE" + }, + { + "package": "@floating-ui/utils", + "version": "0.1.6", + "source": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", + "archiveSha256": "6bd0a4827a71418f21bce7a5d7445dac64686201aaca11f91321b462d3a01d14", + "path": "shoelace/2.12.0/third-party-notices/floating-ui_utils/LICENSE", + "bytes": 1089, + "sha256": "0e4c9a9b6c71019cbbea3bdc20b01223110a9035700f9c960c8fcbf78c2325ce", + "member": "package/LICENSE" + }, + { + "package": "lit-element", + "version": "4.0.0", + "source": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.0.tgz", + "archiveSha256": "65bb7bcdcbe489e732fa141a3c98ae2f2eceae0e5c96f12bed1196183b876b1b", + "path": "shoelace/2.12.0/third-party-notices/lit-element/LICENSE", + "bytes": 1517, + "sha256": "45d31799d0db956cc3eb5469346abbd9b7025babc5ff29fab10d7095da992ef1", + "member": "package/LICENSE" + }, + { + "package": "lit", + "version": "3.0.0", + "source": "https://registry.npmjs.org/lit/-/lit-3.0.0.tgz", + "archiveSha256": "a5de904df40393a721f6415657b02c745a38a979fbe3f9b4077aea2e25479df7", + "path": "shoelace/2.12.0/third-party-notices/lit/LICENSE", + "bytes": 1517, + "sha256": "45d31799d0db956cc3eb5469346abbd9b7025babc5ff29fab10d7095da992ef1", + "member": "package/LICENSE" + }, + { + "package": "lit/node_modules/lit-html", + "version": "3.0.0", + "source": "https://registry.npmjs.org/lit-html/-/lit-html-3.0.0.tgz", + "archiveSha256": "9f5265d5bcb3976f472c7fbbbc989ad3736ce7483421770682349dfac47bf203", + "path": "shoelace/2.12.0/third-party-notices/lit_node_modules_lit-html/LICENSE", + "bytes": 1518, + "sha256": "2b66f9390afde29edfe4314daff93f01fb182e1178053e228120178987e5db75", + "member": "package/LICENSE" + }, + { + "package": "lit/node_modules/@lit/reactive-element", + "version": "2.0.0", + "source": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.0.tgz", + "archiveSha256": "4fa144d414133069ad6b814935e709081b48acc96de3a84edf8e07ee4a347fff", + "path": "shoelace/2.12.0/third-party-notices/lit_node_modules_lit_reactive-element/LICENSE", + "bytes": 1517, + "sha256": "45d31799d0db956cc3eb5469346abbd9b7025babc5ff29fab10d7095da992ef1", + "member": "package/LICENSE" + }, + { + "package": "@shoelace-style/localize", + "version": "3.1.2", + "source": "https://registry.npmjs.org/@shoelace-style/localize/-/localize-3.1.2.tgz", + "archiveSha256": "0b51c307d0342b818df6fb9fd1e0fca591ef7944e469aa121f391c871033afe8", + "path": "shoelace/2.12.0/third-party-notices/shoelace-style_localize/LICENSE.md", + "bytes": 1065, + "sha256": "ac9ffacbfc877b6f4253a481b3c0edbad0c019ac3506a3b51415b336f4a0c054", + "member": "package/LICENSE.md" + } + ] +} diff --git a/static/editor/vendor/shoelace/2.12.0/LICENSE.md b/static/editor/vendor/shoelace/2.12.0/LICENSE.md new file mode 100644 index 0000000..40f8e2e --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/LICENSE.md @@ -0,0 +1,7 @@ +Copyright (c) 2020 A Beautiful Site, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/LICENSE b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/LICENSE new file mode 100644 index 0000000..3f97be6 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019-2023 The Bootstrap Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/code.svg b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/code.svg new file mode 100644 index 0000000..079f5c6 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/code.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/link.svg b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/link.svg new file mode 100644 index 0000000..df35bc8 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/link.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/question-circle.svg b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/question-circle.svg new file mode 100644 index 0000000..1c8cbe7 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/question-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-bold.svg b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-bold.svg new file mode 100644 index 0000000..276d133 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-bold.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-h1.svg b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-h1.svg new file mode 100644 index 0000000..5b412fe --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-h1.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-h2.svg b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-h2.svg new file mode 100644 index 0000000..c92dd91 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-h2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-h3.svg b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-h3.svg new file mode 100644 index 0000000..7136286 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-h3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-italic.svg b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-italic.svg new file mode 100644 index 0000000..3ac6b09 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-italic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-underline.svg b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-underline.svg new file mode 100644 index 0000000..c299b8b --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/assets/icons/type-underline.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.24R5WE5A.js b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.24R5WE5A.js new file mode 100644 index 0000000..987c947 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.24R5WE5A.js @@ -0,0 +1,28 @@ +import { + component_styles_default +} from "./chunk.M2U2QT2K.js"; +import { + i +} from "./chunk.27ILGUWR.js"; + +// src/components/icon/icon.styles.ts +var icon_styles_default = i` + ${component_styles_default} + + :host { + display: inline-block; + width: 1em; + height: 1em; + box-sizing: content-box !important; + } + + svg { + display: block; + height: 100%; + width: 100%; + } +`; + +export { + icon_styles_default +}; diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.27ILGUWR.js b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.27ILGUWR.js new file mode 100644 index 0000000..82864b2 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.27ILGUWR.js @@ -0,0 +1,1265 @@ +// node_modules/lit-element/node_modules/@lit/reactive-element/css-tag.js +var t = globalThis; +var e = t.ShadowRoot && (void 0 === t.ShadyCSS || t.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype; +var s = Symbol(); +var o = /* @__PURE__ */ new WeakMap(); +var n = class { + constructor(t5, e7, o7) { + if (this._$cssResult$ = true, o7 !== s) + throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead."); + this.cssText = t5, this.t = e7; + } + get styleSheet() { + let t5 = this.o; + const s6 = this.t; + if (e && void 0 === t5) { + const e7 = void 0 !== s6 && 1 === s6.length; + e7 && (t5 = o.get(s6)), void 0 === t5 && ((this.o = t5 = new CSSStyleSheet()).replaceSync(this.cssText), e7 && o.set(s6, t5)); + } + return t5; + } + toString() { + return this.cssText; + } +}; +var r = (t5) => new n("string" == typeof t5 ? t5 : t5 + "", void 0, s); +var i = (t5, ...e7) => { + const o7 = 1 === t5.length ? t5[0] : e7.reduce((e8, s6, o8) => e8 + ((t6) => { + if (true === t6._$cssResult$) + return t6.cssText; + if ("number" == typeof t6) + return t6; + throw Error("Value passed to 'css' function must be a 'css' function result: " + t6 + ". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security."); + })(s6) + t5[o8 + 1], t5[0]); + return new n(o7, t5, s); +}; +var S = (s6, o7) => { + if (e) + s6.adoptedStyleSheets = o7.map((t5) => t5 instanceof CSSStyleSheet ? t5 : t5.styleSheet); + else + for (const e7 of o7) { + const o8 = document.createElement("style"), n7 = t.litNonce; + void 0 !== n7 && o8.setAttribute("nonce", n7), o8.textContent = e7.cssText, s6.appendChild(o8); + } +}; +var c = e ? (t5) => t5 : (t5) => t5 instanceof CSSStyleSheet ? ((t6) => { + let e7 = ""; + for (const s6 of t6.cssRules) + e7 += s6.cssText; + return r(e7); +})(t5) : t5; + +// node_modules/lit-element/node_modules/@lit/reactive-element/reactive-element.js +var { is: i2, defineProperty: e2, getOwnPropertyDescriptor: r2, getOwnPropertyNames: h, getOwnPropertySymbols: o2, getPrototypeOf: n2 } = Object; +var a = globalThis; +var c2 = a.trustedTypes; +var l = c2 ? c2.emptyScript : ""; +var p = a.reactiveElementPolyfillSupport; +var d = (t5, s6) => t5; +var u = { toAttribute(t5, s6) { + switch (s6) { + case Boolean: + t5 = t5 ? l : null; + break; + case Object: + case Array: + t5 = null == t5 ? t5 : JSON.stringify(t5); + } + return t5; +}, fromAttribute(t5, s6) { + let i7 = t5; + switch (s6) { + case Boolean: + i7 = null !== t5; + break; + case Number: + i7 = null === t5 ? null : Number(t5); + break; + case Object: + case Array: + try { + i7 = JSON.parse(t5); + } catch (t6) { + i7 = null; + } + } + return i7; +} }; +var f = (t5, s6) => !i2(t5, s6); +var y = { attribute: true, type: String, converter: u, reflect: false, hasChanged: f }; +var _a, _b; +(_a = Symbol.metadata) != null ? _a : Symbol.metadata = Symbol("metadata"), (_b = a.litPropertyMetadata) != null ? _b : a.litPropertyMetadata = /* @__PURE__ */ new WeakMap(); +var b = class extends HTMLElement { + static addInitializer(t5) { + var _a9; + this._$Ei(), ((_a9 = this.l) != null ? _a9 : this.l = []).push(t5); + } + static get observedAttributes() { + return this.finalize(), this._$Eh && [...this._$Eh.keys()]; + } + static createProperty(t5, s6 = y) { + if (s6.state && (s6.attribute = false), this._$Ei(), this.elementProperties.set(t5, s6), !s6.noAccessor) { + const i7 = Symbol(), r8 = this.getPropertyDescriptor(t5, i7, s6); + void 0 !== r8 && e2(this.prototype, t5, r8); + } + } + static getPropertyDescriptor(t5, s6, i7) { + var _a9; + const { get: e7, set: h5 } = (_a9 = r2(this.prototype, t5)) != null ? _a9 : { get() { + return this[s6]; + }, set(t6) { + this[s6] = t6; + } }; + return { get() { + return e7 == null ? void 0 : e7.call(this); + }, set(s7) { + const r8 = e7 == null ? void 0 : e7.call(this); + h5.call(this, s7), this.requestUpdate(t5, r8, i7); + }, configurable: true, enumerable: true }; + } + static getPropertyOptions(t5) { + var _a9; + return (_a9 = this.elementProperties.get(t5)) != null ? _a9 : y; + } + static _$Ei() { + if (this.hasOwnProperty(d("elementProperties"))) + return; + const t5 = n2(this); + t5.finalize(), void 0 !== t5.l && (this.l = [...t5.l]), this.elementProperties = new Map(t5.elementProperties); + } + static finalize() { + if (this.hasOwnProperty(d("finalized"))) + return; + if (this.finalized = true, this._$Ei(), this.hasOwnProperty(d("properties"))) { + const t6 = this.properties, s6 = [...h(t6), ...o2(t6)]; + for (const i7 of s6) + this.createProperty(i7, t6[i7]); + } + const t5 = this[Symbol.metadata]; + if (null !== t5) { + const s6 = litPropertyMetadata.get(t5); + if (void 0 !== s6) + for (const [t6, i7] of s6) + this.elementProperties.set(t6, i7); + } + this._$Eh = /* @__PURE__ */ new Map(); + for (const [t6, s6] of this.elementProperties) { + const i7 = this._$Eu(t6, s6); + void 0 !== i7 && this._$Eh.set(i7, t6); + } + this.elementStyles = this.finalizeStyles(this.styles); + } + static finalizeStyles(s6) { + const i7 = []; + if (Array.isArray(s6)) { + const e7 = new Set(s6.flat(1 / 0).reverse()); + for (const s7 of e7) + i7.unshift(c(s7)); + } else + void 0 !== s6 && i7.push(c(s6)); + return i7; + } + static _$Eu(t5, s6) { + const i7 = s6.attribute; + return false === i7 ? void 0 : "string" == typeof i7 ? i7 : "string" == typeof t5 ? t5.toLowerCase() : void 0; + } + constructor() { + super(), this._$Ep = void 0, this.isUpdatePending = false, this.hasUpdated = false, this._$Em = null, this._$Ev(); + } + _$Ev() { + var _a9; + this._$Eg = new Promise((t5) => this.enableUpdating = t5), this._$AL = /* @__PURE__ */ new Map(), this._$E_(), this.requestUpdate(), (_a9 = this.constructor.l) == null ? void 0 : _a9.forEach((t5) => t5(this)); + } + addController(t5) { + var _a9, _b4; + ((_a9 = this._$ES) != null ? _a9 : this._$ES = []).push(t5), void 0 !== this.renderRoot && this.isConnected && ((_b4 = t5.hostConnected) == null ? void 0 : _b4.call(t5)); + } + removeController(t5) { + var _a9; + (_a9 = this._$ES) == null ? void 0 : _a9.splice(this._$ES.indexOf(t5) >>> 0, 1); + } + _$E_() { + const t5 = /* @__PURE__ */ new Map(), s6 = this.constructor.elementProperties; + for (const i7 of s6.keys()) + this.hasOwnProperty(i7) && (t5.set(i7, this[i7]), delete this[i7]); + t5.size > 0 && (this._$Ep = t5); + } + createRenderRoot() { + var _a9; + const t5 = (_a9 = this.shadowRoot) != null ? _a9 : this.attachShadow(this.constructor.shadowRootOptions); + return S(t5, this.constructor.elementStyles), t5; + } + connectedCallback() { + var _a9, _b4; + (_a9 = this.renderRoot) != null ? _a9 : this.renderRoot = this.createRenderRoot(), this.enableUpdating(true), (_b4 = this._$ES) == null ? void 0 : _b4.forEach((t5) => { + var _a10; + return (_a10 = t5.hostConnected) == null ? void 0 : _a10.call(t5); + }); + } + enableUpdating(t5) { + } + disconnectedCallback() { + var _a9; + (_a9 = this._$ES) == null ? void 0 : _a9.forEach((t5) => { + var _a10; + return (_a10 = t5.hostDisconnected) == null ? void 0 : _a10.call(t5); + }); + } + attributeChangedCallback(t5, s6, i7) { + this._$AK(t5, i7); + } + _$EO(t5, s6) { + var _a9; + const i7 = this.constructor.elementProperties.get(t5), e7 = this.constructor._$Eu(t5, i7); + if (void 0 !== e7 && true === i7.reflect) { + const r8 = (void 0 !== ((_a9 = i7.converter) == null ? void 0 : _a9.toAttribute) ? i7.converter : u).toAttribute(s6, i7.type); + this._$Em = t5, null == r8 ? this.removeAttribute(e7) : this.setAttribute(e7, r8), this._$Em = null; + } + } + _$AK(t5, s6) { + var _a9; + const i7 = this.constructor, e7 = i7._$Eh.get(t5); + if (void 0 !== e7 && this._$Em !== e7) { + const t6 = i7.getPropertyOptions(e7), r8 = "function" == typeof t6.converter ? { fromAttribute: t6.converter } : void 0 !== ((_a9 = t6.converter) == null ? void 0 : _a9.fromAttribute) ? t6.converter : u; + this._$Em = e7, this[e7] = r8.fromAttribute(s6, t6.type), this._$Em = null; + } + } + requestUpdate(t5, s6, i7, e7 = false, r8) { + var _a9; + if (void 0 !== t5) { + if (i7 != null ? i7 : i7 = this.constructor.getPropertyOptions(t5), !((_a9 = i7.hasChanged) != null ? _a9 : f)(e7 ? r8 : this[t5], s6)) + return; + this.C(t5, s6, i7); + } + false === this.isUpdatePending && (this._$Eg = this._$EP()); + } + C(t5, s6, i7) { + var _a9; + this._$AL.has(t5) || this._$AL.set(t5, s6), true === i7.reflect && this._$Em !== t5 && ((_a9 = this._$Ej) != null ? _a9 : this._$Ej = /* @__PURE__ */ new Set()).add(t5); + } + async _$EP() { + this.isUpdatePending = true; + try { + await this._$Eg; + } catch (t6) { + Promise.reject(t6); + } + const t5 = this.scheduleUpdate(); + return null != t5 && await t5, !this.isUpdatePending; + } + scheduleUpdate() { + return this.performUpdate(); + } + performUpdate() { + var _a9; + if (!this.isUpdatePending) + return; + if (!this.hasUpdated) { + if (this._$Ep) { + for (const [t7, s7] of this._$Ep) + this[t7] = s7; + this._$Ep = void 0; + } + const t6 = this.constructor.elementProperties; + if (t6.size > 0) + for (const [s7, i7] of t6) + true !== i7.wrapped || this._$AL.has(s7) || void 0 === this[s7] || this.C(s7, this[s7], i7); + } + let t5 = false; + const s6 = this._$AL; + try { + t5 = this.shouldUpdate(s6), t5 ? (this.willUpdate(s6), (_a9 = this._$ES) == null ? void 0 : _a9.forEach((t6) => { + var _a10; + return (_a10 = t6.hostUpdate) == null ? void 0 : _a10.call(t6); + }), this.update(s6)) : this._$ET(); + } catch (s7) { + throw t5 = false, this._$ET(), s7; + } + t5 && this._$AE(s6); + } + willUpdate(t5) { + } + _$AE(t5) { + var _a9; + (_a9 = this._$ES) == null ? void 0 : _a9.forEach((t6) => { + var _a10; + return (_a10 = t6.hostUpdated) == null ? void 0 : _a10.call(t6); + }), this.hasUpdated || (this.hasUpdated = true, this.firstUpdated(t5)), this.updated(t5); + } + _$ET() { + this._$AL = /* @__PURE__ */ new Map(), this.isUpdatePending = false; + } + get updateComplete() { + return this.getUpdateComplete(); + } + getUpdateComplete() { + return this._$Eg; + } + shouldUpdate(t5) { + return true; + } + update(t5) { + this._$Ej && (this._$Ej = this._$Ej.forEach((t6) => this._$EO(t6, this[t6]))), this._$ET(); + } + updated(t5) { + } + firstUpdated(t5) { + } +}; +var _a2; +b.elementStyles = [], b.shadowRootOptions = { mode: "open" }, b[d("elementProperties")] = /* @__PURE__ */ new Map(), b[d("finalized")] = /* @__PURE__ */ new Map(), p == null ? void 0 : p({ ReactiveElement: b }), ((_a2 = a.reactiveElementVersions) != null ? _a2 : a.reactiveElementVersions = []).push("2.0.0"); + +// node_modules/lit-element/node_modules/lit-html/lit-html.js +var t2 = globalThis; +var i3 = t2.trustedTypes; +var s2 = i3 ? i3.createPolicy("lit-html", { createHTML: (t5) => t5 }) : void 0; +var e3 = "$lit$"; +var h2 = `lit$${(Math.random() + "").slice(9)}$`; +var o3 = "?" + h2; +var n3 = `<${o3}>`; +var r3 = document; +var l2 = () => r3.createComment(""); +var c3 = (t5) => null === t5 || "object" != typeof t5 && "function" != typeof t5; +var a2 = Array.isArray; +var u2 = (t5) => a2(t5) || "function" == typeof (t5 == null ? void 0 : t5[Symbol.iterator]); +var d2 = "[ \n\f\r]"; +var f2 = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g; +var v = /-->/g; +var _ = />/g; +var m = RegExp(`>|${d2}(?:([^\\s"'>=/]+)(${d2}*=${d2}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`, "g"); +var p2 = /'/g; +var g = /"/g; +var $ = /^(?:script|style|textarea|title)$/i; +var y2 = (t5) => (i7, ...s6) => ({ _$litType$: t5, strings: i7, values: s6 }); +var x = y2(1); +var b2 = y2(2); +var w = Symbol.for("lit-noChange"); +var T = Symbol.for("lit-nothing"); +var A = /* @__PURE__ */ new WeakMap(); +var E = r3.createTreeWalker(r3, 129); +function C(t5, i7) { + if (!Array.isArray(t5) || !t5.hasOwnProperty("raw")) + throw Error("invalid template strings array"); + return void 0 !== s2 ? s2.createHTML(i7) : i7; +} +var P = (t5, i7) => { + const s6 = t5.length - 1, o7 = []; + let r8, l5 = 2 === i7 ? "" : "", c7 = f2; + for (let i8 = 0; i8 < s6; i8++) { + const s7 = t5[i8]; + let a5, u5, d5 = -1, y5 = 0; + for (; y5 < s7.length && (c7.lastIndex = y5, u5 = c7.exec(s7), null !== u5); ) + y5 = c7.lastIndex, c7 === f2 ? "!--" === u5[1] ? c7 = v : void 0 !== u5[1] ? c7 = _ : void 0 !== u5[2] ? ($.test(u5[2]) && (r8 = RegExp("" === u5[0] ? (c7 = r8 != null ? r8 : f2, d5 = -1) : void 0 === u5[1] ? d5 = -2 : (d5 = c7.lastIndex - u5[2].length, a5 = u5[1], c7 = void 0 === u5[3] ? m : '"' === u5[3] ? g : p2) : c7 === g || c7 === p2 ? c7 = m : c7 === v || c7 === _ ? c7 = f2 : (c7 = m, r8 = void 0); + const x3 = c7 === m && t5[i8 + 1].startsWith("/>") ? " " : ""; + l5 += c7 === f2 ? s7 + n3 : d5 >= 0 ? (o7.push(a5), s7.slice(0, d5) + e3 + s7.slice(d5) + h2 + x3) : s7 + h2 + (-2 === d5 ? i8 : x3); + } + return [C(t5, l5 + (t5[s6] || "") + (2 === i7 ? "" : "")), o7]; +}; +var V = class _V { + constructor({ strings: t5, _$litType$: s6 }, n7) { + let r8; + this.parts = []; + let c7 = 0, a5 = 0; + const u5 = t5.length - 1, d5 = this.parts, [f5, v3] = P(t5, s6); + if (this.el = _V.createElement(f5, n7), E.currentNode = this.el.content, 2 === s6) { + const t6 = this.el.content.firstChild; + t6.replaceWith(...t6.childNodes); + } + for (; null !== (r8 = E.nextNode()) && d5.length < u5; ) { + if (1 === r8.nodeType) { + if (r8.hasAttributes()) + for (const t6 of r8.getAttributeNames()) + if (t6.endsWith(e3)) { + const i7 = v3[a5++], s7 = r8.getAttribute(t6).split(h2), e7 = /([.?@])?(.*)/.exec(i7); + d5.push({ type: 1, index: c7, name: e7[2], strings: s7, ctor: "." === e7[1] ? k : "?" === e7[1] ? H : "@" === e7[1] ? I : R }), r8.removeAttribute(t6); + } else + t6.startsWith(h2) && (d5.push({ type: 6, index: c7 }), r8.removeAttribute(t6)); + if ($.test(r8.tagName)) { + const t6 = r8.textContent.split(h2), s7 = t6.length - 1; + if (s7 > 0) { + r8.textContent = i3 ? i3.emptyScript : ""; + for (let i7 = 0; i7 < s7; i7++) + r8.append(t6[i7], l2()), E.nextNode(), d5.push({ type: 2, index: ++c7 }); + r8.append(t6[s7], l2()); + } + } + } else if (8 === r8.nodeType) + if (r8.data === o3) + d5.push({ type: 2, index: c7 }); + else { + let t6 = -1; + for (; -1 !== (t6 = r8.data.indexOf(h2, t6 + 1)); ) + d5.push({ type: 7, index: c7 }), t6 += h2.length - 1; + } + c7++; + } + } + static createElement(t5, i7) { + const s6 = r3.createElement("template"); + return s6.innerHTML = t5, s6; + } +}; +function N(t5, i7, s6 = t5, e7) { + var _a9, _b3, _c; + if (i7 === w) + return i7; + let h5 = void 0 !== e7 ? (_a9 = s6._$Co) == null ? void 0 : _a9[e7] : s6._$Cl; + const o7 = c3(i7) ? void 0 : i7._$litDirective$; + return (h5 == null ? void 0 : h5.constructor) !== o7 && ((_b3 = h5 == null ? void 0 : h5._$AO) == null ? void 0 : _b3.call(h5, false), void 0 === o7 ? h5 = void 0 : (h5 = new o7(t5), h5._$AT(t5, s6, e7)), void 0 !== e7 ? ((_c = s6._$Co) != null ? _c : s6._$Co = [])[e7] = h5 : s6._$Cl = h5), void 0 !== h5 && (i7 = N(t5, h5._$AS(t5, i7.values), h5, e7)), i7; +} +var S2 = class { + constructor(t5, i7) { + this._$AV = [], this._$AN = void 0, this._$AD = t5, this._$AM = i7; + } + get parentNode() { + return this._$AM.parentNode; + } + get _$AU() { + return this._$AM._$AU; + } + u(t5) { + var _a9; + const { el: { content: i7 }, parts: s6 } = this._$AD, e7 = ((_a9 = t5 == null ? void 0 : t5.creationScope) != null ? _a9 : r3).importNode(i7, true); + E.currentNode = e7; + let h5 = E.nextNode(), o7 = 0, n7 = 0, l5 = s6[0]; + for (; void 0 !== l5; ) { + if (o7 === l5.index) { + let i8; + 2 === l5.type ? i8 = new M(h5, h5.nextSibling, this, t5) : 1 === l5.type ? i8 = new l5.ctor(h5, l5.name, l5.strings, this, t5) : 6 === l5.type && (i8 = new L(h5, this, t5)), this._$AV.push(i8), l5 = s6[++n7]; + } + o7 !== (l5 == null ? void 0 : l5.index) && (h5 = E.nextNode(), o7++); + } + return E.currentNode = r3, e7; + } + p(t5) { + let i7 = 0; + for (const s6 of this._$AV) + void 0 !== s6 && (void 0 !== s6.strings ? (s6._$AI(t5, s6, i7), i7 += s6.strings.length - 2) : s6._$AI(t5[i7])), i7++; + } +}; +var M = class _M { + get _$AU() { + var _a9, _b3; + return (_b3 = (_a9 = this._$AM) == null ? void 0 : _a9._$AU) != null ? _b3 : this._$Cv; + } + constructor(t5, i7, s6, e7) { + var _a9; + this.type = 2, this._$AH = T, this._$AN = void 0, this._$AA = t5, this._$AB = i7, this._$AM = s6, this.options = e7, this._$Cv = (_a9 = e7 == null ? void 0 : e7.isConnected) != null ? _a9 : true; + } + get parentNode() { + let t5 = this._$AA.parentNode; + const i7 = this._$AM; + return void 0 !== i7 && 11 === (t5 == null ? void 0 : t5.nodeType) && (t5 = i7.parentNode), t5; + } + get startNode() { + return this._$AA; + } + get endNode() { + return this._$AB; + } + _$AI(t5, i7 = this) { + t5 = N(this, t5, i7), c3(t5) ? t5 === T || null == t5 || "" === t5 ? (this._$AH !== T && this._$AR(), this._$AH = T) : t5 !== this._$AH && t5 !== w && this._(t5) : void 0 !== t5._$litType$ ? this.g(t5) : void 0 !== t5.nodeType ? this.$(t5) : u2(t5) ? this.T(t5) : this._(t5); + } + k(t5) { + return this._$AA.parentNode.insertBefore(t5, this._$AB); + } + $(t5) { + this._$AH !== t5 && (this._$AR(), this._$AH = this.k(t5)); + } + _(t5) { + this._$AH !== T && c3(this._$AH) ? this._$AA.nextSibling.data = t5 : this.$(r3.createTextNode(t5)), this._$AH = t5; + } + g(t5) { + var _a9; + const { values: i7, _$litType$: s6 } = t5, e7 = "number" == typeof s6 ? this._$AC(t5) : (void 0 === s6.el && (s6.el = V.createElement(C(s6.h, s6.h[0]), this.options)), s6); + if (((_a9 = this._$AH) == null ? void 0 : _a9._$AD) === e7) + this._$AH.p(i7); + else { + const t6 = new S2(e7, this), s7 = t6.u(this.options); + t6.p(i7), this.$(s7), this._$AH = t6; + } + } + _$AC(t5) { + let i7 = A.get(t5.strings); + return void 0 === i7 && A.set(t5.strings, i7 = new V(t5)), i7; + } + T(t5) { + a2(this._$AH) || (this._$AH = [], this._$AR()); + const i7 = this._$AH; + let s6, e7 = 0; + for (const h5 of t5) + e7 === i7.length ? i7.push(s6 = new _M(this.k(l2()), this.k(l2()), this, this.options)) : s6 = i7[e7], s6._$AI(h5), e7++; + e7 < i7.length && (this._$AR(s6 && s6._$AB.nextSibling, e7), i7.length = e7); + } + _$AR(t5 = this._$AA.nextSibling, i7) { + var _a9; + for ((_a9 = this._$AP) == null ? void 0 : _a9.call(this, false, true, i7); t5 && t5 !== this._$AB; ) { + const i8 = t5.nextSibling; + t5.remove(), t5 = i8; + } + } + setConnected(t5) { + var _a9; + void 0 === this._$AM && (this._$Cv = t5, (_a9 = this._$AP) == null ? void 0 : _a9.call(this, t5)); + } +}; +var R = class { + get tagName() { + return this.element.tagName; + } + get _$AU() { + return this._$AM._$AU; + } + constructor(t5, i7, s6, e7, h5) { + this.type = 1, this._$AH = T, this._$AN = void 0, this.element = t5, this.name = i7, this._$AM = e7, this.options = h5, s6.length > 2 || "" !== s6[0] || "" !== s6[1] ? (this._$AH = Array(s6.length - 1).fill(new String()), this.strings = s6) : this._$AH = T; + } + _$AI(t5, i7 = this, s6, e7) { + const h5 = this.strings; + let o7 = false; + if (void 0 === h5) + t5 = N(this, t5, i7, 0), o7 = !c3(t5) || t5 !== this._$AH && t5 !== w, o7 && (this._$AH = t5); + else { + const e8 = t5; + let n7, r8; + for (t5 = h5[0], n7 = 0; n7 < h5.length - 1; n7++) + r8 = N(this, e8[s6 + n7], i7, n7), r8 === w && (r8 = this._$AH[n7]), o7 || (o7 = !c3(r8) || r8 !== this._$AH[n7]), r8 === T ? t5 = T : t5 !== T && (t5 += (r8 != null ? r8 : "") + h5[n7 + 1]), this._$AH[n7] = r8; + } + o7 && !e7 && this.j(t5); + } + j(t5) { + t5 === T ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, t5 != null ? t5 : ""); + } +}; +var k = class extends R { + constructor() { + super(...arguments), this.type = 3; + } + j(t5) { + this.element[this.name] = t5 === T ? void 0 : t5; + } +}; +var H = class extends R { + constructor() { + super(...arguments), this.type = 4; + } + j(t5) { + this.element.toggleAttribute(this.name, !!t5 && t5 !== T); + } +}; +var I = class extends R { + constructor(t5, i7, s6, e7, h5) { + super(t5, i7, s6, e7, h5), this.type = 5; + } + _$AI(t5, i7 = this) { + var _a9; + if ((t5 = (_a9 = N(this, t5, i7, 0)) != null ? _a9 : T) === w) + return; + const s6 = this._$AH, e7 = t5 === T && s6 !== T || t5.capture !== s6.capture || t5.once !== s6.once || t5.passive !== s6.passive, h5 = t5 !== T && (s6 === T || e7); + e7 && this.element.removeEventListener(this.name, this, s6), h5 && this.element.addEventListener(this.name, this, t5), this._$AH = t5; + } + handleEvent(t5) { + var _a9, _b3; + "function" == typeof this._$AH ? this._$AH.call((_b3 = (_a9 = this.options) == null ? void 0 : _a9.host) != null ? _b3 : this.element, t5) : this._$AH.handleEvent(t5); + } +}; +var L = class { + constructor(t5, i7, s6) { + this.element = t5, this.type = 6, this._$AN = void 0, this._$AM = i7, this.options = s6; + } + get _$AU() { + return this._$AM._$AU; + } + _$AI(t5) { + N(this, t5); + } +}; +var Z = t2.litHtmlPolyfillSupport; +var _a3; +Z == null ? void 0 : Z(V, M), ((_a3 = t2.litHtmlVersions) != null ? _a3 : t2.litHtmlVersions = []).push("3.0.0"); +var j = (t5, i7, s6) => { + var _a9, _b3; + const e7 = (_a9 = s6 == null ? void 0 : s6.renderBefore) != null ? _a9 : i7; + let h5 = e7._$litPart$; + if (void 0 === h5) { + const t6 = (_b3 = s6 == null ? void 0 : s6.renderBefore) != null ? _b3 : null; + e7._$litPart$ = h5 = new M(i7.insertBefore(l2(), t6), t6, void 0, s6 != null ? s6 : {}); + } + return h5._$AI(t5), h5; +}; + +// node_modules/lit/node_modules/@lit/reactive-element/css-tag.js +var t3 = globalThis; +var e4 = t3.ShadowRoot && (void 0 === t3.ShadyCSS || t3.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype; +var s3 = Symbol(); +var o4 = /* @__PURE__ */ new WeakMap(); +var n4 = class { + constructor(t5, e7, o7) { + if (this._$cssResult$ = true, o7 !== s3) + throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead."); + this.cssText = t5, this.t = e7; + } + get styleSheet() { + let t5 = this.o; + const s6 = this.t; + if (e4 && void 0 === t5) { + const e7 = void 0 !== s6 && 1 === s6.length; + e7 && (t5 = o4.get(s6)), void 0 === t5 && ((this.o = t5 = new CSSStyleSheet()).replaceSync(this.cssText), e7 && o4.set(s6, t5)); + } + return t5; + } + toString() { + return this.cssText; + } +}; +var r4 = (t5) => new n4("string" == typeof t5 ? t5 : t5 + "", void 0, s3); +var S3 = (s6, o7) => { + if (e4) + s6.adoptedStyleSheets = o7.map((t5) => t5 instanceof CSSStyleSheet ? t5 : t5.styleSheet); + else + for (const e7 of o7) { + const o8 = document.createElement("style"), n7 = t3.litNonce; + void 0 !== n7 && o8.setAttribute("nonce", n7), o8.textContent = e7.cssText, s6.appendChild(o8); + } +}; +var c4 = e4 ? (t5) => t5 : (t5) => t5 instanceof CSSStyleSheet ? ((t6) => { + let e7 = ""; + for (const s6 of t6.cssRules) + e7 += s6.cssText; + return r4(e7); +})(t5) : t5; + +// node_modules/lit/node_modules/@lit/reactive-element/reactive-element.js +var { is: i5, defineProperty: e5, getOwnPropertyDescriptor: r5, getOwnPropertyNames: h3, getOwnPropertySymbols: o5, getPrototypeOf: n5 } = Object; +var a3 = globalThis; +var c5 = a3.trustedTypes; +var l3 = c5 ? c5.emptyScript : ""; +var p3 = a3.reactiveElementPolyfillSupport; +var d3 = (t5, s6) => t5; +var u3 = { toAttribute(t5, s6) { + switch (s6) { + case Boolean: + t5 = t5 ? l3 : null; + break; + case Object: + case Array: + t5 = null == t5 ? t5 : JSON.stringify(t5); + } + return t5; +}, fromAttribute(t5, s6) { + let i7 = t5; + switch (s6) { + case Boolean: + i7 = null !== t5; + break; + case Number: + i7 = null === t5 ? null : Number(t5); + break; + case Object: + case Array: + try { + i7 = JSON.parse(t5); + } catch (t6) { + i7 = null; + } + } + return i7; +} }; +var f3 = (t5, s6) => !i5(t5, s6); +var y3 = { attribute: true, type: String, converter: u3, reflect: false, hasChanged: f3 }; +var _a4, _b2; +(_a4 = Symbol.metadata) != null ? _a4 : Symbol.metadata = Symbol("metadata"), (_b2 = a3.litPropertyMetadata) != null ? _b2 : a3.litPropertyMetadata = /* @__PURE__ */ new WeakMap(); +var b3 = class extends HTMLElement { + static addInitializer(t5) { + var _a9; + this._$Ei(), ((_a9 = this.l) != null ? _a9 : this.l = []).push(t5); + } + static get observedAttributes() { + return this.finalize(), this._$Eh && [...this._$Eh.keys()]; + } + static createProperty(t5, s6 = y3) { + if (s6.state && (s6.attribute = false), this._$Ei(), this.elementProperties.set(t5, s6), !s6.noAccessor) { + const i7 = Symbol(), r8 = this.getPropertyDescriptor(t5, i7, s6); + void 0 !== r8 && e5(this.prototype, t5, r8); + } + } + static getPropertyDescriptor(t5, s6, i7) { + var _a9; + const { get: e7, set: h5 } = (_a9 = r5(this.prototype, t5)) != null ? _a9 : { get() { + return this[s6]; + }, set(t6) { + this[s6] = t6; + } }; + return { get() { + return e7 == null ? void 0 : e7.call(this); + }, set(s7) { + const r8 = e7 == null ? void 0 : e7.call(this); + h5.call(this, s7), this.requestUpdate(t5, r8, i7); + }, configurable: true, enumerable: true }; + } + static getPropertyOptions(t5) { + var _a9; + return (_a9 = this.elementProperties.get(t5)) != null ? _a9 : y3; + } + static _$Ei() { + if (this.hasOwnProperty(d3("elementProperties"))) + return; + const t5 = n5(this); + t5.finalize(), void 0 !== t5.l && (this.l = [...t5.l]), this.elementProperties = new Map(t5.elementProperties); + } + static finalize() { + if (this.hasOwnProperty(d3("finalized"))) + return; + if (this.finalized = true, this._$Ei(), this.hasOwnProperty(d3("properties"))) { + const t6 = this.properties, s6 = [...h3(t6), ...o5(t6)]; + for (const i7 of s6) + this.createProperty(i7, t6[i7]); + } + const t5 = this[Symbol.metadata]; + if (null !== t5) { + const s6 = litPropertyMetadata.get(t5); + if (void 0 !== s6) + for (const [t6, i7] of s6) + this.elementProperties.set(t6, i7); + } + this._$Eh = /* @__PURE__ */ new Map(); + for (const [t6, s6] of this.elementProperties) { + const i7 = this._$Eu(t6, s6); + void 0 !== i7 && this._$Eh.set(i7, t6); + } + this.elementStyles = this.finalizeStyles(this.styles); + } + static finalizeStyles(s6) { + const i7 = []; + if (Array.isArray(s6)) { + const e7 = new Set(s6.flat(1 / 0).reverse()); + for (const s7 of e7) + i7.unshift(c4(s7)); + } else + void 0 !== s6 && i7.push(c4(s6)); + return i7; + } + static _$Eu(t5, s6) { + const i7 = s6.attribute; + return false === i7 ? void 0 : "string" == typeof i7 ? i7 : "string" == typeof t5 ? t5.toLowerCase() : void 0; + } + constructor() { + super(), this._$Ep = void 0, this.isUpdatePending = false, this.hasUpdated = false, this._$Em = null, this._$Ev(); + } + _$Ev() { + var _a9; + this._$Eg = new Promise((t5) => this.enableUpdating = t5), this._$AL = /* @__PURE__ */ new Map(), this._$E_(), this.requestUpdate(), (_a9 = this.constructor.l) == null ? void 0 : _a9.forEach((t5) => t5(this)); + } + addController(t5) { + var _a9, _b4; + ((_a9 = this._$ES) != null ? _a9 : this._$ES = []).push(t5), void 0 !== this.renderRoot && this.isConnected && ((_b4 = t5.hostConnected) == null ? void 0 : _b4.call(t5)); + } + removeController(t5) { + var _a9; + (_a9 = this._$ES) == null ? void 0 : _a9.splice(this._$ES.indexOf(t5) >>> 0, 1); + } + _$E_() { + const t5 = /* @__PURE__ */ new Map(), s6 = this.constructor.elementProperties; + for (const i7 of s6.keys()) + this.hasOwnProperty(i7) && (t5.set(i7, this[i7]), delete this[i7]); + t5.size > 0 && (this._$Ep = t5); + } + createRenderRoot() { + var _a9; + const t5 = (_a9 = this.shadowRoot) != null ? _a9 : this.attachShadow(this.constructor.shadowRootOptions); + return S3(t5, this.constructor.elementStyles), t5; + } + connectedCallback() { + var _a9, _b4; + (_a9 = this.renderRoot) != null ? _a9 : this.renderRoot = this.createRenderRoot(), this.enableUpdating(true), (_b4 = this._$ES) == null ? void 0 : _b4.forEach((t5) => { + var _a10; + return (_a10 = t5.hostConnected) == null ? void 0 : _a10.call(t5); + }); + } + enableUpdating(t5) { + } + disconnectedCallback() { + var _a9; + (_a9 = this._$ES) == null ? void 0 : _a9.forEach((t5) => { + var _a10; + return (_a10 = t5.hostDisconnected) == null ? void 0 : _a10.call(t5); + }); + } + attributeChangedCallback(t5, s6, i7) { + this._$AK(t5, i7); + } + _$EO(t5, s6) { + var _a9; + const i7 = this.constructor.elementProperties.get(t5), e7 = this.constructor._$Eu(t5, i7); + if (void 0 !== e7 && true === i7.reflect) { + const r8 = (void 0 !== ((_a9 = i7.converter) == null ? void 0 : _a9.toAttribute) ? i7.converter : u3).toAttribute(s6, i7.type); + this._$Em = t5, null == r8 ? this.removeAttribute(e7) : this.setAttribute(e7, r8), this._$Em = null; + } + } + _$AK(t5, s6) { + var _a9; + const i7 = this.constructor, e7 = i7._$Eh.get(t5); + if (void 0 !== e7 && this._$Em !== e7) { + const t6 = i7.getPropertyOptions(e7), r8 = "function" == typeof t6.converter ? { fromAttribute: t6.converter } : void 0 !== ((_a9 = t6.converter) == null ? void 0 : _a9.fromAttribute) ? t6.converter : u3; + this._$Em = e7, this[e7] = r8.fromAttribute(s6, t6.type), this._$Em = null; + } + } + requestUpdate(t5, s6, i7, e7 = false, r8) { + var _a9; + if (void 0 !== t5) { + if (i7 != null ? i7 : i7 = this.constructor.getPropertyOptions(t5), !((_a9 = i7.hasChanged) != null ? _a9 : f3)(e7 ? r8 : this[t5], s6)) + return; + this.C(t5, s6, i7); + } + false === this.isUpdatePending && (this._$Eg = this._$EP()); + } + C(t5, s6, i7) { + var _a9; + this._$AL.has(t5) || this._$AL.set(t5, s6), true === i7.reflect && this._$Em !== t5 && ((_a9 = this._$Ej) != null ? _a9 : this._$Ej = /* @__PURE__ */ new Set()).add(t5); + } + async _$EP() { + this.isUpdatePending = true; + try { + await this._$Eg; + } catch (t6) { + Promise.reject(t6); + } + const t5 = this.scheduleUpdate(); + return null != t5 && await t5, !this.isUpdatePending; + } + scheduleUpdate() { + return this.performUpdate(); + } + performUpdate() { + var _a9; + if (!this.isUpdatePending) + return; + if (!this.hasUpdated) { + if (this._$Ep) { + for (const [t7, s7] of this._$Ep) + this[t7] = s7; + this._$Ep = void 0; + } + const t6 = this.constructor.elementProperties; + if (t6.size > 0) + for (const [s7, i7] of t6) + true !== i7.wrapped || this._$AL.has(s7) || void 0 === this[s7] || this.C(s7, this[s7], i7); + } + let t5 = false; + const s6 = this._$AL; + try { + t5 = this.shouldUpdate(s6), t5 ? (this.willUpdate(s6), (_a9 = this._$ES) == null ? void 0 : _a9.forEach((t6) => { + var _a10; + return (_a10 = t6.hostUpdate) == null ? void 0 : _a10.call(t6); + }), this.update(s6)) : this._$ET(); + } catch (s7) { + throw t5 = false, this._$ET(), s7; + } + t5 && this._$AE(s6); + } + willUpdate(t5) { + } + _$AE(t5) { + var _a9; + (_a9 = this._$ES) == null ? void 0 : _a9.forEach((t6) => { + var _a10; + return (_a10 = t6.hostUpdated) == null ? void 0 : _a10.call(t6); + }), this.hasUpdated || (this.hasUpdated = true, this.firstUpdated(t5)), this.updated(t5); + } + _$ET() { + this._$AL = /* @__PURE__ */ new Map(), this.isUpdatePending = false; + } + get updateComplete() { + return this.getUpdateComplete(); + } + getUpdateComplete() { + return this._$Eg; + } + shouldUpdate(t5) { + return true; + } + update(t5) { + this._$Ej && (this._$Ej = this._$Ej.forEach((t6) => this._$EO(t6, this[t6]))), this._$ET(); + } + updated(t5) { + } + firstUpdated(t5) { + } +}; +var _a5; +b3.elementStyles = [], b3.shadowRootOptions = { mode: "open" }, b3[d3("elementProperties")] = /* @__PURE__ */ new Map(), b3[d3("finalized")] = /* @__PURE__ */ new Map(), p3 == null ? void 0 : p3({ ReactiveElement: b3 }), ((_a5 = a3.reactiveElementVersions) != null ? _a5 : a3.reactiveElementVersions = []).push("2.0.0"); + +// node_modules/lit/node_modules/lit-html/lit-html.js +var t4 = globalThis; +var i6 = t4.trustedTypes; +var s4 = i6 ? i6.createPolicy("lit-html", { createHTML: (t5) => t5 }) : void 0; +var e6 = "$lit$"; +var h4 = `lit$${(Math.random() + "").slice(9)}$`; +var o6 = "?" + h4; +var n6 = `<${o6}>`; +var r6 = document; +var l4 = () => r6.createComment(""); +var c6 = (t5) => null === t5 || "object" != typeof t5 && "function" != typeof t5; +var a4 = Array.isArray; +var u4 = (t5) => a4(t5) || "function" == typeof (t5 == null ? void 0 : t5[Symbol.iterator]); +var d4 = "[ \n\f\r]"; +var f4 = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g; +var v2 = /-->/g; +var _2 = />/g; +var m2 = RegExp(`>|${d4}(?:([^\\s"'>=/]+)(${d4}*=${d4}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`, "g"); +var p4 = /'/g; +var g2 = /"/g; +var $2 = /^(?:script|style|textarea|title)$/i; +var y4 = (t5) => (i7, ...s6) => ({ _$litType$: t5, strings: i7, values: s6 }); +var x2 = y4(1); +var b4 = y4(2); +var w2 = Symbol.for("lit-noChange"); +var T2 = Symbol.for("lit-nothing"); +var A2 = /* @__PURE__ */ new WeakMap(); +var E2 = r6.createTreeWalker(r6, 129); +function C2(t5, i7) { + if (!Array.isArray(t5) || !t5.hasOwnProperty("raw")) + throw Error("invalid template strings array"); + return void 0 !== s4 ? s4.createHTML(i7) : i7; +} +var P2 = (t5, i7) => { + const s6 = t5.length - 1, o7 = []; + let r8, l5 = 2 === i7 ? "" : "", c7 = f4; + for (let i8 = 0; i8 < s6; i8++) { + const s7 = t5[i8]; + let a5, u5, d5 = -1, y5 = 0; + for (; y5 < s7.length && (c7.lastIndex = y5, u5 = c7.exec(s7), null !== u5); ) + y5 = c7.lastIndex, c7 === f4 ? "!--" === u5[1] ? c7 = v2 : void 0 !== u5[1] ? c7 = _2 : void 0 !== u5[2] ? ($2.test(u5[2]) && (r8 = RegExp("" === u5[0] ? (c7 = r8 != null ? r8 : f4, d5 = -1) : void 0 === u5[1] ? d5 = -2 : (d5 = c7.lastIndex - u5[2].length, a5 = u5[1], c7 = void 0 === u5[3] ? m2 : '"' === u5[3] ? g2 : p4) : c7 === g2 || c7 === p4 ? c7 = m2 : c7 === v2 || c7 === _2 ? c7 = f4 : (c7 = m2, r8 = void 0); + const x3 = c7 === m2 && t5[i8 + 1].startsWith("/>") ? " " : ""; + l5 += c7 === f4 ? s7 + n6 : d5 >= 0 ? (o7.push(a5), s7.slice(0, d5) + e6 + s7.slice(d5) + h4 + x3) : s7 + h4 + (-2 === d5 ? i8 : x3); + } + return [C2(t5, l5 + (t5[s6] || "") + (2 === i7 ? "" : "")), o7]; +}; +var V2 = class _V { + constructor({ strings: t5, _$litType$: s6 }, n7) { + let r8; + this.parts = []; + let c7 = 0, a5 = 0; + const u5 = t5.length - 1, d5 = this.parts, [f5, v3] = P2(t5, s6); + if (this.el = _V.createElement(f5, n7), E2.currentNode = this.el.content, 2 === s6) { + const t6 = this.el.content.firstChild; + t6.replaceWith(...t6.childNodes); + } + for (; null !== (r8 = E2.nextNode()) && d5.length < u5; ) { + if (1 === r8.nodeType) { + if (r8.hasAttributes()) + for (const t6 of r8.getAttributeNames()) + if (t6.endsWith(e6)) { + const i7 = v3[a5++], s7 = r8.getAttribute(t6).split(h4), e7 = /([.?@])?(.*)/.exec(i7); + d5.push({ type: 1, index: c7, name: e7[2], strings: s7, ctor: "." === e7[1] ? k2 : "?" === e7[1] ? H2 : "@" === e7[1] ? I2 : R2 }), r8.removeAttribute(t6); + } else + t6.startsWith(h4) && (d5.push({ type: 6, index: c7 }), r8.removeAttribute(t6)); + if ($2.test(r8.tagName)) { + const t6 = r8.textContent.split(h4), s7 = t6.length - 1; + if (s7 > 0) { + r8.textContent = i6 ? i6.emptyScript : ""; + for (let i7 = 0; i7 < s7; i7++) + r8.append(t6[i7], l4()), E2.nextNode(), d5.push({ type: 2, index: ++c7 }); + r8.append(t6[s7], l4()); + } + } + } else if (8 === r8.nodeType) + if (r8.data === o6) + d5.push({ type: 2, index: c7 }); + else { + let t6 = -1; + for (; -1 !== (t6 = r8.data.indexOf(h4, t6 + 1)); ) + d5.push({ type: 7, index: c7 }), t6 += h4.length - 1; + } + c7++; + } + } + static createElement(t5, i7) { + const s6 = r6.createElement("template"); + return s6.innerHTML = t5, s6; + } +}; +function N2(t5, i7, s6 = t5, e7) { + var _a9, _b3, _c; + if (i7 === w2) + return i7; + let h5 = void 0 !== e7 ? (_a9 = s6._$Co) == null ? void 0 : _a9[e7] : s6._$Cl; + const o7 = c6(i7) ? void 0 : i7._$litDirective$; + return (h5 == null ? void 0 : h5.constructor) !== o7 && ((_b3 = h5 == null ? void 0 : h5._$AO) == null ? void 0 : _b3.call(h5, false), void 0 === o7 ? h5 = void 0 : (h5 = new o7(t5), h5._$AT(t5, s6, e7)), void 0 !== e7 ? ((_c = s6._$Co) != null ? _c : s6._$Co = [])[e7] = h5 : s6._$Cl = h5), void 0 !== h5 && (i7 = N2(t5, h5._$AS(t5, i7.values), h5, e7)), i7; +} +var S4 = class { + constructor(t5, i7) { + this._$AV = [], this._$AN = void 0, this._$AD = t5, this._$AM = i7; + } + get parentNode() { + return this._$AM.parentNode; + } + get _$AU() { + return this._$AM._$AU; + } + u(t5) { + var _a9; + const { el: { content: i7 }, parts: s6 } = this._$AD, e7 = ((_a9 = t5 == null ? void 0 : t5.creationScope) != null ? _a9 : r6).importNode(i7, true); + E2.currentNode = e7; + let h5 = E2.nextNode(), o7 = 0, n7 = 0, l5 = s6[0]; + for (; void 0 !== l5; ) { + if (o7 === l5.index) { + let i8; + 2 === l5.type ? i8 = new M2(h5, h5.nextSibling, this, t5) : 1 === l5.type ? i8 = new l5.ctor(h5, l5.name, l5.strings, this, t5) : 6 === l5.type && (i8 = new L2(h5, this, t5)), this._$AV.push(i8), l5 = s6[++n7]; + } + o7 !== (l5 == null ? void 0 : l5.index) && (h5 = E2.nextNode(), o7++); + } + return E2.currentNode = r6, e7; + } + p(t5) { + let i7 = 0; + for (const s6 of this._$AV) + void 0 !== s6 && (void 0 !== s6.strings ? (s6._$AI(t5, s6, i7), i7 += s6.strings.length - 2) : s6._$AI(t5[i7])), i7++; + } +}; +var M2 = class _M { + get _$AU() { + var _a9, _b3; + return (_b3 = (_a9 = this._$AM) == null ? void 0 : _a9._$AU) != null ? _b3 : this._$Cv; + } + constructor(t5, i7, s6, e7) { + var _a9; + this.type = 2, this._$AH = T2, this._$AN = void 0, this._$AA = t5, this._$AB = i7, this._$AM = s6, this.options = e7, this._$Cv = (_a9 = e7 == null ? void 0 : e7.isConnected) != null ? _a9 : true; + } + get parentNode() { + let t5 = this._$AA.parentNode; + const i7 = this._$AM; + return void 0 !== i7 && 11 === (t5 == null ? void 0 : t5.nodeType) && (t5 = i7.parentNode), t5; + } + get startNode() { + return this._$AA; + } + get endNode() { + return this._$AB; + } + _$AI(t5, i7 = this) { + t5 = N2(this, t5, i7), c6(t5) ? t5 === T2 || null == t5 || "" === t5 ? (this._$AH !== T2 && this._$AR(), this._$AH = T2) : t5 !== this._$AH && t5 !== w2 && this._(t5) : void 0 !== t5._$litType$ ? this.g(t5) : void 0 !== t5.nodeType ? this.$(t5) : u4(t5) ? this.T(t5) : this._(t5); + } + k(t5) { + return this._$AA.parentNode.insertBefore(t5, this._$AB); + } + $(t5) { + this._$AH !== t5 && (this._$AR(), this._$AH = this.k(t5)); + } + _(t5) { + this._$AH !== T2 && c6(this._$AH) ? this._$AA.nextSibling.data = t5 : this.$(r6.createTextNode(t5)), this._$AH = t5; + } + g(t5) { + var _a9; + const { values: i7, _$litType$: s6 } = t5, e7 = "number" == typeof s6 ? this._$AC(t5) : (void 0 === s6.el && (s6.el = V2.createElement(C2(s6.h, s6.h[0]), this.options)), s6); + if (((_a9 = this._$AH) == null ? void 0 : _a9._$AD) === e7) + this._$AH.p(i7); + else { + const t6 = new S4(e7, this), s7 = t6.u(this.options); + t6.p(i7), this.$(s7), this._$AH = t6; + } + } + _$AC(t5) { + let i7 = A2.get(t5.strings); + return void 0 === i7 && A2.set(t5.strings, i7 = new V2(t5)), i7; + } + T(t5) { + a4(this._$AH) || (this._$AH = [], this._$AR()); + const i7 = this._$AH; + let s6, e7 = 0; + for (const h5 of t5) + e7 === i7.length ? i7.push(s6 = new _M(this.k(l4()), this.k(l4()), this, this.options)) : s6 = i7[e7], s6._$AI(h5), e7++; + e7 < i7.length && (this._$AR(s6 && s6._$AB.nextSibling, e7), i7.length = e7); + } + _$AR(t5 = this._$AA.nextSibling, i7) { + var _a9; + for ((_a9 = this._$AP) == null ? void 0 : _a9.call(this, false, true, i7); t5 && t5 !== this._$AB; ) { + const i8 = t5.nextSibling; + t5.remove(), t5 = i8; + } + } + setConnected(t5) { + var _a9; + void 0 === this._$AM && (this._$Cv = t5, (_a9 = this._$AP) == null ? void 0 : _a9.call(this, t5)); + } +}; +var R2 = class { + get tagName() { + return this.element.tagName; + } + get _$AU() { + return this._$AM._$AU; + } + constructor(t5, i7, s6, e7, h5) { + this.type = 1, this._$AH = T2, this._$AN = void 0, this.element = t5, this.name = i7, this._$AM = e7, this.options = h5, s6.length > 2 || "" !== s6[0] || "" !== s6[1] ? (this._$AH = Array(s6.length - 1).fill(new String()), this.strings = s6) : this._$AH = T2; + } + _$AI(t5, i7 = this, s6, e7) { + const h5 = this.strings; + let o7 = false; + if (void 0 === h5) + t5 = N2(this, t5, i7, 0), o7 = !c6(t5) || t5 !== this._$AH && t5 !== w2, o7 && (this._$AH = t5); + else { + const e8 = t5; + let n7, r8; + for (t5 = h5[0], n7 = 0; n7 < h5.length - 1; n7++) + r8 = N2(this, e8[s6 + n7], i7, n7), r8 === w2 && (r8 = this._$AH[n7]), o7 || (o7 = !c6(r8) || r8 !== this._$AH[n7]), r8 === T2 ? t5 = T2 : t5 !== T2 && (t5 += (r8 != null ? r8 : "") + h5[n7 + 1]), this._$AH[n7] = r8; + } + o7 && !e7 && this.j(t5); + } + j(t5) { + t5 === T2 ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, t5 != null ? t5 : ""); + } +}; +var k2 = class extends R2 { + constructor() { + super(...arguments), this.type = 3; + } + j(t5) { + this.element[this.name] = t5 === T2 ? void 0 : t5; + } +}; +var H2 = class extends R2 { + constructor() { + super(...arguments), this.type = 4; + } + j(t5) { + this.element.toggleAttribute(this.name, !!t5 && t5 !== T2); + } +}; +var I2 = class extends R2 { + constructor(t5, i7, s6, e7, h5) { + super(t5, i7, s6, e7, h5), this.type = 5; + } + _$AI(t5, i7 = this) { + var _a9; + if ((t5 = (_a9 = N2(this, t5, i7, 0)) != null ? _a9 : T2) === w2) + return; + const s6 = this._$AH, e7 = t5 === T2 && s6 !== T2 || t5.capture !== s6.capture || t5.once !== s6.once || t5.passive !== s6.passive, h5 = t5 !== T2 && (s6 === T2 || e7); + e7 && this.element.removeEventListener(this.name, this, s6), h5 && this.element.addEventListener(this.name, this, t5), this._$AH = t5; + } + handleEvent(t5) { + var _a9, _b3; + "function" == typeof this._$AH ? this._$AH.call((_b3 = (_a9 = this.options) == null ? void 0 : _a9.host) != null ? _b3 : this.element, t5) : this._$AH.handleEvent(t5); + } +}; +var L2 = class { + constructor(t5, i7, s6) { + this.element = t5, this.type = 6, this._$AN = void 0, this._$AM = i7, this.options = s6; + } + get _$AU() { + return this._$AM._$AU; + } + _$AI(t5) { + N2(this, t5); + } +}; +var z = { S: e6, A: h4, P: o6, C: 1, M: P2, L: S4, R: u4, V: N2, D: M2, I: R2, H: H2, N: I2, U: k2, B: L2 }; +var Z2 = t4.litHtmlPolyfillSupport; +var _a6; +Z2 == null ? void 0 : Z2(V2, M2), ((_a6 = t4.litHtmlVersions) != null ? _a6 : t4.litHtmlVersions = []).push("3.0.0"); + +// node_modules/lit-element/lit-element.js +var s5 = class extends b { + constructor() { + super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0; + } + createRenderRoot() { + var _a9, _b3; + const t5 = super.createRenderRoot(); + return (_b3 = (_a9 = this.renderOptions).renderBefore) != null ? _b3 : _a9.renderBefore = t5.firstChild, t5; + } + update(t5) { + const i7 = this.render(); + this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(t5), this._$Do = j(i7, this.renderRoot, this.renderOptions); + } + connectedCallback() { + var _a9; + super.connectedCallback(), (_a9 = this._$Do) == null ? void 0 : _a9.setConnected(true); + } + disconnectedCallback() { + var _a9; + super.disconnectedCallback(), (_a9 = this._$Do) == null ? void 0 : _a9.setConnected(false); + } + render() { + return w; + } +}; +var _a7; +s5._$litElement$ = true, s5["finalized", "finalized"] = true, (_a7 = globalThis.litElementHydrateSupport) == null ? void 0 : _a7.call(globalThis, { LitElement: s5 }); +var r7 = globalThis.litElementPolyfillSupport; +r7 == null ? void 0 : r7({ LitElement: s5 }); +var _a8; +((_a8 = globalThis.litElementVersions) != null ? _a8 : globalThis.litElementVersions = []).push("4.0.0"); + +export { + x2 as x, + b4 as b, + w2 as w, + T2 as T, + z, + u3 as u, + f3 as f, + i, + u as u2, + x as x2, + s5 as s +}; +/*! Bundled license information: + +@lit/reactive-element/css-tag.js: + (** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/reactive-element.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/lit-html.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/css-tag.js: + (** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/reactive-element.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/lit-html.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-element/lit-element.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/is-server.js: + (** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) +*/ diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.2AIXAFCV.js b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.2AIXAFCV.js new file mode 100644 index 0000000..7464c45 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.2AIXAFCV.js @@ -0,0 +1,494 @@ +import { + input_styles_default +} from "./chunk.2SZPKEN5.js"; +import { + l +} from "./chunk.HPQ3PGLN.js"; +import { + defaultValue +} from "./chunk.RIBKWZXP.js"; +import { + FormControlController +} from "./chunk.OQXQSIH6.js"; +import { + o +} from "./chunk.PNPJ7KDG.js"; +import { + HasSlotController +} from "./chunk.NYIIDP5N.js"; +import { + LocalizeController +} from "./chunk.NH3SRVOC.js"; +import { + e +} from "./chunk.DOYC4G7X.js"; +import { + SlIcon +} from "./chunk.B3ILE7LN.js"; +import { + watch +} from "./chunk.XAOA43RZ.js"; +import { + ShoelaceElement, + e as e2, + n, + r +} from "./chunk.7I3HGH5I.js"; +import { + x2 as x +} from "./chunk.27ILGUWR.js"; +import { + __decorateClass +} from "./chunk.YZETUBD6.js"; + +// src/components/input/input.component.ts +var SlInput = class extends ShoelaceElement { + constructor() { + super(...arguments); + this.formControlController = new FormControlController(this, { + assumeInteractionOn: ["sl-blur", "sl-input"] + }); + this.hasSlotController = new HasSlotController(this, "help-text", "label"); + this.localize = new LocalizeController(this); + this.hasFocus = false; + this.title = ""; + // make reactive to pass through + this.__numberInput = Object.assign(document.createElement("input"), { type: "number" }); + this.__dateInput = Object.assign(document.createElement("input"), { type: "date" }); + this.type = "text"; + this.name = ""; + this.value = ""; + this.defaultValue = ""; + this.size = "medium"; + this.filled = false; + this.pill = false; + this.label = ""; + this.helpText = ""; + this.clearable = false; + this.disabled = false; + this.placeholder = ""; + this.readonly = false; + this.passwordToggle = false; + this.passwordVisible = false; + this.noSpinButtons = false; + this.form = ""; + this.required = false; + this.spellcheck = true; + } + // + // NOTE: We use an in-memory input for these getters/setters instead of the one in the template because the properties + // can be set before the component is rendered. + // + /** + * Gets or sets the current value as a `Date` object. Returns `null` if the value can't be converted. This will use the native `` implementation and may result in an error. + */ + get valueAsDate() { + var _a; + this.__dateInput.type = this.type; + this.__dateInput.value = this.value; + return ((_a = this.input) == null ? void 0 : _a.valueAsDate) || this.__dateInput.valueAsDate; + } + set valueAsDate(newValue) { + this.__dateInput.type = this.type; + this.__dateInput.valueAsDate = newValue; + this.value = this.__dateInput.value; + } + /** Gets or sets the current value as a number. Returns `NaN` if the value can't be converted. */ + get valueAsNumber() { + var _a; + this.__numberInput.value = this.value; + return ((_a = this.input) == null ? void 0 : _a.valueAsNumber) || this.__numberInput.valueAsNumber; + } + set valueAsNumber(newValue) { + this.__numberInput.valueAsNumber = newValue; + this.value = this.__numberInput.value; + } + /** Gets the validity state object */ + get validity() { + return this.input.validity; + } + /** Gets the validation message */ + get validationMessage() { + return this.input.validationMessage; + } + firstUpdated() { + this.formControlController.updateValidity(); + } + handleBlur() { + this.hasFocus = false; + this.emit("sl-blur"); + } + handleChange() { + this.value = this.input.value; + this.emit("sl-change"); + } + handleClearClick(event) { + this.value = ""; + this.emit("sl-clear"); + this.emit("sl-input"); + this.emit("sl-change"); + this.input.focus(); + event.stopPropagation(); + } + handleFocus() { + this.hasFocus = true; + this.emit("sl-focus"); + } + handleInput() { + this.value = this.input.value; + this.formControlController.updateValidity(); + this.emit("sl-input"); + } + handleInvalid(event) { + this.formControlController.setValidity(false); + this.formControlController.emitInvalidEvent(event); + } + handleKeyDown(event) { + const hasModifier = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey; + if (event.key === "Enter" && !hasModifier) { + setTimeout(() => { + if (!event.defaultPrevented && !event.isComposing) { + this.formControlController.submit(); + } + }); + } + } + handlePasswordToggle() { + this.passwordVisible = !this.passwordVisible; + } + handleDisabledChange() { + this.formControlController.setValidity(this.disabled); + } + handleStepChange() { + this.input.step = String(this.step); + this.formControlController.updateValidity(); + } + async handleValueChange() { + await this.updateComplete; + this.formControlController.updateValidity(); + } + /** Sets focus on the input. */ + focus(options) { + this.input.focus(options); + } + /** Removes focus from the input. */ + blur() { + this.input.blur(); + } + /** Selects all the text in the input. */ + select() { + this.input.select(); + } + /** Sets the start and end positions of the text selection (0-based). */ + setSelectionRange(selectionStart, selectionEnd, selectionDirection = "none") { + this.input.setSelectionRange(selectionStart, selectionEnd, selectionDirection); + } + /** Replaces a range of text with a new string. */ + setRangeText(replacement, start, end, selectMode) { + this.input.setRangeText(replacement, start, end, selectMode); + if (this.value !== this.input.value) { + this.value = this.input.value; + } + } + /** Displays the browser picker for an input element (only works if the browser supports it for the input type). */ + showPicker() { + if ("showPicker" in HTMLInputElement.prototype) { + this.input.showPicker(); + } + } + /** Increments the value of a numeric input type by the value of the step attribute. */ + stepUp() { + this.input.stepUp(); + if (this.value !== this.input.value) { + this.value = this.input.value; + } + } + /** Decrements the value of a numeric input type by the value of the step attribute. */ + stepDown() { + this.input.stepDown(); + if (this.value !== this.input.value) { + this.value = this.input.value; + } + } + /** Checks for validity but does not show a validation message. Returns `true` when valid and `false` when invalid. */ + checkValidity() { + return this.input.checkValidity(); + } + /** Gets the associated form, if one exists. */ + getForm() { + return this.formControlController.getForm(); + } + /** Checks for validity and shows the browser's validation message if the control is invalid. */ + reportValidity() { + return this.input.reportValidity(); + } + /** Sets a custom validation message. Pass an empty string to restore validity. */ + setCustomValidity(message) { + this.input.setCustomValidity(message); + this.formControlController.updateValidity(); + } + render() { + const hasLabelSlot = this.hasSlotController.test("label"); + const hasHelpTextSlot = this.hasSlotController.test("help-text"); + const hasLabel = this.label ? true : !!hasLabelSlot; + const hasHelpText = this.helpText ? true : !!hasHelpTextSlot; + const hasClearIcon = this.clearable && !this.disabled && !this.readonly; + const isClearIconVisible = hasClearIcon && (typeof this.value === "number" || this.value.length > 0); + return x` +
+ + +
+
+ + + + + + + ${hasClearIcon ? x` + + ` : ""} + ${this.passwordToggle && !this.disabled ? x` + + ` : ""} + + + + +
+
+ +
+ ${this.helpText} +
+
+ `; + } +}; +SlInput.styles = input_styles_default; +SlInput.dependencies = { "sl-icon": SlIcon }; +__decorateClass([ + e2(".input__control") +], SlInput.prototype, "input", 2); +__decorateClass([ + r() +], SlInput.prototype, "hasFocus", 2); +__decorateClass([ + n() +], SlInput.prototype, "title", 2); +__decorateClass([ + n({ reflect: true }) +], SlInput.prototype, "type", 2); +__decorateClass([ + n() +], SlInput.prototype, "name", 2); +__decorateClass([ + n() +], SlInput.prototype, "value", 2); +__decorateClass([ + defaultValue() +], SlInput.prototype, "defaultValue", 2); +__decorateClass([ + n({ reflect: true }) +], SlInput.prototype, "size", 2); +__decorateClass([ + n({ type: Boolean, reflect: true }) +], SlInput.prototype, "filled", 2); +__decorateClass([ + n({ type: Boolean, reflect: true }) +], SlInput.prototype, "pill", 2); +__decorateClass([ + n() +], SlInput.prototype, "label", 2); +__decorateClass([ + n({ attribute: "help-text" }) +], SlInput.prototype, "helpText", 2); +__decorateClass([ + n({ type: Boolean }) +], SlInput.prototype, "clearable", 2); +__decorateClass([ + n({ type: Boolean, reflect: true }) +], SlInput.prototype, "disabled", 2); +__decorateClass([ + n() +], SlInput.prototype, "placeholder", 2); +__decorateClass([ + n({ type: Boolean, reflect: true }) +], SlInput.prototype, "readonly", 2); +__decorateClass([ + n({ attribute: "password-toggle", type: Boolean }) +], SlInput.prototype, "passwordToggle", 2); +__decorateClass([ + n({ attribute: "password-visible", type: Boolean }) +], SlInput.prototype, "passwordVisible", 2); +__decorateClass([ + n({ attribute: "no-spin-buttons", type: Boolean }) +], SlInput.prototype, "noSpinButtons", 2); +__decorateClass([ + n({ reflect: true }) +], SlInput.prototype, "form", 2); +__decorateClass([ + n({ type: Boolean, reflect: true }) +], SlInput.prototype, "required", 2); +__decorateClass([ + n() +], SlInput.prototype, "pattern", 2); +__decorateClass([ + n({ type: Number }) +], SlInput.prototype, "minlength", 2); +__decorateClass([ + n({ type: Number }) +], SlInput.prototype, "maxlength", 2); +__decorateClass([ + n() +], SlInput.prototype, "min", 2); +__decorateClass([ + n() +], SlInput.prototype, "max", 2); +__decorateClass([ + n() +], SlInput.prototype, "step", 2); +__decorateClass([ + n() +], SlInput.prototype, "autocapitalize", 2); +__decorateClass([ + n() +], SlInput.prototype, "autocorrect", 2); +__decorateClass([ + n() +], SlInput.prototype, "autocomplete", 2); +__decorateClass([ + n({ type: Boolean }) +], SlInput.prototype, "autofocus", 2); +__decorateClass([ + n() +], SlInput.prototype, "enterkeyhint", 2); +__decorateClass([ + n({ + type: Boolean, + converter: { + // Allow "true|false" attribute values but keep the property boolean + fromAttribute: (value) => !value || value === "false" ? false : true, + toAttribute: (value) => value ? "true" : "false" + } + }) +], SlInput.prototype, "spellcheck", 2); +__decorateClass([ + n() +], SlInput.prototype, "inputmode", 2); +__decorateClass([ + watch("disabled", { waitUntilFirstUpdate: true }) +], SlInput.prototype, "handleDisabledChange", 1); +__decorateClass([ + watch("step", { waitUntilFirstUpdate: true }) +], SlInput.prototype, "handleStepChange", 1); +__decorateClass([ + watch("value", { waitUntilFirstUpdate: true }) +], SlInput.prototype, "handleValueChange", 1); + +export { + SlInput +}; diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.2SZPKEN5.js b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.2SZPKEN5.js new file mode 100644 index 0000000..331d955 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.2SZPKEN5.js @@ -0,0 +1,313 @@ +import { + form_control_styles_default +} from "./chunk.XQ4KBI5G.js"; +import { + component_styles_default +} from "./chunk.M2U2QT2K.js"; +import { + i +} from "./chunk.27ILGUWR.js"; + +// src/components/input/input.styles.ts +var input_styles_default = i` + ${component_styles_default} + ${form_control_styles_default} + + :host { + display: block; + } + + .input { + flex: 1 1 auto; + display: inline-flex; + align-items: stretch; + justify-content: start; + position: relative; + width: 100%; + font-family: var(--sl-input-font-family); + font-weight: var(--sl-input-font-weight); + letter-spacing: var(--sl-input-letter-spacing); + vertical-align: middle; + overflow: hidden; + cursor: text; + transition: + var(--sl-transition-fast) color, + var(--sl-transition-fast) border, + var(--sl-transition-fast) box-shadow, + var(--sl-transition-fast) background-color; + } + + /* Standard inputs */ + .input--standard { + background-color: var(--sl-input-background-color); + border: solid var(--sl-input-border-width) var(--sl-input-border-color); + } + + .input--standard:hover:not(.input--disabled) { + background-color: var(--sl-input-background-color-hover); + border-color: var(--sl-input-border-color-hover); + } + + .input--standard.input--focused:not(.input--disabled) { + background-color: var(--sl-input-background-color-focus); + border-color: var(--sl-input-border-color-focus); + box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-input-focus-ring-color); + } + + .input--standard.input--focused:not(.input--disabled) .input__control { + color: var(--sl-input-color-focus); + } + + .input--standard.input--disabled { + background-color: var(--sl-input-background-color-disabled); + border-color: var(--sl-input-border-color-disabled); + opacity: 0.5; + cursor: not-allowed; + } + + .input--standard.input--disabled .input__control { + color: var(--sl-input-color-disabled); + } + + .input--standard.input--disabled .input__control::placeholder { + color: var(--sl-input-placeholder-color-disabled); + } + + /* Filled inputs */ + .input--filled { + border: none; + background-color: var(--sl-input-filled-background-color); + color: var(--sl-input-color); + } + + .input--filled:hover:not(.input--disabled) { + background-color: var(--sl-input-filled-background-color-hover); + } + + .input--filled.input--focused:not(.input--disabled) { + background-color: var(--sl-input-filled-background-color-focus); + outline: var(--sl-focus-ring); + outline-offset: var(--sl-focus-ring-offset); + } + + .input--filled.input--disabled { + background-color: var(--sl-input-filled-background-color-disabled); + opacity: 0.5; + cursor: not-allowed; + } + + .input__control { + flex: 1 1 auto; + font-family: inherit; + font-size: inherit; + font-weight: inherit; + min-width: 0; + height: 100%; + color: var(--sl-input-color); + border: none; + background: inherit; + box-shadow: none; + padding: 0; + margin: 0; + cursor: inherit; + -webkit-appearance: none; + } + + .input__control::-webkit-search-decoration, + .input__control::-webkit-search-cancel-button, + .input__control::-webkit-search-results-button, + .input__control::-webkit-search-results-decoration { + -webkit-appearance: none; + } + + .input__control:-webkit-autofill, + .input__control:-webkit-autofill:hover, + .input__control:-webkit-autofill:focus, + .input__control:-webkit-autofill:active { + box-shadow: 0 0 0 var(--sl-input-height-large) var(--sl-input-background-color-hover) inset !important; + -webkit-text-fill-color: var(--sl-color-primary-500); + caret-color: var(--sl-input-color); + } + + .input--filled .input__control:-webkit-autofill, + .input--filled .input__control:-webkit-autofill:hover, + .input--filled .input__control:-webkit-autofill:focus, + .input--filled .input__control:-webkit-autofill:active { + box-shadow: 0 0 0 var(--sl-input-height-large) var(--sl-input-filled-background-color) inset !important; + } + + .input__control::placeholder { + color: var(--sl-input-placeholder-color); + user-select: none; + -webkit-user-select: none; + } + + .input:hover:not(.input--disabled) .input__control { + color: var(--sl-input-color-hover); + } + + .input__control:focus { + outline: none; + } + + .input__prefix, + .input__suffix { + display: inline-flex; + flex: 0 0 auto; + align-items: center; + cursor: default; + } + + .input__prefix ::slotted(sl-icon), + .input__suffix ::slotted(sl-icon) { + color: var(--sl-input-icon-color); + } + + /* + * Size modifiers + */ + + .input--small { + border-radius: var(--sl-input-border-radius-small); + font-size: var(--sl-input-font-size-small); + height: var(--sl-input-height-small); + } + + .input--small .input__control { + height: calc(var(--sl-input-height-small) - var(--sl-input-border-width) * 2); + padding: 0 var(--sl-input-spacing-small); + } + + .input--small .input__clear, + .input--small .input__password-toggle { + width: calc(1em + var(--sl-input-spacing-small) * 2); + } + + .input--small .input__prefix ::slotted(*) { + margin-inline-start: var(--sl-input-spacing-small); + } + + .input--small .input__suffix ::slotted(*) { + margin-inline-end: var(--sl-input-spacing-small); + } + + .input--medium { + border-radius: var(--sl-input-border-radius-medium); + font-size: var(--sl-input-font-size-medium); + height: var(--sl-input-height-medium); + } + + .input--medium .input__control { + height: calc(var(--sl-input-height-medium) - var(--sl-input-border-width) * 2); + padding: 0 var(--sl-input-spacing-medium); + } + + .input--medium .input__clear, + .input--medium .input__password-toggle { + width: calc(1em + var(--sl-input-spacing-medium) * 2); + } + + .input--medium .input__prefix ::slotted(*) { + margin-inline-start: var(--sl-input-spacing-medium); + } + + .input--medium .input__suffix ::slotted(*) { + margin-inline-end: var(--sl-input-spacing-medium); + } + + .input--large { + border-radius: var(--sl-input-border-radius-large); + font-size: var(--sl-input-font-size-large); + height: var(--sl-input-height-large); + } + + .input--large .input__control { + height: calc(var(--sl-input-height-large) - var(--sl-input-border-width) * 2); + padding: 0 var(--sl-input-spacing-large); + } + + .input--large .input__clear, + .input--large .input__password-toggle { + width: calc(1em + var(--sl-input-spacing-large) * 2); + } + + .input--large .input__prefix ::slotted(*) { + margin-inline-start: var(--sl-input-spacing-large); + } + + .input--large .input__suffix ::slotted(*) { + margin-inline-end: var(--sl-input-spacing-large); + } + + /* + * Pill modifier + */ + + .input--pill.input--small { + border-radius: var(--sl-input-height-small); + } + + .input--pill.input--medium { + border-radius: var(--sl-input-height-medium); + } + + .input--pill.input--large { + border-radius: var(--sl-input-height-large); + } + + /* + * Clearable + Password Toggle + */ + + .input__clear:not(.input__clear--visible) { + visibility: hidden; + } + + .input__clear, + .input__password-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + font-size: inherit; + color: var(--sl-input-icon-color); + border: none; + background: none; + padding: 0; + transition: var(--sl-transition-fast) color; + cursor: pointer; + } + + .input__clear:hover, + .input__password-toggle:hover { + color: var(--sl-input-icon-color-hover); + } + + .input__clear:focus, + .input__password-toggle:focus { + outline: none; + } + + .input--empty .input__clear { + visibility: hidden; + } + + /* Don't show the browser's password toggle in Edge */ + ::-ms-reveal { + display: none; + } + + /* Hide the built-in number spinner */ + .input--no-spin-buttons input[type='number']::-webkit-outer-spin-button, + .input--no-spin-buttons input[type='number']::-webkit-inner-spin-button { + -webkit-appearance: none; + display: none; + } + + .input--no-spin-buttons input[type='number'] { + -moz-appearance: textfield; + } +`; + +export { + input_styles_default +}; diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.2X5EERK2.js b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.2X5EERK2.js new file mode 100644 index 0000000..0b97cb2 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.2X5EERK2.js @@ -0,0 +1,299 @@ +import { + SlSpinner +} from "./chunk.G2QSU2BF.js"; +import { + FormControlController, + validValidityState +} from "./chunk.OQXQSIH6.js"; +import { + button_styles_default +} from "./chunk.BXWBYEBW.js"; +import { + n as n2, + s +} from "./chunk.MKSGYDJ7.js"; +import { + o +} from "./chunk.PNPJ7KDG.js"; +import { + HasSlotController +} from "./chunk.NYIIDP5N.js"; +import { + LocalizeController +} from "./chunk.NH3SRVOC.js"; +import { + e +} from "./chunk.DOYC4G7X.js"; +import { + SlIcon +} from "./chunk.B3ILE7LN.js"; +import { + watch +} from "./chunk.XAOA43RZ.js"; +import { + ShoelaceElement, + e as e2, + n, + r +} from "./chunk.7I3HGH5I.js"; +import { + __decorateClass +} from "./chunk.YZETUBD6.js"; + +// src/components/button/button.component.ts +var SlButton = class extends ShoelaceElement { + constructor() { + super(...arguments); + this.formControlController = new FormControlController(this, { + assumeInteractionOn: ["click"] + }); + this.hasSlotController = new HasSlotController(this, "[default]", "prefix", "suffix"); + this.localize = new LocalizeController(this); + this.hasFocus = false; + this.invalid = false; + this.title = ""; + this.variant = "default"; + this.size = "medium"; + this.caret = false; + this.disabled = false; + this.loading = false; + this.outline = false; + this.pill = false; + this.circle = false; + this.type = "button"; + this.name = ""; + this.value = ""; + this.href = ""; + this.rel = "noreferrer noopener"; + } + /** Gets the validity state object */ + get validity() { + if (this.isButton()) { + return this.button.validity; + } + return validValidityState; + } + /** Gets the validation message */ + get validationMessage() { + if (this.isButton()) { + return this.button.validationMessage; + } + return ""; + } + firstUpdated() { + if (this.isButton()) { + this.formControlController.updateValidity(); + } + } + handleBlur() { + this.hasFocus = false; + this.emit("sl-blur"); + } + handleFocus() { + this.hasFocus = true; + this.emit("sl-focus"); + } + handleClick() { + if (this.type === "submit") { + this.formControlController.submit(this); + } + if (this.type === "reset") { + this.formControlController.reset(this); + } + } + handleInvalid(event) { + this.formControlController.setValidity(false); + this.formControlController.emitInvalidEvent(event); + } + isButton() { + return this.href ? false : true; + } + isLink() { + return this.href ? true : false; + } + handleDisabledChange() { + if (this.isButton()) { + this.formControlController.setValidity(this.disabled); + } + } + /** Simulates a click on the button. */ + click() { + this.button.click(); + } + /** Sets focus on the button. */ + focus(options) { + this.button.focus(options); + } + /** Removes focus from the button. */ + blur() { + this.button.blur(); + } + /** Checks for validity but does not show a validation message. Returns `true` when valid and `false` when invalid. */ + checkValidity() { + if (this.isButton()) { + return this.button.checkValidity(); + } + return true; + } + /** Gets the associated form, if one exists. */ + getForm() { + return this.formControlController.getForm(); + } + /** Checks for validity and shows the browser's validation message if the control is invalid. */ + reportValidity() { + if (this.isButton()) { + return this.button.reportValidity(); + } + return true; + } + /** Sets a custom validation message. Pass an empty string to restore validity. */ + setCustomValidity(message) { + if (this.isButton()) { + this.button.setCustomValidity(message); + this.formControlController.updateValidity(); + } + } + render() { + const isLink = this.isLink(); + const tag = isLink ? s`a` : s`button`; + return n2` + <${tag} + part="base" + class=${e({ + button: true, + "button--default": this.variant === "default", + "button--primary": this.variant === "primary", + "button--success": this.variant === "success", + "button--neutral": this.variant === "neutral", + "button--warning": this.variant === "warning", + "button--danger": this.variant === "danger", + "button--text": this.variant === "text", + "button--small": this.size === "small", + "button--medium": this.size === "medium", + "button--large": this.size === "large", + "button--caret": this.caret, + "button--circle": this.circle, + "button--disabled": this.disabled, + "button--focused": this.hasFocus, + "button--loading": this.loading, + "button--standard": !this.outline, + "button--outline": this.outline, + "button--pill": this.pill, + "button--rtl": this.localize.dir() === "rtl", + "button--has-label": this.hasSlotController.test("[default]"), + "button--has-prefix": this.hasSlotController.test("prefix"), + "button--has-suffix": this.hasSlotController.test("suffix") + })} + ?disabled=${o(isLink ? void 0 : this.disabled)} + type=${o(isLink ? void 0 : this.type)} + title=${this.title} + name=${o(isLink ? void 0 : this.name)} + value=${o(isLink ? void 0 : this.value)} + href=${o(isLink ? this.href : void 0)} + target=${o(isLink ? this.target : void 0)} + download=${o(isLink ? this.download : void 0)} + rel=${o(isLink ? this.rel : void 0)} + role=${o(isLink ? void 0 : "button")} + aria-disabled=${this.disabled ? "true" : "false"} + tabindex=${this.disabled ? "-1" : "0"} + @blur=${this.handleBlur} + @focus=${this.handleFocus} + @invalid=${this.isButton() ? this.handleInvalid : null} + @click=${this.handleClick} + > + + + + ${this.caret ? n2` ` : ""} + ${this.loading ? n2`` : ""} + + `; + } +}; +SlButton.styles = button_styles_default; +SlButton.dependencies = { + "sl-icon": SlIcon, + "sl-spinner": SlSpinner +}; +__decorateClass([ + e2(".button") +], SlButton.prototype, "button", 2); +__decorateClass([ + r() +], SlButton.prototype, "hasFocus", 2); +__decorateClass([ + r() +], SlButton.prototype, "invalid", 2); +__decorateClass([ + n() +], SlButton.prototype, "title", 2); +__decorateClass([ + n({ reflect: true }) +], SlButton.prototype, "variant", 2); +__decorateClass([ + n({ reflect: true }) +], SlButton.prototype, "size", 2); +__decorateClass([ + n({ type: Boolean, reflect: true }) +], SlButton.prototype, "caret", 2); +__decorateClass([ + n({ type: Boolean, reflect: true }) +], SlButton.prototype, "disabled", 2); +__decorateClass([ + n({ type: Boolean, reflect: true }) +], SlButton.prototype, "loading", 2); +__decorateClass([ + n({ type: Boolean, reflect: true }) +], SlButton.prototype, "outline", 2); +__decorateClass([ + n({ type: Boolean, reflect: true }) +], SlButton.prototype, "pill", 2); +__decorateClass([ + n({ type: Boolean, reflect: true }) +], SlButton.prototype, "circle", 2); +__decorateClass([ + n() +], SlButton.prototype, "type", 2); +__decorateClass([ + n() +], SlButton.prototype, "name", 2); +__decorateClass([ + n() +], SlButton.prototype, "value", 2); +__decorateClass([ + n() +], SlButton.prototype, "href", 2); +__decorateClass([ + n() +], SlButton.prototype, "target", 2); +__decorateClass([ + n() +], SlButton.prototype, "rel", 2); +__decorateClass([ + n() +], SlButton.prototype, "download", 2); +__decorateClass([ + n() +], SlButton.prototype, "form", 2); +__decorateClass([ + n({ attribute: "formaction" }) +], SlButton.prototype, "formAction", 2); +__decorateClass([ + n({ attribute: "formenctype" }) +], SlButton.prototype, "formEnctype", 2); +__decorateClass([ + n({ attribute: "formmethod" }) +], SlButton.prototype, "formMethod", 2); +__decorateClass([ + n({ attribute: "formnovalidate", type: Boolean }) +], SlButton.prototype, "formNoValidate", 2); +__decorateClass([ + n({ attribute: "formtarget" }) +], SlButton.prototype, "formTarget", 2); +__decorateClass([ + watch("disabled", { waitUntilFirstUpdate: true }) +], SlButton.prototype, "handleDisabledChange", 1); + +export { + SlButton +}; diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.3GITBIC4.js b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.3GITBIC4.js new file mode 100644 index 0000000..04e7fc7 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.3GITBIC4.js @@ -0,0 +1,71 @@ +import { + component_styles_default +} from "./chunk.M2U2QT2K.js"; +import { + i +} from "./chunk.27ILGUWR.js"; + +// src/components/spinner/spinner.styles.ts +var spinner_styles_default = i` + ${component_styles_default} + + :host { + --track-width: 2px; + --track-color: rgb(128 128 128 / 25%); + --indicator-color: var(--sl-color-primary-600); + --speed: 2s; + + display: inline-flex; + width: 1em; + height: 1em; + } + + .spinner { + flex: 1 1 auto; + height: 100%; + width: 100%; + } + + .spinner__track, + .spinner__indicator { + fill: none; + stroke-width: var(--track-width); + r: calc(0.5em - var(--track-width) / 2); + cx: 0.5em; + cy: 0.5em; + transform-origin: 50% 50%; + } + + .spinner__track { + stroke: var(--track-color); + transform-origin: 0% 0%; + } + + .spinner__indicator { + stroke: var(--indicator-color); + stroke-linecap: round; + stroke-dasharray: 150% 75%; + animation: spin var(--speed) linear infinite; + } + + @keyframes spin { + 0% { + transform: rotate(0deg); + stroke-dasharray: 0.01em, 2.75em; + } + + 50% { + transform: rotate(450deg); + stroke-dasharray: 1.375em, 1.375em; + } + + 100% { + transform: rotate(1080deg); + stroke-dasharray: 0.01em, 2.75em; + } + } +`; + +export { + spinner_styles_default +}; diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.3TFKS637.js b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.3TFKS637.js new file mode 100644 index 0000000..f5e37b5 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.3TFKS637.js @@ -0,0 +1,127 @@ +// src/components/icon/library.system.ts +var icons = { + caret: ` + + + + `, + check: ` + + + + + + + + + + + `, + "chevron-down": ` + + + + `, + "chevron-left": ` + + + + `, + "chevron-right": ` + + + + `, + copy: ` + + + + `, + eye: ` + + + + + `, + "eye-slash": ` + + + + + + `, + eyedropper: ` + + + + `, + "grip-vertical": ` + + + + `, + indeterminate: ` + + + + + + + + + + `, + "person-fill": ` + + + + `, + "play-fill": ` + + + + `, + "pause-fill": ` + + + + `, + radio: ` + + + + + + + + `, + "star-fill": ` + + + + `, + "x-lg": ` + + + + `, + "x-circle-fill": ` + + + + ` +}; +var systemLibrary = { + name: "system", + resolver: (name) => { + if (name in icons) { + return `data:image/svg+xml,${encodeURIComponent(icons[name])}`; + } + return ""; + } +}; +var library_system_default = systemLibrary; + +export { + library_system_default +}; diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.3Y6SB6QS.js b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.3Y6SB6QS.js new file mode 100644 index 0000000..fd55a44 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.3Y6SB6QS.js @@ -0,0 +1,29 @@ +// src/utilities/base-path.ts +var basePath = ""; +function setBasePath(path) { + basePath = path; +} +function getBasePath(subpath = "") { + if (!basePath) { + const scripts = [...document.getElementsByTagName("script")]; + const configScript = scripts.find((script) => script.hasAttribute("data-shoelace")); + if (configScript) { + setBasePath(configScript.getAttribute("data-shoelace")); + } else { + const fallbackScript = scripts.find((s) => { + return /shoelace(\.min)?\.js($|\?)/.test(s.src) || /shoelace-autoloader(\.min)?\.js($|\?)/.test(s.src); + }); + let path = ""; + if (fallbackScript) { + path = fallbackScript.getAttribute("src"); + } + setBasePath(path.split("/").slice(0, -1).join("/")); + } + } + return basePath.replace(/\/$/, "") + (subpath ? `/${subpath.replace(/^\//, "")}` : ``); +} + +export { + setBasePath, + getBasePath +}; diff --git a/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.43GPPE74.js b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.43GPPE74.js new file mode 100644 index 0000000..d903bb5 --- /dev/null +++ b/static/editor/vendor/shoelace/2.12.0/cdn/chunks/chunk.43GPPE74.js @@ -0,0 +1,1680 @@ +import { + popup_styles_default +} from "./chunk.6LJMKLWZ.js"; +import { + e +} from "./chunk.DOYC4G7X.js"; +import { + ShoelaceElement, + e as e2, + n +} from "./chunk.7I3HGH5I.js"; +import { + x2 as x +} from "./chunk.27ILGUWR.js"; +import { + __decorateClass, + __objRest, + __spreadProps, + __spreadValues +} from "./chunk.YZETUBD6.js"; + +// node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs +var min = Math.min; +var max = Math.max; +var round = Math.round; +var floor = Math.floor; +var createCoords = (v) => ({ + x: v, + y: v +}); +var oppositeSideMap = { + left: "right", + right: "left", + bottom: "top", + top: "bottom" +}; +var oppositeAlignmentMap = { + start: "end", + end: "start" +}; +function clamp(start, value, end) { + return max(start, min(value, end)); +} +function evaluate(value, param) { + return typeof value === "function" ? value(param) : value; +} +function getSide(placement) { + return placement.split("-")[0]; +} +function getAlignment(placement) { + return placement.split("-")[1]; +} +function getOppositeAxis(axis) { + return axis === "x" ? "y" : "x"; +} +function getAxisLength(axis) { + return axis === "y" ? "height" : "width"; +} +function getSideAxis(placement) { + return ["top", "bottom"].includes(getSide(placement)) ? "y" : "x"; +} +function getAlignmentAxis(placement) { + return getOppositeAxis(getSideAxis(placement)); +} +function getAlignmentSides(placement, rects, rtl) { + if (rtl === void 0) { + rtl = false; + } + const alignment = getAlignment(placement); + const alignmentAxis = getAlignmentAxis(placement); + const length = getAxisLength(alignmentAxis); + let mainAlignmentSide = alignmentAxis === "x" ? alignment === (rtl ? "end" : "start") ? "right" : "left" : alignment === "start" ? "bottom" : "top"; + if (rects.reference[length] > rects.floating[length]) { + mainAlignmentSide = getOppositePlacement(mainAlignmentSide); + } + return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)]; +} +function getExpandedPlacements(placement) { + const oppositePlacement = getOppositePlacement(placement); + return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)]; +} +function getOppositeAlignmentPlacement(placement) { + return placement.replace(/start|end/g, (alignment) => oppositeAlignmentMap[alignment]); +} +function getSideList(side, isStart, rtl) { + const lr = ["left", "right"]; + const rl = ["right", "left"]; + const tb = ["top", "bottom"]; + const bt = ["bottom", "top"]; + switch (side) { + case "top": + case "bottom": + if (rtl) + return isStart ? rl : lr; + return isStart ? lr : rl; + case "left": + case "right": + return isStart ? tb : bt; + default: + return []; + } +} +function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) { + const alignment = getAlignment(placement); + let list = getSideList(getSide(placement), direction === "start", rtl); + if (alignment) { + list = list.map((side) => side + "-" + alignment); + if (flipAlignment) { + list = list.concat(list.map(getOppositeAlignmentPlacement)); + } + } + return list; +} +function getOppositePlacement(placement) { + return placement.replace(/left|right|bottom|top/g, (side) => oppositeSideMap[side]); +} +function expandPaddingObject(padding) { + return __spreadValues({ + top: 0, + right: 0, + bottom: 0, + left: 0 + }, padding); +} +function getPaddingObject(padding) { + return typeof padding !== "number" ? expandPaddingObject(padding) : { + top: padding, + right: padding, + bottom: padding, + left: padding + }; +} +function rectToClientRect(rect) { + return __spreadProps(__spreadValues({}, rect), { + top: rect.y, + left: rect.x, + right: rect.x + rect.width, + bottom: rect.y + rect.height + }); +} + +// node_modules/@floating-ui/core/dist/floating-ui.core.mjs +function computeCoordsFromPlacement(_ref, placement, rtl) { + let { + reference, + floating + } = _ref; + const sideAxis = getSideAxis(placement); + const alignmentAxis = getAlignmentAxis(placement); + const alignLength = getAxisLength(alignmentAxis); + const side = getSide(placement); + const isVertical = sideAxis === "y"; + const commonX = reference.x + reference.width / 2 - floating.width / 2; + const commonY = reference.y + reference.height / 2 - floating.height / 2; + const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2; + let coords; + switch (side) { + case "top": + coords = { + x: commonX, + y: reference.y - floating.height + }; + break; + case "bottom": + coords = { + x: commonX, + y: reference.y + reference.height + }; + break; + case "right": + coords = { + x: reference.x + reference.width, + y: commonY + }; + break; + case "left": + coords = { + x: reference.x - floating.width, + y: commonY + }; + break; + default: + coords = { + x: reference.x, + y: reference.y + }; + } + switch (getAlignment(placement)) { + case "start": + coords[alignmentAxis] -= commonAlign * (rtl && isVertical ? -1 : 1); + break; + case "end": + coords[alignmentAxis] += commonAlign * (rtl && isVertical ? -1 : 1); + break; + } + return coords; +} +var computePosition = async (reference, floating, config) => { + const { + placement = "bottom", + strategy = "absolute", + middleware = [], + platform: platform2 + } = config; + const validMiddleware = middleware.filter(Boolean); + const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(floating)); + let rects = await platform2.getElementRects({ + reference, + floating, + strategy + }); + let { + x: x2, + y + } = computeCoordsFromPlacement(rects, placement, rtl); + let statefulPlacement = placement; + let middlewareData = {}; + let resetCount = 0; + for (let i = 0; i < validMiddleware.length; i++) { + const { + name, + fn + } = validMiddleware[i]; + const { + x: nextX, + y: nextY, + data, + reset + } = await fn({ + x: x2, + y, + initialPlacement: placement, + placement: statefulPlacement, + strategy, + middlewareData, + rects, + platform: platform2, + elements: { + reference, + floating + } + }); + x2 = nextX != null ? nextX : x2; + y = nextY != null ? nextY : y; + middlewareData = __spreadProps(__spreadValues({}, middlewareData), { + [name]: __spreadValues(__spreadValues({}, middlewareData[name]), data) + }); + if (reset && resetCount <= 50) { + resetCount++; + if (typeof reset === "object") { + if (reset.placement) { + statefulPlacement = reset.placement; + } + if (reset.rects) { + rects = reset.rects === true ? await platform2.getElementRects({ + reference, + floating, + strategy + }) : reset.rects; + } + ({ + x: x2, + y + } = computeCoordsFromPlacement(rects, statefulPlacement, rtl)); + } + i = -1; + continue; + } + } + return { + x: x2, + y, + placement: statefulPlacement, + strategy, + middlewareData + }; +}; +async function detectOverflow(state, options) { + var _await$platform$isEle; + if (options === void 0) { + options = {}; + } + const { + x: x2, + y, + platform: platform2, + rects, + elements, + strategy + } = state; + const { + boundary = "clippingAncestors", + rootBoundary = "viewport", + elementContext = "floating", + altBoundary = false, + padding = 0 + } = evaluate(options, state); + const paddingObject = getPaddingObject(padding); + const altContext = elementContext === "floating" ? "reference" : "floating"; + const element = elements[altBoundary ? altContext : elementContext]; + const clippingClientRect = rectToClientRect(await platform2.getClippingRect({ + element: ((_await$platform$isEle = await (platform2.isElement == null ? void 0 : platform2.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || await (platform2.getDocumentElement == null ? void 0 : platform2.getDocumentElement(elements.floating)), + boundary, + rootBoundary, + strategy + })); + const rect = elementContext === "floating" ? __spreadProps(__spreadValues({}, rects.floating), { + x: x2, + y + }) : rects.reference; + const offsetParent = await (platform2.getOffsetParent == null ? void 0 : platform2.getOffsetParent(elements.floating)); + const offsetScale = await (platform2.isElement == null ? void 0 : platform2.isElement(offsetParent)) ? await (platform2.getScale == null ? void 0 : platform2.getScale(offsetParent)) || { + x: 1, + y: 1 + } : { + x: 1, + y: 1 + }; + const elementClientRect = rectToClientRect(platform2.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform2.convertOffsetParentRelativeRectToViewportRelativeRect({ + rect, + offsetParent, + strategy + }) : rect); + return { + top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y, + bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y, + left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x, + right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x + }; +} +var arrow = (options) => ({ + name: "arrow", + options, + async fn(state) { + const { + x: x2, + y, + placement, + rects, + platform: platform2, + elements, + middlewareData + } = state; + const { + element, + padding = 0 + } = evaluate(options, state) || {}; + if (element == null) { + return {}; + } + const paddingObject = getPaddingObject(padding); + const coords = { + x: x2, + y + }; + const axis = getAlignmentAxis(placement); + const length = getAxisLength(axis); + const arrowDimensions = await platform2.getDimensions(element); + const isYAxis = axis === "y"; + const minProp = isYAxis ? "top" : "left"; + const maxProp = isYAxis ? "bottom" : "right"; + const clientProp = isYAxis ? "clientHeight" : "clientWidth"; + const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length]; + const startDiff = coords[axis] - rects.reference[axis]; + const arrowOffsetParent = await (platform2.getOffsetParent == null ? void 0 : platform2.getOffsetParent(element)); + let clientSize = arrowOffsetParent ? arrowOffsetParent[clientProp] : 0; + if (!clientSize || !await (platform2.isElement == null ? void 0 : platform2.isElement(arrowOffsetParent))) { + clientSize = elements.floating[clientProp] || rects.floating[length]; + } + const centerToReference = endDiff / 2 - startDiff / 2; + const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1; + const minPadding = min(paddingObject[minProp], largestPossiblePadding); + const maxPadding = min(paddingObject[maxProp], largestPossiblePadding); + const min$1 = minPadding; + const max2 = clientSize - arrowDimensions[length] - maxPadding; + const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference; + const offset2 = clamp(min$1, center, max2); + const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center != offset2 && rects.reference[length] / 2 - (center < min$1 ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0; + const alignmentOffset = shouldAddOffset ? center < min$1 ? center - min$1 : center - max2 : 0; + return { + [axis]: coords[axis] + alignmentOffset, + data: __spreadValues({ + [axis]: offset2, + centerOffset: center - offset2 - alignmentOffset + }, shouldAddOffset && { + alignmentOffset + }), + reset: shouldAddOffset + }; + } +}); +var flip = function(options) { + if (options === void 0) { + options = {}; + } + return { + name: "flip", + options, + async fn(state) { + var _middlewareData$arrow, _middlewareData$flip; + const { + placement, + middlewareData, + rects, + initialPlacement, + platform: platform2, + elements + } = state; + const _a = evaluate(options, state), { + mainAxis: checkMainAxis = true, + crossAxis: checkCrossAxis = true, + fallbackPlacements: specifiedFallbackPlacements, + fallbackStrategy = "bestFit", + fallbackAxisSideDirection = "none", + flipAlignment = true + } = _a, detectOverflowOptions = __objRest(_a, [ + "mainAxis", + "crossAxis", + "fallbackPlacements", + "fallbackStrategy", + "fallbackAxisSideDirection", + "flipAlignment" + ]); + if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) { + return {}; + } + const side = getSide(placement); + const isBasePlacement = getSide(initialPlacement) === initialPlacement; + const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)); + const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement)); + if (!specifiedFallbackPlacements && fallbackAxisSideDirection !== "none") { + fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl)); + } + const placements2 = [initialPlacement, ...fallbackPlacements]; + const overflow = await detectOverflow(state, detectOverflowOptions); + const overflows = []; + let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || []; + if (checkMainAxis) { + overflows.push(overflow[side]); + } + if (checkCrossAxis) { + const sides2 = getAlignmentSides(placement, rects, rtl); + overflows.push(overflow[sides2[0]], overflow[sides2[1]]); + } + overflowsData = [...overflowsData, { + placement, + overflows + }]; + if (!overflows.every((side2) => side2 <= 0)) { + var _middlewareData$flip2, _overflowsData$filter; + const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1; + const nextPlacement = placements2[nextIndex]; + if (nextPlacement) { + return { + data: { + index: nextIndex, + overflows: overflowsData + }, + reset: { + placement: nextPlacement + } + }; + } + let resetPlacement = (_overflowsData$filter = overflowsData.filter((d) => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement; + if (!resetPlacement) { + switch (fallbackStrategy) { + case "bestFit": { + var _overflowsData$map$so; + const placement2 = (_overflowsData$map$so = overflowsData.map((d) => [d.placement, d.overflows.filter((overflow2) => overflow2 > 0).reduce((acc, overflow2) => acc + overflow2, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$map$so[0]; + if (placement2) { + resetPlacement = placement2; + } + break; + } + case "initialPlacement": + resetPlacement = initialPlacement; + break; + } + } + if (placement !== resetPlacement) { + return { + reset: { + placement: resetPlacement + } + }; + } + } + return {}; + } + }; +}; +async function convertValueToCoords(state, options) { + const { + placement, + platform: platform2, + elements + } = state; + const rtl = await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)); + const side = getSide(placement); + const alignment = getAlignment(placement); + const isVertical = getSideAxis(placement) === "y"; + const mainAxisMulti = ["left", "top"].includes(side) ? -1 : 1; + const crossAxisMulti = rtl && isVertical ? -1 : 1; + const rawValue = evaluate(options, state); + let { + mainAxis, + crossAxis, + alignmentAxis + } = typeof rawValue === "number" ? { + mainAxis: rawValue, + crossAxis: 0, + alignmentAxis: null + } : __spreadValues({ + mainAxis: 0, + crossAxis: 0, + alignmentAxis: null + }, rawValue); + if (alignment && typeof alignmentAxis === "number") { + crossAxis = alignment === "end" ? alignmentAxis * -1 : alignmentAxis; + } + return isVertical ? { + x: crossAxis * crossAxisMulti, + y: mainAxis * mainAxisMulti + } : { + x: mainAxis * mainAxisMulti, + y: crossAxis * crossAxisMulti + }; +} +var offset = function(options) { + if (options === void 0) { + options = 0; + } + return { + name: "offset", + options, + async fn(state) { + const { + x: x2, + y + } = state; + const diffCoords = await convertValueToCoords(state, options); + return { + x: x2 + diffCoords.x, + y: y + diffCoords.y, + data: diffCoords + }; + } + }; +}; +var shift = function(options) { + if (options === void 0) { + options = {}; + } + return { + name: "shift", + options, + async fn(state) { + const { + x: x2, + y, + placement + } = state; + const _a = evaluate(options, state), { + mainAxis: checkMainAxis = true, + crossAxis: checkCrossAxis = false, + limiter = { + fn: (_ref) => { + let { + x: x3, + y: y2 + } = _ref; + return { + x: x3, + y: y2 + }; + } + } + } = _a, detectOverflowOptions = __objRest(_a, [ + "mainAxis", + "crossAxis", + "limiter" + ]); + const coords = { + x: x2, + y + }; + const overflow = await detectOverflow(state, detectOverflowOptions); + const crossAxis = getSideAxis(getSide(placement)); + const mainAxis = getOppositeAxis(crossAxis); + let mainAxisCoord = coords[mainAxis]; + let crossAxisCoord = coords[crossAxis]; + if (checkMainAxis) { + const minSide = mainAxis === "y" ? "top" : "left"; + const maxSide = mainAxis === "y" ? "bottom" : "right"; + const min2 = mainAxisCoord + overflow[minSide]; + const max2 = mainAxisCoord - overflow[maxSide]; + mainAxisCoord = clamp(min2, mainAxisCoord, max2); + } + if (checkCrossAxis) { + const minSide = crossAxis === "y" ? "top" : "left"; + const maxSide = crossAxis === "y" ? "bottom" : "right"; + const min2 = crossAxisCoord + overflow[minSide]; + const max2 = crossAxisCoord - overflow[maxSide]; + crossAxisCoord = clamp(min2, crossAxisCoord, max2); + } + const limitedCoords = limiter.fn(__spreadProps(__spreadValues({}, state), { + [mainAxis]: mainAxisCoord, + [crossAxis]: crossAxisCoord + })); + return __spreadProps(__spreadValues({}, limitedCoords), { + data: { + x: limitedCoords.x - x2, + y: limitedCoords.y - y + } + }); + } + }; +}; +var size = function(options) { + if (options === void 0) { + options = {}; + } + return { + name: "size", + options, + async fn(state) { + const { + placement, + rects, + platform: platform2, + elements + } = state; + const _a = evaluate(options, state), { + apply = () => { + } + } = _a, detectOverflowOptions = __objRest(_a, [ + "apply" + ]); + const overflow = await detectOverflow(state, detectOverflowOptions); + const side = getSide(placement); + const alignment = getAlignment(placement); + const isYAxis = getSideAxis(placement) === "y"; + const { + width, + height + } = rects.floating; + let heightSide; + let widthSide; + if (side === "top" || side === "bottom") { + heightSide = side; + widthSide = alignment === (await (platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)) ? "start" : "end") ? "left" : "right"; + } else { + widthSide = side; + heightSide = alignment === "end" ? "top" : "bottom"; + } + const overflowAvailableHeight = height - overflow[heightSide]; + const overflowAvailableWidth = width - overflow[widthSide]; + const noShift = !state.middlewareData.shift; + let availableHeight = overflowAvailableHeight; + let availableWidth = overflowAvailableWidth; + if (isYAxis) { + const maximumClippingWidth = width - overflow.left - overflow.right; + availableWidth = alignment || noShift ? min(overflowAvailableWidth, maximumClippingWidth) : maximumClippingWidth; + } else { + const maximumClippingHeight = height - overflow.top - overflow.bottom; + availableHeight = alignment || noShift ? min(overflowAvailableHeight, maximumClippingHeight) : maximumClippingHeight; + } + if (noShift && !alignment) { + const xMin = max(overflow.left, 0); + const xMax = max(overflow.right, 0); + const yMin = max(overflow.top, 0); + const yMax = max(overflow.bottom, 0); + if (isYAxis) { + availableWidth = width - 2 * (xMin !== 0 || xMax !== 0 ? xMin + xMax : max(overflow.left, overflow.right)); + } else { + availableHeight = height - 2 * (yMin !== 0 || yMax !== 0 ? yMin + yMax : max(overflow.top, overflow.bottom)); + } + } + await apply(__spreadProps(__spreadValues({}, state), { + availableWidth, + availableHeight + })); + const nextDimensions = await platform2.getDimensions(elements.floating); + if (width !== nextDimensions.width || height !== nextDimensions.height) { + return { + reset: { + rects: true + } + }; + } + return {}; + } + }; +}; + +// node_modules/@floating-ui/utils/dom/dist/floating-ui.utils.dom.mjs +function getNodeName(node) { + if (isNode(node)) { + return (node.nodeName || "").toLowerCase(); + } + return "#document"; +} +function getWindow(node) { + var _node$ownerDocument; + return (node == null ? void 0 : (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window; +} +function getDocumentElement(node) { + var _ref; + return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement; +} +function isNode(value) { + return value instanceof Node || value instanceof getWindow(value).Node; +} +function isElement(value) { + return value instanceof Element || value instanceof getWindow(value).Element; +} +function isHTMLElement(value) { + return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement; +} +function isShadowRoot(value) { + if (typeof ShadowRoot === "undefined") { + return false; + } + return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot; +} +function isOverflowElement(element) { + const { + overflow, + overflowX, + overflowY, + display + } = getComputedStyle2(element); + return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !["inline", "contents"].includes(display); +} +function isTableElement(element) { + return ["table", "td", "th"].includes(getNodeName(element)); +} +function isContainingBlock(element) { + const webkit = isWebKit(); + const css = getComputedStyle2(element); + return css.transform !== "none" || css.perspective !== "none" || (css.containerType ? css.containerType !== "normal" : false) || !webkit && (css.backdropFilter ? css.backdropFilter !== "none" : false) || !webkit && (css.filter ? css.filter !== "none" : false) || ["transform", "perspective", "filter"].some((value) => (css.willChange || "").includes(value)) || ["paint", "layout", "strict", "content"].some((value) => (css.contain || "").includes(value)); +} +function getContainingBlock(element) { + let currentNode = getParentNode(element); + while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) { + if (isContainingBlock(currentNode)) { + return currentNode; + } else { + currentNode = getParentNode(currentNode); + } + } + return null; +} +function isWebKit() { + if (typeof CSS === "undefined" || !CSS.supports) + return false; + return CSS.supports("-webkit-backdrop-filter", "none"); +} +function isLastTraversableNode(node) { + return ["html", "body", "#document"].includes(getNodeName(node)); +} +function getComputedStyle2(element) { + return getWindow(element).getComputedStyle(element); +} +function getNodeScroll(element) { + if (isElement(element)) { + return { + scrollLeft: element.scrollLeft, + scrollTop: element.scrollTop + }; + } + return { + scrollLeft: element.pageXOffset, + scrollTop: element.pageYOffset + }; +} +function getParentNode(node) { + if (getNodeName(node) === "html") { + return node; + } + const result = ( + // Step into the shadow DOM of the parent of a slotted node. + node.assignedSlot || // DOM Element detected. + node.parentNode || // ShadowRoot detected. + isShadowRoot(node) && node.host || // Fallback. + getDocumentElement(node) + ); + return isShadowRoot(result) ? result.host : result; +} +function getNearestOverflowAncestor(node) { + const parentNode = getParentNode(node); + if (isLastTraversableNode(parentNode)) { + return node.ownerDocument ? node.ownerDocument.body : node.body; + } + if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) { + return parentNode; + } + return getNearestOverflowAncestor(parentNode); +} +function getOverflowAncestors(node, list, traverseIframes) { + var _node$ownerDocument2; + if (list === void 0) { + list = []; + } + if (traverseIframes === void 0) { + traverseIframes = true; + } + const scrollableAncestor = getNearestOverflowAncestor(node); + const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body); + const win = getWindow(scrollableAncestor); + if (isBody) { + return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], win.frameElement && traverseIframes ? getOverflowAncestors(win.frameElement) : []); + } + return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes)); +} + +// node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs +function getCssDimensions(element) { + const css = getComputedStyle2(element); + let width = parseFloat(css.width) || 0; + let height = parseFloat(css.height) || 0; + const hasOffset = isHTMLElement(element); + const offsetWidth = hasOffset ? element.offsetWidth : width; + const offsetHeight = hasOffset ? element.offsetHeight : height; + const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight; + if (shouldFallback) { + width = offsetWidth; + height = offsetHeight; + } + return { + width, + height, + $: shouldFallback + }; +} +function unwrapElement(element) { + return !isElement(element) ? element.contextElement : element; +} +function getScale(element) { + const domElement = unwrapElement(element); + if (!isHTMLElement(domElement)) { + return createCoords(1); + } + const rect = domElement.getBoundingClientRect(); + const { + width, + height, + $ + } = getCssDimensions(domElement); + let x2 = ($ ? round(rect.width) : rect.width) / width; + let y = ($ ? round(rect.height) : rect.height) / height; + if (!x2 || !Number.isFinite(x2)) { + x2 = 1; + } + if (!y || !Number.isFinite(y)) { + y = 1; + } + return { + x: x2, + y + }; +} +var noOffsets = /* @__PURE__ */ createCoords(0); +function getVisualOffsets(element) { + const win = getWindow(element); + if (!isWebKit() || !win.visualViewport) { + return noOffsets; + } + return { + x: win.visualViewport.offsetLeft, + y: win.visualViewport.offsetTop + }; +} +function shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) { + if (isFixed === void 0) { + isFixed = false; + } + if (!floatingOffsetParent || isFixed && floatingOffsetParent !== getWindow(element)) { + return false; + } + return isFixed; +} +function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) { + if (includeScale === void 0) { + includeScale = false; + } + if (isFixedStrategy === void 0) { + isFixedStrategy = false; + } + const clientRect = element.getBoundingClientRect(); + const domElement = unwrapElement(element); + let scale = createCoords(1); + if (includeScale) { + if (offsetParent) { + if (isElement(offsetParent)) { + scale = getScale(offsetParent); + } + } else { + scale = getScale(element); + } + } + const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0); + let x2 = (clientRect.left + visualOffsets.x) / scale.x; + let y = (clientRect.top + visualOffsets.y) / scale.y; + let width = clientRect.width / scale.x; + let height = clientRect.height / scale.y; + if (domElement) { + const win = getWindow(domElement); + const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent; + let currentIFrame = win.frameElement; + while (currentIFrame && offsetParent && offsetWin !== win) { + const iframeScale = getScale(currentIFrame); + const iframeRect = currentIFrame.getBoundingClientRect(); + const css = getComputedStyle2(currentIFrame); + const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x; + const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y; + x2 *= iframeScale.x; + y *= iframeScale.y; + width *= iframeScale.x; + height *= iframeScale.y; + x2 += left; + y += top; + currentIFrame = getWindow(currentIFrame).frameElement; + } + } + return rectToClientRect({ + width, + height, + x: x2, + y + }); +} +function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) { + let { + rect, + offsetParent, + strategy + } = _ref; + const isOffsetParentAnElement = isHTMLElement(offsetParent); + const documentElement = getDocumentElement(offsetParent); + if (offsetParent === documentElement) { + return rect; + } + let scroll = { + scrollLeft: 0, + scrollTop: 0 + }; + let scale = createCoords(1); + const offsets = createCoords(0); + if (isOffsetParentAnElement || !isOffsetParentAnElement && strategy !== "fixed") { + if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) { + scroll = getNodeScroll(offsetParent); + } + if (isHTMLElement(offsetParent)) { + const offsetRect = getBoundingClientRect(offsetParent); + scale = getScale(offsetParent); + offsets.x = offsetRect.x + offsetParent.clientLeft; + offsets.y = offsetRect.y + offsetParent.clientTop; + } + } + return { + width: rect.width * scale.x, + height: rect.height * scale.y, + x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x, + y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + }; +} +function getClientRects(element) { + return Array.from(element.getClientRects()); +} +function getWindowScrollBarX(element) { + return getBoundingClientRect(getDocumentElement(element)).left + getNodeScroll(element).scrollLeft; +} +function getDocumentRect(element) { + const html = getDocumentElement(element); + const scroll = getNodeScroll(element); + const body = element.ownerDocument.body; + const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth); + const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight); + let x2 = -scroll.scrollLeft + getWindowScrollBarX(element); + const y = -scroll.scrollTop; + if (getComputedStyle2(body).direction === "rtl") { + x2 += max(html.clientWidth, body.clientWidth) - width; + } + return { + width, + height, + x: x2, + y + }; +} +function getViewportRect(element, strategy) { + const win = getWindow(element); + const html = getDocumentElement(element); + const visualViewport = win.visualViewport; + let width = html.clientWidth; + let height = html.clientHeight; + let x2 = 0; + let y = 0; + if (visualViewport) { + width = visualViewport.width; + height = visualViewport.height; + const visualViewportBased = isWebKit(); + if (!visualViewportBased || visualViewportBased && strategy === "fixed") { + x2 = visualViewport.offsetLeft; + y = visualViewport.offsetTop; + } + } + return { + width, + height, + x: x2, + y + }; +} +function getInnerBoundingClientRect(element, strategy) { + const clientRect = getBoundingClientRect(element, true, strategy === "fixed"); + const top = clientRect.top + element.clientTop; + const left = clientRect.left + element.clientLeft; + const scale = isHTMLElement(element) ? getScale(element) : createCoords(1); + const width = element.clientWidth * scale.x; + const height = element.clientHeight * scale.y; + const x2 = left * scale.x; + const y = top * scale.y; + return { + width, + height, + x: x2, + y + }; +} +function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) { + let rect; + if (clippingAncestor === "viewport") { + rect = getViewportRect(element, strategy); + } else if (clippingAncestor === "document") { + rect = getDocumentRect(getDocumentElement(element)); + } else if (isElement(clippingAncestor)) { + rect = getInnerBoundingClientRect(clippingAncestor, strategy); + } else { + const visualOffsets = getVisualOffsets(element); + rect = __spreadProps(__spreadValues({}, clippingAncestor), { + x: clippingAncestor.x - visualOffsets.x, + y: clippingAncestor.y - visualOffsets.y + }); + } + return rectToClientRect(rect); +} +function hasFixedPositionAncestor(element, stopNode) { + const parentNode = getParentNode(element); + if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) { + return false; + } + return getComputedStyle2(parentNode).position === "fixed" || hasFixedPositionAncestor(parentNode, stopNode); +} +function getClippingElementAncestors(element, cache) { + const cachedResult = cache.get(element); + if (cachedResult) { + return cachedResult; + } + let result = getOverflowAncestors(element, [], false).filter((el) => isElement(el) && getNodeName(el) !== "body"); + let currentContainingBlockComputedStyle = null; + const elementIsFixed = getComputedStyle2(element).position === "fixed"; + let currentNode = elementIsFixed ? getParentNode(element) : element; + while (isElement(currentNode) && !isLastTraversableNode(currentNode)) { + const computedStyle = getComputedStyle2(currentNode); + const currentNodeIsContaining = isContainingBlock(currentNode); + if (!currentNodeIsContaining && computedStyle.position === "fixed") { + currentContainingBlockComputedStyle = null; + } + const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === "static" && !!currentContainingBlockComputedStyle && ["absolute", "fixed"].includes(currentContainingBlockComputedStyle.position) || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode); + if (shouldDropCurrentNode) { + result = result.filter((ancestor) => ancestor !== currentNode); + } else { + currentContainingBlockComputedStyle = computedStyle; + } + currentNode = getParentNode(currentNode); + } + cache.set(element, result); + return result; +} +function getClippingRect(_ref) { + let { + element, + boundary, + rootBoundary, + strategy + } = _ref; + const elementClippingAncestors = boundary === "clippingAncestors" ? getClippingElementAncestors(element, this._c) : [].concat(boundary); + const clippingAncestors = [...elementClippingAncestors, rootBoundary]; + const firstClippingAncestor = clippingAncestors[0]; + const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => { + const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy); + accRect.top = max(rect.top, accRect.top); + accRect.right = min(rect.right, accRect.right); + accRect.bottom = min(rect.bottom, accRect.bottom); + accRect.left = max(rect.left, accRect.left); + return accRect; + }, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy)); + return { + width: clippingRect.right - clippingRect.left, + height: clippingRect.bottom - clippingRect.top, + x: clippingRect.left, + y: clippingRect.top + }; +} +function getDimensions(element) { + return getCssDimensions(element); +} +function getRectRelativeToOffsetParent(element, offsetParent, strategy) { + const isOffsetParentAnElement = isHTMLElement(offsetParent); + const documentElement = getDocumentElement(offsetParent); + const isFixed = strategy === "fixed"; + const rect = getBoundingClientRect(element, true, isFixed, offsetParent); + let scroll = { + scrollLeft: 0, + scrollTop: 0 + }; + const offsets = createCoords(0); + if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) { + if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) { + scroll = getNodeScroll(offsetParent); + } + if (isOffsetParentAnElement) { + const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent); + offsets.x = offsetRect.x + offsetParent.clientLeft; + offsets.y = offsetRect.y + offsetParent.clientTop; + } else if (documentElement) { + offsets.x = getWindowScrollBarX(documentElement); + } + } + return { + x: rect.left + scroll.scrollLeft - offsets.x, + y: rect.top + scroll.scrollTop - offsets.y, + width: rect.width, + height: rect.height + }; +} +function getTrueOffsetParent(element, polyfill) { + if (!isHTMLElement(element) || getComputedStyle2(element).position === "fixed") { + return null; + } + if (polyfill) { + return polyfill(element); + } + return element.offsetParent; +} +function getOffsetParent(element, polyfill) { + const window2 = getWindow(element); + if (!isHTMLElement(element)) { + return window2; + } + let offsetParent = getTrueOffsetParent(element, polyfill); + while (offsetParent && isTableElement(offsetParent) && getComputedStyle2(offsetParent).position === "static") { + offsetParent = getTrueOffsetParent(offsetParent, polyfill); + } + if (offsetParent && (getNodeName(offsetParent) === "html" || getNodeName(offsetParent) === "body" && getComputedStyle2(offsetParent).position === "static" && !isContainingBlock(offsetParent))) { + return window2; + } + return offsetParent || getContainingBlock(element) || window2; +} +var getElementRects = async function(_ref) { + let { + reference, + floating, + strategy + } = _ref; + const getOffsetParentFn = this.getOffsetParent || getOffsetParent; + const getDimensionsFn = this.getDimensions; + return { + reference: getRectRelativeToOffsetParent(reference, await getOffsetParentFn(floating), strategy), + floating: __spreadValues({ + x: 0, + y: 0 + }, await getDimensionsFn(floating)) + }; +}; +function isRTL(element) { + return getComputedStyle2(element).direction === "rtl"; +} +var platform = { + convertOffsetParentRelativeRectToViewportRelativeRect, + getDocumentElement, + getClippingRect, + getOffsetParent, + getElementRects, + getClientRects, + getDimensions, + getScale, + isElement, + isRTL +}; +function observeMove(element, onMove) { + let io = null; + let timeoutId; + const root = getDocumentElement(element); + function cleanup() { + clearTimeout(timeoutId); + io && io.disconnect(); + io = null; + } + function refresh(skip, threshold) { + if (skip === void 0) { + skip = false; + } + if (threshold === void 0) { + threshold = 1; + } + cleanup(); + const { + left, + top, + width, + height + } = element.getBoundingClientRect(); + if (!skip) { + onMove(); + } + if (!width || !height) { + return; + } + const insetTop = floor(top); + const insetRight = floor(root.clientWidth - (left + width)); + const insetBottom = floor(root.clientHeight - (top + height)); + const insetLeft = floor(left); + const rootMargin = -insetTop + "px " + -insetRight + "px " + -insetBottom + "px " + -insetLeft + "px"; + const options = { + rootMargin, + threshold: max(0, min(1, threshold)) || 1 + }; + let isFirstUpdate = true; + function handleObserve(entries) { + const ratio = entries[0].intersectionRatio; + if (ratio !== threshold) { + if (!isFirstUpdate) { + return refresh(); + } + if (!ratio) { + timeoutId = setTimeout(() => { + refresh(false, 1e-7); + }, 100); + } else { + refresh(false, ratio); + } + } + isFirstUpdate = false; + } + try { + io = new IntersectionObserver(handleObserve, __spreadProps(__spreadValues({}, options), { + // Handle