diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml
deleted file mode 100644
index 2701862..0000000
--- a/.forgejo/workflows/deploy.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: Deploy
-
-on:
- push:
- branches:
- - main
-
-jobs:
- deploy:
- name: Deploy to Production
- runs-on: docker
- steps:
- - name: Checkout code
- run: |
- git clone --depth 1 --branch main https://oauth2:${{ github.token }}@forge.studio-variable.com/${{ github.repository }}.git .
- ls -la
-
- - name: Deploy via FTP
- env:
- USERNAME: ${{ secrets.USERNAME }}
- PASSWORD: ${{ secrets.PASSWORD }}
- HOST: ${{ secrets.HOST }}
- run: |
- apt-get update -qq && apt-get install -y -qq lftp
- cat > /tmp/lftp-script.txt <
-
-
diff --git a/site/plugins/code-editor/src/index.js b/site/plugins/code-editor/src/index.js
deleted file mode 100644
index 89e4600..0000000
--- a/site/plugins/code-editor/src/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import CodeEditor from "./components/field/CodeEditor.vue";
-
-window.panel.plugin("sylvainjule/code-editor", {
- fields: {
- "code-editor": CodeEditor,
- },
-});
diff --git a/site/plugins/code-editor/src/node/patchVuePrismEditor.mjs b/site/plugins/code-editor/src/node/patchVuePrismEditor.mjs
deleted file mode 100644
index 3b80640..0000000
--- a/site/plugins/code-editor/src/node/patchVuePrismEditor.mjs
+++ /dev/null
@@ -1,38 +0,0 @@
-import { existsSync, readFileSync, writeFileSync } from "fs";
-import chalk from "chalk";
-import consola from "consola";
-
-const srcPath = "node_modules/vue-prism-editor/dist/prismeditor.esm.js";
-
-async function main() {
- consola.start("Vue Prism Editor patcher");
-
- if (!existsSync(srcPath)) {
- consola.error(
- `couldn't find ${chalk.cyan(srcPath)}, did you run ${chalk.green(
- "npm install"
- )}?`
- );
- return;
- }
-
- const source = readFileSync(srcPath, "utf8");
-
- if (!source.includes("Vue.extend")) {
- consola.success("already patched");
- return;
- }
-
- consola.info("patching the source component...");
-
- let output = source
- .replace(/^import Vue from 'vue';/, "")
- .replace("/*#__PURE__*/Vue.extend(", "")
- .replace(/\}\)(;\s+export)/, "}$1");
-
- writeFileSync(srcPath, output, "utf8");
-
- consola.success("successfully patched");
-}
-
-main().catch((err) => consola.error(err));
diff --git a/site/plugins/custom-marks/index.css b/site/plugins/custom-marks/index.css
deleted file mode 100644
index 805cc33..0000000
--- a/site/plugins/custom-marks/index.css
+++ /dev/null
@@ -1,11 +0,0 @@
-/* Mark Green - Couleur verte */
-.k-writer span.green {
- color: #04fea0;
-}
-
-/* Mark Pixel - Typo serif (placeholder pour future typo pixel) */
-.k-writer span.pixel {
- font-family: "Terminal", serif;
- font-size: 1.15em;
- text-transform: uppercase;
-}
diff --git a/site/plugins/custom-marks/index.js b/site/plugins/custom-marks/index.js
deleted file mode 100644
index 580f787..0000000
--- a/site/plugins/custom-marks/index.js
+++ /dev/null
@@ -1,22 +0,0 @@
-panel.plugin("custom/marks", {
- icons: {
- "single-quote":
- '',
- },
- writerMarks: {
- cite: {
- button: {
- icon: "single-quote",
- label: "Cite",
- },
- commands() {
- return () => this.toggle();
- },
- name: "cite",
- schema: {
- parseDOM: [{ tag: "cite" }],
- toDOM: () => ["cite", 0],
- },
- },
- },
-});
diff --git a/site/plugins/custom-marks/index.php b/site/plugins/custom-marks/index.php
deleted file mode 100644
index bae7014..0000000
--- a/site/plugins/custom-marks/index.php
+++ /dev/null
@@ -1,7 +0,0 @@
-parent()]);
-
- foreach (site()->find('textes')->children() as $year) {
- if ($year->linkedTexts()->toPages()->has($article)) {
- $years = $years->add($year);
- }
- }
- return $years;
-}
-
function setTitleFontSizeClass($title, $level = 'h1')
{
$length = strlen($title);
@@ -35,10 +23,15 @@ function setTitleFontSizeClass($title, $level = 'h1')
function getAuthorBySlug($slug)
{
- $site = site();
- $authors = page("auteurs")->children();
- $author = $authors->find($slug);
+ $kirby = kirby();
+ $author = '';
+ foreach ($kirby->users() as $user) {
+
+ if (Str::slug($user->name()) === $slug) {
+
+ $author = $user;
+ }
+ }
return $author;
}
-
\ No newline at end of file
diff --git a/site/plugins/kirby-seo/.husky/pre-commit b/site/plugins/kirby-seo/.husky/pre-commit
deleted file mode 100755
index 7dc800e..0000000
--- a/site/plugins/kirby-seo/.husky/pre-commit
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env sh
-. "$(dirname -- "$0")/_/husky.sh"
-
-pnpm run build && git add index.css index.js
diff --git a/site/plugins/kirby-seo/.nvmrc b/site/plugins/kirby-seo/.nvmrc
deleted file mode 100644
index 2edeafb..0000000
--- a/site/plugins/kirby-seo/.nvmrc
+++ /dev/null
@@ -1 +0,0 @@
-20
\ No newline at end of file
diff --git a/site/plugins/kirby-seo/.php-cs-fixer.dist.php b/site/plugins/kirby-seo/.php-cs-fixer.dist.php
deleted file mode 100644
index 6abe02c..0000000
--- a/site/plugins/kirby-seo/.php-cs-fixer.dist.php
+++ /dev/null
@@ -1,59 +0,0 @@
-exclude('vendor')
- ->in(__DIR__);
-
-$config = new PhpCsFixer\Config();
-return $config
- ->setRules([
- '@PSR12' => true,
- 'align_multiline_comment' => ['comment_type' => 'phpdocs_like'],
- 'array_indentation' => true,
- 'array_syntax' => ['syntax' => 'short'],
- 'cast_spaces' => ['space' => 'none'],
- // 'class_keyword_remove' => true, // replaces static::class with 'static' (won't work)
- 'combine_consecutive_issets' => true,
- 'combine_consecutive_unsets' => true,
- 'combine_nested_dirname' => true,
- 'concat_space' => ['spacing' => 'one'],
- 'declare_equal_normalize' => ['space' => 'single'],
- 'dir_constant' => true,
- 'function_typehint_space' => true,
- 'include' => true,
- 'logical_operators' => true,
- 'lowercase_cast' => true,
- 'lowercase_static_reference' => true,
- 'magic_constant_casing' => true,
- 'magic_method_casing' => true,
- 'method_chaining_indentation' => true,
- 'modernize_types_casting' => true,
- 'multiline_comment_opening_closing' => true,
- 'native_function_casing' => true,
- 'native_function_type_declaration_casing' => true,
- 'new_with_braces' => true,
- 'no_blank_lines_after_class_opening' => true,
- 'no_blank_lines_after_phpdoc' => true,
- 'no_empty_comment' => true,
- 'no_empty_phpdoc' => true,
- 'no_empty_statement' => true,
- 'no_leading_namespace_whitespace' => true,
- 'no_mixed_echo_print' => ['use' => 'echo'],
- 'no_unneeded_control_parentheses' => true,
- 'no_unused_imports' => true,
- 'no_useless_return' => true,
- 'ordered_imports' => ['sort_algorithm' => 'alpha'],
- // 'phpdoc_add_missing_param_annotation' => ['only_untyped' => false], // adds params in the wrong order
- 'phpdoc_align' => ['align' => 'left'],
- 'phpdoc_indent' => true,
- 'phpdoc_scalar' => true,
- 'phpdoc_trim' => true,
- 'short_scalar_cast' => true,
- 'single_line_comment_style' => true,
- 'single_quote' => true,
- 'ternary_to_null_coalescing' => true,
- 'whitespace_after_comma_in_array' => true
- ])
- ->setRiskyAllowed(true)
- ->setIndent("\t")
- ->setFinder($finder);
diff --git a/site/plugins/kirby-seo/LICENSE b/site/plugins/kirby-seo/LICENSE
deleted file mode 100644
index 8bf1eeb..0000000
--- a/site/plugins/kirby-seo/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2023-2024 Tobias Möritz
-
-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/site/plugins/kirby-seo/README.md b/site/plugins/kirby-seo/README.md
deleted file mode 100644
index 832fb31..0000000
--- a/site/plugins/kirby-seo/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
Kirby SEO
-All-in-one toolkit that makes implementing SEO & Meta best practices a breeze
-
----
-
-## Features
-
-- 🔎 All-in-one SEO and meta solution
-- 🪜 The Meta Cascade: Intelligently merge metadata from multiple sources
-- 🎛 Completely configurable: Disable features you don't need
-- 💻 Simple Panel UI with previews for Google, Twitter, Facebook & Co.
-- 📮 [Schema.org (JSON-LD)](https://schema.org/) support with fluent classes
-- 🤖 Automatic Robots rule generation, based on page status
-- 📝 Sitemap generation with multi-lang support
-
-## Get started
-
-[Read the documentation](https://plugins.andkindness.com/seo/docs/get-started/feature-overview) to get started with Kirby SEO.
-
-## Support the project
-
-> [!NOTE]
-> This plugin is provided free of charge & published under the permissive MIT License. If you use it in a commercial project, please consider sponsoring me on GitHub to support further development and continued maintenance of Kirby SEO.
-
-## License
-
-[MIT License](./../LICENSE)
-Copyright © 2023-2024 Tobias Möritz
diff --git a/site/plugins/kirby-seo/blueprints/fields/meta-group.yml b/site/plugins/kirby-seo/blueprints/fields/meta-group.yml
deleted file mode 100644
index 8845eb1..0000000
--- a/site/plugins/kirby-seo/blueprints/fields/meta-group.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-type: group
-fields:
- metaHeadline:
- label: meta-headline
- type: headline
- numbered: false
- metaTitle:
- label: title-overwrite
- type: text
- placeholder: '{{ page.title }}'
- metaTemplate:
- label: meta-title-template
- type: text
- help: meta-title-template-help
- width: 2/3
- placeholder: '{{ page.metadata.metaTemplate }}'
- useTitleTemplate:
- label: use-title-template
- type: toggle
- help: use-title-template-help
- width: 1/3
- default: true
- text:
- - "{{ t('use-title-template-no') }}"
- - "{{ t('use-title-template-yes') }}"
- metaDescription:
- label: meta-description
- type: textarea
- help: meta-description-help
- placeholder: '{{ page.metadata.metaDescription }}'
- buttons: false
- seoLine1:
- type: line
diff --git a/site/plugins/kirby-seo/blueprints/fields/og-group.yml b/site/plugins/kirby-seo/blueprints/fields/og-group.yml
deleted file mode 100644
index 11fc7b8..0000000
--- a/site/plugins/kirby-seo/blueprints/fields/og-group.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-type: group
-fields:
- ogHeadline:
- label: og-headline
- type: headline
- numbered: false
- help: global-og-headline-help
- ogTemplate:
- label: og-title-template
- type: text
- width: 2/3
- help: meta-title-template-help
- placeholder: '{{ page.metadata.ogTemplate }}'
- useOgTemplate:
- label: use-title-template
- type: toggle
- help: use-title-template-help
- width: 1/3
- default: true
- text:
- - "{{ t('use-title-template-no') }}"
- - "{{ t('use-title-template-yes') }}"
- ogDescription:
- label: og-description
- type: textarea
- buttons: false
- placeholder: '{{ page.metadata.ogDescription }}'
- ogImage:
- label: og-image
- extends: seo/fields/og-image
- empty: og-image-empty
- twitterCardType:
- label: twitter-card-type
- width: 1/2
- placeholder: "{{ t('default-select') }} {{ t(site.twitterCardType) }}"
- type: select
- options:
- summary: "{{ t('summary') }}"
- summary_large_image: "{{ t('summary_large_image') }}"
- help: twitter-card-type-help
- twitterAuthor:
- label: twitter-author
- width: 1/2
- type: text
- before: '@'
- placeholder: '{{ page.metadata.twitterCreator }}'
- seoLine2:
- type: line
diff --git a/site/plugins/kirby-seo/blueprints/fields/og-image.php b/site/plugins/kirby-seo/blueprints/fields/og-image.php
deleted file mode 100644
index 05462a9..0000000
--- a/site/plugins/kirby-seo/blueprints/fields/og-image.php
+++ /dev/null
@@ -1,31 +0,0 @@
- 'files',
- 'multiple' => false,
- 'uploads' => [],
- 'query' => Str::contains($kirby->path(), '/site') && !Str::contains($kirby->path(), 'pages') ? 'site.images' : 'page.images' // small hack to get context for field using api path
- ];
-
- if ($parent = option('tobimori.seo.files.parent')) {
- $blueprint['uploads'] = [
- 'parent' => $parent
- ];
- $blueprint['query'] = "{$parent}.images";
- }
-
- if ($template = option('tobimori.seo.files.template')) {
- $blueprint['uploads'] = [
- ...$blueprint['uploads'],
- 'template' => $template
- ];
-
- $blueprint['query'] = "{$blueprint['query']}.filterBy('template', '{$template}')";
- }
-
- return $blueprint;
-};
diff --git a/site/plugins/kirby-seo/blueprints/fields/robots.php b/site/plugins/kirby-seo/blueprints/fields/robots.php
deleted file mode 100644
index 1aebd1f..0000000
--- a/site/plugins/kirby-seo/blueprints/fields/robots.php
+++ /dev/null
@@ -1,55 +0,0 @@
-option('tobimori.seo.robots.active') || !$kirby->option('tobimori.seo.robots.pageSettings')) {
- return [
- 'type' => 'hidden'
- ];
- }
-
- $fields = [
- 'robotsHeadline' => [
- 'label' => 'robots',
- 'type' => 'headline',
- 'numbered' => false,
- ]
- ];
-
- $page = Meta::currentPage();
- foreach ($kirby->option('tobimori.seo.robots.types') as $robots) {
- $upper = Str::ucfirst($robots);
-
- $fields["robots{$upper}"] = [
- 'label' => "robots-{$robots}",
- 'type' => 'toggles',
- 'help' => "robots-{$robots}-help",
- 'width' => '1/2',
- 'default' => 'default',
- 'reset' => false,
- 'options' => [
- 'default' => $page ?
- A::join([
- t('default-select'),
- $page->metadata()->get("robots{$upper}", ['fields'])->toBool() ? t('yes') : t('no')
- ], ' ')
- : t('default-select'),
- 'true' => t('yes'),
- 'false' => t('no'),
- ]
- ];
- }
-
- $fields['seoLine3'] = [
- 'type' => 'line'
- ];
-
- return [
- 'type' => 'group',
- 'fields' => $fields,
- ];
-};
diff --git a/site/plugins/kirby-seo/blueprints/fields/site-robots.php b/site/plugins/kirby-seo/blueprints/fields/site-robots.php
deleted file mode 100644
index 17bfcc9..0000000
--- a/site/plugins/kirby-seo/blueprints/fields/site-robots.php
+++ /dev/null
@@ -1,49 +0,0 @@
-option('tobimori.seo.robots.active') || !$kirby->option('tobimori.seo.robots.pageSettings')) {
- return [
- 'type' => 'hidden'
- ];
- }
-
- $fields = [
- 'robotsHeadline' => [
- 'label' => 'robots',
- 'type' => 'headline',
- 'numbered' => false,
- ]
- ];
-
- foreach ($kirby->option('tobimori.seo.robots.types') as $robots) {
- $index = $kirby->option('tobimori.seo.robots.index');
- if (is_callable($index)) {
- $index = $index();
- }
-
- $fields["robots{$robots}"] = [
- 'label' => "robots-{$robots}",
- 'type' => 'toggles',
- 'help' => "robots-{$robots}-help",
- 'width' => '1/2',
- 'default' => 'default',
- 'reset' => false,
- 'options' => [
- 'default' => t('default-select') . ' ' . ($index ? t('yes') : t('no')),
- 'true' => t('yes'),
- 'false' => t('no'),
- ]
- ];
- }
-
- $fields['seoLine3'] = [
- 'type' => 'line'
- ];
-
- return [
- 'type' => 'group',
- 'fields' => $fields,
- ];
-};
diff --git a/site/plugins/kirby-seo/blueprints/fields/social-media.php b/site/plugins/kirby-seo/blueprints/fields/social-media.php
deleted file mode 100644
index 8ab9f33..0000000
--- a/site/plugins/kirby-seo/blueprints/fields/social-media.php
+++ /dev/null
@@ -1,30 +0,0 @@
-option('tobimori.seo.socialMedia') as $key => $value) {
- if ($value) {
- $fields[$key] = [
- 'label' => ucfirst($key),
- 'type' => 'url',
- 'icon' => strtolower($key),
- 'placeholder' => $value
- ];
- }
- }
-
- return [
- 'label' => 'social-media-accounts',
- 'type' => 'object',
- 'help' => 'social-media-accounts-help',
- 'fields' => $fields
- ];
-};
diff --git a/site/plugins/kirby-seo/blueprints/page.yml b/site/plugins/kirby-seo/blueprints/page.yml
deleted file mode 100644
index ed86854..0000000
--- a/site/plugins/kirby-seo/blueprints/page.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-label: metadata-site
-icon: search
-
-columns:
- main:
- width: 7/12
- fields:
- metaGroup: seo/fields/meta-group
- ogGroup: seo/fields/og-group
- robots: seo/fields/robots
- metaInherit:
- label: inherit-settings
- type: multiselect
- help: inherit-settings-help
- options:
- metaTemplate: "{{ t('meta-title-template') }}"
- metaDescription: "{{ t('meta-description') }}"
- ogTemplate: "{{ t('og-title-template') }}"
- ogDescription: "{{ t('og-description') }}"
- ogImage: "{{ t('og-image') }}"
- twitterCardType: "{{ t('twitter-card-type') }}"
- twitterAuthor: "{{ t('twitter-author') }}"
- robots: '{{ t("robots") }}'
- sidebar:
- width: 5/12
- sticky: true
- sections:
- seoPreview:
- type: seo-preview
diff --git a/site/plugins/kirby-seo/blueprints/site.yml b/site/plugins/kirby-seo/blueprints/site.yml
deleted file mode 100644
index eebba22..0000000
--- a/site/plugins/kirby-seo/blueprints/site.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-label: metadata-site
-icon: search
-
-columns:
- main:
- width: 7/12
- fields:
- metaHeadline:
- label: global-meta-headline
- type: headline
- numbered: false
- help: global-meta-headline-help
- metaTemplate:
- label: meta-title-template
- type: text
- help: meta-title-template-help
- metaDescription:
- label: meta-description
- type: textarea
- help: meta-description-help
- buttons: false
- seoLine1:
- type: line
- ogHeadline:
- label: global-og-headline
- type: headline
- numbered: false
- help: global-og-headline-help
- ogTemplate:
- label: og-title-template
- type: text
- default: '{{ title }}'
- help: meta-title-template-help
- placeholder: '{{ site.metaTemplate }}'
- ogDescription:
- label: og-description
- type: textarea
- buttons: false
- placeholder: '{{ site.metaDescription }}'
- ogSiteName:
- label: og-site-name
- type: text
- default: '{{ site.title }}'
- placeholder: '{{ site.title }}'
- width: 1/2
- ogImage:
- label: og-image
- extends: seo/fields/og-image
- empty: og-image-empty
- width: 1/2
- twitterCardType:
- label: twitter-card-type
- width: 1/2
- type: select
- default: summary
- required: true
- options:
- summary: "{{ t('summary') }}"
- summary_large_image: "{{ t('summary_large_image') }}"
- help: twitter-card-type-help
- seoLine2:
- type: line
- robots: seo/fields/site-robots
- socialMediaAccounts: seo/fields/social-media
- sidebar:
- width: 5/12
- sticky: true
- sections:
- seoPreview:
- type: seo-preview
diff --git a/site/plugins/kirby-seo/classes/Meta.php b/site/plugins/kirby-seo/classes/Meta.php
deleted file mode 100644
index 806152b..0000000
--- a/site/plugins/kirby-seo/classes/Meta.php
+++ /dev/null
@@ -1,670 +0,0 @@
-page = $page;
- $this->lang = $lang;
-
- if (method_exists($this->page, 'metaDefaults')) {
- $this->metaDefaults = $this->page->metaDefaults($this->lang);
- }
- }
-
- /**
- * Returns the meta array which maps meta tags to their fieldnames
- */
- protected function metaArray(): array
- {
- if ($this->metaArray) {
- return $this->metaArray;
- }
-
- /**
- * We have to specify field names and resolve them later, so we can use this
- * function to resolve meta tags from field names in the programmatic function
- */
- $meta =
- [
- 'title' => 'metaTitle',
- 'description' => 'metaDescription',
- 'date' => fn () => $this->page->modified($this->dateFormat()),
- 'og:title' => 'ogTitle',
- 'og:description' => 'ogDescription',
- 'og:site_name' => 'ogSiteName',
- 'og:image' => 'ogImage',
- 'og:image:width' => fn () => $this->ogImage() ? $this->get('ogImage')->toFile()?->width() : null,
- 'og:image:height' => fn () => $this->ogImage() ? $this->get('ogImage')->toFile()?->height() : null,
- 'og:image:alt' => fn () => $this->get('ogImage')->toFile()?->alt(),
- 'og:type' => 'ogType',
- ];
-
-
- // Robots
- if ($robotsActive = option('tobimori.seo.robots.active')) {
- $meta['robots'] = fn () => $this->robots();
- }
-
- // only add canonical and alternate tags if the page is indexable
- // we have to resolve this lazily (using a callable) to avoid an infinite loop
- $allowsIndexFn = fn () => !$robotsActive || !Str::contains($this->robots(), 'noindex');
-
- // canonical
- $canonicalFn = fn () => $allowsIndexFn() ? $this->canonicalUrl() : null;
- $meta['canonical'] = $canonicalFn;
- $meta['og:url'] = $canonicalFn;
-
- // Multi-lang alternate tags
- if (kirby()->languages()->count() > 1 && kirby()->language() !== null) {
- foreach (kirby()->languages() as $lang) {
- // only add alternate tags if the page is indexable
- $meta['alternate'][] = fn () => $allowsIndexFn() ? [
- 'hreflang' => $lang->code(),
- 'href' => $this->page->url($lang->code()),
- ] : null;
-
- if ($lang !== kirby()->language()) {
- $meta['og:locale:alternate'][] = fn () => $lang->code();
- }
- }
-
- // only add alternate tags if the page is indexable
- $meta['alternate'][] = fn () => $allowsIndexFn() ? [
- 'hreflang' => 'x-default',
- 'href' => $this->page->url(kirby()->language()->code()),
- ] : null;
- $meta['og:locale'] = fn () => kirby()->language()->locale(LC_ALL);
- } else {
- $meta['og:locale'] = fn () => $this->locale(LC_ALL);
- }
-
- // Twitter tags "opt-in" - TODO: wip
- if (option('tobimori.seo.twitter', true)) {
- $meta = array_merge($meta, [
- 'twitter:card' => 'twitterCardType',
- 'twitter:title' => 'ogTitle',
- 'twitter:description' => 'ogDescription',
- 'twitter:image' => 'ogImage',
- 'twitter:site' => 'twitterSite',
- 'twitter:creator' => 'twitterCreator',
- ]);
- }
-
- // This array will be normalized for use in the snippet in $this->snippetData()
- return $this->metaArray = $meta;
- }
-
- /**
- * This array defines what HTML tag the corresponding meta tags are using including the attributes,
- * so everything is a bit more elegant when defining programmatic content (supports regex)
- */
- public const TAG_TYPE_MAP = [
- [
- 'tag' => 'title',
- 'tags' => [
- 'title'
- ]
- ],
- [
- 'tag' => 'link',
- 'attributes' => [
- 'name' => 'rel',
- 'content' => 'href',
- ],
- 'tags' => [
- 'canonical',
- 'alternate',
- ]
- ],
- [
- 'tag' => 'meta',
- 'attributes' => [
- 'name' => 'property',
- 'content' => 'content',
- ],
- 'tags' => [
- '/og:.+/'
- ]
- ]
- ];
-
- /**
- * Normalize the meta array and remaining meta defaults to be used in the snippet,
- * also resolve the content, if necessary
- */
- public function snippetData(array $raw = null): array
- {
- $mergeWithDefaults = !isset($raw);
- $raw ??= $this->metaArray();
- $tags = [];
-
- foreach ($raw as $name => $value) {
- // if the key is numeric, it is already normalized to the correct array syntax
- if (is_numeric($name)) {
- // but we still check if the array is valid
- if (!is_array($value) || count(array_intersect(['tag', 'content', 'attributes'], array_keys($value))) !== count($value)) {
- throw new InvalidArgumentException("[kirby-seo] Invalid array structure found in programmatic content for page {$this->slug()}. Please check your metaDefaults method for template {$this->template()->name()}.");
- }
-
- $tags[] = $value;
- continue;
- }
-
- // allow overrides from metaDefaults for keys that are a callable or array by default
- // (all fields from meta array that are not part of the regular cascade)
- if ((is_callable($value) || is_array($value)) && $mergeWithDefaults && array_key_exists($name, $this->metaDefaults)) {
- $this->consumed[] = $name;
- $value = $this->metaDefaults[$name];
- }
-
- // if the value is a string, we know it's a field name
- if (is_string($value)) {
- $value = $this->$value($name);
- }
-
- // if the value is a callable, we resolve it
- if (is_callable($value)) {
- $value = $value($this->page);
- }
-
- // if the value is empty, we don't want to output it
- if ((is_a($value, 'Kirby\Content\Field') && $value->isEmpty()) || !$value) {
- continue;
- }
-
- // resolve the tag type from the meta array
- // so we can use the correct attributes to normalize it
- $tag = $this->resolveTag($name);
-
- // if the value is an associative array now, all of them are attributes
- // and we don't look for what the TAG_TYPE_MAP says
- // or there should be multiple tags with the same name (non-associative array)
- if (is_array($value)) {
- if (!A::isAssociative($value)) {
- foreach ($value as $val) {
- $tags = array_merge($tags, $this->snippetData([$name => $val]));
- }
- continue;
- }
-
- // array is associative, so it's an array of attributes
- // we resolve the values, if they are callable
- array_walk($value, function (&$item) {
- if (is_callable($item)) {
- $item = $item($this->page);
- }
- });
-
- // add the tag to the array
- $tags[] = [
- 'tag' => $tag['tag'],
- 'attributes' => $value,
- 'content' => null,
- ];
- continue;
- }
-
- // if the value is a string, we use the TAG_TYPE_MAP
- // to correctly map the attributes
- $tags[] = [
- 'tag' => $tag['tag'],
- 'attributes' => isset($tag['attributes']) ? [
- $tag['attributes']['name'] => $name,
- $tag['attributes']['content'] => $value,
- ] : null,
- 'content' => !isset($tag['attributes']) ? $value : null,
- ];
- }
-
- if ($mergeWithDefaults) {
- // merge the remaining meta defaults
- $tags = array_merge($tags, $this->snippetData(array_diff_key($this->metaDefaults, array_flip($this->consumed))));
- }
-
- return $tags;
- }
-
- /**
- * Resolves the tag type from the meta array
- */
- protected function resolveTag(string $tag): array
- {
- foreach (self::TAG_TYPE_MAP as $type) {
- foreach ($type['tags'] as $regexOrString) {
- // Check if the supplied tag is a regex or a normal tag name
- if (Str::startsWith($regexOrString, '/') && Str::endsWith($regexOrString, '/') ?
- Str::match($tag, $regexOrString) : $tag === $regexOrString
- ) {
- return $type;
- }
- }
- }
-
- return [
- 'tag' => 'meta',
- 'attributes' => [
- 'name' => 'name',
- 'content' => 'content',
- ]
- ];
- }
-
- /**
- * Magic method to get a meta value by calling the method name
- */
- public function __call($name, $args = null): mixed
- {
- if (method_exists($this, $name)) {
- return $this->$name($args);
- }
-
- return $this->get($name);
- }
-
- /**
- * Get the meta value for a given key
- */
- public function get(string $key, array $exclude = []): Field
- {
- $cascade = option('tobimori.seo.cascade');
- if (count(array_intersect(get_class_methods($this), $cascade)) !== count($cascade)) {
- throw new InvalidArgumentException('[kirby-seo] Invalid cascade method in config. Please check your options for `tobimori.seo.cascade`.');
- }
-
- // Track consumed keys, so we don't output legacy field values
- $toBeConsumed = $key;
- if (
- (array_key_exists($toBeConsumed, $this->metaDefaults)
- || array_key_exists($toBeConsumed = $this->findTagForField($toBeConsumed), $this->metaDefaults))
- && !in_array($toBeConsumed, $this->consumed)
- ) {
- $this->consumed[] = $toBeConsumed;
- }
-
- foreach (array_diff($cascade, $exclude) as $method) {
- if ($field = $this->$method($key)) {
- return $field;
- }
- }
-
- return new Field($this->page, $key, '');
- }
-
- /**
- * Get the meta value for a given key from the page's fields
- */
- protected function fields(string $key): Field|null
- {
- if (($field = $this->page->content($this->lang)->get($key))) {
- if (Str::contains($key, 'robots') && !option('tobimori.seo.robots.pageSettings')) {
- return null;
- }
-
- if ($field->isNotEmpty() && !A::has(self::DEFAULT_VALUES, $field->value())) {
- return $field;
- }
- }
-
- return null;
- }
-
- /**
- * Maps Open Graph fields to Meta fields for fallbackFields
- * cascade method
- */
- public const FALLBACK_MAP = [
- 'ogTitle' => 'metaTitle',
- 'ogDescription' => 'metaDescription',
- 'ogTemplate' => 'metaTemplate',
- ];
-
- /**
- * We only allow the following cascade methods for fallbacks,
- * because we don't want to fallback to the config defaults for
- * Meta fields, because we most likely already have those set
- * for the Open Graph fields
- */
- public const FALLBACK_CASCADE = [
- 'fields',
- 'programmatic',
- 'parent',
- 'site'
- ];
-
- /**
- * Get the meta value for a given key using the fallback fields
- * defined above (usually Open Graph > Meta Fields)
- */
- protected function fallbackFields(string $key): Field|null
- {
- if (array_key_exists($key, self::FALLBACK_MAP)) {
- $fallback = self::FALLBACK_MAP[$key];
- $cascade = option('tobimori.seo.cascade');
-
- foreach (array_intersect($cascade, self::FALLBACK_CASCADE) as $method) {
- if ($field = $this->$method($fallback)) {
- return $field;
- }
- }
- }
-
- return null;
- }
-
- protected function findTagForField(string $fieldName): string|null
- {
- return array_search($fieldName, $this->metaArray());
- }
-
- /**
- * Get the meta value for a given key from the page's meta
- * array, which can be set in the page's model metaDefaults method
- */
- protected function programmatic(string $key): Field|null
- {
- if (!$this->metaDefaults) {
- return null;
- }
-
- // Check if the key (field name) is in the array syntax
- if (array_key_exists($key, $this->metaDefaults)) {
- $val = $this->metaDefaults[$key];
- }
-
- /* If there is no programmatic value for the key,
- * try looking it up in the meta array
- * maybe it is a meta tag and not a field name?
- */
- if (!isset($val) && ($key = $this->findTagForField($key)) && array_key_exists($key, $this->metaDefaults)) {
- $val = $this->metaDefaults[$key];
- }
-
- if (isset($val)) {
- if (is_callable($val)) {
- $val = $val($this->page);
- }
-
- if (is_array($val)) {
- $val = $val['content'] ?? $val['href'] ?? null;
-
- // Last sanity check, if the array syntax doesn't have a supported key
- if ($val === null) {
- // Remove the key from the consumed array, so it doesn't get filtered out
- // (we can assume the entry is a custom meta tag that uses different attributes)
- $this->consumed = array_filter($this->consumed, fn ($item) => $item !== $key);
- return null;
- }
- }
-
- if (is_a($val, 'Kirby\Content\Field')) {
- return new Field($this->page, $key, $val->value());
- }
-
- return new Field($this->page, $key, $val);
- }
-
- return null;
- }
-
- /**
- * Get the meta value for a given key from the page's parent,
- * if the page is allowed to inherit the value
- */
- protected function parent(string $key): Field|null
- {
- if ($this->canInherit($key)) {
- $parent = $this->page->parent();
- $parentMeta = new Meta($parent, $this->lang);
- if ($value = $parentMeta->get($key)) {
- return $value;
- }
- }
-
- return null;
- }
-
- /**
- * Get the meta value for a given key from the
- * site's meta blueprint & content
- */
- protected function site(string $key): Field|null
- {
- if (($site = $this->page->site()->content($this->lang)->get($key)) && ($site->isNotEmpty() && !A::has(self::DEFAULT_VALUES, $site->value))) {
- return $site;
- }
-
- return null;
- }
-
- /**
- * Get the meta value for a given key from the
- * config.php options
- */
- protected function options(string $key): Field|null
- {
- if ($option = option("tobimori.seo.default.{$key}")) {
- if (is_callable($option)) {
- $option = $option($this->page);
- }
-
- if (is_a($option, 'Kirby\Content\Field')) {
- return $option;
- }
-
- return new Field($this->page, $key, $option);
- }
-
- return null;
- }
-
- /**
- * Checks if the page can inherit a meta value from its parent
- */
- private function canInherit(string $key): bool
- {
- $parent = $this->page->parent();
- if (!$parent) {
- return false;
- }
-
- $inherit = $parent->metaInherit()->split();
- if (Str::contains($key, 'robots') && A::has($inherit, 'robots')) {
- return true;
- }
- return A::has($inherit, $key);
- }
-
- /**
- * Applies the title template, and returns the correct title
- */
- public function metaTitle()
- {
- $title = $this->get('metaTitle');
- $template = $this->get('metaTemplate');
-
- $useTemplate = $this->page->useTitleTemplate();
- $useTemplate = $useTemplate->isEmpty() ? true : $useTemplate->toBool();
-
- $string = $title->value();
- if ($useTemplate) {
- $string = $this->page->toString(
- $template,
- ['title' => $title]
- );
- }
-
- return new Field(
- $this->page,
- 'metaTitle',
- $string
- );
- }
-
- /**
- * Applies the OG title template, and returns the OG Title
- */
- public function ogTitle()
- {
- $title = $this->get('metaTitle');
- $template = $this->get('ogTemplate');
-
- $useTemplate = $this->page->useOgTemplate();
- $useTemplate = $useTemplate->isEmpty() ? true : $useTemplate->toBool();
-
- $string = $title->value();
- if ($useTemplate) {
- $string = $this->page->toString(
- $template,
- ['title' => $title]
- );
- }
-
- return new Field(
- $this->page,
- 'ogTitle',
- $string
- );
- }
-
- /**
- * Gets the canonical url for the page
- */
- public function canonicalUrl()
- {
- return $this->page->site()->canonicalFor($this->page->url());
- }
-
- /**
- * Get the Twitter username from an account url set in the site options
- */
- public function twitterSite()
- {
- $accs = $this->page->site()->socialMediaAccounts()->toObject();
- $username = '';
-
- if ($accs->twitter()->isNotEmpty()) {
- // tries to match all twitter urls, and extract the username
- $matches = [];
- preg_match('/^(https?:\/\/)?(www\.)?twitter\.com\/(#!\/)?@?(?[^\/\?]*)$/', $accs->twitter()->value(), $matches);
- if (isset($matches['name'])) {
- $username = $matches['name'];
- }
- }
-
- return new Field($this->page, 'twitter', $username);
- }
-
- /**
- * Gets the date format for modified meta tags, based on the registered date handler
- */
- public function dateFormat(): string
- {
- if ($custom = option('tobimori.seo.dateFormat')) {
- if (is_callable($custom)) {
- return $custom($this->page);
- }
-
- return $custom;
- }
-
- switch (option('date.handler')) {
- case 'strftime':
- return '%Y-%m-%d';
- case 'intl':
- return 'yyyy-MM-dd';
- case 'date':
- default:
- return 'Y-m-d';
- }
- }
-
- /**
- * Get the pages' robots rules as string
- */
- public function robots()
- {
- $robots = [];
- foreach (option('tobimori.seo.robots.types') as $type) {
- if (!$this->get('robots' . Str::ucfirst($type))->toBool()) {
- $robots[] = 'no' . Str::lower($type);
- }
- }
-
- if (A::count($robots) === 0) {
- $robots = ['all'];
- }
-
- return A::join($robots, ',');
- }
-
- /**
- * Get the og:image url
- */
- public function ogImage(): string|null
- {
- $field = $this->get('ogImage');
-
- if ($ogImage = $field->toFile()?->thumb([
- 'width' => 1200,
- 'height' => 630,
- 'crop' => true,
- ])) {
- return $ogImage->url();
- }
-
- if ($field->isNotEmpty()) {
- return $field->value();
- }
-
- return null;
- }
-
- /**
- * Helper method the get the current page from the URL path,
- * for use in programmatic blueprints
- */
- public static function currentPage(): Page|null
- {
- $path = App::instance()->request()->url()->toString();
- $matches = Str::match($path, "/pages\/([a-zA-Z0-9-_+]+)\/?/m");
- $segments = Str::split($matches[1], '+');
-
- $page = App::instance()->site();
- foreach ($segments as $segment) {
- if ($page = $page->findPageOrDraft($segment)) {
- continue;
- }
-
- return null;
- }
-
- return $page;
- }
-}
diff --git a/site/plugins/kirby-seo/classes/SchemaSingleton.php b/site/plugins/kirby-seo/classes/SchemaSingleton.php
deleted file mode 100644
index c5057b7..0000000
--- a/site/plugins/kirby-seo/classes/SchemaSingleton.php
+++ /dev/null
@@ -1,29 +0,0 @@
-id() ?? 'default'][$type])) {
- self::$instances[$page?->id() ?? 'default'][$type] = Schema::{$type}();
- }
-
- return self::$instances[$page?->id() ?? 'default'][$type];
- }
-
- public static function getInstances(Page|null $page = null): array
- {
- return self::$instances[$page?->id() ?? 'default'] ?? [];
- }
-}
diff --git a/site/plugins/kirby-seo/classes/Sitemap/Sitemap.php b/site/plugins/kirby-seo/classes/Sitemap/Sitemap.php
deleted file mode 100644
index 7264bcf..0000000
--- a/site/plugins/kirby-seo/classes/Sitemap/Sitemap.php
+++ /dev/null
@@ -1,82 +0,0 @@
-key;
- }
-
- public function loc(): string
- {
- return kirby()->site()->canonicalFor('sitemap-' . $this->key . '.xml');
- }
-
- public function lastmod(): string
- {
- $lastmod = 0;
- foreach ($this as $url) {
- $lastmod = max($lastmod, strtotime($url->lastmod()));
- }
-
- if ($lastmod > 0) {
- return date('c', $lastmod);
- }
-
- return date('c');
- }
-
- public function createUrl(string $loc): SitemapUrl
- {
- $url = $this->makeUrl($loc);
- $this->append($url);
- return $url;
- }
-
- public static function makeUrl(string $url): SitemapUrl
- {
- return new SitemapUrl($url);
- }
-
- public function toDOMNode(DOMDocument $doc = new DOMDocument('1.0', 'UTF-8'))
- {
- $doc->formatOutput = true;
-
- $root = $doc->createElement('sitemap');
- foreach (['loc', 'lastmod'] as $key) {
- $root->appendChild($doc->createElement($key, $this->$key()));
- }
-
- return $root;
- }
-
- public function toString(): string
- {
- $doc = new DOMDocument('1.0', 'UTF-8');
- $doc->formatOutput = true;
-
- $doc->appendChild($doc->createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="/sitemap.xsl"'));
-
- $root = $doc->createElementNS('http://www.sitemaps.org/schemas/sitemap/0.9', 'urlset');
- $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
- $root->setAttribute('seo-version', App::plugin('tobimori/seo')->version());
-
- foreach ($this as $url) {
- $root->appendChild($url->toDOMNode($doc));
- }
-
- $doc->appendChild($root);
- return $doc->saveXML();
- }
-}
diff --git a/site/plugins/kirby-seo/classes/Sitemap/SitemapIndex.php b/site/plugins/kirby-seo/classes/Sitemap/SitemapIndex.php
deleted file mode 100644
index e8d12a2..0000000
--- a/site/plugins/kirby-seo/classes/Sitemap/SitemapIndex.php
+++ /dev/null
@@ -1,100 +0,0 @@
-make($key);
- $this->append($sitemap);
- return $sitemap;
- }
-
- public static function make(string $key = 'pages'): Sitemap
- {
- return new Sitemap($key);
- }
-
- public static function makeUrl(string $url): SitemapUrl
- {
- return new SitemapUrl($url);
- }
-
- public function toString(): string
- {
- $doc = new DOMDocument('1.0', 'UTF-8');
- $doc->formatOutput = true;
-
- $doc->appendChild($doc->createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="sitemap.xsl"'));
-
- $root = $doc->createElementNS('http://www.sitemaps.org/schemas/sitemap/0.9', 'sitemapindex');
- $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
- $root->setAttribute('seo-version', App::plugin('tobimori/seo')->version());
- $doc->appendChild($root);
-
- foreach ($this as $sitemap) {
- $root->appendChild($sitemap->toDOMNode($doc));
- }
-
- return $doc->saveXML();
- }
-
- public function isValidIndex(string $key = null): bool
- {
- if ($key === null) {
- return $this->count() > 1;
- }
-
- return !!$this->findBy('key', $key) && $this->count() > 1;
- }
-
- public function generate(): void
- {
- $generator = option('tobimori.seo.sitemap.generator');
- if (is_callable($generator)) {
- $generator($this);
- }
- }
-
- public function render(Page $page): string|null
- {
- // There always has to be at least one index,
- // otherwise the sitemap will fail to render
- if ($this->count() === 0) {
- $this->generate();
- }
-
- if ($this->count() === 0) {
- $this->create();
- }
-
- if (($index = $page->content()->get('index'))->isEmpty()) {
- // If there is only one index, we do not need to render the index page
- return $this->count() > 1 ? $this->toString() : $this->first()->toString();
- }
-
- $sitemap = $this->findBy('key', $index->value());
- if ($sitemap) {
- return $sitemap->toString();
- }
-
- return null;
- }
-}
diff --git a/site/plugins/kirby-seo/classes/Sitemap/SitemapUrl.php b/site/plugins/kirby-seo/classes/Sitemap/SitemapUrl.php
deleted file mode 100644
index 886844c..0000000
--- a/site/plugins/kirby-seo/classes/Sitemap/SitemapUrl.php
+++ /dev/null
@@ -1,113 +0,0 @@
-loc;
- }
-
- $this->loc = $url;
- return $this;
- }
-
- public function lastmod(string $lastmod = null): SitemapUrl|string
- {
- if ($lastmod === null) {
- return $this->lastmod;
- }
-
- $this->lastmod = date('c', $lastmod);
- return $this;
- }
-
- public function changefreq(string $changefreq = null): SitemapUrl|string
- {
- if ($changefreq === null) {
- return $this->changefreq;
- }
-
- $this->changefreq = $changefreq;
- return $this;
- }
-
- public function priority(string $priority = null): SitemapUrl|string
- {
- if ($priority === null) {
- return $this->priority;
- }
-
- $this->priority = $priority;
- return $this;
- }
-
- public function alternates(array $alternates = []): SitemapUrl|array
- {
- if (empty($alternates)) {
- return $this->alternates;
- }
-
- foreach ($alternates as $alternate) {
- foreach (['href', 'hreflang'] as $key) {
- if (!array_key_exists($key, $alternate)) {
- new Exception("[kirby-seo] The alternate link to '{$this->loc()} is missing the '{$key}' attribute");
- }
- }
- }
-
-
- $this->alternates = $alternates;
- return $this;
- }
-
- public function toDOMNode(DOMDocument $doc = new DOMDocument('1.0', 'UTF-8')): DOMNode
- {
- $doc->formatOutput = true;
-
- $node = $doc->createElement('url');
-
- foreach (array_diff_key(get_object_vars($this), array_flip(['alternates'])) as $key => $value) {
- $node->appendChild($doc->createElement($key, $value));
- }
-
- if (!empty($this->alternates())) {
- foreach ($this->alternates() as $alternate) {
- $alternateNode = $doc->createElement('xhtml:link');
- foreach ($alternate as $key => $value) {
- $alternateNode->setAttribute($key, $value);
- }
-
- $node->appendChild($alternateNode);
- }
- }
-
- return $node;
- }
-
- public function toString(): string
- {
- $doc = new DOMDocument('1.0', 'UTF-8');
- $doc->formatOutput = true;
-
- $node = $this->toDOMNode();
- $doc->appendChild($node);
-
- return $doc->saveXML($node);
- }
-}
diff --git a/site/plugins/kirby-seo/composer.json b/site/plugins/kirby-seo/composer.json
deleted file mode 100644
index b2d5582..0000000
--- a/site/plugins/kirby-seo/composer.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
- "name": "tobimori/kirby-seo",
- "description": "The ultimate Kirby SEO toolkit",
- "type": "kirby-plugin",
- "version": "1.1.2",
- "license": "MIT",
- "homepage": "https://github.com/tobimori/kirby-seo#readme",
- "authors": [
- {
- "name": "Tobias Möritz",
- "email": "tobias@moeritz.io"
- }
- ],
- "autoload": {
- "psr-4": {
- "tobimori\\Seo\\": "classes"
- }
- },
- "require": {
- "php": ">=8.1.0",
- "getkirby/composer-installer": "^1.2.1",
- "spatie/schema-org": "^3.14"
- },
- "require-dev": {
- "getkirby/cms": "^4.0",
- "getkirby/cli": "^1.2",
- "friendsofphp/php-cs-fixer": "^3.48"
- },
- "scripts": {
- "dist": "composer install --no-dev --optimize-autoloader"
- },
- "config": {
- "optimize-autoloader": true,
- "allow-plugins": {
- "getkirby/composer-installer": true
- }
- },
- "extra": {
- "kirby-cms-path": false
- }
-}
diff --git a/site/plugins/kirby-seo/config/api.php b/site/plugins/kirby-seo/config/api.php
deleted file mode 100644
index 0470e1c..0000000
--- a/site/plugins/kirby-seo/config/api.php
+++ /dev/null
@@ -1,102 +0,0 @@
- [
- 'dirtyPageOrSite' => function (string $slug) {
- $kirby = kirby();
- $page = $slug == 'site' ? $kirby->site() : $kirby->page(Str::replace($slug, '+', '/'));
-
- if ($this->requestBody()) {
- $form = Form::for($page, [ // Form class handles transformation of changed items
- 'ignoreDisabled' => true,
- 'input' => array_merge(['title' => $page->title()], $page->content()->data(), $this->requestBody()),
- 'language' => $kirby->language()?->code()
- ]);
-
- $page = $page->clone(['content' => $form->data()]);
- }
-
- return $page;
- }
- ],
- 'routes' => [
- [
- 'pattern' => '/k-seo/(:any)/heading-structure',
- 'method' => 'POST',
- 'action' => function (string $slug) {
- $model = $this->dirtyPageOrSite($slug);
-
- if ($model instanceof Page) {
- $page = $model->render();
- $dom = new DOMDocument();
- $dom->loadHTML(htmlspecialchars_decode(mb_convert_encoding(htmlentities($page, ENT_COMPAT, 'UTF-8'), 'ISO-8859-1', 'UTF-8'), ENT_QUOTES), libxml_use_internal_errors(true));
-
- $xpath = new DOMXPath($dom);
- $headings = $xpath->query('//h1|//h2|//h3|//h4|//h5|//h6');
- $data = [];
-
- foreach ($headings as $heading) {
- $data[] = [
- 'level' => (int)str_replace('h', '', $heading->nodeName),
- 'text' => $heading->textContent,
- ];
- }
-
- return $data;
- }
-
- return null;
- }
- ],
- [
- 'pattern' => '/k-seo/(:any)/seo-preview',
- 'method' => 'POST',
- 'action' => function (string $slug) {
- $model = $this->dirtyPageOrSite($slug);
-
- if ($model instanceof Site) {
- $model = $model->homePage();
- }
-
- if ($model instanceof Page) {
- $meta = $model->metadata();
-
- return [
- 'page' => $model->slug(),
- 'url' => $model->url(),
- 'title' => $meta->metaTitle()->value(),
- 'description' => $meta->metaDescription()->value(),
- 'ogSiteName' => $meta->ogSiteName()->value(),
- 'ogTitle' => $meta->ogTitle()->value(),
- 'ogDescription' => $meta->ogDescription()->value(),
- 'ogImage' => $meta->ogImage(),
- 'twitterCardType' => $meta->twitterCardType()->value(),
- ];
- }
-
- return null;
- }
- ],
- [
- 'pattern' => '/k-seo/(:any)/robots',
- 'method' => 'POST',
- 'action' => function (string $slug) {
- $model = $this->dirtyPageOrSite($slug);
- if (!($model instanceof Page)) {
- return null;
- }
- $robots = $model->robots();
-
- return [
- 'active' => option('tobimori.seo.robots.indicator', option('tobimori.seo.robots.active', true)),
- 'state' => $robots,
- ];
- }
- ]
- ]
-];
diff --git a/site/plugins/kirby-seo/config/commands/hello.php b/site/plugins/kirby-seo/config/commands/hello.php
deleted file mode 100644
index f41b20b..0000000
--- a/site/plugins/kirby-seo/config/commands/hello.php
+++ /dev/null
@@ -1,11 +0,0 @@
- 'Hello world',
- 'args' => [],
- 'command' => static function (CLI $cli): void {
- $cli->success('Hello world! This command is a preparation for a future release.');
- }
-];
diff --git a/site/plugins/kirby-seo/config/hooks.php b/site/plugins/kirby-seo/config/hooks.php
deleted file mode 100644
index f5c8a9f..0000000
--- a/site/plugins/kirby-seo/config/hooks.php
+++ /dev/null
@@ -1,27 +0,0 @@
- function (Page $newPage, Page $oldPage) {
- foreach ($newPage->kirby()->option('tobimori.seo.robots.types') as $robots) {
- $upper = Str::ucfirst($robots);
- if ($newPage->content()->get("robots{$upper}")->value() === "") {
- $newPage = $newPage->update([
- "robots{$upper}" => 'default'
- ]);
- }
- }
- },
- 'page.render:before' => function (string $contentType, array $data, Page $page) {
- if (option('tobimori.seo.generateSchema')) {
- $page->schema('WebSite')
- ->url($page->metadata()->canonicalUrl())
- ->copyrightYear(date('Y'))
- ->description($page->metadata()->metaDescription())
- ->name($page->metadata()->metaTitle())
- ->headline($page->metadata()->title());
- }
- },
-];
diff --git a/site/plugins/kirby-seo/config/options.php b/site/plugins/kirby-seo/config/options.php
deleted file mode 100644
index 0d5ea67..0000000
--- a/site/plugins/kirby-seo/config/options.php
+++ /dev/null
@@ -1,82 +0,0 @@
- [
- 'fields',
- 'programmatic',
- // 'fallbackFields', // fallback to meta fields for open graph fields
- 'parent',
- 'site',
- 'options'
- ],
- 'default' => [ // default field values for metadata, format is [field => value]
- 'metaTitle' => fn (Page $page) => $page->title(),
- 'metaTemplate' => '{{ title }} - {{ site.title }}',
- 'ogTemplate' => '{{ title }}',
- 'ogSiteName' => fn (Page $page) => $page->site()->title(),
- 'ogType' => 'website',
- 'twitterCardType' => 'summary',
- 'ogDescription' => fn (Page $page) => $page->metadata()->metaDescription(),
- 'twitterCreator' => fn (Page $page) => $page->metadata()->twitterSite(),
- 'lang' => fn (Page $page) => $page->kirby()->language()?->locale(LC_ALL) ?? $page->kirby()->option('tobimori.seo.lang', 'en_US'),
- // default for robots: noIndex if global index configuration is set, otherwise fall back to page status
- 'robotsIndex' => function (Page $page) {
- $index = $page->kirby()->option('tobimori.seo.robots.index');
- if (is_callable($index)) {
- $index = $index();
- }
-
- if (!$index) {
- return false;
- }
-
- return $page->kirby()->option('tobimori.seo.robots.followPageStatus', true) ? $page->isListed() : true;
- },
- 'robotsFollow' => fn (Page $page) => $page->kirby()->option('tobimori.seo.default.robotsIndex')($page),
- 'robotsArchive' => fn (Page $page) => $page->kirby()->option('tobimori.seo.default.robotsIndex')($page),
- 'robotsImageindex' => fn (Page $page) => $page->kirby()->option('tobimori.seo.default.robotsIndex')($page),
- 'robotsSnippet' => fn (Page $page) => $page->kirby()->option('tobimori.seo.default.robotsIndex')($page),
- ],
- 'socialMedia' => [ // default fields for social media links, format is [field => placeholder]
- 'twitter' => 'https://twitter.com/my-company',
- 'facebook' => 'https://facebook.com/my-company',
- 'instagram' => 'https://instagram.com/my-company',
- 'youtube' => 'https://youtube.com/channel/my-company',
- 'linkedin' => 'https://linkedin.com/company/my-company',
- ],
- 'previews' => [
- 'google',
- 'facebook',
- 'slack'
- ],
- 'robots' => [
- 'active' => true, // whether robots handling should be done by the plugin
- 'followPageStatus' => true, // should unlisted pages be noindex by default?
- 'pageSettings' => true, // whether to have robots settings on each page
- 'indicator' => true, // whether the indicator should be shown in the panel
- 'index' => fn () => !option('debug'), // default site-wide robots setting
- 'sitemap' => null, // sets sitemap url, will be replaced by plugin sitemap in the future
- 'content' => [], // custom robots content
- 'types' => ['index', 'follow', 'archive', 'imageindex', 'snippet'] // available robots types
- ],
- 'sitemap' => [
- 'active' => true,
- 'redirect' => true, // redirect /sitemap to /sitemap.xml
- 'lang' => 'en',
- 'generator' => require __DIR__ . '/options/sitemap.php',
- 'changefreq' => 'weekly',
- 'groupByTemplate' => false,
- 'excludeTemplates' => ['error'],
- 'priority' => fn (Page $p) => number_format(($p->isHomePage()) ? 1 : max(1 - 0.2 * $p->depth(), 0.2), 1),
- ],
- 'files' => [
- 'parent' => null,
- 'template' => null,
- ],
- 'canonicalBase' => null, // base url for canonical links
- 'generateSchema' => true, // whether to generate default schema.org data
- 'lang' => 'en_US', // default language, used for single-language sites
- 'dateFormat' => null, // custom date format
-];
diff --git a/site/plugins/kirby-seo/config/options/sitemap.php b/site/plugins/kirby-seo/config/options/sitemap.php
deleted file mode 100644
index 2cf2486..0000000
--- a/site/plugins/kirby-seo/config/options/sitemap.php
+++ /dev/null
@@ -1,37 +0,0 @@
-index()->filter(fn ($page) => $page->metadata()->robotsIndex()->toBool() && !in_array($page->intendedTemplate()->name(), $exclude));
-
- if ($group = option('tobimori.seo.sitemap.groupByTemplate')) {
- $pages = $pages->group('intendedTemplate');
- }
-
- if (is_a($pages->first(), 'Kirby\Cms\Page')) {
- $pages = $pages->group(fn () => 'pages');
- }
-
- foreach ($pages as $group) {
- $index = $sitemap->create($group ? $group->first()->intendedTemplate()->name() : 'pages');
-
- foreach ($group as $page) {
- $url = $index->createUrl($page->metadata()->canonicalUrl())
- ->lastmod($page->modified() ?? (int)(date('c')))
- ->changefreq(is_callable($changefreq = option('tobimori.seo.sitemap.changefreq')) ? $changefreq($page) : $changefreq)
- ->priority(is_callable($priority = option('tobimori.seo.sitemap.priority')) ? $priority($page) : $priority);
-
- if (kirby()->languages()->count() > 1 && kirby()->language() !== null) {
- $url->alternates(
- kirby()->languages()->map(fn ($language) => new Obj([
- 'hreflang' => $language->code() === kirby()->language()->code() ? 'x-default' : $language->code(),
- 'href' => $page->url($language->code()),
- ]))->toArray()
- );
- }
- }
- }
-};
diff --git a/site/plugins/kirby-seo/config/pageMethods.php b/site/plugins/kirby-seo/config/pageMethods.php
deleted file mode 100644
index 76c2baf..0000000
--- a/site/plugins/kirby-seo/config/pageMethods.php
+++ /dev/null
@@ -1,11 +0,0 @@
- fn ($type) => SchemaSingleton::getInstance($type, $this),
- 'schemas' => fn () => SchemaSingleton::getInstances($this),
- 'metadata' => fn (?string $lang = null) => new Meta($this, $lang),
- 'robots' => fn (?string $lang = null) => $this->metadata($lang)->robots(),
-];
diff --git a/site/plugins/kirby-seo/config/routes.php b/site/plugins/kirby-seo/config/routes.php
deleted file mode 100644
index fe070f0..0000000
--- a/site/plugins/kirby-seo/config/routes.php
+++ /dev/null
@@ -1,98 +0,0 @@
- 'robots.txt',
- 'action' => function () {
- if (option('tobimori.seo.robots.active', true)) {
- $content = snippet('seo/robots.txt', [], true);
- return new Response($content, 'text/plain', 200);
- }
-
- $this->next();
- }
- ],
- [
- 'pattern' => 'sitemap',
- 'action' => function () {
- if (!option('tobimori.seo.sitemap.redirect', true) || !option('tobimori.seo.sitemap.active', true)) {
- $this->next();
- }
-
- go('/sitemap.xml');
- }
- ],
- [
- 'pattern' => 'sitemap.xsl',
- 'action' => function () {
- if (!option('tobimori.seo.sitemap.active', true)) {
- $this->next();
- }
-
- kirby()->response()->type('text/xsl');
-
- $lang = option('tobimori.seo.sitemap.lang', 'en');
- if (is_callable($lang)) {
- $lang = $lang();
- }
- kirby()->setCurrentTranslation($lang);
-
- return Page::factory([
- 'slug' => 'sitemap',
- 'template' => 'sitemap',
- 'model' => 'sitemap',
- 'content' => [
- 'title' => t('sitemap'),
- ],
- ])->render(contentType: 'xsl');
- }
- ],
- [
- 'pattern' => 'sitemap.xml',
- 'action' => function () {
- if (!option('tobimori.seo.sitemap.active', true)) {
- $this->next();
- }
-
- SitemapIndex::instance()->generate();
- kirby()->response()->type('text/xml');
- return Page::factory([
- 'slug' => 'sitemap',
- 'template' => 'sitemap',
- 'model' => 'sitemap',
- 'content' => [
- 'title' => t('sitemap'),
- 'index' => null,
- ],
- ])->render(contentType: 'xml');
- }
- ],
- [
- 'pattern' => 'sitemap-(:any).xml',
- 'action' => function (string $index) {
- if (!option('tobimori.seo.sitemap.active', true)) {
- $this->next();
- }
-
- SitemapIndex::instance()->generate();
- if (!SitemapIndex::instance()->isValidIndex($index)) {
- $this->next();
- }
-
- kirby()->response()->type('text/xml');
- return Page::factory([
- 'slug' => 'sitemap-' . $index,
- 'template' => 'sitemap',
- 'model' => 'sitemap',
- 'content' => [
- 'title' => t('sitemap'),
- 'index' => $index,
- ],
- ])->render(contentType: 'xml');
- }
- ]
-];
diff --git a/site/plugins/kirby-seo/config/sections.php b/site/plugins/kirby-seo/config/sections.php
deleted file mode 100644
index 74eeef3..0000000
--- a/site/plugins/kirby-seo/config/sections.php
+++ /dev/null
@@ -1,20 +0,0 @@
- [
- 'mixins' => ['headline'],
- 'computed' => [
- 'options' => function () {
- return A::map(option('tobimori.seo.previews'), fn ($item) => [
- 'value' => $item,
- 'text' => t($item)
- ]);
- }
- ]
- ],
- 'heading-structure' => [
- 'mixins' => ['headline']
- ]
-];
diff --git a/site/plugins/kirby-seo/config/siteMethods.php b/site/plugins/kirby-seo/config/siteMethods.php
deleted file mode 100644
index 13e6180..0000000
--- a/site/plugins/kirby-seo/config/siteMethods.php
+++ /dev/null
@@ -1,28 +0,0 @@
- fn ($type) => SchemaSingleton::getInstance($type),
- 'schemas' => fn () => SchemaSingleton::getInstances(),
- 'lang' => fn () => Str::replace(option('tobimori.seo.default.lang')($this->homePage()), '_', '-'),
- 'canonicalFor' => function (string $url) {
- $base = option('tobimori.seo.canonicalBase');
- if (is_callable($base)) {
- $base = $base($url);
- }
-
- if ($base === null) {
- $base = $this->url(); // graceful fallback to site url
- }
-
- if (Str::startsWith($url, $base)) {
- return $url;
- }
-
- $path = Url::path($url);
- return url($base . '/' . $path);
- }
-];
diff --git a/site/plugins/kirby-seo/index.css b/site/plugins/kirby-seo/index.css
deleted file mode 100644
index 0dd6799..0000000
--- a/site/plugins/kirby-seo/index.css
+++ /dev/null
@@ -1 +0,0 @@
-@charset "UTF-8";.k-heading-structure__label{display:flex;align-items:center;justify-content:flex-start;gap:var(--spacing-2)}.k-heading-structure__label>.k-icon{color:var(--color-gray-700)}.k-heading-structure__label>.k-loader{margin-left:auto;color:var(--color-gray-700)}.k-heading-structure__notice{margin-top:var(--spacing-2);display:flex;align-items:flex-start}.k-heading-structure__notice>.k-icon{margin-top:.125rem;margin-right:var(--spacing-1);color:var(--color-red)}.k-heading-structure__list{overflow:hidden}.k-heading-structure__item{position:relative;background:var(--theme-color-back);padding-top:var(--spacing-px);padding-bottom:var(--spacing-px);display:flex}.k-heading-structure__item__level{font-family:var(--font-mono);font-weight:700;margin-right:var(--spacing-2)}.k-heading-structure__item__text{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.k-heading-structure__item.is-invalid{color:var(--color-red)}.k-heading-structure__item.level-2{margin-left:0rem;padding-left:1.6rem}.k-heading-structure__item.level-2:before{content:"";position:absolute;top:calc(50% - .0625rem);left:.4rem;width:.8rem;height:.125rem;background-color:currentColor}.k-heading-structure__item.level-2:after{content:"";position:absolute;bottom:calc(50% - .0625rem);left:.4rem;height:9999px;width:.125rem;background-color:currentColor}.k-heading-structure__item.level-3{margin-left:1.6rem;padding-left:1.6rem}.k-heading-structure__item.level-3:before{content:"";position:absolute;top:calc(50% - .0625rem);left:.4rem;width:.8rem;height:.125rem;background-color:currentColor}.k-heading-structure__item.level-3:after{content:"";position:absolute;bottom:calc(50% - .0625rem);left:.4rem;height:9999px;width:.125rem;background-color:currentColor}.k-heading-structure__item.level-4{margin-left:3.2rem;padding-left:1.6rem}.k-heading-structure__item.level-4:before{content:"";position:absolute;top:calc(50% - .0625rem);left:.4rem;width:.8rem;height:.125rem;background-color:currentColor}.k-heading-structure__item.level-4:after{content:"";position:absolute;bottom:calc(50% - .0625rem);left:.4rem;height:9999px;width:.125rem;background-color:currentColor}.k-heading-structure__item.level-5{margin-left:4.8rem;padding-left:1.6rem}.k-heading-structure__item.level-5:before{content:"";position:absolute;top:calc(50% - .0625rem);left:.4rem;width:.8rem;height:.125rem;background-color:currentColor}.k-heading-structure__item.level-5:after{content:"";position:absolute;bottom:calc(50% - .0625rem);left:.4rem;height:9999px;width:.125rem;background-color:currentColor}.k-heading-structure__item.level-6{margin-left:6.4rem;padding-left:1.6rem}.k-heading-structure__item.level-6:before{content:"";position:absolute;top:calc(50% - .0625rem);left:.4rem;width:.8rem;height:.125rem;background-color:currentColor}.k-heading-structure__item.level-6:after{content:"";position:absolute;bottom:calc(50% - .0625rem);left:.4rem;height:9999px;width:.125rem;background-color:currentColor}.k-facebook-preview{background:#f0f2f5;border:1px solid #ced0d4;overflow:hidden;border-radius:var(--rounded)}.k-facebook-preview__image{width:100%;height:0;padding-bottom:52.355%;position:relative}.k-facebook-preview__image img{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover}.k-facebook-preview__content{padding:.75rem 1rem}.k-facebook-preview__title,.k-facebook-preview__description,.k-facebook-preview__url{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;-webkit-line-clamp:1}.k-facebook-preview__url{color:#65676b;font-size:.75rem;text-transform:uppercase;line-height:1.1;margin-bottom:.25rem}.k-facebook-preview__title{font-weight:600;line-height:1.1765;font-size:1rem;color:#050505;margin:.3125rem 0}.k-facebook-preview__description{line-height:1.3333;color:#65676b;font-size:.875rem}.k-google-search-preview{padding:1em;background:#fff;border:1px solid #ccc;letter-spacing:-.005em;border-radius:var(--rounded)}.k-google-search-preview__headline,.k-google-search-preview__paragraph{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden}.k-google-search-preview__headline{margin-top:0;margin-bottom:.25em;font-size:1.25em;font-weight:400;color:#1a0dab;-webkit-line-clamp:1}.k-google-search-preview__headline:hover{text-decoration:underline}.k-google-search-preview__url{display:inline-block;margin-bottom:.5em;font-size:.875em;line-height:1.3;color:#202124;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;max-width:100%}.k-google-search-preview__url>*{margin-right:.25em}.k-google-search-preview__url__breadcrumb{color:#5f6368;display:inline-block}.k-google-search-preview__url__breadcrumb:before{content:"› "}.k-google-search-preview__url .k-icon{margin-left:.1em}.k-google-search-preview__paragraph{margin:0;font-size:.875em;line-height:1.3em;color:#3c4043;-webkit-line-clamp:3}.k-slack-preview{max-width:32.5rem;position:relative;padding-left:1rem;line-height:1.46666667;font-size:.9375rem}.k-slack-preview:before{position:absolute;content:"";top:0;left:0;bottom:0;width:.25rem;border-radius:.5rem;background:#ddd}.k-slack-preview__site-name{display:flex;align-items:center;color:#717274}.k-slack-preview__title{font-weight:700;display:block;color:#0576b9;cursor:pointer}.k-slack-preview__title:hover{text-decoration:underline}.k-slack-preview__description{color:#2c2d30}.k-slack-preview__image{border-radius:.5rem;max-width:22.5rem;overflow:hidden;position:relative;margin-top:.5rem}.k-slack-preview__image:before{border-radius:.5rem;content:"";top:0;right:0;bottom:0;left:0;z-index:2;position:absolute;box-shadow:inset 0 0 0 1px #0000001a}.k-slack-preview__image img{width:100%;height:100%;display:block}.k-field-name-seo-preview-type .k-field-header{display:none}.k-seo-preview__inner{margin-top:var(--spacing-2)}.k-seo-preview__debugger{margin-top:1rem;display:flex;font-size:var(--text-sm);color:var(--color-gray-700);line-height:1.25rem;width:-moz-max-content;width:max-content;margin-left:auto}.k-seo-preview__debugger:hover{text-decoration:underline;color:var(--text-gray-800)}.k-seo-preview__debugger>.k-icon{margin-left:var(--spacing-2)}.k-seo-preview__label{display:flex;align-items:center;justify-content:flex-start;gap:var(--spacing-2)}.k-seo-preview__label>.k-icon{color:var(--color-gray-700)}.k-seo-preview__label>.k-loader{margin-left:auto;color:var(--color-gray-700)}.k-page-view-robots{--color-green-boost: -15%}
diff --git a/site/plugins/kirby-seo/index.js b/site/plugins/kirby-seo/index.js
deleted file mode 100644
index daf1e3c..0000000
--- a/site/plugins/kirby-seo/index.js
+++ /dev/null
@@ -1 +0,0 @@
-(function(){"use strict";function l(s,e,t,a,i,c,_,X){var n=typeof s=="function"?s.options:s;e&&(n.render=e,n.staticRenderFns=t,n._compiled=!0),a&&(n.functional=!0),c&&(n._scopeId="data-v-"+c);var o;if(_?(o=function(r){r=r||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,!r&&typeof __VUE_SSR_CONTEXT__<"u"&&(r=__VUE_SSR_CONTEXT__),i&&i.call(this,r),r&&r._registeredComponents&&r._registeredComponents.add(_)},n._ssrRegister=o):i&&(o=X?function(){i.call(this,(n.functional?this.parent:this).$root.$options.shadowRoot)}:i),o)if(n.functional){n._injectStyles=o;var W=n.render;n.render=function(q,u){return o.call(u),W(q,u)}}else{var d=n.beforeCreate;n.beforeCreate=d?[].concat(d,o):[o]}return{exports:s,options:n}}const p={data(){return{label:null,value:null,isLoading:!0}},created(){this.isLoading=!0,this.load().then(s=>{this.label=s.label}),this.handleLoad(),this.debouncedLoad=this.$helper.debounce(s=>{this.handleLoad(s)},200)},computed:{changes(){return this.$store.getters["content/changes"]()},incorrectOrder(){var s;return(s=this.value)==null?void 0:s.some((e,t)=>{var a;return e.level>(((a=this.value[t-1])==null?void 0:a.level)??0)+1})},multipleH1(){var s;return((s=this.value)==null?void 0:s.filter(e=>e.level===1).length)>1},noH1(){var s;return((s=this.value)==null?void 0:s.filter(e=>e.level===1).length)===0}},methods:{async handleLoad(s){this.isLoading=!0;const e=panel.view.props.model.id;if(!e)throw new Error("[kirby-seo] The Heading structure section is only available for pages");const t=await panel.api.post(`/k-seo/${e.replaceAll("/","+")}/heading-structure`,s??this.changes);this.value=t,this.isLoading=!1},itemInvalid(s,e){var t;return!!(s.level>(((t=this.value[e-1])==null?void 0:t.level)??0)+1||s.level===1&&this.value[e-1]||s.level===1&&this.value.filter(a=>a.level===1).length>1)}},watch:{changes(s){this.debouncedLoad(s)}}};var v=function(){var e=this,t=e._self._c;return e.value?t("div",{staticClass:"k-section k-heading-structure"},[t("div",{staticClass:"k-field-header k-heading-structure__label k-label k-field-label"},[t("k-icon",{attrs:{type:"headline"}}),t("span",{staticClass:"k-label-text"},[e._v(e._s(e.label||e.$t("heading-structure")))]),e.isLoading?t("k-loader"):e._e()],1),t("k-box",{attrs:{theme:"white"}},[t("ol",{staticClass:"k-heading-structure__list"},e._l(e.value,function(a,i){return t("li",{key:i,class:`k-heading-structure__item level-${a.level} ${e.itemInvalid(a,i)?"is-invalid":""}`,style:`z-index: ${e.value.length-i}`},[t("span",{staticClass:"k-heading-structure__item__level"},[e._v("H"+e._s(a.level))]),t("span",{staticClass:"k-heading-structure__item__text"},[e._v(e._s(a.text))])])}),0)]),e.incorrectOrder&&!e.noH1?t("k-box",{staticClass:"k-heading-structure__notice",attrs:{theme:"negative"}},[t("k-icon",{attrs:{type:"alert"}}),t("k-text",[e._v(e._s(e.$t("incorrect-heading-order")))])],1):e._e(),e.multipleH1?t("k-box",{staticClass:"k-heading-structure__notice",attrs:{theme:"negative"}},[t("k-icon",{attrs:{type:"alert"}}),t("k-text",[e._v(e._s(e.$t("multiple-h1-tags")))])],1):e._e(),e.noH1?t("k-box",{staticClass:"k-heading-structure__notice",attrs:{theme:"negative"}},[t("k-icon",{attrs:{type:"alert"}}),t("k-text",[e._v(e._s(e.$t("missing-h1-tag")))])],1):e._e()],1):e._e()},g=[],h=l(p,v,g,!1,null,null,null,null);const f=h.exports,k=Object.freeze(Object.defineProperty({__proto__:null,default:f},Symbol.toStringTag,{value:"Module"})),b={props:{ogTitle:String,url:String,ogDescription:String,ogImage:String},computed:{host(){return new URL(this.url).host}}};var m=function(){var e=this,t=e._self._c;return t("div",[t("div",{staticClass:"k-facebook-preview"},[e.ogImage?t("div",{staticClass:"k-facebook-preview__image"},[t("img",{staticClass:"k-facebook-preview__img",attrs:{src:e.ogImage}})]):e._e(),t("div",{staticClass:"k-facebook-preview__content"},[t("span",{staticClass:"k-facebook-preview__url"},[e._v(e._s(e.host))]),t("span",{staticClass:"k-facebook-preview__title"},[e._v(e._s(e.ogTitle))]),t("p",{staticClass:"k-facebook-preview__description"},[e._v(e._s(e.ogDescription))])])]),t("a",{staticClass:"k-seo-preview__debugger",attrs:{href:"https://developers.facebook.com/tools/debug/","aria-label":"Facebook Sharing Debugger",target:"_blank",rel:"noopener noreferrer"}},[e._v(" "+e._s(e.$t("open-debugger"))+" "),t("k-icon",{attrs:{type:"open"}})],1)])},w=[],$=l(b,m,w,!1,null,null,null,null);const C=$.exports,y={props:{title:String,url:String,description:String},computed:{origin(){return new URL(this.url).origin},breadcrumbs(){return this.url.split("/").slice(3)}}};var L=function(){var e=this,t=e._self._c;return t("div",[t("div",{staticClass:"k-google-search-preview"},[t("span",{staticClass:"k-google-search-preview__url"},[t("span",[e._v(e._s(e.origin))]),e._l(e.breadcrumbs,function(a,i){return t("span",{key:i,staticClass:"k-google-search-preview__url__breadcrumb"},[e._v(" "+e._s(a)+" ")])})],2),t("h2",{staticClass:"k-google-search-preview__headline"},[e._v(e._s(e.title))]),t("p",{staticClass:"k-google-search-preview__paragraph"},[e._v(" "+e._s(e.description)+" ")])]),t("a",{staticClass:"k-seo-preview__debugger",attrs:{href:"https://search.google.com/search-console","aria-label":"Google Search Console",target:"_blank",rel:"noopener noreferrer"}},[e._v(" "+e._s(e.$t("open-search-console"))+" "),t("k-icon",{attrs:{type:"open"}})],1)])},S=[],x=l(y,L,S,!1,null,null,null,null);const T=x.exports,O={props:{ogTitle:String,ogSiteName:String,ogDescription:String,ogImage:String},computed:{origin(){return new URL(this.url).origin}}};var H=function(){var e=this,t=e._self._c;return t("div",{staticClass:"k-slack-preview"},[t("div",{staticClass:"k-slack-preview__content"},[t("div",{staticClass:"k-slack-preview__site-name"},[e._v(e._s(e.ogSiteName||e.origin))]),t("span",{staticClass:"k-slack-preview__title"},[e._v(e._s(e.ogTitle))]),t("p",{staticClass:"k-slack-preview__description"},[e._v(e._s(e.ogDescription))])]),e.ogImage?t("div",{staticClass:"k-slack-preview__image"},[t("img",{attrs:{src:e.ogImage}})]):e._e()])},R=[],I=l(O,H,R,!1,null,null,null,null);const Z=I.exports,V={components:{GooglePreview:T,FacebookPreview:C,SlackPreview:Z},data(){const s=localStorage.getItem("kSEOPreviewType")??"google";return{label:null,value:null,isLoading:!0,options:[],type:s}},created(){this.isLoading=!0,this.load().then(s=>{this.label=s.label,this.options=s.options}),this.handleLoad(),this.debouncedLoad=this.$helper.debounce(s=>{this.handleLoad(s)},200)},computed:{changes(){return this.$store.getters["content/changes"]()}},methods:{async handleLoad(s){var a,i;this.isLoading=!0;const e=((i=(a=panel.view.props.model)==null?void 0:a.id)==null?void 0:i.replaceAll("/","+"))??"site",t=await panel.api.post(`/k-seo/${e}/seo-preview`,s??this.changes);this.value=t,this.isLoading=!1}},watch:{changes(s){this.debouncedLoad(s)},type(){localStorage.setItem("kSEOPreviewType",this.type)}}};var F=function(){var e=this,t=e._self._c;return t("div",{staticClass:"k-section k-seo-preview"},[t("div",{staticClass:"k-field-header k-seo-preview__label k-label k-field-label"},[t("k-icon",{attrs:{type:"preview"}}),t("span",{staticClass:"k-label-text"},[e._v(e._s(e.label||e.$t("seo-preview")))]),e.isLoading?t("k-loader"):e._e()],1),t("k-select-field",{attrs:{type:"select",name:"seo-preview-type",before:e.$t("seo-preview-for"),options:e.options,empty:!1},model:{value:e.type,callback:function(a){e.type=a},expression:"type"}}),e.value?t("div",{staticClass:"k-seo-preview__inner"},[e.type==="google"?t("google-preview",e._b({},"google-preview",e.value,!1)):e._e(),e.type==="facebook"?t("facebook-preview",e._b({},"facebook-preview",e.value,!1)):e._e(),e.type==="slack"?t("slack-preview",e._b({},"slack-preview",e.value,!1)):e._e()],1):e._e()],1)},P=[],z=l(V,F,P,!1,null,null,null,null);const M=z.exports,U=Object.freeze(Object.defineProperty({__proto__:null,default:M},Symbol.toStringTag,{value:"Module"})),j=Object.freeze({import(s){return Object.entries(s).reduce((t,[a,i])=>(t[A(a)]=i.default,t),{})}});function A(s){return s.substring(s.lastIndexOf("/")+1,s.lastIndexOf(".")).toLowerCase()}const D={extends:"k-page-view",data(){return{dirty:!1,robots:{active:!1,state:[]}}},async mounted(){await this.handleLoad()},methods:{openSeoTab(){panel.view.open(panel.view.path+"?tab=seo")},async handleLoad(s){if(!panel.view.props.tabs.some(t=>t.name==="seo"))return;const e=this.model.id.replaceAll("/","+");this.robots=await panel.api.post(`/k-seo/${e}/robots`,s??this.changes)}},computed:{changes(){return this.$store.getters["content/changes"]()},robotsBtn(){const s={responsive:!0,size:"sm",icon:"robots",theme:"positive",text:this.$t("indicator-index"),variant:"filled"};return this.robots.state.includes("no")&&(s.text=this.$t("indicator-any"),s.theme="notice",s.icon="robots-off"),this.robots.state.includes("noindex")&&(s.text=this.$t("indicator-noindex"),s.theme="negative"),s}},watch:{changes(s){(Object.keys(s).some(e=>e.includes("robots"))||this.dirty)&&(this.dirty=!1,this.handleLoad(s),s&&(this.dirty=!0))}}};var N=function(){var e=this,t=e._self._c;return t("k-panel-inside",{staticClass:"k-page-view",attrs:{"data-has-tabs":e.tabs.length>1,"data-id":e.model.id,"data-locked":e.isLocked,"data-template":e.blueprint},scopedSlots:e._u([{key:"topbar",fn:function(){return[e.model.id?t("k-prev-next",{attrs:{prev:e.prev,next:e.next}}):e._e()]},proxy:!0}])},[t("k-header",{staticClass:"k-page-view-header",attrs:{editable:e.permissions.changeTitle&&!e.isLocked},on:{edit:function(a){return e.$dialog(e.id+"/changeTitle")}},scopedSlots:e._u([{key:"buttons",fn:function(){return[t("k-button-group",[e.permissions.preview&&e.model.previewUrl?t("k-button",{staticClass:"k-page-view-preview",attrs:{link:e.model.previewUrl,title:e.$t("open"),icon:"open",target:"_blank",variant:"filled",size:"sm"}}):e._e(),t("k-button",{staticClass:"k-page-view-options",attrs:{disabled:e.isLocked===!0,dropdown:!0,title:e.$t("settings"),icon:"cog",variant:"filled",size:"sm"},on:{click:function(a){return e.$refs.settings.toggle()}}}),t("k-dropdown-content",{ref:"settings",attrs:{options:e.$dropdown(e.id),"align-x":"end"}}),t("k-languages-dropdown"),e.status?t("k-button",e._b({staticClass:"k-page-view-status",attrs:{variant:"filled"},on:{click:function(a){return e.$dialog(e.id+"/changeStatus")}}},"k-button",e.statusBtn,!1)):e._e(),e.robots&&e.robots.active?t("k-button",e._b({staticClass:"k-page-view-status k-page-view-robots",on:{click:e.openSeoTab}},"k-button",e.robotsBtn,!1)):e._e()],1),t("k-form-buttons",{attrs:{lock:e.lock}})]},proxy:!0}])},[e._v(" "+e._s(e.model.title)+" ")]),t("k-model-tabs",{attrs:{tab:e.tab.name,tabs:e.tabs}}),t("k-sections",{attrs:{blueprint:e.blueprint,empty:e.$t("page.blueprint",{blueprint:e.$esc(e.blueprint)}),lock:e.lock,parent:e.id,tab:e.tab}})],1)},E=[],B=l(D,N,E,!1,null,null,null,null);const G=B.exports;panel.plugin("tobimori/seo",{icons:{robots:'',"robots-off":''},sections:j.import(Object.assign({"./sections/heading-structure.vue":k,"./sections/seo-preview.vue":U})),components:{"k-page-view":G}})})();
diff --git a/site/plugins/kirby-seo/index.php b/site/plugins/kirby-seo/index.php
deleted file mode 100644
index 704dd45..0000000
--- a/site/plugins/kirby-seo/index.php
+++ /dev/null
@@ -1,69 +0,0 @@
-') === true
-) {
- throw new Exception('Kirby SEO requires Kirby 4.0.2 or higher.');
-}
-
-App::plugin('tobimori/seo', [
- 'options' => require __DIR__ . '/config/options.php',
- 'sections' => require __DIR__ . '/config/sections.php',
- 'api' => require __DIR__ . '/config/api.php',
- 'siteMethods' => require __DIR__ . '/config/siteMethods.php',
- 'pageMethods' => require __DIR__ . '/config/pageMethods.php',
- 'hooks' => require __DIR__ . '/config/hooks.php',
- 'routes' => require __DIR__ . '/config/routes.php',
- // load all commands automatically
- 'commands' => A::keyBy(A::map(
- Dir::read(__DIR__ . '/config/commands'),
- fn ($file) => A::merge([
- 'id' => 'seo:' . F::name($file),
- ], require __DIR__ . '/config/commands/' . $file)
- ), 'id'),
- // get all files from /translations and register them as language files
- 'translations' => A::keyBy(A::map(
- Dir::read(__DIR__ . '/translations'),
- fn ($file) => A::merge([
- 'lang' => F::name($file),
- ], Yaml::decode(F::read(__DIR__ . '/translations/' . $file)))
- ), 'lang'),
- 'snippets' => [
- 'seo/schemas' => __DIR__ . '/snippets/schemas.php',
- 'seo/head' => __DIR__ . '/snippets/head.php',
- 'seo/robots.txt' => __DIR__ . '/snippets/robots.txt.php',
- ],
- 'templates' => [
- 'sitemap' => __DIR__ . '/templates/sitemap.php',
- 'sitemap.xml' => __DIR__ . '/templates/sitemap.xml.php',
- 'sitemap.xsl' => __DIR__ . '/templates/sitemap.xsl.php',
- ],
- 'blueprints' => [
- 'seo/site' => __DIR__ . '/blueprints/site.yml',
- 'seo/page' => __DIR__ . '/blueprints/page.yml',
- 'seo/fields/og-image' => require __DIR__ . '/blueprints/fields/og-image.php',
- 'seo/fields/og-group' => __DIR__ . '/blueprints/fields/og-group.yml',
- 'seo/fields/meta-group' => __DIR__ . '/blueprints/fields/meta-group.yml',
- 'seo/fields/robots' => require __DIR__ . '/blueprints/fields/robots.php',
- 'seo/fields/site-robots' => require __DIR__ . '/blueprints/fields/site-robots.php',
- 'seo/fields/social-media' => require __DIR__ . '/blueprints/fields/social-media.php',
- ],
-]);
-
-if (!function_exists('schema')) {
- function schema($type)
- {
- return Schema::{$type}();
- }
-}
diff --git a/site/plugins/kirby-seo/package.json b/site/plugins/kirby-seo/package.json
deleted file mode 100644
index b63c9dd..0000000
--- a/site/plugins/kirby-seo/package.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "private": true,
- "license": "MIT",
- "author": "Tobias Möritz",
- "type": "module",
- "scripts": {
- "dev": "kirbyup serve src/index.js",
- "build": "kirbyup src/index.js",
- "prepare": "husky install"
- },
- "devDependencies": {
- "husky": "^9.0.11",
- "kirbyup": "^3.1.4",
- "postcss": "^8.4.35",
- "sass": "^1.71.1"
- }
-}
diff --git a/site/plugins/kirby-seo/postcss.config.js b/site/plugins/kirby-seo/postcss.config.js
deleted file mode 100644
index 9091bd5..0000000
--- a/site/plugins/kirby-seo/postcss.config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export default {
- plugins: {
- 'postcss-logical': {},
- autoprefixer: {}
- }
-}
diff --git a/site/plugins/kirby-seo/snippets/head.php b/site/plugins/kirby-seo/snippets/head.php
deleted file mode 100644
index a742523..0000000
--- a/site/plugins/kirby-seo/snippets/head.php
+++ /dev/null
@@ -1,13 +0,0 @@
-metadata()->snippetData();
-
-foreach ($tags as $tag) {
- echo Html::tag($tag['tag'], $tag['content'] ?? null, $tag['attributes'] ?? []) . PHP_EOL;
-}
diff --git a/site/plugins/kirby-seo/snippets/robots.txt.php b/site/plugins/kirby-seo/snippets/robots.txt.php
deleted file mode 100644
index 51efe27..0000000
--- a/site/plugins/kirby-seo/snippets/robots.txt.php
+++ /dev/null
@@ -1,58 +0,0 @@
- $data) {
- $str[] = 'User-agent: ' . $ua;
- foreach ($data as $type => $values) {
- foreach ($values as $value) {
- $str[] = $type . ': ' . $value;
- }
- }
- }
-
- $content = A::join($str, PHP_EOL);
- }
-
- echo $content;
-} else {
- // output default
- echo "User-agent: *\n";
-
- $index = option('tobimori.seo.robots.index');
- if (is_callable($index)) {
- $index = $index();
- }
-
- if ($index) {
- echo 'Allow: /';
- echo "\nDisallow: /panel";
- } else {
- echo 'Disallow: /';
- }
-}
-
-if (($sitemap = option('tobimori.seo.robots.sitemap')) || ($sitemapModule = option('tobimori.seo.sitemap.active'))) {
- // Allow closure to be used
- if (is_callable($sitemap)) {
- $sitemap = $sitemap();
- }
-
- // Use default sitemap if none is set
- if (!$sitemap && $sitemapModule) {
- $sitemap = site()->canonicalFor('/sitemap.xml');
- }
-
- // Check again, so falsy values can't be used
- if ($sitemap) {
- echo "\n\nSitemap: {$sitemap}";
- }
-}
diff --git a/site/plugins/kirby-seo/snippets/schemas.php b/site/plugins/kirby-seo/snippets/schemas.php
deleted file mode 100644
index d87c130..0000000
--- a/site/plugins/kirby-seo/snippets/schemas.php
+++ /dev/null
@@ -1,8 +0,0 @@
-schemas() : [], $pageSchema ? $page->schemas() : []) as $schema) {
- echo $schema;
-}
diff --git a/site/plugins/kirby-seo/src/components/Previews/FacebookPreview.vue b/site/plugins/kirby-seo/src/components/Previews/FacebookPreview.vue
deleted file mode 100644
index d467e0f..0000000
--- a/site/plugins/kirby-seo/src/components/Previews/FacebookPreview.vue
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
-
-
diff --git a/site/plugins/kirby-seo/src/components/Previews/GooglePreview.vue b/site/plugins/kirby-seo/src/components/Previews/GooglePreview.vue
deleted file mode 100644
index 4273404..0000000
--- a/site/plugins/kirby-seo/src/components/Previews/GooglePreview.vue
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-
-
-
-
diff --git a/site/plugins/kirby-seo/src/components/Previews/SlackPreview.vue b/site/plugins/kirby-seo/src/components/Previews/SlackPreview.vue
deleted file mode 100644
index b3901c4..0000000
--- a/site/plugins/kirby-seo/src/components/Previews/SlackPreview.vue
+++ /dev/null
@@ -1,93 +0,0 @@
-
-
-
-
{{ ogSiteName || origin }}
-
{{ ogTitle }}
-
{{ ogDescription }}
-
-
-
![]()
-
-
-
-
-
-
-
diff --git a/site/plugins/kirby-seo/src/components/Views/PageView.vue b/site/plugins/kirby-seo/src/components/Views/PageView.vue
deleted file mode 100644
index 871021c..0000000
--- a/site/plugins/kirby-seo/src/components/Views/PageView.vue
+++ /dev/null
@@ -1,150 +0,0 @@
-https://github.com/getkirby/kirby/blob/main/panel/src/components/Views/Pages/PageView.vue
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/site/plugins/kirby-seo/src/index.js b/site/plugins/kirby-seo/src/index.js
deleted file mode 100644
index f664df4..0000000
--- a/site/plugins/kirby-seo/src/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { kirbyup } from 'kirbyup/plugin'
-import PageView from './components/Views/PageView.vue'
-
-panel.plugin('tobimori/seo', {
- icons: {
- robots: ``,
- 'robots-off': ``
- },
- sections: kirbyup.import('./sections/*.vue'),
- components: {
- 'k-page-view': PageView
- }
-})
diff --git a/site/plugins/kirby-seo/src/sections/heading-structure.vue b/site/plugins/kirby-seo/src/sections/heading-structure.vue
deleted file mode 100644
index eb5c2c4..0000000
--- a/site/plugins/kirby-seo/src/sections/heading-structure.vue
+++ /dev/null
@@ -1,191 +0,0 @@
-
-
-
-
-
- -
- H{{ item.level }}
- {{ item.text }}
-
-
-
-
-
- {{ $t('incorrect-heading-order') }}
-
-
-
- {{ $t('multiple-h1-tags') }}
-
-
-
- {{ $t('missing-h1-tag') }}
-
-
-
-
-
-
-
diff --git a/site/plugins/kirby-seo/src/sections/seo-preview.vue b/site/plugins/kirby-seo/src/sections/seo-preview.vue
deleted file mode 100644
index 209ba08..0000000
--- a/site/plugins/kirby-seo/src/sections/seo-preview.vue
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-
-
-
-
-
diff --git a/site/plugins/kirby-seo/templates/sitemap.php b/site/plugins/kirby-seo/templates/sitemap.php
deleted file mode 100644
index 618162b..0000000
--- a/site/plugins/kirby-seo/templates/sitemap.php
+++ /dev/null
@@ -1,3 +0,0 @@
-url() . '.xml');
diff --git a/site/plugins/kirby-seo/templates/sitemap.xml.php b/site/plugins/kirby-seo/templates/sitemap.xml.php
deleted file mode 100644
index f57b648..0000000
--- a/site/plugins/kirby-seo/templates/sitemap.xml.php
+++ /dev/null
@@ -1,5 +0,0 @@
-render($page);
diff --git a/site/plugins/kirby-seo/templates/sitemap.xsl.php b/site/plugins/kirby-seo/templates/sitemap.xsl.php
deleted file mode 100644
index 5cf4eb4..0000000
--- a/site/plugins/kirby-seo/templates/sitemap.xsl.php
+++ /dev/null
@@ -1,196 +0,0 @@
-= '' ?>
-
-
-
-
-
-
- = $page->metadata()->title()->escape() ?>
-
-
-
-
-
-
= $page->title() ?>
-
= t('sitemap-description') ?>
-
-
-
-
-
- | = t('sitemap') ?> |
- = t('sitemap-last-updated') ?> |
-
-
-
-
-
- |
-
-
-
-
- |
-
-
- |
-
-
-
-
-
-
-
-
-
-
- | = t('sitemap-url') ?> |
- = t('sitemap-priority') ?> |
- = t('sitemap-changefreq') ?> |
- = t('sitemap-last-updated') ?> |
-
-
-
-
-
- |
-
-
-
-
- |
-
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
-
- = t('sitemap-no-entries') ?>
-
-
-
-
- Kirby SEO
-
- v
-
-
- v
-
- = t('sitemap-by') ?> Tobias Möritz
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/site/plugins/kirby-seo/translations/de.yml b/site/plugins/kirby-seo/translations/de.yml
deleted file mode 100644
index 9472d59..0000000
--- a/site/plugins/kirby-seo/translations/de.yml
+++ /dev/null
@@ -1,87 +0,0 @@
-# Heading Structure
-heading-structure: Überschriftenstruktur
-incorrect-heading-order: Deine Überschriftenstruktur hat eine falsche Abfolge und ist ungültig.
-missing-h1-tag: Deine Überschriftenstruktur enthält keine H1 und ist ungültig.
-multiple-h1-tags: Deine Überschriftenstruktur enthält mehr als eine H1 und ist ungültig.
-
-# SEO Preview
-seo-preview: Vorschau
-seo-preview-for: Zeige mir
-open-debugger: Sharing-Debugger öffnen
-open-search-console: Search Console öffnen
-
-# Robots
-indicator-index: Indexierung erlaubt
-indicator-any: Indexierung teilw. verboten
-indicator-noindex: Indexierung verboten
-robots: Richtlinien für Suchmaschinen & Crawler
-robots-index: Indexierung
-robots-index-help: Ob Suchmaschinen die Seite indexieren dürfen.
-robots-follow: Links folgen
-robots-follow-help: Ob Suchmaschinen Links auf dieser Seite folgen dürfen.
-robots-archive: Archivierung
-robots-archive-help: Ob Suchmaschinen zwischengespeicherte Versionen der Seite ausliefern dürfen.
-robots-imageindex: Bilder-Indexierung
-robots-imageindex-help: Ob Bilder dieser Seite in der Bildersuche angezeigt werden dürfen.
-robots-snippet: Snippets
-robots-snippet-help: Ob Suchmaschinen Textausschnitte aus der Seite anzeigen dürfen.
-
-# Blueprint - Site/Common
-metadata-site: Metadaten & SEO
-global-meta-headline: Globale SEO-Einstellungen
-global-meta-headline-help: |
- Diese Einstellungen werden für alle Seiten verwendet, die keine eigenen Metadaten haben.
- Du kannst sie für jede Seite überschreiben.
-meta-title-template: Titel-Template
-meta-title-template-help: |
- Ein Template, das für alle Seitentitel verwendet werden soll.
- "\{\{ title }}" wird durch den Titel der Seite ersetzt.
-meta-description: Seitenbeschreibung
-meta-description-help: Empfohlene Länge von max. 150 Zeichen. Wird verwendet, falls keine Seitenbeschreibung angegeben ist.
-
-global-og-headline: Globale Open Graph-Einstellungen
-global-og-headline-help: Stelle ein, wie deine Website erscheint, wenn sie auf sozialen Netzwerken wie Facebook oder Twitter geteilt wird.
-og-title-template: Open Graph-Titel-Template
-og-description: Open Graph-Beschreibung
-og-site-name: Open Graph-Seitenname
-og-image: Open Graph-Bild
-og-image-help: Empfohlene Größe von 1200x630 Pixeln.
-og-image-empty: Kein Open Graph-Bild ausgewählt
-twitter-card-type: Twitter-Kartentyp
-twitter-card-type-help: Der Twitter-Kartentyp bestimmt, wie deine Website aussieht, wenn sie auf Twitter geteilt wird. [Mehr Informationen](https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards).
-
-social-media-accounts: Social Media-Accounts
-social-media-accounts-help: URLs bzw. @-Handles zu deinen Social Media-Accounts. Werden verwendet, um Links zu deinen Accounts in den Metadaten zu setzen.
-
-# Blueprint - Page
-meta-headline: SEO-Einstellungen
-og-headline: Open Graph-Einstellungen
-title-overwrite: Titel (Überschreiben)
-summary: Karte mit quadratischem Bild
-summary_large_image: Karte mit großem Bild
-default-select: 'Standard:'
-twitter-author: '@username des Autors auf Twitter'
-inherit-settings: Einstellungen vererben
-inherit-settings-help: |
- Wähle aus, welche Einstellungen an Unterseiten vererbt werden sollen.
- Dies kann z.B. hilfreich sein, wenn alle Beiträge eines Blogs ein eigenes Titel-Template haben sollen, welches vom Seiten-Standard abweicht. Alle Einstellungen lassen sich weiterhin in der Hauptseite überschreiben.
-use-title-template: Titel-Template verwenden?
-use-title-template-no: 'Nein - reiner Titel'
-use-title-template-yes: 'Ja - mit Template'
-use-title-template-help: Gibt an, ob das Titel-Template verwendet werden soll. Wird nicht vererbt.
-
-# Labels
-google: Google
-facebook: Facebook
-slack: Slack
-
-# Sitemap
-sitemap: Sitemap
-sitemap-index: Sitemap-Index
-sitemap-description: Dies ist die Sitemap für deine Website, die Suchmaschinen über die Seiten auf deiner Website informiert, die indexiert werden können.
-sitemap-by: von
-sitemap-changefreq: Änderungsfrequenz
-sitemap-last-updated: Letzte Änderung
-sitemap-priority: Priorität
-sitemap-url: URL
-sitemap-no-entries: Keine Einträge
diff --git a/site/plugins/kirby-seo/translations/en.yml b/site/plugins/kirby-seo/translations/en.yml
deleted file mode 100644
index f41a224..0000000
--- a/site/plugins/kirby-seo/translations/en.yml
+++ /dev/null
@@ -1,87 +0,0 @@
-# Heading Structure
-heading-structure: Heading Structure
-incorrect-heading-order: Your heading structure has an incorrect order and is invalid.
-missing-h1-tag: Your heading structure does not contain an H1 and is invalid.
-multiple-h1-tags: Your heading structure contains more than one H1 and is invalid.
-
-# SEO Preview
-seo-preview: Preview
-seo-preview-for: Show me
-open-debugger: Open Sharing Debugger
-open-search-console: Open Search Console
-
-# Robots
-indicator-index: Indexing allowed
-indicator-any: Indexing partly forbidden
-indicator-noindex: Indexing forbidden
-robots: Robots Directives
-robots-index: Indexing
-robots-index-help: Whether search engines may index this page.
-robots-follow: Follow Links
-robots-follow-help: Whether search engines may follow links on this page.
-robots-archive: Archive
-robots-archive-help: Whether search engines may archive this page.
-robots-imageindex: Image Indexing
-robots-imageindex-help: Whether search engines may index images on this page.
-robots-snippet: Snippets
-robots-snippet-help: Whether search engines may show text snippets of this page.
-
-# Blueprint - Site/Common
-metadata-site: Metadata & SEO
-global-meta-headline: Global SEO Settings
-global-meta-headline-help: |
- These settings are used for all pages that do not have their own metadata.
- You can override them for each page.
-meta-title-template: Title Template
-meta-title-template-help: |
- A template to use for all page titles.
- "\{\{ title }}" will be replaced with the page title.
-meta-description: Page Description
-meta-description-help: Recommended length of 150 characters max. Used if no page description is specified.
-
-global-og-headline: Global Open Graph Settings
-global-og-headline-help: Set how your website appears when shared on social networks like Facebook or Twitter.
-og-title-template: Open Graph Title Template
-og-description: Open Graph Description
-og-site-name: Open Graph Site Name
-og-image: Open Graph Image
-og-image-help: Recommended size of 1200x630 pixels.
-og-image-empty: No Open Graph Image selected
-twitter-card-type: Twitter Card Type
-twitter-card-type-help: Twitter Card Type determines how your website looks when shared on Twitter. [Read more](https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards).
-
-social-media-accounts: Social Media Accounts
-social-media-accounts-help: URLs or @handles to your social media accounts. Used to put links to your accounts in the metadata.
-
-# Blueprint - Page
-meta-headline: SEO Settings
-og-headline: Open Graph Settings
-title-overwrite: Title (overwrite)
-summary: Summary with Square Image
-summary_large_image: Summary with Large Image
-default-select: 'Default:'
-twitter-author: 'Author @username on Twitter'
-inherit-settings: Inherit Settings
-inherit-settings-help: |
- Select which settings should be inherited by subpages.
- This can be helpful, for example, if all posts of a blog should have their own title template, which differs from the page default. All settings can still be overridden in the main page.
-use-title-template: Use title template?
-use-title-template-no: 'No - only title'
-use-title-template-yes: 'Yes - with template'
-use-title-template-help: Specifies whether the title template should be used. Will not be inherited.
-
-# Labels
-google: Google
-facebook: Facebook
-slack: Slack
-
-# Sitemap
-sitemap: Sitemap
-sitemap-index: Sitemap Index
-sitemap-description: This is the sitemap for your website that informs search engines about the pages on your website that can be indexed.
-sitemap-by: by
-sitemap-changefreq: Change Frequency
-sitemap-last-updated: Last Updated
-sitemap-priority: Priority
-sitemap-url: URL
-sitemap-no-entries: No entries
diff --git a/site/plugins/kirby-seo/translations/fr.yml b/site/plugins/kirby-seo/translations/fr.yml
deleted file mode 100644
index 932a93b..0000000
--- a/site/plugins/kirby-seo/translations/fr.yml
+++ /dev/null
@@ -1,75 +0,0 @@
-# Heading Structure
-heading-structure: Structure des titres
-incorrect-heading-order: Votre structure de titres a un ordre incorrect et est invalide.
-missing-h1-tag: Votre structure de titres ne contient pas de balise H1 et est invalide.
-multiple-h1-tags: Votre structure de titres contient plus d’une balise H1 et est invalide.
-
-# SEO Preview
-seo-preview: Aperçu
-seo-preview-for: Montre-moi
-open-debugger: Ouvrir le débogueur de partage
-open-search-console: Ouvrir la Console de recherche
-
-# Robots
-indicator-index: Indexation autorisée
-indicator-any: Indexation partiellement interdite
-indicator-noindex: Indexation interdite
-robots: Directives des robots
-robots-index: Indexation
-robots-index-help: Indique si les moteurs de recherche peuvent indexer cette page.
-robots-follow: Suivre les liens
-robots-follow-help: Indique si les moteurs de recherche peuvent suivre les liens de cette page.
-robots-archive: Archiver
-robots-archive-help: Indique si les moteurs de recherche peuvent archiver cette page.
-robots-imageindex: Indexation des images
-robots-imageindex-help: Indique si les moteurs de recherche peuvent indexer les images de cette page.
-robots-snippet: Extraits
-robots-snippet-help: Indique si les moteurs de recherche peuvent afficher des extraits de texte de cette page.
-
-# Blueprint - Site/Common
-metadata-site: Métadonnées et SEO
-global-meta-headline: Paramètres SEO globaux
-global-meta-headline-help: |
- Ces paramètres sont utilisés pour toutes les pages qui n’ont pas leurs propres métadonnées.
- Vous pouvez les remplacer pour chaque page.
-meta-title-template: Modèle de titre
-meta-title-template-help: |
- Un modèle à utiliser pour tous les titres de page.
- "\{\{ title }}" sera remplacé par le titre de la page.
-meta-description: Description de la page
-meta-description-help: Longueur recommandée de 150 caractères maximum. Utilisée si aucune description de page n’est spécifiée.
-
-global-og-headline: Paramètres globaux Open Graph
-global-og-headline-help: Définissez l’apparence de votre site web lorsqu’il est partagé sur les réseaux sociaux tels que Facebook ou Twitter.
-og-title-template: Modèle de titre Open Graph
-og-description: Description Open Graph
-og-site-name: Nom du site Open Graph
-og-image: Image Open Graph
-og-image-help: Taille recommandée de 1200x630 pixels.
-og-image-empty: Aucune image Open Graph sélectionnée
-twitter-card-type: Type de carte Twitter
-twitter-card-type-help: Le type de carte Twitter détermine l’apparence de votre site web lorsqu’il est partagé sur Twitter. [En savoir plus](https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards).
-
-social-media-accounts: Comptes de réseaux sociaux
-social-media-accounts-help: URLs ou @nom_utilisateur de vos comptes de réseaux sociaux. Utilisés pour mettre des liens vers vos comptes dans les métadonnées.
-# Blueprint - Page
-meta-headline: Paramètres SEO
-og-headline: Paramètres Open Graph
-title-overwrite: Titre (remplacement)
-summary: Résumé avec une image carrée
-summary_large_image: Résumé avec une grande image
-default-select: 'Par défaut :'
-twitter-author: 'Auteur @nom_utilisateur sur Twitter'
-inherit-settings: Hériter des paramètres
-inherit-settings-help: |
- Sélectionnez les paramètres à hériter par les sous-pages.
- Cela peut être utile, par exemple, si tous les articles d’un blog doivent avoir leur propre modèle de titre, différent de celui de la page par défaut. Tous les paramètres peuvent toujours être remplacés dans la page principale.
-use-title-template: Utiliser le modèle de titre ?
-use-title-template-no: 'Non - seulement le titre'
-use-title-template-yes: 'Oui - avec le modèle'
-use-title-template-help: Indique si le modèle de titre doit être utilisé. Ne sera pas hérité.
-
-# Labels
-google: Google
-facebook: Facebook
-slack: Slack
diff --git a/site/plugins/kirby-seo/translations/pt_PT.yml b/site/plugins/kirby-seo/translations/pt_PT.yml
deleted file mode 100644
index 06caab9..0000000
--- a/site/plugins/kirby-seo/translations/pt_PT.yml
+++ /dev/null
@@ -1,87 +0,0 @@
-# Heading Structure
-heading-structure: Estrutura de Títulos
-incorrect-heading-order: A estrutura de títulos tem uma ordem incorrecta e é inválida.
-missing-h1-tag: A estrutura de títulos não contém uma tag H1 e é inválida.
-multiple-h1-tags: A estrutura de títulos contém mais do que uma tag H1 e é inválida.
-
-# SEO Preview
-seo-preview: Pré-visualização
-seo-preview-for: Mostrar
-open-debugger: Abrir Sharing Debugger
-open-search-console: Abrir Search Console
-
-# Robots
-indicator-index: Indexação permitida
-indicator-any: Indexação parcialmente proibida
-indicator-noindex: Indexação proibida
-robots: Diretivas Robots
-robots-index: Indexação
-robots-index-help: Se os motores de pesquisa podem indexar esta página.
-robots-follow: Seguir Links
-robots-follow-help: Se os motores de pesquisa podem seguir links nesta página.
-robots-archive: Arquivo
-robots-archive-help: Se os motores de pesquisa podem arquivar esta página.
-robots-imageindex: Indexação de Imagens
-robots-imageindex-help: Se os motores de pesquisa podem indexar imagens desta página.
-robots-snippet: Snippets
-robots-snippet-help: Se os motores de pesquisa podem mostrar snippets de texto desta página.
-
-# Blueprint - Site/Common
-metadata-site: Metadados & SEO
-global-meta-headline: Configurações Globais SEO
-global-meta-headline-help: |
- Estas configurações são usadas para todas as páginas que não têm os seus próprios metadados.
- Pode substituí-las em cada página.
-meta-title-template: Template de Título
-meta-title-template-help: |
- Template para usar em todos os títulos de páginas.
- "\{\{ title }}" será substituído pelo título da página.
-meta-description: Descrição de Página
-meta-description-help: Recomendado um tamanho de 150 caracteres no máximo. Usada se nenhuma descrição de página for especificada.
-
-global-og-headline: Configurações Globais Open Graph
-global-og-headline-help: Defina como o seu site aparece quando é partilhado em redes sociais como o Facebook ou o Twitter.
-og-title-template: Template de Título Open Graph
-og-description: Descrição Open Graph
-og-site-name: Nome do Site Open Graph
-og-image: Imagem Open Graph
-og-image-help: Tamanho recomendado de 1200x630 pixels.
-og-image-empty: Nenhuma Imagem Open Graph selecionada
-twitter-card-type: Tipo de Twitter Card
-twitter-card-type-help: O tipo de Twitter Card determina a aparência do seu site quando é partilhado no Twitter. [Leia mais](https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards).
-
-social-media-accounts: Contas de Redes Sociais
-social-media-accounts-help: URLs ou @handles para as suas contas de redes sociais. Usado para colocar links para as suas contas nos metadados.
-
-# Blueprint - Page
-meta-headline: Configurações SEO
-og-headline: Configurações Open Graph
-title-overwrite: Título (substituir)
-summary: Resumo com Imagem Quadrada
-summary_large_image: Resumo com Imagem Grande
-default-select: 'Por defeito:'
-twitter-author: 'Autor @username no Twitter'
-inherit-settings: Herdar Configurações
-inherit-settings-help: |
- Selecione quais as configurações que devem ser herdadas pelas subpáginas.
- Isto pode ser útil, por exemplo, se todos os posts de um blog tiverem o seu próprio template de título, que pode ser diferente do pré-configurado na página. Todas as configurações continuam a poder ser substituídas na página principal.
-use-title-template: Usar o template de título?
-use-title-template-no: 'Não - apenas o título'
-use-title-template-yes: 'Sim - com template'
-use-title-template-help: Especifica se o template de título deve ser usado. Não será herdado.
-
-# Labels
-google: Google
-facebook: Facebook
-slack: Slack
-
-# Sitemap
-sitemap: Sitemap
-sitemap-index: Índice Sitemap
-sitemap-description: Este é o sitemap do site que informa os motores de pesquisa sobre as páginas que podem ser indexadas.
-sitemap-by: por
-sitemap-changefreq: Frequência de Mudança
-sitemap-last-updated: Última Atualização
-sitemap-priority: Prioridade
-sitemap-url: URL
-sitemap-no-entries: Sem registos
diff --git a/site/plugins/kirby-seo/translations/tr.yml b/site/plugins/kirby-seo/translations/tr.yml
deleted file mode 100644
index 531694f..0000000
--- a/site/plugins/kirby-seo/translations/tr.yml
+++ /dev/null
@@ -1,87 +0,0 @@
-# Başlık Yapısı
-heading-structure: Başlık Yapısı
-incorrect-heading-order: Başlık yapınız yanlış bir sıraya sahip ve geçersizdir.
-missing-h1-tag: Başlık yapınızda bir H1 etiketi bulunmuyor ve geçersizdir.
-multiple-h1-tags: Başlık yapınızda birden fazla H1 etiketi bulunuyor ve geçersizdir.
-
-# SEO Önizleme
-seo-preview: Önizleme
-seo-preview-for: Bana göster
-open-debugger: Paylaşım Hata Ayıklayıcıyı Aç
-open-search-console: Arama Konsolunu Aç
-
-# Robotlar
-indicator-index: İndekslemeye izin verildi
-indicator-any: İndeksleme kısmen yasaklandı
-indicator-noindex: İndeksleme yasaklandı
-robots: Robot Talimatları
-robots-index: İndeksleme
-robots-index-help: Arama motorlarının bu sayfayı indeksleyip indekslemeyeceğini belirtir.
-robots-follow: Bağlantıları Takip Et
-robots-follow-help: Arama motorlarının bu sayfadaki bağlantıları takip edip etmeyeceğini belirtir.
-robots-archive: Arşivle
-robots-archive-help: Arama motorlarının bu sayfayı arşivleyip arşivlemeyeceğini belirtir.
-robots-imageindex: İmaj İndeksleme
-robots-imageindex-help: Arama motorlarının bu sayfadaki görüntüleri indeksleyip indekslemeyeceğini belirtir.
-robots-snippet: Özetler
-robots-snippet-help: Arama motorlarının bu sayfanın metin özetlerini gösterip göstermeyeceğini belirtir.
-
-# Şablona Dayalı - Site/Ortak
-metadata-site: Meta Verileri ve SEO
-global-meta-headline: Genel SEO Ayarları
-global-meta-headline-help: |
- Bu ayarlar, kendi meta verileri olmayan tüm sayfalar için kullanılır.
- Her sayfa için bunları geçersiz kılabilirsiniz.
-meta-title-template: Başlık Şablonu
-meta-title-template-help: |
- Tüm sayfa başlıkları için kullanılacak bir şablon.
- "\{\{ başlık }}" sayfa başlığı ile değiştirilecektir.
-meta-description: Sayfa Açıklaması
-meta-description-help: Maksimum 150 karakter uzunluğunda önerilen bir sayfa açıklamasıdır. Sayfa açıklaması belirtilmemişse kullanılır.
-
-global-og-headline: Genel Açık Grafik (OG) Ayarları
-global-og-headline-help: Web sitenizin Facebook veya Twitter gibi sosyal ağlarda paylaşıldığında nasıl göründüğünü ayarlar.
-og-title-template: Açık Grafik (OG) Başlık Şablonu
-og-description: Açık Grafik (OG) Açıklaması
-og-site-name: Açık Grafik (OG) Site Adı
-og-image: Açık Grafik (OG) Görseli
-og-image-help: Önerilen boyut 1200x630 pikseldir.
-og-image-empty: Boş Açık Grafik (OG) Görseli
-twitter-card-type: Twitter Kart Türü
-twitter-card-type-help: Twitter Kart Türü, Twitter'da paylaşıldığında web sitenizin nasıl göründüğünü belirler. [Daha fazla bilgi](https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards).
-
-social-media-accounts: Sosyal Medya Hesapları
-social-media-accounts-help: Sosyal medya hesaplarınızın URL'leri veya @kullanici_adi. Meta verilerinde hesaplarınıza bağlantı eklemek için kullanılır.
-
-# Şablona Dayalı - Sayfa
-meta-headline: SEO Ayarları
-og-headline: Açık Grafik (OG) Ayarları
-title-overwrite: Başlık (üzerine yaz)
-summary: Kare İmaj ile Özet
-summary_large_image: Büyük İmaj ile Özet
-default-select: 'Varsayılan:'
-twitter-author: Twitter'da @kullanici_adi
-inherit-settings: Ayarları Miras Al
-inherit-settings-help: |
- Alt sayfalar tarafından miras alınacak ayarları seçin.
- Örneğin, bir blogun tüm yazılarının sayfa varsayılanından farklı bir başlık şablonuna sahip olması gerekiyorsa bu yardımcı olabilir. Tüm ayarlar ana sayfada hala geçersiz kılınabilir.
-use-title-template: Başlık şablonunu kullan?
-use-title-template-no: 'Hayır - sadece başlık'
-use-title-template-yes: 'Evet - şablonla'
-use-title-template-help: Başlık şablonunun kullanılıp kullanılmayacağını belirtir. Miras alınmayacaktır.
-
-# Etiketler
-google: Google
-facebook: Facebook
-slack: Slack
-
-# Sitemap
-sitemap: Site Haritası
-sitemap-index: Site Haritası Dizini
-sitemap-description: Web sitenizdeki dizine eklenebilecek sayfalar hakkında arama motorlarını bilgilendiren web sitenizin site haritasıdır.
-sitemap-by: tarafından
-sitemap-changefreq: Değişim Sıklığı
-sitemap-last-updated: Son Güncelleme
-sitemap-priority: Öncelik
-sitemap-url: URL
-sitemap-no-entries: Giriş yok
diff --git a/site/plugins/loop/.editorconfig b/site/plugins/loop/.editorconfig
deleted file mode 100644
index 3b762c9..0000000
--- a/site/plugins/loop/.editorconfig
+++ /dev/null
@@ -1,20 +0,0 @@
-# This file is for unifying the coding style for different editors and IDEs
-# editorconfig.org
-
-[*]
-charset = utf-8
-indent_style = space
-indent_size = 2
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
-
-[*.php]
-indent_size = 4
-
-[*.md,*.txt]
-trim_trailing_whitespace = false
-insert_final_newline = false
-
-[composer.json]
-indent_size = 4
diff --git a/site/plugins/loop/.paradocs.json b/site/plugins/loop/.paradocs.json
deleted file mode 100644
index 237dddb..0000000
--- a/site/plugins/loop/.paradocs.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "title": "Kirby Loop"
-}
\ No newline at end of file
diff --git a/site/plugins/loop/.release-it.json b/site/plugins/loop/.release-it.json
deleted file mode 100644
index cee4d59..0000000
--- a/site/plugins/loop/.release-it.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "git": {
- "requireUpstream": true,
- "push": true,
- "tagName": "v${version}",
- "commitMessage": "chore: release v${version}"
- },
- "npm": {
- "publish": false
- },
- "github": {
- "release": true,
- "web": true
- },
- "plugins": {
- "@release-it/conventional-changelog": {
- "preset": "conventionalcommits",
- "infile": "CHANGELOG.md"
- },
- "@release-it/bumper": {
- "in": "composer.json",
- "out": "composer.json"
- }
- }
-}
diff --git a/site/plugins/loop/CHANGELOG.md b/site/plugins/loop/CHANGELOG.md
deleted file mode 100644
index 5edbef9..0000000
--- a/site/plugins/loop/CHANGELOG.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# Changelog
-
-## [1.0.1](///compare/v1.0.0...v1.0.1) (2025-07-08)
-
-### Features
-
-* add allow draft pages option 351471e
-* add major version check 02d3690
-
-### Bug Fixes
-
-* add token to fetch universally (if set) 9c33fdd
-* check for valid draft token 091a135
-* check for verified token with kirby 4 too dec2d34
-* remove draft page access docs 7e71ee9
-* remove draftaccess option 98148a0
-* show ui only if comments fetch goes through a5188f0
-* use default minification b9ce0f6
-
-## [1.0.0](///compare/v1.0.0-beta.7...v1.0.0) (2025-07-05)
-
-## [1.0.0-beta.7](///compare/v1.0.0-beta.6...v1.0.0-beta.7) (2025-07-05)
-
-### Bug Fixes
-
-* use autoloader for classes 7464553
-
-## [1.0.0-beta.6](///compare/v1.0.0-beta.5...v1.0.0-beta.6) (2025-07-02)
-
-### Features
-
-* add screenshot c3ef6f3
-
-## [1.0.0-beta.5](///compare/v1.0.0-beta.4...v1.0.0-beta.5) (2025-06-30)
-
-### Bug Fixes
-
-* decode html entities cf00460
-* make sure to add a guest name before replying or commenting 763c904
-
-## [1.0.0-beta.4](///compare/v1.0.0-beta.3...v1.0.0-beta.4) (2025-06-30)
-
-### Features
-
-* add aria labels to improve voice over 63fc81d
-* improve reply voiceover 93e294e
-
-### Bug Fixes
-
-* cleanup icons f956a34
-* date display 9a4ad00
-* header button focus styles 26ebb19
-* hide reply button on solved 545a094
-* improve context menu voiceover a6c514a
-* outline on comment header a9c2d37
-* pulse marker 8ec0bd5
-* skip focus if panel closed ee23bde
-* switch icons, little style fixes 958f0d1
-* use dialog element for panel f3dff13
-
-## [1.0.0-beta.3](///compare/v1.0.0-beta.2...v1.0.0-beta.3) (2025-06-22)
-
-### Features
-
-* dark theme e34269f
-* add dark theme 5255d64
-* add theme option 51e3b67
-* add theming docs 2d74d63
-* better shadows, rename theme light to default 90e6e6c
-* refactor css custom properties 6daf0de
-* refactor css custom properties f6ffb0b
-
-### Bug Fixes
-
-* frosted glass style 8807e3d
-* Plugin name in paradocs 62ac94b
-* remove cursor property, don’t set to auto 7cf72c2
-
-## [1.0.0-beta.2](///compare/v1.0.0-beta.1...v1.0.0-beta.2) (2025-06-21)
-
-### Bug Fixes
-
-* handle $page variable in hook 113f77d
-* installer name 77c9e18
-* update types b79d7b6
-* use unified api base with language included 109d850
-
-## [1.0.0-beta.1](///compare/v1.0.0-beta.0...v1.0.0-beta.1) (2025-06-21)
-
-## 1.0.0-beta.0 (2025-06-21)
diff --git a/site/plugins/loop/CLAUDE.md b/site/plugins/loop/CLAUDE.md
deleted file mode 100644
index c82e30b..0000000
--- a/site/plugins/loop/CLAUDE.md
+++ /dev/null
@@ -1,97 +0,0 @@
-# CLAUDE.md
-
-This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
-
-## Project Structure
-
-This is a Kirby CMS plugin that provides a feedback tool for web pages. The architecture consists of:
-
-**Backend (PHP):**
-- Kirby plugin structure with main entry point at `index.php`
-- Core logic in `src/App.php` with database abstraction
-- API routes in `src/Routes.php` for comment management
-- Models in `src/Models/` for Comment and Reply entities
-- Database layer in `src/Database.php`
-
-**Frontend (Svelte):**
-- Svelte 5 component library in `frontend/src/`
-- Builds to ES modules in `assets/` directory
-- Uses Vite for build process with custom element compilation
-- State management via Svelte stores in `frontend/src/store/`
-
-## Development Commands
-
-**Frontend Development:**
-```bash
-# Start development server
-pnpm dev
-
-# Build assets
-pnpm build
-
-# Type checking
-pnpm --filter=frontend run check
-```
-
-**PHP Development:**
-```bash
-# Static analysis
-vendor/bin/phpstan analyse
-
-# PHP analysis level 8 with strict rules
-# Configuration in phpstan.neon
-```
-
-**Documentation:**
-Use context7 to find out about Kirby CMS, Documentation for this plugin is placed in the `docs/` folder.
-
-## Key Architecture Details
-
-**Plugin Integration:**
-- Auto-injects feedback component into all HTML pages via `page.render:after` hook
-- Component snippet located at `snippets/loop/app.php`
-- Requires authenticated users (see `Middleware::auth()`)
-
-**API Endpoints:**
-- `GET /loop/comments/{pageId}` - Get comments for a specific page
-- `POST /loop/comment/new` - Create new comment
-- `POST /loop/comment/reply` - Reply to existing comment
-- `POST /loop/comment/resolve` - Mark comment as resolved
-- `POST /loop/comment/unresolve` - Mark comment as unresolved
-- `POST /loop/guest/name` - Set guest name for non-authenticated users
-
-**Data Flow:**
-- Comments are tied to Kirby page ids
-- Position tracking via CSS selectors and page coordinates
-- Validation happens at model level before database operations
-
-## Translations
-
-**IMPORTANT: When adding new translatable text to the frontend:**
-
-1. **Add translation key to PHP backend** (`index.php`):
- ```php
- 'moinframe.loop.ui.component.key' => 'Default English text',
- ```
-
-2. **Add translation key to snippet** (`snippets/loop/app.php`):
- ```php
- 'ui.component.key' => t('moinframe.loop.ui.component.key'),
- ```
-
-3. **Use translation in Svelte components**:
- ```svelte
- {t("ui.component.key", "Default fallback text")}
- ```
-
-**Translation Architecture:**
-- PHP translations defined in `index.php` under `'translations'` key
-- Frontend translations passed via `snippets/loop/app.php`
-- Svelte components use `t()` function from `store/translations.svelte.ts`
-- Always provide fallback text in components for development
-
-## Linting and Code Quality
-
-- Biome for frontend linting (config in `biome.json`)
-- PHPStan level 8 analysis with strict rules
-- TypeScript checking via `svelte-check`
diff --git a/site/plugins/loop/LICENSE.md b/site/plugins/loop/LICENSE.md
deleted file mode 100755
index 716097a..0000000
--- a/site/plugins/loop/LICENSE.md
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2025 Justus Kraft
-
-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/site/plugins/loop/README.md b/site/plugins/loop/README.md
deleted file mode 100755
index d3f6b82..0000000
--- a/site/plugins/loop/README.md
+++ /dev/null
@@ -1,96 +0,0 @@
-
-# Kirby Loop
-
-Stay in the loop. A powerful visual feedback plugin for Kirby CMS that allows users to add comments directly on web pages by clicking on elements. Perfect for client reviews, content collaboration, and team feedback workflows.
-
-## Features
-
-- 🎯 **Click-to-comment**: Toggle between navigate mode for normal browsing and comment mode to click anywhere and add feedback
-- 🌍 **Multi-language support**: Full support for Kirby's multi-language sites with automatic language detection
-- 💬 **Threaded comments**: Reply to comments for contextual discussions
-- 🔒 **Authentication**: Choose whether to restrict access to authenticated users only or allow guest commenting
-- 🎨 **Theming**: Built-in light/dark themes with full customization support
-- ⚙️ **Auto-injection**: Automatically inject into all pages or manually control placement
-- 🗄️ **Local storage**: All data stored locally in SQLite - no external dependencies
-
-## How It Works
-
-Kirby Loop transforms your website into a collaborative workspace where teams can provide feedback directly on web pages.
-
-**Visual Context**: Users can click on any element to leave specific comments, creating a direct connection between feedback and content.
-
-**Streamlined Communication**: Team members, clients, and stakeholders can point out issues and suggest improvements right where they see them.
-
-**Organized Discussions**: Comments support threaded replies and can be marked as resolved to maintain a clean feedback pipeline.
-
-**Privacy & Data Control**: All feedback data is stored locally in a SQLite database on your server - no external services or cloud dependencies.
-
-## Quick Start
-
-1. **Install**: `composer require moinframe/kirby-loop`
-2. **Use**: Kirby Loop is automatically active on all pages for authenticated users
-3. **Configure**: Customize settings in `site/config/config.php` (optional)
-
-## Documentation
-
-Detailed documentation is available in the `docs/` folder:
-
-- **[Installation Guide](https://moinfra.me/docs/moinframe-loop/01-installation)** - Complete installation instructions
-- **[Configuration Guide](https://moinfra.me/docs/moinframe-loop/02-configuration)** - All configuration options and advanced settings
-- **[Multi-Language Support](https://moinfra.me/docs/moinframe-loop/03-multi-language)** - Setup and customization for multi-language sites
-- **[API Reference](https://moinfra.me/docs/moinframe-loop/05-api)** - API documentation
-- **[Theming Guide](https://moinfra.me/docs/moinframe-loop/04-theming)** - Theme customization and creating custom themes
-
-## Basic Configuration
-
-Add these options to your `site/config/config.php`:
-
-```php
-return [
- // Enable/disable loop (default: true)
- 'moinframe.loop.enabled' => true,
-
- // Or use a callback for conditional enabling
- 'moinframe.loop.enabled' => function($page) {
- return in_array($page->template()->name(), ['article', 'blog']);
- },
-
- // Disable auto-injection (default: true)
- 'moinframe.loop.auto-inject' => false,
-
- // Set header position: 'top' or 'bottom' (default: 'top')
- 'moinframe.loop.position' => 'bottom',
-
- // Make feedback public (default: false - requires auth)
- 'moinframe.loop.public' => true,
-
- // Force UI language (default: null - auto-detect)
- 'moinframe.loop.language' => 'de',
-
- // Set theme: 'default', 'dark', or custom theme name
- 'moinframe.loop.theme' => 'dark',
-];
-```
-
-See the [Configuration Guide](https://moinfra.me/docs/moinframe-loop/02-configuration) for all available options.
-
-## Requirements
-
-- Kirby CMS 4.0+
-- PHP 8.3+
-- SQLite support
-
-## Important Notes
-
-> [!WARNING]
-> Pages with the snippet automatically have Kirby's page **cache** **disabled**. This is necessary for CSRF token validation and User authentication checks.
-
-
-## Support
-
-- **Documentation**: See the [Documentation](https://moinfra.me/docs/moinframe-loop) for installation and usage instructions
-- **Issues**: Report bugs on [GitHub Issues](https://github.com/moinframe/kirby-loop/issues)
-
-## License
-
-MIT License - see [LICENSE.md](LICENSE.md)
diff --git a/site/plugins/loop/assets/loop.js b/site/plugins/loop/assets/loop.js
deleted file mode 100644
index e679326..0000000
--- a/site/plugins/loop/assets/loop.js
+++ /dev/null
@@ -1,3941 +0,0 @@
-(function(){"use strict";try{if(typeof document<"u"){var r=document.createElement("style");r.appendChild(document.createTextNode('kirby-loop{--color-neutral-h:900;--color-neutral-c:0;--color-accent-h:900;--color-accent-c:.18;--color-accent-l:.75;--color-accent-dark-factor:.4;--color-accent-light-factor:1.2;--color-neutral-l-0:1;--color-neutral-l-100:.95;--color-neutral-l-200:.9;--color-neutral-l-300:.7;--color-neutral-l-400:.6;--color-neutral-l-600:.4;--color-neutral-l-500:.5;--color-neutral-l-700:.3;--color-neutral-l-800:.2;--color-neutral-l-900:.1;--color-neutral-l-1000:0}kirby-loop[theme=dark]{--color-accent-l:.85;--color-neutral-l-0:0;--color-neutral-l-100:.1;--color-neutral-l-200:.2;--color-neutral-l-300:.45;--color-neutral-l-400:.5;--color-neutral-l-600:.55;--color-neutral-l-500:.6;--color-neutral-l-700:.7;--color-neutral-l-800:.8;--color-neutral-l-900:.95;--color-neutral-l-1000:1;--shadow-s:0 .1em .25em oklch(var(--color-neutral-l-100)var(--color-neutral-c)var(--color-neutral-h)/.1);--shadow-m:0 2px 8px oklch(var(--color-neutral-l-100)var(--color-neutral-c)var(--color-neutral-h)/.08),0 8px 16px oklch(var(--color-neutral-l-100)var(--color-neutral-c)var(--color-neutral-h)/.12),0 16px 24px oklch(var(--color-neutral-l-100)var(--color-neutral-c)var(--color-neutral-h)/.08);--shadow-l:0 4px 16px oklch(var(--color-neutral-l-100)var(--color-neutral-c)var(--color-neutral-h)/.08),0 12px 32px oklch(var(--color-neutral-l-100)var(--color-neutral-c)var(--color-neutral-h)/.12),0 24px 48px oklch(var(--color-neutral-l-100)var(--color-neutral-c)var(--color-neutral-h)/.16),0 48px 80px oklch(var(--color-neutral-l-100)var(--color-neutral-c)var(--color-neutral-h)/.08);--shadow-light-edge:inset 1px 1px 1px oklch(var(--color-neutral-l-1000)var(--color-neutral-c)var(--color-neutral-h)/.3);--shadow-dark-edge:inset -1px -1px 1px oklch(var(--color-neutral-l-100)var(--color-neutral-c)var(--color-neutral-h)/.3);--background-glass:linear-gradient(135deg,transparent,var(--color-base-background-o-50));--panel-threads-background:oklch(var(--color-neutral-l-200)var(--color-neutral-c)var(--color-neutral-h)/.99)}kirby-loop{--color-base:var(--color-neutral-900);--color-base-background:var(--color-neutral-0);--color-base-background-o-5:oklch(var(--color-neutral-l-0)var(--color-neutral-c)var(--color-neutral-h)/.05);--color-base-background-o-10:oklch(var(--color-neutral-l-0)var(--color-neutral-c)var(--color-neutral-h)/.1);--color-base-background-o-20:oklch(var(--color-neutral-l-0)var(--color-neutral-c)var(--color-neutral-h)/.2);--color-base-background-o-50:oklch(var(--color-neutral-l-0)var(--color-neutral-c)var(--color-neutral-h)/.5);--color-base-background-o-60:oklch(var(--color-neutral-l-0)var(--color-neutral-c)var(--color-neutral-h)/.6);--color-base-background-o-75:oklch(var(--color-neutral-l-0)var(--color-neutral-c)var(--color-neutral-h)/.75);--color-base-background-o-95:oklch(var(--color-neutral-l-0)var(--color-neutral-c)var(--color-neutral-h)/.95);--color-accent-light:oklch(calc(var(--color-accent-l)*var(--color-accent-light-factor))var(--color-accent-c)var(--color-accent-h));--color-accent:oklch(var(--color-accent-l)var(--color-accent-c)var(--color-accent-h));--color-accent-dark:oklch(calc(var(--color-accent-l)*var(--color-accent-dark-factor))var(--color-accent-c)var(--color-accent-h));--color-neutral-0:oklch(var(--color-neutral-l-0)var(--color-neutral-c)var(--color-neutral-h));--color-neutral-100:oklch(var(--color-neutral-l-100)var(--color-neutral-c)var(--color-neutral-h));--color-neutral-200:oklch(var(--color-neutral-l-200)var(--color-neutral-c)var(--color-neutral-h));--color-neutral-300:oklch(var(--color-neutral-l-300)var(--color-neutral-c)var(--color-neutral-h));--color-neutral-400:oklch(var(--color-neutral-l-400)var(--color-neutral-c)var(--color-neutral-h));--color-neutral-500:oklch(var(--color-neutral-l-500)var(--color-neutral-c)var(--color-neutral-h));--color-neutral-600:oklch(var(--color-neutral-l-600)var(--color-neutral-c)var(--color-neutral-h));--color-neutral-700:oklch(var(--color-neutral-l-700)var(--color-neutral-c)var(--color-neutral-h));--color-neutral-800:oklch(var(--color-neutral-l-800)var(--color-neutral-c)var(--color-neutral-h));--color-neutral-900:oklch(var(--color-neutral-l-900)var(--color-neutral-c)var(--color-neutral-h));--color-neutral-1000:oklch(var(--color-neutral-l-1000)var(--color-neutral-c)var(--color-neutral-h));--color-success:oklch(.65 .15 150);--color-warning:oklch(.75 .15 80);--color-error:oklch(.65 .18 25);--color-info:oklch(.65 .15 220);--font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol",sans-serif;--line-height:1.4;--font-weight-light:300;--font-weight-normal:400;--font-weight-medium:550;--font-weight-bold:700;--font-size-3:clamp(1.9531rem,1.4262rem + 1.7565vw,3.5339rem);--font-size-4:clamp(1.5625rem,1.2503rem + 1.0408vw,2.4992rem);--font-size-5:clamp(1.25rem,1.0775rem + .575vw,1.7675rem);--font-size-6:clamp(1rem,.9167rem + .2778vw,1.25rem);--font-size-7:clamp(.8rem,.772rem + .0934vw,.884rem);--font-size-8:clamp(.6252rem,.6449rem + -.0165vw,.64rem);--border-radius-s:.125rem;--border-radius:.25rem;--border-radius-rounded:4096px;--space-2xs:clamp(.25rem,.2292rem + .0694vw,.3125rem);--space-xs:clamp(.5rem,.4583rem + .1389vw,.625rem);--space-s:clamp(1rem,.9167rem + .2778vw,1.25rem);--space-m:clamp(1.5rem,1.375rem + .4167vw,1.875rem);--space-l:clamp(2rem,1.8333rem + .5556vw,2.5rem);--space-2xs-xs:clamp(.25rem,.125rem + .4167vw,.625rem);--space-xs-s:clamp(.5rem,.25rem + .8333vw,1.25rem);--space-s-m:clamp(1rem,.7083rem + .9722vw,1.875rem);--space-m-l:clamp(1.5rem,1.1667rem + 1.1111vw,2.5rem);--space-s-l:clamp(1rem,.5rem + 1.6667vw,2.5rem);--shadow-s:0 .1em .25em oklch(var(--color-neutral-l-900)var(--color-neutral-c)var(--color-neutral-h)/.1);--shadow-m:0 2px 8px oklch(var(--color-neutral-l-900)var(--color-neutral-c)var(--color-neutral-h)/.08),0 8px 16px oklch(var(--color-neutral-l-900)var(--color-neutral-c)var(--color-neutral-h)/.12),0 16px 24px oklch(var(--color-neutral-l-900)var(--color-neutral-c)var(--color-neutral-h)/.08);--shadow-l:0 4px 16px oklch(var(--color-neutral-l-900)var(--color-neutral-c)var(--color-neutral-h)/.08),0 12px 32px oklch(var(--color-neutral-l-900)var(--color-neutral-c)var(--color-neutral-h)/.12),0 24px 48px oklch(var(--color-neutral-l-900)var(--color-neutral-c)var(--color-neutral-h)/.16),0 48px 80px oklch(var(--color-neutral-l-900)var(--color-neutral-c)var(--color-neutral-h)/.08);--shadow-light-edge:inset 1px 1px 1px oklch(var(--color-neutral-l-0)var(--color-neutral-c)var(--color-neutral-h)/.3);--shadow-dark-edge:inset 0 -1px 1px oklch(var(--color-neutral-l-900)var(--color-neutral-c)var(--color-neutral-h)/.3);--backdrop-blur:blur(6px);--backdrop-glass:var(--backdrop-blur)saturate(1.4)brightness(1.2);--background-glass:linear-gradient(135deg,transparent,var(--color-base-background-o-95));--background-glass-frosted:linear-gradient(0deg,var(--color-base-background-o-75)0%,var(--color-base-background-o-95)50%);--opacity-subtle:.5;--opacity-medium:.7;--opacity-strong:.9;--outline-color:var(--color-accent);--outline-offset:.25rem;--transition-duration:.2s;--transition-duration-jump:.4s;--transition-easing-jump:cubic-bezier(.44,1.2,.64,1);--transition-easing:cubic-bezier(0,0,.2,1);--z-loop-marker:9998;--z-loop-panel:9999;--z-loop-dialog:10000;--author-avatar-color:var(--color-neutral-600);--author-avatar-background-color:var(--color-neutral-100);--author-avatar-size:2.5rem;--author-avatar-border-radius:var(--border-radius-rounded);--author-avatar-font-size:var(--font-size-6);--button-background:transparent;--button-color:var(--color-neutral-600);--button-border-radius:var(--border-radius);--button-padding:0 var(--space-xs);--button-gap:var(--space-2xs);--button-font-size:var(--font-size-7);--button-font-weight:var(--font-weight-medium);--button-height:2.25rem;--button-transition:var(--transition-duration)var(--transition-easing);--button-outline-color:var(--outline-color);--button-outline-offset:var(--outline-offset);--button-hover-color:var(--color-neutral-900);--button-hover-background:var(--color-neutral-200);--button-header-background:transparent;--button-header-height:3rem;--button-header-padding:0 var(--space-s);--button-header-hover-background:var(--color-base-background-o-95);--button-header-blend-mode:multiply;--button-panel-background:transparent;--button-panel-padding:0 calc(var(--space-s)*.4);--button-solid-background:var(--color-neutral-100);--button-solid-hover-color:var(--color-neutral-900);--button-solid-hover-background:var(--color-neutral-200);--button-small-height:1.5rem;--button-small-font-size:var(--font-size-7);--button-icon-background:var(--color-neutral-0);--button-icon-color:var(--color-neutral-500);--button-icon-height:3rem;--button-icon-shadow:var(--shadow-s);--button-icon-border-radius:var(--border-radius-rounded);--button-icon-font-size:var(--font-size-6);--button-icon-hover-background:var(--color-neutral-200);--button-icon-hover-color:var(--color-neutral-900);--button-marker-background:var(--color-accent);--button-marker-color:var(--color-accent-dark);--button-marker-font-weight:var(--font-weight-bold);--button-marker-border-radius:var(--border-radius-rounded);--button-marker-highlighted-background:var(--color-accent);--button-marker-highlighted-color:var(--color-accent-dark);--button-filter-background:transparent;--button-filter-color:var(--color-neutral-500);--button-filter-height:1.75rem;--button-filter-font-size:var(--font-size-8);--button-filter-padding:0 var(--space-xs);--button-filter-border-radius:calc(var(--border-radius) - 2px);--button-filter-hover-color:var(--color-neutral-700);--button-filter-hover-background:var(--color-neutral-200);--button-filter-active-background:var(--color-base-background);--button-filter-active-color:var(--color-base);--button-filter-active-font-weight:var(--font-weight-medium);--button-menu-item-background:transparent;--button-menu-item-color:var(--color-neutral-700);--button-menu-item-padding:var(--space-2xs)var(--space-xs);--button-menu-item-border-radius:calc(var(--border-radius) - 2px);--button-menu-item-font-size:var(--font-size-7);--button-menu-item-gap:var(--space-2xs);--button-menu-item-hover-background:var(--color-neutral-100);--button-menu-item-hover-color:var(--color-neutral-900);--button-menu-item-active-background:var(--color-accent-light);--button-menu-item-active-color:var(--color-accent-dark);--button-menu-item-active-font-weight:var(--font-weight-medium);--button-active-background:var(--color-accent);--button-active-color:var(--color-accent-dark);--button-disabled-opacity:var(--opacity-subtle);--button-disabled-hover-color:var(--color-neutral-700);--button-disabled-hover-background:var(--color-neutral-100);--comment-avatar-size:2.5rem;--comment-marker-background:var(--color-neutral-200);--comment-marker-color:var(--color-neutral-800);--comment-line-background:var(--color-neutral-100);--comment-line-width:.1rem;--comment-line-offset:calc(var(--space-s) + var(--comment-avatar-size)/2);--comment-header-font-size:var(--font-size-7);--comment-header-padding:var(--space-s);--comment-header-gap:var(--space-s);--comment-header-outline-color:var(--outline-color);--comment-header-outline-offset:-2px;--comment-header-border-radius:var(--border-radius);--comment-content-padding:var(--space-xs);--comment-content-background:var(--color-neutral-100);--comment-content-background-dark:var(--color-neutral-200);--comment-content-border-radius:var(--border-radius);--comment-author-gap:var(--space-xs);--comment-author-margin-bottom:var(--space-2xs);--comment-timestamp-font-size:var(--font-size-8);--comment-timestamp-color:var(--color-neutral-300);--comment-replies-padding:0 var(--space-s);--comment-replies-gap:var(--space-s);--comment-footer-padding:var(--space-s);--comment-footer-gap:var(--space-s);--comment-buttons-gap:var(--space-xs);--comment-dialog-position:absolute;--comment-dialog-max-width:300px;--comment-dialog-border-radius:var(--border-radius);--comment-dialog-shadow:var(--shadow-s);--comment-dialog-backdrop-background:transparent;--comment-dialog-textarea-font-size:var(--font-size-6);--comment-form-background:var(--color-base-background);--comment-form-color:var(--color-base);--comment-form-border:1px solid var(--color-neutral-200);--comment-form-border-radius:var(--border-radius);--comment-form-textarea-height:15ch;--comment-form-textarea-padding:var(--space-s);--comment-form-textarea-background:var(--color-base-background);--comment-form-textarea-font-family:var(--font-family);--comment-form-textarea-font-size:var(--font-size-7);--comment-form-footer-padding:var(--space-xs);--comment-form-footer-gap:var(--space-xs);--comment-form-hint-font-size:var(--font-size-8);--comment-form-hint-color:var(--color-neutral-300);--comment-form-hint-padding:0 var(--space-xs)var(--space-xs)var(--space-xs);--context-menu-container-bottom:var(--space-s);--context-menu-container-right:var(--space-s);--context-menu-container-z-index:10;--context-menu-trigger-size:2.5rem;--context-menu-trigger-border-radius:var(--border-radius-rounded);--context-menu-background:var(--color-base-background);--context-menu-border-radius:var(--border-radius);--context-menu-shadow:var(--shadow-s);--context-menu-padding:var(--space-xs);--context-menu-min-width:12rem;--context-menu-backdrop-background:transparent;--context-menu-section-gap:var(--space-2xs);--context-menu-title-font-size:var(--font-size-8);--context-menu-title-font-weight:var(--font-weight-medium);--context-menu-title-color:var(--color-neutral-500);--context-menu-title-margin-bottom:var(--space-2xs);--context-menu-title-letter-spacing:.05em;--context-menu-filter-gap:1px;--context-menu-filter-dot-size:.5em;--context-menu-filter-dot-border-radius:50%;--context-menu-filter-dot-margin-right:var(--space-2xs);--context-menu-filter-dot-open-background:var(--color-accent);--context-menu-filter-dot-resolved-background:var(--color-neutral-400);--header-position:fixed;--header-top:var(--space-xs);--header-transform:translateX(-50%);--header-color:var(--color-base);--header-border-radius:var(--border-radius-rounded);--header-z-index:9999;--header-bottom-position:var(--space-xs);--header-backdrop-filter:var(--backdrop-glass);--header-background:var(--background-glass);--header-count-size:2rem;--header-count-border-radius:var(--border-radius-rounded);--header-count-backdrop-filter:var(--backdrop-glass);--header-count-background:var(--background-glass);--marker-size:2rem;--marker-position:absolute;--marker-z-index:var(--z-loop-marker);--marker-transform:translate(-50%,-50%);--marker-border-radius:var(--border-radius-rounded);--panel-width:380px;--panel-mobile-width:85svw;--panel-position:fixed;--panel-right:var(--space-xs);--panel-top:var(--space-xs);--panel-height:calc(100svh - var(--space-xs)*2);--panel-transform-closed:translateX(calc(100% + var(--space-xs)));--panel-transform-open:translateX(0);--panel-color:var(--color-base);--panel-border-radius:var(--border-radius);--panel-border-top-left-radius:0;--panel-transition:var(--transition-duration-jump)var(--transition-easing-jump);--panel-z-index:var(--z-loop-panel);--panel-shadow:var(--shadow-m);--panel-header-transform-closed:translate(-95%);--panel-header-transform-open:translate(calc(-100% + 1px));--panel-header-transform-hover:translate(calc(-100% + 1px));--panel-header-border-radius:var(--border-radius-rounded);--panel-header-gap:var(--space-xs);--panel-header-backdrop-filter:var(--backdrop-glass);--panel-header-background:var(--background-glass);--panel-threads-background:var(--color-base-background-o-95);--panel-threads-backdrop:var(--backdrop-blur);--panel-threads-border-radius:var(--border-radius);--panel-threads-border-top-left-radius:0;--panel-threads-padding:0 0 var(--space-s)0;--panel-threads-item-margin:var(--space-s);--panel-threads-scrollbar-width:thin;--panel-no-threads-padding:var(--space-s)var(--space-l);--panel-no-threads-font-size:var(--font-size-6);--panel-no-threads-color:var(--color-neutral-300);--reply-gap:var(--space-s);--reply-content-padding:var(--space-xs);--reply-content-background:var(--color-neutral-100);--reply-content-background-dark:var(--color-neutral-200);--reply-content-border-radius:var(--border-radius);--reply-header-gap:var(--space-xs);--reply-header-margin-bottom:var(--space-2xs);--reply-timestamp-font-size:var(--font-size-8);--reply-timestamp-color:var(--color-neutral-300);--welcome-dialog-background:var(--background-glass-frosted);--welcome-dialog-backdrop-filter:var(--backdrop-glass);--welcome-dialog-border:0px;--welcome-dialog-border-radius:var(--border-radius);--welcome-dialog-shadow:var(--shadow-l),var(--shadow-light-edge),var(--shadow-dark-edge);--welcome-dialog-max-width:500px;--welcome-dialog-backdrop-background:var(--color-base-background-o-10);--welcome-dialog-backdrop-backdrop-filter:none;--welcome-dialog-form-padding:var(--space-l);--welcome-dialog-title-margin:0 0 var(--space-s)0;--welcome-dialog-title-font-size:var(--font-size-4);--welcome-dialog-title-color:var(--color-base);--welcome-dialog-title-font-weight:var(--font-weight-bold);--welcome-dialog-text-margin:0 0 var(--space-m)0;--welcome-dialog-text-font-size:var(--font-size-6);--welcome-dialog-text-color:var(--color-neutral-600);--welcome-dialog-text-line-height:var(--line-height);--welcome-dialog-name-section-margin:var(--space-l);--welcome-dialog-input-border:1px solid var(--color-neutral-300);--welcome-dialog-input-border-radius:var(--border-radius-s);--welcome-dialog-input-padding:var(--space-xs);--welcome-dialog-input-font-family:var(--font-family);--welcome-dialog-input-font-size:var(--font-size-6);--welcome-dialog-input-color:var(--color-base);--welcome-dialog-input-background:var(--color-base-background);--welcome-dialog-input-outline-color:var(--outline-color);--welcome-dialog-input-outline-offset:var(--outline-offset);--welcome-dialog-footer-gap:var(--space-xs);--icon-size:1em}kirby-loop[data-theme=dark]{--color-neutral-l-0:0;--color-neutral-l-100:.1;--color-neutral-l-200:.2;--color-neutral-l-300:.3;--color-neutral-l-400:.4;--color-neutral-l-500:.5;--color-neutral-l-600:.6;--color-neutral-l-700:.7;--color-neutral-l-800:.9;--color-neutral-l-900:.95;--color-neutral-l-1000:1}kirby-loop{font-family:var(--font-family);line-height:var(--line-height);font-weight:var(--font-weight-normal);font-size:var(--font-size-7);box-sizing:border-box}html.loop-overlay-open a{pointer-events:none}')),document.head.appendChild(r)}}catch(o){console.error("vite-plugin-css-injected-by-js",o)}})();
-var io = Object.defineProperty;
-var Dr = (e) => {
- throw TypeError(e);
-};
-var lo = (e, t, r) => t in e ? io(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
-var oe = (e, t, r) => lo(e, typeof t != "symbol" ? t + "" : t, r), jr = (e, t, r) => t.has(e) || Dr("Cannot " + r);
-var de = (e, t, r) => (jr(e, t, "read from private field"), r ? r.call(e) : t.get(e)), ar = (e, t, r) => t.has(e) ? Dr("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(e) : t.set(e, r), sr = (e, t, r, n) => (jr(e, t, "write to private field"), n ? n.call(e, r) : t.set(e, r), r);
-const uo = "5";
-var nn;
-typeof window < "u" && ((nn = window.__svelte ?? (window.__svelte = {})).v ?? (nn.v = /* @__PURE__ */ new Set())).add(uo);
-const pr = 1, br = 2, on = 4, co = 8, fo = 16, vo = 1, ho = 2, mo = 4, go = 8, po = 16, bo = 1, wo = 2, wr = "[", _r = "[!", yr = "]", wt = {}, fe = Symbol(), _o = "http://www.w3.org/1999/xhtml", Hr = !1, Se = 2, an = 4, Zt = 8, kr = 16, Le = 32, rt = 64, Tt = 128, Ce = 256, Ot = 512, be = 1024, De = 2048, dt = 4096, Te = 8192, Jt = 16384, yo = 32768, xr = 65536, ko = 1 << 17, xo = 1 << 19, sn = 1 << 20, Ke = Symbol("$state"), ln = Symbol("legacy props"), $o = Symbol("");
-var $r = Array.isArray, Co = Array.prototype.indexOf, Cr = Array.from, At = Object.keys, Lt = Object.defineProperty, Ge = Object.getOwnPropertyDescriptor, un = Object.getOwnPropertyDescriptors, zo = Object.prototype, So = Array.prototype, zr = Object.getPrototypeOf;
-function Eo(e) {
- return e();
-}
-function Dt(e) {
- for (var t = 0; t < e.length; t++)
- e[t]();
-}
-const qo = typeof requestIdleCallback > "u" ? (e) => setTimeout(e, 1) : requestIdleCallback;
-let yt = [], kt = [];
-function cn() {
- var e = yt;
- yt = [], Dt(e);
-}
-function dn() {
- var e = kt;
- kt = [], Dt(e);
-}
-function Kt(e) {
- yt.length === 0 && queueMicrotask(cn), yt.push(e);
-}
-function Io(e) {
- kt.length === 0 && qo(dn), kt.push(e);
-}
-function Fr() {
- yt.length > 0 && cn(), kt.length > 0 && dn();
-}
-function fn(e) {
- return e === this.v;
-}
-function Mo(e, t) {
- return e != e ? t == t : e !== t || e !== null && typeof e == "object" || typeof e == "function";
-}
-function Sr(e) {
- return !Mo(e, this.v);
-}
-function Po(e) {
- throw new Error("https://svelte.dev/e/effect_in_teardown");
-}
-function No() {
- throw new Error("https://svelte.dev/e/effect_in_unowned_derived");
-}
-function Ro(e) {
- throw new Error("https://svelte.dev/e/effect_orphan");
-}
-function To() {
- throw new Error("https://svelte.dev/e/effect_update_depth_exceeded");
-}
-function Oo() {
- throw new Error("https://svelte.dev/e/hydration_failed");
-}
-function Ao(e) {
- throw new Error("https://svelte.dev/e/props_invalid_value");
-}
-function Lo() {
- throw new Error("https://svelte.dev/e/state_descriptors_fixed");
-}
-function Do() {
- throw new Error("https://svelte.dev/e/state_prototype_fixed");
-}
-function jo() {
- throw new Error("https://svelte.dev/e/state_unsafe_local_read");
-}
-function Ho() {
- throw new Error("https://svelte.dev/e/state_unsafe_mutation");
-}
-let ft = !1, Fo = !1;
-function Vo() {
- ft = !0;
-}
-const xt = /* @__PURE__ */ new Map();
-function ve(e, t) {
- var r = {
- f: 0,
- // TODO ideally we could skip this altogether, but it causes type errors
- v: e,
- reactions: null,
- equals: fn,
- rv: 0,
- wv: 0
- };
- return r;
-}
-function ue(e) {
- return /* @__PURE__ */ vn(ve(e));
-}
-// @__NO_SIDE_EFFECTS__
-function Gt(e, t = !1) {
- var n;
- const r = ve(e);
- return t || (r.equals = Sr), ft && U !== null && U.l !== null && ((n = U.l).s ?? (n.s = [])).push(r), r;
-}
-function Vr(e, t = !1) {
- return /* @__PURE__ */ vn(/* @__PURE__ */ Gt(e, t));
-}
-// @__NO_SIDE_EFFECTS__
-function vn(e) {
- return Y !== null && !ze && (Y.f & Se) !== 0 && (Ie === null ? Wo([e]) : Ie.push(e)), e;
-}
-function Mt(e, t) {
- return T(
- e,
- Ue(() => h(e))
- ), t;
-}
-function T(e, t) {
- return Y !== null && !ze && Et() && (Y.f & (Se | kr)) !== 0 && // If the source was created locally within the current derived, then
- // we allow the mutation.
- (Ie === null || !Ie.includes(e)) && Ho(), dr(e, t);
-}
-function dr(e, t) {
- if (!e.equals(t)) {
- var r = e.v;
- zt ? xt.set(e, t) : xt.set(e, r), e.v = t, e.wv = kn(), hn(e, De), Et() && B !== null && (B.f & be) !== 0 && (B.f & (Le | rt)) === 0 && (ke === null ? Xo([e]) : ke.push(e));
- }
- return t;
-}
-function hn(e, t) {
- var r = e.reactions;
- if (r !== null)
- for (var n = Et(), o = r.length, a = 0; a < o; a++) {
- var s = r[a], i = s.f;
- (i & De) === 0 && (!n && s === B || (Pe(s, t), (i & (be | Ce)) !== 0 && ((i & Se) !== 0 ? hn(
- /** @type {Derived} */
- s,
- dt
- ) : er(
- /** @type {Effect} */
- s
- ))));
- }
-}
-// @__NO_SIDE_EFFECTS__
-function ee(e) {
- var t = Se | De, r = Y !== null && (Y.f & Se) !== 0 ? (
- /** @type {Derived} */
- Y
- ) : null;
- return B === null || r !== null && (r.f & Ce) !== 0 ? t |= Ce : B.f |= sn, {
- ctx: U,
- deps: null,
- effects: null,
- equals: fn,
- f: t,
- fn: e,
- reactions: null,
- rv: 0,
- v: (
- /** @type {V} */
- null
- ),
- wv: 0,
- parent: r ?? B
- };
-}
-// @__NO_SIDE_EFFECTS__
-function me(e) {
- const t = /* @__PURE__ */ ee(e);
- return t.equals = Sr, t;
-}
-function mn(e) {
- var t = e.effects;
- if (t !== null) {
- e.effects = null;
- for (var r = 0; r < t.length; r += 1)
- Ae(
- /** @type {Effect} */
- t[r]
- );
- }
-}
-function Bo(e) {
- for (var t = e.parent; t !== null; ) {
- if ((t.f & Se) === 0)
- return (
- /** @type {Effect} */
- t
- );
- t = t.parent;
- }
- return null;
-}
-function Yo(e) {
- var t, r = B;
- Xe(Bo(e));
- try {
- mn(e), t = $n(e);
- } finally {
- Xe(r);
- }
- return t;
-}
-function gn(e) {
- var t = Yo(e), r = (Be || (e.f & Ce) !== 0) && e.deps !== null ? dt : be;
- Pe(e, r), e.equals(t) || (e.v = t, e.wv = kn());
-}
-function Er(e) {
- console.warn("https://svelte.dev/e/hydration_mismatch");
-}
-let A = !1;
-function Re(e) {
- A = e;
-}
-let H;
-function pe(e) {
- if (e === null)
- throw Er(), wt;
- return H = e;
-}
-function Ct() {
- return pe(
- /** @type {TemplateNode} */
- /* @__PURE__ */ je(H)
- );
-}
-function x(e) {
- if (A) {
- if (/* @__PURE__ */ je(H) !== null)
- throw Er(), wt;
- H = e;
- }
-}
-function ge(e = 1) {
- if (A) {
- for (var t = e, r = H; t--; )
- r = /** @type {TemplateNode} */
- /* @__PURE__ */ je(r);
- H = r;
- }
-}
-function fr() {
- for (var e = 0, t = H; ; ) {
- if (t.nodeType === 8) {
- var r = (
- /** @type {Comment} */
- t.data
- );
- if (r === yr) {
- if (e === 0) return t;
- e -= 1;
- } else (r === wr || r === _r) && (e += 1);
- }
- var n = (
- /** @type {TemplateNode} */
- /* @__PURE__ */ je(t)
- );
- t.remove(), t = n;
- }
-}
-function J(e, t = null, r) {
- if (typeof e != "object" || e === null || Ke in e)
- return e;
- const n = zr(e);
- if (n !== zo && n !== So)
- return e;
- var o = /* @__PURE__ */ new Map(), a = $r(e), s = ve(0);
- a && o.set("length", ve(
- /** @type {any[]} */
- e.length
- ));
- var i;
- return new Proxy(
- /** @type {any} */
- e,
- {
- defineProperty(u, c, v) {
- (!("value" in v) || v.configurable === !1 || v.enumerable === !1 || v.writable === !1) && Lo();
- var m = o.get(c);
- return m === void 0 ? (m = ve(v.value), o.set(c, m)) : T(m, J(v.value, i)), !0;
- },
- deleteProperty(u, c) {
- var v = o.get(c);
- if (v === void 0)
- c in u && o.set(c, ve(fe));
- else {
- if (a && typeof c == "string") {
- var m = (
- /** @type {Source} */
- o.get("length")
- ), l = Number(c);
- Number.isInteger(l) && l < m.v && T(m, l);
- }
- T(v, fe), Br(s);
- }
- return !0;
- },
- get(u, c, v) {
- var d;
- if (c === Ke)
- return e;
- var m = o.get(c), l = c in u;
- if (m === void 0 && (!l || (d = Ge(u, c)) != null && d.writable) && (m = ve(J(l ? u[c] : fe, i)), o.set(c, m)), m !== void 0) {
- var f = h(m);
- return f === fe ? void 0 : f;
- }
- return Reflect.get(u, c, v);
- },
- getOwnPropertyDescriptor(u, c) {
- var v = Reflect.getOwnPropertyDescriptor(u, c);
- if (v && "value" in v) {
- var m = o.get(c);
- m && (v.value = h(m));
- } else if (v === void 0) {
- var l = o.get(c), f = l == null ? void 0 : l.v;
- if (l !== void 0 && f !== fe)
- return {
- enumerable: !0,
- configurable: !0,
- value: f,
- writable: !0
- };
- }
- return v;
- },
- has(u, c) {
- var f;
- if (c === Ke)
- return !0;
- var v = o.get(c), m = v !== void 0 && v.v !== fe || Reflect.has(u, c);
- if (v !== void 0 || B !== null && (!m || (f = Ge(u, c)) != null && f.writable)) {
- v === void 0 && (v = ve(m ? J(u[c], i) : fe), o.set(c, v));
- var l = h(v);
- if (l === fe)
- return !1;
- }
- return m;
- },
- set(u, c, v, m) {
- var k;
- var l = o.get(c), f = c in u;
- if (a && c === "length")
- for (var d = v; d < /** @type {Source} */
- l.v; d += 1) {
- var p = o.get(d + "");
- p !== void 0 ? T(p, fe) : d in u && (p = ve(fe), o.set(d + "", p));
- }
- l === void 0 ? (!f || (k = Ge(u, c)) != null && k.writable) && (l = ve(void 0), T(l, J(v, i)), o.set(c, l)) : (f = l.v !== fe, T(l, J(v, i)));
- var w = Reflect.getOwnPropertyDescriptor(u, c);
- if (w != null && w.set && w.set.call(m, v), !f) {
- if (a && typeof c == "string") {
- var I = (
- /** @type {Source} */
- o.get("length")
- ), _ = Number(c);
- Number.isInteger(_) && _ >= I.v && T(I, _ + 1);
- }
- Br(s);
- }
- return !0;
- },
- ownKeys(u) {
- h(s);
- var c = Reflect.ownKeys(u).filter((l) => {
- var f = o.get(l);
- return f === void 0 || f.v !== fe;
- });
- for (var [v, m] of o)
- m.v !== fe && !(v in u) && c.push(v);
- return c;
- },
- setPrototypeOf() {
- Do();
- }
- }
- );
-}
-function Br(e, t = 1) {
- T(e, e.v + t);
-}
-var Yr, pn, bn, wn, _n;
-function vr() {
- if (Yr === void 0) {
- Yr = window, pn = document, bn = /Firefox/.test(navigator.userAgent);
- var e = Element.prototype, t = Node.prototype;
- wn = Ge(t, "firstChild").get, _n = Ge(t, "nextSibling").get, e.__click = void 0, e.__className = void 0, e.__attributes = null, e.__style = void 0, e.__e = void 0, Text.prototype.__t = void 0;
- }
-}
-function Ye(e = "") {
- return document.createTextNode(e);
-}
-// @__NO_SIDE_EFFECTS__
-function Ee(e) {
- return wn.call(e);
-}
-// @__NO_SIDE_EFFECTS__
-function je(e) {
- return _n.call(e);
-}
-function C(e, t) {
- if (!A)
- return /* @__PURE__ */ Ee(e);
- var r = (
- /** @type {TemplateNode} */
- /* @__PURE__ */ Ee(H)
- );
- if (r === null)
- r = H.appendChild(Ye());
- else if (t && r.nodeType !== 3) {
- var n = Ye();
- return r == null || r.before(n), pe(n), n;
- }
- return pe(r), r;
-}
-function Oe(e, t) {
- if (!A) {
- var r = (
- /** @type {DocumentFragment} */
- /* @__PURE__ */ Ee(
- /** @type {Node} */
- e
- )
- );
- return r instanceof Comment && r.data === "" ? /* @__PURE__ */ je(r) : r;
- }
- return H;
-}
-function V(e, t = 1, r = !1) {
- let n = A ? H : e;
- for (var o; t--; )
- o = n, n = /** @type {TemplateNode} */
- /* @__PURE__ */ je(n);
- if (!A)
- return n;
- var a = n == null ? void 0 : n.nodeType;
- if (r && a !== 3) {
- var s = Ye();
- return n === null ? o == null || o.after(s) : n.before(s), pe(s), s;
- }
- return pe(n), /** @type {TemplateNode} */
- n;
-}
-function qr(e) {
- e.textContent = "";
-}
-let Nt = !1, jt = !1, Ht = null, Qe = !1, zt = !1;
-function Wr(e) {
- zt = e;
-}
-let _t = [];
-let Y = null, ze = !1;
-function We(e) {
- Y = e;
-}
-let B = null;
-function Xe(e) {
- B = e;
-}
-let Ie = null;
-function Wo(e) {
- Ie = e;
-}
-let ie = null, he = 0, ke = null;
-function Xo(e) {
- ke = e;
-}
-let yn = 1, Ft = 0, Be = !1;
-function kn() {
- return ++yn;
-}
-function St(e) {
- var m;
- var t = e.f;
- if ((t & De) !== 0)
- return !0;
- if ((t & dt) !== 0) {
- var r = e.deps, n = (t & Ce) !== 0;
- if (r !== null) {
- var o, a, s = (t & Ot) !== 0, i = n && B !== null && !Be, u = r.length;
- if (s || i) {
- var c = (
- /** @type {Derived} */
- e
- ), v = c.parent;
- for (o = 0; o < u; o++)
- a = r[o], (s || !((m = a == null ? void 0 : a.reactions) != null && m.includes(c))) && (a.reactions ?? (a.reactions = [])).push(c);
- s && (c.f ^= Ot), i && v !== null && (v.f & Ce) === 0 && (c.f ^= Ce);
- }
- for (o = 0; o < u; o++)
- if (a = r[o], St(
- /** @type {Derived} */
- a
- ) && gn(
- /** @type {Derived} */
- a
- ), a.wv > e.wv)
- return !0;
- }
- (!n || B !== null && !Be) && Pe(e, be);
- }
- return !1;
-}
-function Uo(e, t) {
- for (var r = t; r !== null; ) {
- if ((r.f & Tt) !== 0)
- try {
- r.fn(e);
- return;
- } catch {
- r.f ^= Tt;
- }
- r = r.parent;
- }
- throw Nt = !1, e;
-}
-function Zo(e) {
- return (e.f & Jt) === 0 && (e.parent === null || (e.parent.f & Tt) === 0);
-}
-function Qt(e, t, r, n) {
- if (Nt) {
- if (r === null && (Nt = !1), Zo(t))
- throw e;
- return;
- }
- r !== null && (Nt = !0);
- {
- Uo(e, t);
- return;
- }
-}
-function xn(e, t, r = !0) {
- var n = e.reactions;
- if (n !== null)
- for (var o = 0; o < n.length; o++) {
- var a = n[o];
- (a.f & Se) !== 0 ? xn(
- /** @type {Derived} */
- a,
- t,
- !1
- ) : t === a && (r ? Pe(a, De) : (a.f & be) !== 0 && Pe(a, dt), er(
- /** @type {Effect} */
- a
- ));
- }
-}
-function $n(e) {
- var f;
- var t = ie, r = he, n = ke, o = Y, a = Be, s = Ie, i = U, u = ze, c = e.f;
- ie = /** @type {null | Value[]} */
- null, he = 0, ke = null, Be = (c & Ce) !== 0 && (ze || !Qe || Y === null), Y = (c & (Le | rt)) === 0 ? e : null, Ie = null, Xr(e.ctx), ze = !1, Ft++;
- try {
- var v = (
- /** @type {Function} */
- (0, e.fn)()
- ), m = e.deps;
- if (ie !== null) {
- var l;
- if (Vt(e, he), m !== null && he > 0)
- for (m.length = he + ie.length, l = 0; l < ie.length; l++)
- m[he + l] = ie[l];
- else
- e.deps = m = ie;
- if (!Be)
- for (l = he; l < m.length; l++)
- ((f = m[l]).reactions ?? (f.reactions = [])).push(e);
- } else m !== null && he < m.length && (Vt(e, he), m.length = he);
- if (Et() && ke !== null && !ze && m !== null && (e.f & (Se | dt | De)) === 0)
- for (l = 0; l < /** @type {Source[]} */
- ke.length; l++)
- xn(
- ke[l],
- /** @type {Effect} */
- e
- );
- return o !== null && (Ft++, ke !== null && (n === null ? n = ke : n.push(.../** @type {Source[]} */
- ke))), v;
- } finally {
- ie = t, he = r, ke = n, Y = o, Be = a, Ie = s, Xr(i), ze = u;
- }
-}
-function Jo(e, t) {
- let r = t.reactions;
- if (r !== null) {
- var n = Co.call(r, e);
- if (n !== -1) {
- var o = r.length - 1;
- o === 0 ? r = t.reactions = null : (r[n] = r[o], r.pop());
- }
- }
- r === null && (t.f & Se) !== 0 && // Destroying a child effect while updating a parent effect can cause a dependency to appear
- // to be unused, when in fact it is used by the currently-updating parent. Checking `new_deps`
- // allows us to skip the expensive work of disconnecting and immediately reconnecting it
- (ie === null || !ie.includes(t)) && (Pe(t, dt), (t.f & (Ce | Ot)) === 0 && (t.f ^= Ot), mn(
- /** @type {Derived} **/
- t
- ), Vt(
- /** @type {Derived} **/
- t,
- 0
- ));
-}
-function Vt(e, t) {
- var r = e.deps;
- if (r !== null)
- for (var n = t; n < r.length; n++)
- Jo(e, r[n]);
-}
-function Ir(e) {
- var t = e.f;
- if ((t & Jt) === 0) {
- Pe(e, be);
- var r = B, n = U, o = Qe;
- B = e, Qe = !0;
- try {
- (t & kr) !== 0 ? sa(e) : En(e), Sn(e);
- var a = $n(e);
- e.teardown = typeof a == "function" ? a : null, e.wv = yn;
- var s = e.deps, i;
- Hr && Fo && e.f & De;
- } catch (u) {
- Qt(u, e, r, n || e.ctx);
- } finally {
- Qe = o, B = r;
- }
- }
-}
-function Ko() {
- try {
- To();
- } catch (e) {
- if (Ht !== null)
- Qt(e, Ht, null);
- else
- throw e;
- }
-}
-function Cn() {
- var e = Qe;
- try {
- var t = 0;
- for (Qe = !0; _t.length > 0; ) {
- t++ > 1e3 && Ko();
- var r = _t, n = r.length;
- _t = [];
- for (var o = 0; o < n; o++) {
- var a = Qo(r[o]);
- Go(a);
- }
- }
- } finally {
- jt = !1, Qe = e, Ht = null, xt.clear();
- }
-}
-function Go(e) {
- var t = e.length;
- if (t !== 0)
- for (var r = 0; r < t; r++) {
- var n = e[r];
- if ((n.f & (Jt | Te)) === 0)
- try {
- St(n) && (Ir(n), n.deps === null && n.first === null && n.nodes_start === null && (n.teardown === null ? qn(n) : n.fn = null));
- } catch (o) {
- Qt(o, n, null, n.ctx);
- }
- }
-}
-function er(e) {
- jt || (jt = !0, queueMicrotask(Cn));
- for (var t = Ht = e; t.parent !== null; ) {
- t = t.parent;
- var r = t.f;
- if ((r & (rt | Le)) !== 0) {
- if ((r & be) === 0) return;
- t.f ^= be;
- }
- }
- _t.push(t);
-}
-function Qo(e) {
- for (var t = [], r = e; r !== null; ) {
- var n = r.f, o = (n & (Le | rt)) !== 0, a = o && (n & be) !== 0;
- if (!a && (n & Te) === 0) {
- if ((n & an) !== 0)
- t.push(r);
- else if (o)
- r.f ^= be;
- else {
- var s = Y;
- try {
- Y = r, St(r) && Ir(r);
- } catch (c) {
- Qt(c, r, null, r.ctx);
- } finally {
- Y = s;
- }
- }
- var i = r.first;
- if (i !== null) {
- r = i;
- continue;
- }
- }
- var u = r.parent;
- for (r = r.next; r === null && u !== null; )
- r = u.next, u = u.parent;
- }
- return t;
-}
-function E(e) {
- var t;
- for (Fr(); _t.length > 0; )
- jt = !0, Cn(), Fr();
- return (
- /** @type {T} */
- t
- );
-}
-function h(e) {
- var t = e.f, r = (t & Se) !== 0;
- if (Y !== null && !ze) {
- Ie !== null && Ie.includes(e) && jo();
- var n = Y.deps;
- e.rv < Ft && (e.rv = Ft, ie === null && n !== null && n[he] === e ? he++ : ie === null ? ie = [e] : (!Be || !ie.includes(e)) && ie.push(e));
- } else if (r && /** @type {Derived} */
- e.deps === null && /** @type {Derived} */
- e.effects === null) {
- var o = (
- /** @type {Derived} */
- e
- ), a = o.parent;
- a !== null && (a.f & Ce) === 0 && (o.f ^= Ce);
- }
- return r && (o = /** @type {Derived} */
- e, St(o) && gn(o)), zt && xt.has(e) ? xt.get(e) : e.v;
-}
-function Ue(e) {
- var t = ze;
- try {
- return ze = !0, e();
- } finally {
- ze = t;
- }
-}
-const ea = -7169;
-function Pe(e, t) {
- e.f = e.f & ea | t;
-}
-function ta(e) {
- if (!(typeof e != "object" || !e || e instanceof EventTarget)) {
- if (Ke in e)
- hr(e);
- else if (!Array.isArray(e))
- for (let t in e) {
- const r = e[t];
- typeof r == "object" && r && Ke in r && hr(r);
- }
- }
-}
-function hr(e, t = /* @__PURE__ */ new Set()) {
- if (typeof e == "object" && e !== null && // We don't want to traverse DOM elements
- !(e instanceof EventTarget) && !t.has(e)) {
- t.add(e), e instanceof Date && e.getTime();
- for (let n in e)
- try {
- hr(e[n], t);
- } catch {
- }
- const r = zr(e);
- if (r !== Object.prototype && r !== Array.prototype && r !== Map.prototype && r !== Set.prototype && r !== Date.prototype) {
- const n = un(r);
- for (let o in n) {
- const a = n[o].get;
- if (a)
- try {
- a.call(e);
- } catch {
- }
- }
- }
- }
-}
-function zn(e) {
- B === null && Y === null && Ro(), Y !== null && (Y.f & Ce) !== 0 && B === null && No(), zt && Po();
-}
-function ra(e, t) {
- var r = t.last;
- r === null ? t.last = t.first = e : (r.next = e, e.prev = r, t.last = e);
-}
-function nt(e, t, r, n = !0) {
- var o = B, a = {
- ctx: U,
- deps: null,
- nodes_start: null,
- nodes_end: null,
- f: e | De,
- first: null,
- fn: t,
- last: null,
- next: null,
- parent: o,
- prev: null,
- teardown: null,
- transitions: null,
- wv: 0
- };
- if (r)
- try {
- Ir(a), a.f |= yo;
- } catch (u) {
- throw Ae(a), u;
- }
- else t !== null && er(a);
- var s = r && a.deps === null && a.first === null && a.nodes_start === null && a.teardown === null && (a.f & (sn | Tt)) === 0;
- if (!s && n && (o !== null && ra(a, o), Y !== null && (Y.f & Se) !== 0)) {
- var i = (
- /** @type {Derived} */
- Y
- );
- (i.effects ?? (i.effects = [])).push(a);
- }
- return a;
-}
-function tr(e) {
- const t = nt(Zt, null, !1);
- return Pe(t, be), t.teardown = e, t;
-}
-function et(e) {
- zn();
- var t = B !== null && (B.f & Le) !== 0 && U !== null && !U.m;
- if (t) {
- var r = (
- /** @type {ComponentContext} */
- U
- );
- (r.e ?? (r.e = [])).push({
- fn: e,
- effect: B,
- reaction: Y
- });
- } else {
- var n = Mr(e);
- return n;
- }
-}
-function na(e) {
- return zn(), vt(e);
-}
-function oa(e) {
- const t = nt(rt, e, !0);
- return () => {
- Ae(t);
- };
-}
-function aa(e) {
- const t = nt(rt, e, !0);
- return (r = {}) => new Promise((n) => {
- r.outro ? Bt(t, () => {
- Ae(t), n(void 0);
- }) : (Ae(t), n(void 0));
- });
-}
-function Mr(e) {
- return nt(an, e, !1);
-}
-function vt(e) {
- return nt(Zt, e, !0);
-}
-function j(e, t = [], r = ee) {
- const n = t.map(r);
- return Pr(() => e(...n.map(h)));
-}
-function Pr(e, t = 0) {
- return nt(Zt | kr | t, e, !0);
-}
-function $t(e, t = !0) {
- return nt(Zt | Le, e, !0, t);
-}
-function Sn(e) {
- var t = e.teardown;
- if (t !== null) {
- const r = zt, n = Y;
- Wr(!0), We(null);
- try {
- t.call(null);
- } finally {
- Wr(r), We(n);
- }
- }
-}
-function En(e, t = !1) {
- var r = e.first;
- for (e.first = e.last = null; r !== null; ) {
- var n = r.next;
- (r.f & rt) !== 0 ? r.parent = null : Ae(r, t), r = n;
- }
-}
-function sa(e) {
- for (var t = e.first; t !== null; ) {
- var r = t.next;
- (t.f & Le) === 0 && Ae(t), t = r;
- }
-}
-function Ae(e, t = !0) {
- var r = !1;
- if ((t || (e.f & xo) !== 0) && e.nodes_start !== null) {
- for (var n = e.nodes_start, o = e.nodes_end; n !== null; ) {
- var a = n === o ? null : (
- /** @type {TemplateNode} */
- /* @__PURE__ */ je(n)
- );
- n.remove(), n = a;
- }
- r = !0;
- }
- En(e, t && !r), Vt(e, 0), Pe(e, Jt);
- var s = e.transitions;
- if (s !== null)
- for (const u of s)
- u.stop();
- Sn(e);
- var i = e.parent;
- i !== null && i.first !== null && qn(e), e.next = e.prev = e.teardown = e.ctx = e.deps = e.fn = e.nodes_start = e.nodes_end = null;
-}
-function qn(e) {
- var t = e.parent, r = e.prev, n = e.next;
- r !== null && (r.next = n), n !== null && (n.prev = r), t !== null && (t.first === e && (t.first = n), t.last === e && (t.last = r));
-}
-function Bt(e, t) {
- var r = [];
- Nr(e, r, !0), In(r, () => {
- Ae(e), t && t();
- });
-}
-function In(e, t) {
- var r = e.length;
- if (r > 0) {
- var n = () => --r || t();
- for (var o of e)
- o.out(n);
- } else
- t();
-}
-function Nr(e, t, r) {
- if ((e.f & Te) === 0) {
- if (e.f ^= Te, e.transitions !== null)
- for (const s of e.transitions)
- (s.is_global || r) && t.push(s);
- for (var n = e.first; n !== null; ) {
- var o = n.next, a = (n.f & xr) !== 0 || (n.f & Le) !== 0;
- Nr(n, t, a ? r : !1), n = o;
- }
- }
-}
-function Yt(e) {
- Mn(e, !0);
-}
-function Mn(e, t) {
- if ((e.f & Te) !== 0) {
- e.f ^= Te, (e.f & be) === 0 && (e.f ^= be), St(e) && (Pe(e, De), er(e));
- for (var r = e.first; r !== null; ) {
- var n = r.next, o = (r.f & xr) !== 0 || (r.f & Le) !== 0;
- Mn(r, o ? t : !1), r = n;
- }
- if (e.transitions !== null)
- for (const a of e.transitions)
- (a.is_global || t) && a.in();
- }
-}
-function Pn(e) {
- throw new Error("https://svelte.dev/e/lifecycle_outside_component");
-}
-let U = null;
-function Xr(e) {
- U = e;
-}
-function we(e, t = !1, r) {
- var n = U = {
- p: U,
- c: null,
- d: !1,
- e: null,
- m: !1,
- s: e,
- x: null,
- l: null
- };
- ft && !t && (U.l = {
- s: null,
- u: null,
- r1: [],
- r2: ve(!1)
- }), tr(() => {
- n.d = !0;
- });
-}
-function _e(e) {
- const t = U;
- if (t !== null) {
- e !== void 0 && (t.x = e);
- const s = t.e;
- if (s !== null) {
- var r = B, n = Y;
- t.e = null;
- try {
- for (var o = 0; o < s.length; o++) {
- var a = s[o];
- Xe(a.effect), We(a.reaction), Mr(a.fn);
- }
- } finally {
- Xe(r), We(n);
- }
- }
- U = t.p, t.m = !0;
- }
- return e || /** @type {T} */
- {};
-}
-function Et() {
- return !ft || U !== null && U.l === null;
-}
-const ia = ["touchstart", "touchmove"];
-function la(e) {
- return ia.includes(e);
-}
-function ua(e) {
- A && /* @__PURE__ */ Ee(e) !== null && qr(e);
-}
-let Ur = !1;
-function Nn() {
- Ur || (Ur = !0, document.addEventListener(
- "reset",
- (e) => {
- Promise.resolve().then(() => {
- var t;
- if (!e.defaultPrevented)
- for (
- const r of
- /**@type {HTMLFormElement} */
- e.target.elements
- )
- (t = r.__on_r) == null || t.call(r);
- });
- },
- // In the capture phase to guarantee we get noticed of it (no possiblity of stopPropagation)
- { capture: !0 }
- ));
-}
-function Rn(e) {
- var t = Y, r = B;
- We(null), Xe(null);
- try {
- return e();
- } finally {
- We(t), Xe(r);
- }
-}
-function ca(e, t, r, n = r) {
- e.addEventListener(t, () => Rn(r));
- const o = e.__on_r;
- o ? e.__on_r = () => {
- o(), n(!0);
- } : e.__on_r = () => n(!0), Nn();
-}
-const Tn = /* @__PURE__ */ new Set(), mr = /* @__PURE__ */ new Set();
-function da(e, t, r, n = {}) {
- function o(a) {
- if (n.capture || bt.call(t, a), !a.cancelBubble)
- return Rn(() => r == null ? void 0 : r.call(this, a));
- }
- return e.startsWith("pointer") || e.startsWith("touch") || e === "wheel" ? Kt(() => {
- t.addEventListener(e, o, n);
- }) : t.addEventListener(e, o, n), o;
-}
-function Ze(e, t, r, n, o) {
- var a = { capture: n, passive: o }, s = da(e, t, r, a);
- (t === document.body || t === window || t === document) && tr(() => {
- t.removeEventListener(e, s, a);
- });
-}
-function On(e) {
- for (var t = 0; t < e.length; t++)
- Tn.add(e[t]);
- for (var r of mr)
- r(e);
-}
-function bt(e) {
- var _;
- var t = this, r = (
- /** @type {Node} */
- t.ownerDocument
- ), n = e.type, o = ((_ = e.composedPath) == null ? void 0 : _.call(e)) || [], a = (
- /** @type {null | Element} */
- o[0] || e.target
- ), s = 0, i = e.__root;
- if (i) {
- var u = o.indexOf(i);
- if (u !== -1 && (t === document || t === /** @type {any} */
- window)) {
- e.__root = t;
- return;
- }
- var c = o.indexOf(t);
- if (c === -1)
- return;
- u <= c && (s = u);
- }
- if (a = /** @type {Element} */
- o[s] || e.target, a !== t) {
- Lt(e, "currentTarget", {
- configurable: !0,
- get() {
- return a || r;
- }
- });
- var v = Y, m = B;
- We(null), Xe(null);
- try {
- for (var l, f = []; a !== null; ) {
- var d = a.assignedSlot || a.parentNode || /** @type {any} */
- a.host || null;
- try {
- var p = a["__" + n];
- if (p != null && (!/** @type {any} */
- a.disabled || // DOM could've been updated already by the time this is reached, so we check this as well
- // -> the target could not have been disabled because it emits the event in the first place
- e.target === a))
- if ($r(p)) {
- var [w, ...I] = p;
- w.apply(a, [e, ...I]);
- } else
- p.call(a, e);
- } catch (k) {
- l ? f.push(k) : l = k;
- }
- if (e.cancelBubble || d === t || d === null)
- break;
- a = d;
- }
- if (l) {
- for (let k of f)
- queueMicrotask(() => {
- throw k;
- });
- throw l;
- }
- } finally {
- e.__root = t, delete e.currentTarget, We(v), Xe(m);
- }
- }
-}
-function An(e) {
- var t = document.createElement("template");
- return t.innerHTML = e, t.content;
-}
-function Me(e, t) {
- var r = (
- /** @type {Effect} */
- B
- );
- r.nodes_start === null && (r.nodes_start = e, r.nodes_end = t);
-}
-// @__NO_SIDE_EFFECTS__
-function W(e, t) {
- var r = (t & bo) !== 0, n = (t & wo) !== 0, o, a = !e.startsWith("");
- return () => {
- if (A)
- return Me(H, null), H;
- o === void 0 && (o = An(a ? e : "" + e), r || (o = /** @type {Node} */
- /* @__PURE__ */ Ee(o)));
- var s = (
- /** @type {TemplateNode} */
- n || bn ? document.importNode(o, !0) : o.cloneNode(!0)
- );
- if (r) {
- var i = (
- /** @type {TemplateNode} */
- /* @__PURE__ */ Ee(s)
- ), u = (
- /** @type {TemplateNode} */
- s.lastChild
- );
- Me(i, u);
- } else
- Me(s, s);
- return s;
- };
-}
-// @__NO_SIDE_EFFECTS__
-function qt(e, t, r = "svg") {
- var n = !e.startsWith(""), o = `<${r}>${n ? e : "" + e}${r}>`, a;
- return () => {
- if (A)
- return Me(H, null), H;
- if (!a) {
- var s = (
- /** @type {DocumentFragment} */
- An(o)
- ), i = (
- /** @type {Element} */
- /* @__PURE__ */ Ee(s)
- );
- a = /** @type {Element} */
- /* @__PURE__ */ Ee(i);
- }
- var u = (
- /** @type {TemplateNode} */
- a.cloneNode(!0)
- );
- return Me(u, u), u;
- };
-}
-function le(e = "") {
- if (!A) {
- var t = Ye(e + "");
- return Me(t, t), t;
- }
- var r = H;
- return r.nodeType !== 3 && (r.before(r = Ye()), pe(r)), Me(r, r), r;
-}
-function Rr() {
- if (A)
- return Me(H, null), H;
- var e = document.createDocumentFragment(), t = document.createComment(""), r = Ye();
- return e.append(t, r), Me(t, r), e;
-}
-function $(e, t) {
- if (A) {
- B.nodes_end = H, Ct();
- return;
- }
- e !== null && e.before(
- /** @type {Node} */
- t
- );
-}
-function F(e, t) {
- var r = t == null ? "" : typeof t == "object" ? t + "" : t;
- r !== (e.__t ?? (e.__t = e.nodeValue)) && (e.__t = r, e.nodeValue = r + "");
-}
-function Ln(e, t) {
- return Dn(e, t);
-}
-function fa(e, t) {
- vr(), t.intro = t.intro ?? !1;
- const r = t.target, n = A, o = H;
- try {
- for (var a = (
- /** @type {TemplateNode} */
- /* @__PURE__ */ Ee(r)
- ); a && (a.nodeType !== 8 || /** @type {Comment} */
- a.data !== wr); )
- a = /** @type {TemplateNode} */
- /* @__PURE__ */ je(a);
- if (!a)
- throw wt;
- Re(!0), pe(
- /** @type {Comment} */
- a
- ), Ct();
- const s = Dn(e, { ...t, anchor: a });
- if (H === null || H.nodeType !== 8 || /** @type {Comment} */
- H.data !== yr)
- throw Er(), wt;
- return Re(!1), /** @type {Exports} */
- s;
- } catch (s) {
- if (s === wt)
- return t.recover === !1 && Oo(), vr(), qr(r), Re(!1), Ln(e, t);
- throw s;
- } finally {
- Re(n), pe(o);
- }
-}
-const at = /* @__PURE__ */ new Map();
-function Dn(e, { target: t, anchor: r, props: n = {}, events: o, context: a, intro: s = !0 }) {
- vr();
- var i = /* @__PURE__ */ new Set(), u = (m) => {
- for (var l = 0; l < m.length; l++) {
- var f = m[l];
- if (!i.has(f)) {
- i.add(f);
- var d = la(f);
- t.addEventListener(f, bt, { passive: d });
- var p = at.get(f);
- p === void 0 ? (document.addEventListener(f, bt, { passive: d }), at.set(f, 1)) : at.set(f, p + 1);
- }
- }
- };
- u(Cr(Tn)), mr.add(u);
- var c = void 0, v = aa(() => {
- var m = r ?? t.appendChild(Ye());
- return $t(() => {
- if (a) {
- we({});
- var l = (
- /** @type {ComponentContext} */
- U
- );
- l.c = a;
- }
- o && (n.$$events = o), A && Me(
- /** @type {TemplateNode} */
- m,
- null
- ), c = e(m, n) || {}, A && (B.nodes_end = H), a && _e();
- }), () => {
- var d;
- for (var l of i) {
- t.removeEventListener(l, bt);
- var f = (
- /** @type {number} */
- at.get(l)
- );
- --f === 0 ? (document.removeEventListener(l, bt), at.delete(l)) : at.set(l, f);
- }
- mr.delete(u), m !== r && ((d = m.parentNode) == null || d.removeChild(m));
- };
- });
- return gr.set(c, v), c;
-}
-let gr = /* @__PURE__ */ new WeakMap();
-function va(e, t) {
- const r = gr.get(e);
- return r ? (gr.delete(e), r(t)) : Promise.resolve();
-}
-function $e(e, t, [r, n] = [0, 0]) {
- A && r === 0 && Ct();
- var o = e, a = null, s = null, i = fe, u = r > 0 ? xr : 0, c = !1;
- const v = (l, f = !0) => {
- c = !0, m(f, l);
- }, m = (l, f) => {
- if (i === (i = l)) return;
- let d = !1;
- if (A && n !== -1) {
- if (r === 0) {
- const w = (
- /** @type {Comment} */
- o.data
- );
- w === wr ? n = 0 : w === _r ? n = 1 / 0 : (n = parseInt(w.substring(1)), n !== n && (n = i ? 1 / 0 : -1));
- }
- const p = n > r;
- !!i === p && (o = fr(), pe(o), Re(!1), d = !0, n = -1);
- }
- i ? (a ? Yt(a) : f && (a = $t(() => f(o))), s && Bt(s, () => {
- s = null;
- })) : (s ? Yt(s) : f && (s = $t(() => f(o, [r + 1, n]))), a && Bt(a, () => {
- a = null;
- })), d && Re(!0);
- };
- Pr(() => {
- c = !1, t(v), c || m(null, null);
- }, u), A && (o = H);
-}
-function Zr(e, t) {
- A && pe(
- /** @type {TemplateNode} */
- /* @__PURE__ */ Ee(e)
- ), vt(() => {
- var r = t();
- for (var n in r) {
- var o = r[n];
- o ? e.style.setProperty(n, o) : e.style.removeProperty(n);
- }
- }), tr(() => {
- e.remove();
- });
-}
-function ha(e, t, r, n) {
- for (var o = [], a = t.length, s = 0; s < a; s++)
- Nr(t[s].e, o, !0);
- var i = a > 0 && o.length === 0 && r !== null;
- if (i) {
- var u = (
- /** @type {Element} */
- /** @type {Element} */
- r.parentNode
- );
- qr(u), u.append(
- /** @type {Element} */
- r
- ), n.clear(), Ve(e, t[0].prev, t[a - 1].next);
- }
- In(o, () => {
- for (var c = 0; c < a; c++) {
- var v = t[c];
- i || (n.delete(v.k), Ve(e, v.prev, v.next)), Ae(v.e, !i);
- }
- });
-}
-function Tr(e, t, r, n, o, a = null) {
- var s = e, i = { flags: t, items: /* @__PURE__ */ new Map(), first: null }, u = (t & on) !== 0;
- if (u) {
- var c = (
- /** @type {Element} */
- e
- );
- s = A ? pe(
- /** @type {Comment | Text} */
- /* @__PURE__ */ Ee(c)
- ) : c.appendChild(Ye());
- }
- A && Ct();
- var v = null, m = !1, l = /* @__PURE__ */ me(() => {
- var f = r();
- return $r(f) ? f : f == null ? [] : Cr(f);
- });
- Pr(() => {
- var f = h(l), d = f.length;
- if (m && d === 0)
- return;
- m = d === 0;
- let p = !1;
- if (A) {
- var w = (
- /** @type {Comment} */
- s.data === _r
- );
- w !== (d === 0) && (s = fr(), pe(s), Re(!1), p = !0);
- }
- if (A) {
- for (var I = null, _, k = 0; k < d; k++) {
- if (H.nodeType === 8 && /** @type {Comment} */
- H.data === yr) {
- s = /** @type {Comment} */
- H, p = !0, Re(!1);
- break;
- }
- var y = f[k], g = n(y, k);
- _ = jn(
- H,
- i,
- I,
- null,
- y,
- g,
- k,
- o,
- t,
- r
- ), i.items.set(g, _), I = _;
- }
- d > 0 && pe(fr());
- }
- A || ma(f, i, s, o, t, n, r), a !== null && (d === 0 ? v ? Yt(v) : v = $t(() => a(s)) : v !== null && Bt(v, () => {
- v = null;
- })), p && Re(!0), h(l);
- }), A && (s = H);
-}
-function ma(e, t, r, n, o, a, s) {
- var re, z, S, D;
- var i = (o & co) !== 0, u = (o & (pr | br)) !== 0, c = e.length, v = t.items, m = t.first, l = m, f, d = null, p, w = [], I = [], _, k, y, g;
- if (i)
- for (g = 0; g < c; g += 1)
- _ = e[g], k = a(_, g), y = v.get(k), y !== void 0 && ((re = y.a) == null || re.measure(), (p ?? (p = /* @__PURE__ */ new Set())).add(y));
- for (g = 0; g < c; g += 1) {
- if (_ = e[g], k = a(_, g), y = v.get(k), y === void 0) {
- var R = l ? (
- /** @type {TemplateNode} */
- l.e.nodes_start
- ) : r;
- d = jn(
- R,
- t,
- d,
- d === null ? t.first : d.next,
- _,
- k,
- g,
- n,
- o,
- s
- ), v.set(k, d), w = [], I = [], l = d.next;
- continue;
- }
- if (u && ga(y, _, g, o), (y.e.f & Te) !== 0 && (Yt(y.e), i && ((z = y.a) == null || z.unfix(), (p ?? (p = /* @__PURE__ */ new Set())).delete(y))), y !== l) {
- if (f !== void 0 && f.has(y)) {
- if (w.length < I.length) {
- var M = I[0], b;
- d = M.prev;
- var P = w[0], N = w[w.length - 1];
- for (b = 0; b < w.length; b += 1)
- Jr(w[b], M, r);
- for (b = 0; b < I.length; b += 1)
- f.delete(I[b]);
- Ve(t, P.prev, N.next), Ve(t, d, P), Ve(t, N, M), l = M, d = N, g -= 1, w = [], I = [];
- } else
- f.delete(y), Jr(y, l, r), Ve(t, y.prev, y.next), Ve(t, y, d === null ? t.first : d.next), Ve(t, d, y), d = y;
- continue;
- }
- for (w = [], I = []; l !== null && l.k !== k; )
- (l.e.f & Te) === 0 && (f ?? (f = /* @__PURE__ */ new Set())).add(l), I.push(l), l = l.next;
- if (l === null)
- continue;
- y = l;
- }
- w.push(y), d = y, l = y.next;
- }
- if (l !== null || f !== void 0) {
- for (var L = f === void 0 ? [] : Cr(f); l !== null; )
- (l.e.f & Te) === 0 && L.push(l), l = l.next;
- var K = L.length;
- if (K > 0) {
- var Q = (o & on) !== 0 && c === 0 ? r : null;
- if (i) {
- for (g = 0; g < K; g += 1)
- (S = L[g].a) == null || S.measure();
- for (g = 0; g < K; g += 1)
- (D = L[g].a) == null || D.fix();
- }
- ha(t, L, Q, v);
- }
- }
- i && Kt(() => {
- var X;
- if (p !== void 0)
- for (y of p)
- (X = y.a) == null || X.apply();
- }), B.first = t.first && t.first.e, B.last = d && d.e;
-}
-function ga(e, t, r, n) {
- (n & pr) !== 0 && dr(e.v, t), (n & br) !== 0 ? dr(
- /** @type {Value} */
- e.i,
- r
- ) : e.i = r;
-}
-function jn(e, t, r, n, o, a, s, i, u, c) {
- var v = (u & pr) !== 0, m = (u & fo) === 0, l = v ? m ? /* @__PURE__ */ Gt(o) : ve(o) : o, f = (u & br) === 0 ? s : ve(s), d = {
- i: f,
- v: l,
- k: a,
- a: null,
- // @ts-expect-error
- e: null,
- prev: r,
- next: n
- };
- try {
- return d.e = $t(() => i(e, l, f, c), A), d.e.prev = r && r.e, d.e.next = n && n.e, r === null ? t.first = d : (r.next = d, r.e.next = d.e), n !== null && (n.prev = d, n.e.prev = d.e), d;
- } finally {
- }
-}
-function Jr(e, t, r) {
- for (var n = e.next ? (
- /** @type {TemplateNode} */
- e.next.e.nodes_start
- ) : r, o = t ? (
- /** @type {TemplateNode} */
- t.e.nodes_start
- ) : r, a = (
- /** @type {TemplateNode} */
- e.e.nodes_start
- ); a !== n; ) {
- var s = (
- /** @type {TemplateNode} */
- /* @__PURE__ */ je(a)
- );
- o.before(a), a = s;
- }
-}
-function Ve(e, t, r) {
- t === null ? e.first = r : (t.next = r, t.e.next = r && r.e), r !== null && (r.prev = t, r.e.prev = t && t.e);
-}
-function Kr(e, t, r, n, o) {
- var i;
- A && Ct();
- var a = (i = t.$$slots) == null ? void 0 : i[r], s = !1;
- a === !0 && (a = t[r === "default" ? "children" : r], s = !0), a === void 0 || a(e, s ? () => n : n);
-}
-function pa(e) {
- const t = {};
- e.children && (t.default = !0);
- for (const r in e.$$slots)
- t[r] = !0;
- return t;
-}
-function se(e, t) {
- Kt(() => {
- var r = e.getRootNode(), n = (
- /** @type {ShadowRoot} */
- r.host ? (
- /** @type {ShadowRoot} */
- r
- ) : (
- /** @type {Document} */
- r.head ?? /** @type {Document} */
- r.ownerDocument.head
- )
- );
- if (!n.querySelector("#" + t.hash)) {
- const o = document.createElement("style");
- o.id = t.hash, o.textContent = t.code, n.appendChild(o);
- }
- });
-}
-const Gr = [...`
-\r\f \v\uFEFF`];
-function ba(e, t, r) {
- var n = e == null ? "" : "" + e;
- if (t && (n = n ? n + " " + t : t), r) {
- for (var o in r)
- if (r[o])
- n = n ? n + " " + o : o;
- else if (n.length)
- for (var a = o.length, s = 0; (s = n.indexOf(o, s)) >= 0; ) {
- var i = s + a;
- (s === 0 || Gr.includes(n[s - 1])) && (i === n.length || Gr.includes(n[i])) ? n = (s === 0 ? "" : n.substring(0, s)) + n.substring(i + 1) : s = i;
- }
- }
- return n === "" ? null : n;
-}
-function wa(e, t) {
- return e == null ? null : String(e);
-}
-function ot(e, t, r, n, o, a) {
- var s = e.__className;
- if (A || s !== r) {
- var i = ba(r, n, a);
- (!A || i !== e.getAttribute("class")) && (i == null ? e.removeAttribute("class") : e.className = i), e.__className = r;
- } else if (a && o !== a)
- for (var u in a) {
- var c = !!a[u];
- (o == null || c !== !!o[u]) && e.classList.toggle(u, c);
- }
- return a;
-}
-function _a(e, t, r, n) {
- var o = e.__style;
- if (A || o !== t) {
- var a = wa(t);
- (!A || a !== e.getAttribute("style")) && (a == null ? e.removeAttribute("style") : e.style.cssText = a), e.__style = t;
- }
- return n;
-}
-const ya = Symbol("is custom element"), ka = Symbol("is html");
-function xa(e) {
- if (A) {
- var t = !1, r = () => {
- if (!t) {
- if (t = !0, e.hasAttribute("value")) {
- var n = e.value;
- G(e, "value", null), e.value = n;
- }
- if (e.hasAttribute("checked")) {
- var o = e.checked;
- G(e, "checked", null), e.checked = o;
- }
- }
- };
- e.__on_r = r, Io(r), Nn();
- }
-}
-function G(e, t, r, n) {
- var o = $a(e);
- A && (o[t] = e.getAttribute(t), t === "src" || t === "srcset" || t === "href" && e.nodeName === "LINK") || o[t] !== (o[t] = r) && (t === "loading" && (e[$o] = r), r == null ? e.removeAttribute(t) : typeof r != "string" && Ca(e).includes(t) ? e[t] = r : e.setAttribute(t, r));
-}
-function $a(e) {
- return (
- /** @type {Record} **/
- // @ts-expect-error
- e.__attributes ?? (e.__attributes = {
- [ya]: e.nodeName.includes("-"),
- [ka]: e.namespaceURI === _o
- })
- );
-}
-var Qr = /* @__PURE__ */ new Map();
-function Ca(e) {
- var t = Qr.get(e.nodeName);
- if (t) return t;
- Qr.set(e.nodeName, t = []);
- for (var r, n = e, o = Element.prototype; o !== n; ) {
- r = un(n);
- for (var a in r)
- r[a].set && t.push(a);
- n = zr(n);
- }
- return t;
-}
-function Hn(e, t, r = t) {
- var n = Et();
- ca(e, "input", (o) => {
- var a = o ? e.defaultValue : e.value;
- if (a = ir(e) ? lr(a) : a, r(a), n && a !== (a = t())) {
- var s = e.selectionStart, i = e.selectionEnd;
- e.value = a ?? "", i !== null && (e.selectionStart = s, e.selectionEnd = Math.min(i, e.value.length));
- }
- }), // If we are hydrating and the value has since changed,
- // then use the updated value from the input instead.
- (A && e.defaultValue !== e.value || // If defaultValue is set, then value == defaultValue
- // TODO Svelte 6: remove input.value check and set to empty string?
- Ue(t) == null && e.value) && r(ir(e) ? lr(e.value) : e.value), vt(() => {
- var o = t();
- ir(e) && o === lr(e.value) || e.type === "date" && !o && !e.value || o !== e.value && (e.value = o ?? "");
- });
-}
-function ir(e) {
- var t = e.type;
- return t === "number" || t === "range";
-}
-function lr(e) {
- return e === "" ? null : +e;
-}
-function en(e, t) {
- return e === t || (e == null ? void 0 : e[Ke]) === t;
-}
-function tt(e = {}, t, r, n) {
- return Mr(() => {
- var o, a;
- return vt(() => {
- o = a, a = [], Ue(() => {
- e !== r(...a) && (t(e, ...a), o && en(r(...o), e) && t(null, ...o));
- });
- }), () => {
- Kt(() => {
- a && en(r(...a), e) && t(null, ...a);
- });
- };
- }), e;
-}
-function za(e, t, r, n, o) {
- var a = () => {
- n(r[e]);
- };
- r.addEventListener(t, a), o ? vt(() => {
- r[e] = o();
- }) : a(), (r === document.body || r === window || r === document) && tr(() => {
- r.removeEventListener(t, a);
- });
-}
-function Fn(e = !1) {
- const t = (
- /** @type {ComponentContextLegacy} */
- U
- ), r = t.l.u;
- if (!r) return;
- let n = () => ta(t.s);
- if (e) {
- let o = 0, a = (
- /** @type {Record} */
- {}
- );
- const s = /* @__PURE__ */ ee(() => {
- let i = !1;
- const u = t.s;
- for (const c in u)
- u[c] !== a[c] && (a[c] = u[c], i = !0);
- return i && o++, o;
- });
- n = () => h(s);
- }
- r.b.length && na(() => {
- tn(t, n), Dt(r.b);
- }), et(() => {
- const o = Ue(() => r.m.map(Eo));
- return () => {
- for (const a of o)
- typeof a == "function" && a();
- };
- }), r.a.length && et(() => {
- tn(t, n), Dt(r.a);
- });
-}
-function tn(e, t) {
- if (e.l.s)
- for (const r of e.l.s) h(r);
- t();
-}
-function Sa(e) {
- var t = ve(0);
- return function() {
- return arguments.length === 1 ? (T(t, h(t) + 1), arguments[0]) : (h(t), e());
- };
-}
-function rr(e) {
- U === null && Pn(), ft && U.l !== null ? qa(U).m.push(e) : et(() => {
- const t = Ue(e);
- if (typeof t == "function") return (
- /** @type {() => void} */
- t
- );
- });
-}
-function Ea(e) {
- U === null && Pn(), rr(() => () => Ue(e));
-}
-function qa(e) {
- var t = (
- /** @type {ComponentContextLegacy} */
- e.l
- );
- return t.u ?? (t.u = { a: [], b: [], m: [] });
-}
-let Pt = !1;
-function Ia(e) {
- var t = Pt;
- try {
- return Pt = !1, [e(), Pt];
- } finally {
- Pt = t;
- }
-}
-function rn(e) {
- var t;
- return ((t = e.ctx) == null ? void 0 : t.d) ?? !1;
-}
-function q(e, t, r, n) {
- var R;
- var o = (r & vo) !== 0, a = !ft || (r & ho) !== 0, s = (r & go) !== 0, i = (r & po) !== 0, u = !1, c;
- s ? [c, u] = Ia(() => (
- /** @type {V} */
- e[t]
- )) : c = /** @type {V} */
- e[t];
- var v = Ke in e || ln in e, m = s && (((R = Ge(e, t)) == null ? void 0 : R.set) ?? (v && t in e && ((M) => e[t] = M))) || void 0, l = (
- /** @type {V} */
- n
- ), f = !0, d = !1, p = () => (d = !0, f && (f = !1, i ? l = Ue(
- /** @type {() => V} */
- n
- ) : l = /** @type {V} */
- n), l);
- c === void 0 && n !== void 0 && (m && a && Ao(), c = p(), m && m(c));
- var w;
- if (a)
- w = () => {
- var M = (
- /** @type {V} */
- e[t]
- );
- return M === void 0 ? p() : (f = !0, d = !1, M);
- };
- else {
- var I = (o ? ee : me)(
- () => (
- /** @type {V} */
- e[t]
- )
- );
- I.f |= ko, w = () => {
- var M = h(I);
- return M !== void 0 && (l = /** @type {V} */
- void 0), M === void 0 ? l : M;
- };
- }
- if ((r & mo) === 0)
- return w;
- if (m) {
- var _ = e.$$legacy;
- return function(M, b) {
- return arguments.length > 0 ? ((!a || !b || _ || u) && m(b ? w() : M), M) : w();
- };
- }
- var k = !1, y = /* @__PURE__ */ Gt(c), g = /* @__PURE__ */ ee(() => {
- var M = w(), b = h(y);
- return k ? (k = !1, b) : y.v = M;
- });
- return s && h(g), o || (g.equals = Sr), function(M, b) {
- if (arguments.length > 0) {
- const P = b ? h(g) : a && s ? J(M) : M;
- if (!g.equals(P)) {
- if (k = !0, T(y, P), d && l !== void 0 && (l = P), rn(g))
- return M;
- Ue(() => h(g));
- }
- return M;
- }
- return rn(g) ? g.v : h(g);
- };
-}
-function Ma(e) {
- return new Pa(e);
-}
-var Ne, xe;
-class Pa {
- /**
- * @param {ComponentConstructorOptions & {
- * component: any;
- * }} options
- */
- constructor(t) {
- /** @type {any} */
- ar(this, Ne);
- /** @type {Record} */
- ar(this, xe);
- var a;
- var r = /* @__PURE__ */ new Map(), n = (s, i) => {
- var u = /* @__PURE__ */ Gt(i);
- return r.set(s, u), u;
- };
- const o = new Proxy(
- { ...t.props || {}, $$events: {} },
- {
- get(s, i) {
- return h(r.get(i) ?? n(i, Reflect.get(s, i)));
- },
- has(s, i) {
- return i === ln ? !0 : (h(r.get(i) ?? n(i, Reflect.get(s, i))), Reflect.has(s, i));
- },
- set(s, i, u) {
- return T(r.get(i) ?? n(i, u), u), Reflect.set(s, i, u);
- }
- }
- );
- sr(this, xe, (t.hydrate ? fa : Ln)(t.component, {
- target: t.target,
- anchor: t.anchor,
- props: o,
- context: t.context,
- intro: t.intro ?? !1,
- recover: t.recover
- })), (!((a = t == null ? void 0 : t.props) != null && a.$$host) || t.sync === !1) && E(), sr(this, Ne, o.$$events);
- for (const s of Object.keys(de(this, xe)))
- s === "$set" || s === "$destroy" || s === "$on" || Lt(this, s, {
- get() {
- return de(this, xe)[s];
- },
- /** @param {any} value */
- set(i) {
- de(this, xe)[s] = i;
- },
- enumerable: !0
- });
- de(this, xe).$set = /** @param {Record} next */
- (s) => {
- Object.assign(o, s);
- }, de(this, xe).$destroy = () => {
- va(de(this, xe));
- };
- }
- /** @param {Record} props */
- $set(t) {
- de(this, xe).$set(t);
- }
- /**
- * @param {string} event
- * @param {(...args: any[]) => any} callback
- * @returns {any}
- */
- $on(t, r) {
- de(this, Ne)[t] = de(this, Ne)[t] || [];
- const n = (...o) => r.call(this, ...o);
- return de(this, Ne)[t].push(n), () => {
- de(this, Ne)[t] = de(this, Ne)[t].filter(
- /** @param {any} fn */
- (o) => o !== n
- );
- };
- }
- $destroy() {
- de(this, xe).$destroy();
- }
-}
-Ne = new WeakMap(), xe = new WeakMap();
-let Vn;
-typeof HTMLElement == "function" && (Vn = class extends HTMLElement {
- /**
- * @param {*} $$componentCtor
- * @param {*} $$slots
- * @param {*} use_shadow_dom
- */
- constructor(t, r, n) {
- super();
- /** The Svelte component constructor */
- oe(this, "$$ctor");
- /** Slots */
- oe(this, "$$s");
- /** @type {any} The Svelte component instance */
- oe(this, "$$c");
- /** Whether or not the custom element is connected */
- oe(this, "$$cn", !1);
- /** @type {Record} Component props data */
- oe(this, "$$d", {});
- /** `true` if currently in the process of reflecting component props back to attributes */
- oe(this, "$$r", !1);
- /** @type {Record} Props definition (name, reflected, type etc) */
- oe(this, "$$p_d", {});
- /** @type {Record} Event listeners */
- oe(this, "$$l", {});
- /** @type {Map} Event listener unsubscribe functions */
- oe(this, "$$l_u", /* @__PURE__ */ new Map());
- /** @type {any} The managed render effect for reflecting attributes */
- oe(this, "$$me");
- this.$$ctor = t, this.$$s = r, n && this.attachShadow({ mode: "open" });
- }
- /**
- * @param {string} type
- * @param {EventListenerOrEventListenerObject} listener
- * @param {boolean | AddEventListenerOptions} [options]
- */
- addEventListener(t, r, n) {
- if (this.$$l[t] = this.$$l[t] || [], this.$$l[t].push(r), this.$$c) {
- const o = this.$$c.$on(t, r);
- this.$$l_u.set(r, o);
- }
- super.addEventListener(t, r, n);
- }
- /**
- * @param {string} type
- * @param {EventListenerOrEventListenerObject} listener
- * @param {boolean | AddEventListenerOptions} [options]
- */
- removeEventListener(t, r, n) {
- if (super.removeEventListener(t, r, n), this.$$c) {
- const o = this.$$l_u.get(r);
- o && (o(), this.$$l_u.delete(r));
- }
- }
- async connectedCallback() {
- if (this.$$cn = !0, !this.$$c) {
- let t = function(o) {
- return (a) => {
- const s = document.createElement("slot");
- o !== "default" && (s.name = o), $(a, s);
- };
- };
- if (await Promise.resolve(), !this.$$cn || this.$$c)
- return;
- const r = {}, n = Na(this);
- for (const o of this.$$s)
- o in n && (o === "default" && !this.$$d.children ? (this.$$d.children = t(o), r.default = !0) : r[o] = t(o));
- for (const o of this.attributes) {
- const a = this.$$g_p(o.name);
- a in this.$$d || (this.$$d[a] = Rt(a, o.value, this.$$p_d, "toProp"));
- }
- for (const o in this.$$p_d)
- !(o in this.$$d) && this[o] !== void 0 && (this.$$d[o] = this[o], delete this[o]);
- this.$$c = Ma({
- component: this.$$ctor,
- target: this.shadowRoot || this,
- props: {
- ...this.$$d,
- $$slots: r,
- $$host: this
- }
- }), this.$$me = oa(() => {
- vt(() => {
- var o;
- this.$$r = !0;
- for (const a of At(this.$$c)) {
- if (!((o = this.$$p_d[a]) != null && o.reflect)) continue;
- this.$$d[a] = this.$$c[a];
- const s = Rt(
- a,
- this.$$d[a],
- this.$$p_d,
- "toAttribute"
- );
- s == null ? this.removeAttribute(this.$$p_d[a].attribute || a) : this.setAttribute(this.$$p_d[a].attribute || a, s);
- }
- this.$$r = !1;
- });
- });
- for (const o in this.$$l)
- for (const a of this.$$l[o]) {
- const s = this.$$c.$on(o, a);
- this.$$l_u.set(a, s);
- }
- this.$$l = {};
- }
- }
- // We don't need this when working within Svelte code, but for compatibility of people using this outside of Svelte
- // and setting attributes through setAttribute etc, this is helpful
- /**
- * @param {string} attr
- * @param {string} _oldValue
- * @param {string} newValue
- */
- attributeChangedCallback(t, r, n) {
- var o;
- this.$$r || (t = this.$$g_p(t), this.$$d[t] = Rt(t, n, this.$$p_d, "toProp"), (o = this.$$c) == null || o.$set({ [t]: this.$$d[t] }));
- }
- disconnectedCallback() {
- this.$$cn = !1, Promise.resolve().then(() => {
- !this.$$cn && this.$$c && (this.$$c.$destroy(), this.$$me(), this.$$c = void 0);
- });
- }
- /**
- * @param {string} attribute_name
- */
- $$g_p(t) {
- return At(this.$$p_d).find(
- (r) => this.$$p_d[r].attribute === t || !this.$$p_d[r].attribute && r.toLowerCase() === t
- ) || t;
- }
-});
-function Rt(e, t, r, n) {
- var a;
- const o = (a = r[e]) == null ? void 0 : a.type;
- if (t = o === "Boolean" && typeof t != "boolean" ? t != null : t, !n || !r[e])
- return t;
- if (n === "toAttribute")
- switch (o) {
- case "Object":
- case "Array":
- return t == null ? null : JSON.stringify(t);
- case "Boolean":
- return t ? "" : null;
- case "Number":
- return t ?? null;
- default:
- return t;
- }
- else
- switch (o) {
- case "Object":
- case "Array":
- return t && JSON.parse(t);
- case "Boolean":
- return t;
- // conversion already handled above
- case "Number":
- return t != null ? +t : t;
- default:
- return t;
- }
-}
-function Na(e) {
- const t = {};
- return e.childNodes.forEach((r) => {
- t[
- /** @type {Element} node */
- r.slot || "default"
- ] = !0;
- }), t;
-}
-function ae(e, t, r, n, o, a) {
- let s = class extends Vn {
- constructor() {
- super(e, r, o), this.$$p_d = t;
- }
- static get observedAttributes() {
- return At(t).map(
- (i) => (t[i].attribute || i).toLowerCase()
- );
- }
- };
- return At(t).forEach((i) => {
- Lt(s.prototype, i, {
- get() {
- return this.$$c && i in this.$$c ? this.$$c[i] : this.$$d[i];
- },
- set(u) {
- var m;
- u = Rt(i, u, t), this.$$d[i] = u;
- var c = this.$$c;
- if (c) {
- var v = (m = Ge(c, i)) == null ? void 0 : m.get;
- v ? c[i] = u : c.$set({ [i]: u });
- }
- }
- });
- }), n.forEach((i) => {
- Lt(s.prototype, i, {
- get() {
- var u;
- return (u = this.$$c) == null ? void 0 : u[i];
- }
- });
- }), e.element = /** @type {any} */
- s, s;
-}
-const Z = J({
- open: !1,
- currentCommentId: 0,
- showResolvedOnly: !1,
- pulseMarkerId: 0
-}), Je = J({ open: !1 });
-let st = ue("");
-const lt = {
- get value() {
- return h(st);
- },
- set(e) {
- T(st, J(e)), typeof window < "u" && sessionStorage.setItem("loop-guest-name", e);
- },
- get() {
- return !h(st) && typeof window < "u" && T(st, J(sessionStorage.getItem("loop-guest-name") || "")), h(st);
- },
- clear() {
- T(st, ""), typeof window < "u" && sessionStorage.removeItem("loop-guest-name");
- }
-};
-let Or = ue(J({}));
-const O = (e, t) => h(Or)[e] || t || e, ur = (e, t, r) => {
- let n = h(Or)[e] || t || e;
- for (const [o, a] of Object.entries(r))
- n = n.replace(`{${o}}`, a);
- return n;
-}, Ra = (e) => {
- T(Or, J(e));
-};
-Vo();
-var Ta = /* @__PURE__ */ qt('');
-const Oa = {
- hash: "svelte-1q9atlb",
- code: "svg.svelte-1q9atlb{width:var(--size,var(--icon-size));height:var(--size,var(--icon-size))}"
-};
-function Bn(e) {
- se(e, Oa);
- var t = Ta();
- $(e, t);
-}
-ae(Bn, {}, [], [], !0);
-var Aa = /* @__PURE__ */ W(''), La = /* @__PURE__ */ W("");
-const Da = {
- hash: "svelte-1qoozz7",
- code: "button.svelte-1qoozz7{appearance:none;background-color:var(--button-background);color:var(--button-color);padding:var(--button-padding);font-family:var(--font-family);letter-spacing:.01em;border-radius:var(--button-border-radius);gap:var(--button-gap);cursor:pointer;font-size:var(--button-font-size);font-weight:var(--button-font-weight);transition:var(--button-transition);white-space:nowrap;height:var(--button-height);outline-color:var(--button-outline-color);border:0;flex:none;justify-content:center;align-items:center;line-height:1;display:inline-flex}button.svelte-1qoozz7:focus-visible{outline-offset:var(--button-outline-offset)}button.svelte-1qoozz7:hover,button.svelte-1qoozz7:focus-visible{color:var(--button-hover-color);background-color:var(--button-hover-background)}button.svelte-1qoozz7 span:where(.svelte-1qoozz7){text-overflow:ellipsis;min-width:0;display:block;overflow-x:clip}button.button--header.svelte-1qoozz7{--icon-size:1.25rem;background-color:var(--button-header-background);height:var(--button-header-height);padding:var(--button-header-padding);mix-blend-mode:var(--button-header-blend-mode);border:0;border-radius:0}button.button--header.svelte-1qoozz7:first-child{border-top-left-radius:var(--border-radius-rounded);border-bottom-left-radius:var(--border-radius-rounded)}button.button--header.svelte-1qoozz7:hover,button.button--header.svelte-1qoozz7:focus-visible{background-color:var(--button-header-hover-background)}button.button--panel.svelte-1qoozz7{background-color:var(--button-panel-background);height:auto;padding:var(--button-panel-padding);border-radius:var(--border-radius-rounded);border:0}button.button--panel.svelte-1qoozz7 span:where(.svelte-1qoozz7){overflow:visible}button.button--solid.svelte-1qoozz7{background-color:var(--button-solid-background)}button.button--solid.svelte-1qoozz7:hover,button.button--solid.svelte-1qoozz7:focus-visible{color:var(--button-solid-hover-color);background-color:var(--button-solid-hover-background)}button.button--small.svelte-1qoozz7{height:var(--button-small-height);font-size:var(--button-small-font-size)}button.button--icon.svelte-1qoozz7{background-color:var(--button-icon-background);color:var(--button-icon-color);height:var(--button-icon-height);box-shadow:var(--button-icon-shadow);aspect-ratio:1;font-size:var(--button-icon-font-size);border-radius:var(--button-icon-border-radius);border:0;padding:0}button.button--icon.svelte-1qoozz7:hover,button.button--icon.svelte-1qoozz7:focus-visible{background-color:var(--button-icon-hover-background);color:var(--button-icon-hover-color)}button.button--marker.svelte-1qoozz7{background-color:var(--button-marker-background);color:var(--button-marker-color);height:var(--marker-size);width:var(--marker-size);font-weight:var(--button-marker-font-weight);border-radius:var(--button-marker-border-radius);border:0;padding:0}button.button--marker.svelte-1qoozz7 :where(.svelte-1qoozz7){pointer-events:none}button.button--marker.button--marker-highlighted.svelte-1qoozz7{background-color:var(--button-marker-highlighted-background);color:var(--button-marker-highlighted-color)}button.button--marker-open.svelte-1qoozz7{background-color:var(--color-accent);color:var(--color-accent-dark)}button.button--filter.svelte-1qoozz7{background-color:var(--button-filter-background);color:var(--button-filter-color);height:var(--button-filter-height);font-size:var(--button-filter-font-size);padding:var(--button-filter-padding);border-radius:var(--button-filter-border-radius);flex:1}button.button--filter.svelte-1qoozz7:hover,button.button--filter.svelte-1qoozz7:focus-visible{color:var(--button-filter-hover-color);background-color:var(--button-filter-hover-background)}button.button--filter.button--filter-active.svelte-1qoozz7{background-color:var(--button-filter-active-background);color:var(--button-filter-active-color);font-weight:var(--button-filter-active-font-weight)}button.button--filter.button--filter-active.svelte-1qoozz7:hover,button.button--filter.button--filter-active.svelte-1qoozz7:focus-visible{background-color:var(--button-filter-active-background);color:var(--button-filter-active-color)}button.button--menu-item.svelte-1qoozz7{background-color:var(--button-menu-item-background);color:var(--button-menu-item-color);width:100%;padding:var(--button-menu-item-padding);border-radius:var(--button-menu-item-border-radius);font-size:var(--button-menu-item-font-size);justify-content:flex-start;gap:var(--button-menu-item-gap)}button.button--menu-item.svelte-1qoozz7:hover,button.button--menu-item.svelte-1qoozz7:focus-visible{background-color:var(--button-menu-item-hover-background);color:var(--button-menu-item-hover-color)}button.button--menu-item.button--menu-item-active.svelte-1qoozz7{background-color:var(--button-menu-item-active-background);color:var(--button-menu-item-active-color);font-weight:var(--button-menu-item-active-font-weight)}button.button--menu-item.button--menu-item-active.svelte-1qoozz7:hover,button.button--menu-item.button--menu-item-active.svelte-1qoozz7:focus-visible{background-color:var(--button-menu-item-active-background);color:var(--button-menu-item-active-color)}button.is-active.svelte-1qoozz7{background-color:var(--button-active-background);color:var(--button-active-color)}button.is-active.svelte-1qoozz7:hover,button.is-active.svelte-1qoozz7:focus-visible{color:var(--button-active-color);background-color:var(--button-active-background)}button.svelte-1qoozz7:disabled{opacity:var(--button-disabled-opacity);cursor:not-allowed}button.svelte-1qoozz7:disabled:hover{color:var(--button-disabled-hover-color);background-color:var(--button-disabled-hover-background)}"
-};
-function te(e, t) {
- const r = pa(t);
- we(t, !0), se(e, Da);
- const n = q(t, "onclick", 7), o = q(t, "onmouseenter", 7), a = q(t, "onmouseout", 7), s = q(t, "onblur", 7), i = q(t, "active", 7, !1), u = q(t, "type", 7, "button"), c = q(t, "style", 7, ""), v = q(t, "disabled", 7, !1), m = q(t, "ariaLabel", 7, ""), l = q(t, "id", 7, ""), f = q(t, "ariaHaspopup", 7, ""), d = q(t, "ariaExpanded", 7, ""), p = q(t, "ariaControls", 7, "");
- var w = La();
- w.__click = function(...g) {
- var R;
- (R = n()) == null || R.apply(this, g);
- };
- let I;
- w.__mouseout = function(...g) {
- var R;
- (R = a()) == null || R.apply(this, g);
- };
- var _ = C(w);
- Kr(_, t, "icon", {});
- var k = V(_, 2);
- {
- var y = (g) => {
- var R = Aa(), M = C(R);
- Kr(M, t, "default", {}), x(R), $(g, R);
- };
- $e(k, (g) => {
- r.default && g(y);
- });
- }
- return x(w), j(
- (g) => {
- I = ot(w, 1, `button ${c() ?? ""}`, "svelte-1qoozz7", I, g), G(w, "type", u()), G(w, "aria-label", m()), G(w, "id", l()), G(w, "aria-haspopup", f() === "menu" ? "menu" : null), G(w, "aria-expanded", d() === "true" ? !0 : d() === "false" ? !1 : null), G(w, "aria-controls", p() || null), w.disabled = v();
- },
- [() => ({ "is-active": i() })]
- ), Ze("mouseenter", w, function(...g) {
- var R;
- (R = o()) == null || R.apply(this, g);
- }), Ze("blur", w, function(...g) {
- var R;
- (R = s()) == null || R.apply(this, g);
- }), $(e, w), _e({
- get onclick() {
- return n();
- },
- set onclick(g) {
- n(g), E();
- },
- get onmouseenter() {
- return o();
- },
- set onmouseenter(g) {
- o(g), E();
- },
- get onmouseout() {
- return a();
- },
- set onmouseout(g) {
- a(g), E();
- },
- get onblur() {
- return s();
- },
- set onblur(g) {
- s(g), E();
- },
- get active() {
- return i();
- },
- set active(g = !1) {
- i(g), E();
- },
- get type() {
- return u();
- },
- set type(g = "button") {
- u(g), E();
- },
- get style() {
- return c();
- },
- set style(g = "") {
- c(g), E();
- },
- get disabled() {
- return v();
- },
- set disabled(g = !1) {
- v(g), E();
- },
- get ariaLabel() {
- return m();
- },
- set ariaLabel(g = "") {
- m(g), E();
- },
- get id() {
- return l();
- },
- set id(g = "") {
- l(g), E();
- },
- get ariaHaspopup() {
- return f();
- },
- set ariaHaspopup(g = "") {
- f(g), E();
- },
- get ariaExpanded() {
- return d();
- },
- set ariaExpanded(g = "") {
- d(g), E();
- },
- get ariaControls() {
- return p();
- },
- set ariaControls(g = "") {
- p(g), E();
- }
- });
-}
-On(["click", "mouseout"]);
-ae(
- te,
- {
- onclick: {},
- onmouseenter: {},
- onmouseout: {},
- onblur: {},
- active: {},
- type: {},
- style: {},
- disabled: {},
- ariaLabel: {},
- id: {},
- ariaHaspopup: {},
- ariaExpanded: {},
- ariaControls: {}
- },
- ["icon", "default"],
- [],
- !0
-);
-var ja = /* @__PURE__ */ qt('');
-const Ha = {
- hash: "svelte-1q9atlb",
- code: "svg.svelte-1q9atlb{width:var(--size,var(--icon-size));height:var(--size,var(--icon-size))}"
-};
-function Yn(e) {
- se(e, Ha);
- var t = ja();
- $(e, t);
-}
-ae(Yn, {}, [], [], !0);
-var Fa = /* @__PURE__ */ W('', 1), Va = /* @__PURE__ */ W('', 1), Ba = /* @__PURE__ */ W(' '), Ya = /* @__PURE__ */ W('');
-const Wa = {
- hash: "svelte-12aqd5e",
- code: ".toggle.svelte-12aqd5e{display:flex}header.svelte-12aqd5e{position:var(--header-position);top:var(--header-top);max-width:100%;transform:var(--header-transform);color:var(--header-color);border-radius:var(--header-border-radius);z-index:var(--header-z-index);-webkit-backdrop-filter:var(--header-backdrop-filter);backdrop-filter:var(--header-backdrop-filter);box-shadow:var(--shadow-l),var(--shadow-light-edge),var(--shadow-dark-edge);background:var(--header-background);justify-content:space-between;align-items:stretch;display:flex;left:50%}header.bottom.svelte-12aqd5e{top:auto;bottom:var(--header-bottom-position)}.count.svelte-12aqd5e{width:var(--header-count-size);height:var(--header-count-size);border-radius:var(--header-count-border-radius);-webkit-backdrop-filter:var(--header-count-backdrop-filter);backdrop-filter:var(--header-count-backdrop-filter);box-shadow:var(--shadow-s),var(--shadow-light-edge),var(--shadow-dark-edge);background:var(--header-count-background);justify-content:center;align-items:center;display:flex}"
-};
-function Wn(e, t) {
- we(t, !0), se(e, Wa);
- const r = q(t, "position", 7), n = q(t, "commentsCount", 7);
- var o = Ya();
- let a;
- var s = C(o), i = C(s);
- const u = /* @__PURE__ */ ee(() => !Je.open);
- te(i, {
- onclick: () => {
- Je.open = !1;
- },
- get active() {
- return h(u);
- },
- style: "button--header",
- children: (l, f) => {
- ge();
- var d = le();
- j((p) => F(d, p), [() => O("ui.header.browse.mode", "Browse")]), $(l, d);
- },
- $$slots: {
- default: !0,
- icon: (l, f) => {
- var d = Fa(), p = Oe(d);
- Zr(p, () => ({ "--size": "1.5em" })), Yn(p.lastChild), x(p), $(l, d);
- }
- }
- });
- var c = V(i, 2);
- te(c, {
- onclick: () => {
- Je.open = !0;
- },
- style: "button--header",
- get active() {
- return Je.open;
- },
- children: (l, f) => {
- ge();
- var d = le();
- j((p) => F(d, p), [
- () => O("ui.header.comment.mode", "Comment")
- ]), $(l, d);
- },
- $$slots: {
- default: !0,
- icon: (l, f) => {
- var d = Va(), p = Oe(d);
- Zr(p, () => ({ "--size": "1.5em" })), Bn(p.lastChild), x(p), $(l, d);
- }
- }
- }), x(s);
- var v = V(s, 2);
- const m = /* @__PURE__ */ ee(() => `${n()} ${O("ui.header.aria.count", "unresolved comments")}`);
- return te(v, {
- onclick: () => Z.open = !Z.open,
- style: "button--panel",
- get ariaLabel() {
- return h(m);
- },
- children: (l, f) => {
- var d = Ba(), p = C(d, !0);
- x(d), j(() => F(p, n())), $(l, d);
- },
- $$slots: { default: !0 }
- }), x(o), j((l) => a = ot(o, 1, "svelte-12aqd5e", null, a, l), [
- () => ({ bottom: r() === "bottom" })
- ]), $(e, o), _e({
- get position() {
- return r();
- },
- set position(l) {
- r(l), E();
- },
- get commentsCount() {
- return n();
- },
- set commentsCount(l) {
- n(l), E();
- }
- });
-}
-ae(Wn, { position: {}, commentsCount: {} }, [], [], !0);
-class Xa {
- constructor(t = {}) {
- oe(this, "callbacks", /* @__PURE__ */ new Set());
- oe(this, "debounceTimer", null);
- oe(this, "rafId", null);
- oe(this, "isListening", !1);
- oe(this, "options");
- oe(this, "handleResize", () => {
- this.debounceTimer && clearTimeout(this.debounceTimer), this.rafId && cancelAnimationFrame(this.rafId), this.debounceTimer = window.setTimeout(() => {
- this.options.useRAF ? this.rafId = requestAnimationFrame(() => {
- this.executeCallbacks();
- }) : this.executeCallbacks();
- }, this.options.debounceDelay);
- });
- this.options = {
- debounceDelay: t.debounceDelay ?? 100,
- useRAF: t.useRAF ?? !0
- };
- }
- executeCallbacks() {
- this.callbacks.forEach((t) => {
- try {
- t();
- } catch (r) {
- console.error("Error in resize callback:", r);
- }
- });
- }
- startListening() {
- this.isListening || (window.addEventListener("resize", this.handleResize, { passive: !0 }), this.isListening = !0);
- }
- stopListening() {
- this.isListening && (window.removeEventListener("resize", this.handleResize), this.isListening = !1);
- }
- /**
- * Add a callback to be executed on resize
- */
- subscribe(t) {
- return this.callbacks.add(t), this.startListening(), () => {
- this.callbacks.delete(t), this.callbacks.size === 0 && this.stopListening();
- };
- }
- /**
- * Clean up all resources
- */
- destroy() {
- this.callbacks.clear(), this.stopListening(), this.debounceTimer && clearTimeout(this.debounceTimer), this.rafId && cancelAnimationFrame(this.rafId);
- }
-}
-const Ua = new Xa();
-function Za(e, t) {
- return Ua.subscribe(e);
-}
-const Xn = () => {
- const e = document.body, t = document.documentElement;
- return Math.max(
- e.scrollHeight,
- e.offsetHeight,
- t.clientHeight,
- t.scrollHeight,
- t.offsetHeight
- );
-};
-var Ja = /* @__PURE__ */ W("");
-const Ka = {
- hash: "svelte-4f1edl",
- code: ".marker.svelte-4f1edl{position:var(--marker-position);z-index:var(--marker-z-index);transform:var(--marker-transform);border-radius:var(--marker-border-radius)}.marker--pulse.svelte-4f1edl{animation:1.5s ease-in-out infinite svelte-4f1edl-kirby-loop-pulse}@keyframes svelte-4f1edl-kirby-loop-pulse{0%{box-shadow:0 0 0 0 var(--color-accent),0 0 0 0 #8080804d,0 0 0 0 #80808033}30%{box-shadow:0 0 0 8px #0000,0 0 #8080804d,0 0 #80808033}60%{box-shadow:0 0 0 8px #80808026,0 0 0 12px #0000,0 0 #80808033}to{box-shadow:0 0 0 16px #0000,0 0 0 12px #0000,0 0 0 8px #0000}}"
-};
-function Un(e, t) {
- we(t, !0), se(e, Ka);
- const r = q(t, "comment", 7);
- let n = ue(!1);
- et(() => {
- T(n, Z.pulseMarkerId === r().id);
- });
- let o = ue(null), a = ue(null), s = ue(null);
- rr(() => {
- requestAnimationFrame(() => {
- i();
- }), T(s, J(Za(() => {
- i();
- })));
- }), Ea(() => {
- h(s) && h(s)();
- });
- function i() {
- if (!(!r() || !h(o)))
- try {
- T(a, J(document.querySelector(r().selector)));
- let d, p;
- if (h(a)) {
- const Q = h(a).getBoundingClientRect(), re = Q.width * r().selectorOffsetX / 100, z = Q.height * r().selectorOffsetY / 100;
- d = Q.left + window.scrollX + re, p = Q.top + window.scrollY + z;
- } else
- d = Number(r().pagePositionX), p = Number(r().pagePositionY);
- const w = h(o).getBoundingClientRect(), I = w.width || 32, _ = w.height || 32, k = I / 2, y = _ / 2, g = Xn(), R = window.innerWidth, M = k, b = R - k, P = y, N = g - y, L = Math.max(M, Math.min(b, d)), K = Math.max(P, Math.min(N, p));
- h(o).style.left = `${L}px`, h(o).style.top = `${K}px`;
- } catch (d) {
- console.error("Error positioning marker:", d);
- }
- }
- function u(d) {
- Z.currentCommentId = d;
- }
- function c() {
- Z.currentCommentId = 0;
- }
- function v() {
- var p, w;
- Z.open = !0;
- const d = (w = (p = document.querySelector("loop")) == null ? void 0 : p.shadowRoot) == null ? void 0 : w.querySelector(`#comment-${r().id}`);
- d && d.scrollIntoView({ behavior: "smooth", block: "start" });
- }
- var m = Rr(), l = Oe(m);
- {
- var f = (d) => {
- var p = Ja();
- let w;
- var I = C(p);
- te(I, {
- onmouseenter: () => u(r().id),
- onmouseout: c,
- onblur: c,
- onclick: v,
- get style() {
- return `button--marker button--marker-${r().status ?? ""}`;
- },
- children: (_, k) => {
- ge();
- var y = le();
- j(() => F(y, r().id)), $(_, y);
- },
- $$slots: { default: !0 }
- }), x(p), tt(p, (_) => T(o, _), () => h(o)), j(
- (_) => {
- w = ot(p, 1, `marker marker--${r().status ?? ""}`, "svelte-4f1edl", w, _), G(p, "id", `marker-${r().id ?? ""}`);
- },
- [
- () => ({ "marker--pulse": h(n) })
- ]
- ), $(d, p);
- };
- $e(l, (d) => {
- r() && d(f);
- });
- }
- return $(e, m), _e({
- get comment() {
- return r();
- },
- set comment(d) {
- r(d), E();
- }
- });
-}
-ae(Un, { comment: {} }, [], [], !0);
-const qe = J({ comments: [] }), Ga = "loop", ut = document.querySelector("kirby-loop"), Qa = (ut == null ? void 0 : ut.getAttribute("csrf-token")) || "", es = (ut == null ? void 0 : ut.getAttribute("apibase")) || "/", ht = {
- "Content-Type": "application/json",
- "X-CSRF-Token": Qa || ""
-}, mt = (e) => {
- const t = new URL(`${es}/${Ga}/${e}`, window.location.origin), r = new URLSearchParams(window.location.search), n = r.get("token") || r.get("_token");
- return n && t.searchParams.set(r.has("token") ? "token" : "_token", n), t.toString();
-}, ts = async (e) => {
- const t = mt(`comments/${e}`), n = await (await fetch(t, { headers: ht })).json();
- return n.status === "ok" && (qe.comments = n.comments), n.status === "ok";
-}, rs = async (e) => {
- const t = mt("comment/new"), n = await (await fetch(t, {
- method: "POST",
- headers: ht,
- body: JSON.stringify(e)
- })).json();
- n.status === "ok" && (qe.comments = [n.comment, ...qe.comments]);
-}, ns = async (e) => {
- const t = mt("comment/resolve"), n = await (await fetch(t, {
- method: "POST",
- headers: ht,
- body: JSON.stringify({ id: e.id })
- })).json();
- if (n.success) {
- const o = qe.comments.findIndex((a) => a.id === e.id);
- o !== -1 && (qe.comments[o].status = "RESOLVED");
- }
- return n.success;
-}, os = async (e) => {
- const t = mt("comment/unresolve"), n = await (await fetch(t, {
- method: "POST",
- headers: ht,
- body: JSON.stringify({ id: e.id })
- })).json();
- if (n.success) {
- const o = qe.comments.findIndex((a) => a.id === e.id);
- o !== -1 && (qe.comments[o].status = "OPEN");
- }
- return n.success;
-}, as = async (e) => await (await fetch(mt("guest/name"), {
- method: "POST",
- headers: ht,
- body: JSON.stringify({ name: e })
-})).json(), ss = async (e) => {
- const t = mt("comment/reply"), n = await (await fetch(t, {
- method: "POST",
- headers: ht,
- body: JSON.stringify(e)
- })).json();
- if (n.status === "ok") {
- const o = qe.comments.find((a) => a.id === n.reply.parentId);
- o && (o.replies = [...o.replies, n.reply]);
- }
-}, ct = J({ text: "", parentId: null }), cr = () => {
- ct.text = "", ct.parentId = null;
-};
-function is(e) {
- var t;
- if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
- e.preventDefault();
- const r = (t = e.currentTarget) == null ? void 0 : t.closest("form");
- r && r.requestSubmit();
- }
-}
-var ls = /* @__PURE__ */ W('');
-const us = {
- hash: "svelte-w3h34c",
- code: "form.svelte-w3h34c{cursor:auto;background-color:var(--comment-form-background);color:var(--comment-form-color);border-radius:var(--comment-form-border-radius);border:var(--comment-form-border);padding:0;overflow:hidden}textarea.svelte-w3h34c{width:100%;height:var(--comment-form-textarea-height);resize:none;padding:var(--comment-form-textarea-padding);box-sizing:border-box;background-color:var(--comment-form-textarea-background);font-family:var(--comment-form-textarea-font-family);font-size:var(--comment-form-textarea-font-size);color:currentColor;border:0;margin:0}textarea.svelte-w3h34c:focus-visible,textarea.svelte-w3h34c:focus{outline:0}footer.svelte-w3h34c{padding:var(--comment-form-footer-padding);gap:var(--comment-form-footer-gap);display:flex}footer.svelte-w3h34c button{flex:1}.keyboard-hint.svelte-w3h34c{font-size:var(--comment-form-hint-font-size);color:var(--comment-form-hint-color);padding:var(--comment-form-hint-padding);white-space:nowrap;align-self:center;margin-left:auto}"
-};
-function Ar(e, t) {
- we(t, !0), se(e, us);
- const r = q(t, "handleSubmit", 7), n = q(t, "cancel", 7), o = q(t, "parentId", 7, null);
- ct.parentId = o() ? Number(o()) : null;
- var a = ls(), s = C(a), i = C(s);
- ua(i), i.__keydown = [is], x(s);
- var u = V(s, 2), c = C(u, !0);
- x(u);
- var v = V(u, 2), m = C(v);
- te(m, {
- type: "submit",
- style: "button--solid",
- children: (f, d) => {
- ge();
- var p = le();
- j((w) => F(p, w), [
- () => o() ? O("ui.reply.submit", "Reply") : O("ui.comment.submit", "Submit")
- ]), $(f, p);
- },
- $$slots: { default: !0 }
- });
- var l = V(m, 2);
- return te(l, {
- get onclick() {
- return n();
- },
- children: (f, d) => {
- ge();
- var p = le();
- j((w) => F(p, w), [() => O("ui.comment.cancel", "Cancel")]), $(f, p);
- },
- $$slots: { default: !0 }
- }), x(v), x(a), j(
- (f, d) => {
- G(i, "placeholder", f), F(c, d);
- },
- [
- () => o() ? O("ui.reply.placeholder", "Write a reply...") : O("ui.comment.placeholder", "Enter your comment..."),
- () => O("ui.comment.keyboardHint", "⌘+Enter or Ctrl+Enter to submit")
- ]
- ), Ze("submit", a, function(...f) {
- var d;
- (d = r()) == null || d.apply(this, f);
- }), Hn(i, () => ct.text, (f) => ct.text = f), $(e, a), _e({
- get handleSubmit() {
- return r();
- },
- set handleSubmit(f) {
- r(f), E();
- },
- get cancel() {
- return n();
- },
- set cancel(f) {
- n(f), E();
- },
- get parentId() {
- return o();
- },
- set parentId(f = null) {
- o(f), E();
- }
- });
-}
-On(["keydown"]);
-ae(Ar, { handleSubmit: {}, cancel: {}, parentId: {} }, [], [], !0);
-var cs = /* @__PURE__ */ W('
');
-const ds = {
- hash: "svelte-1xlsy1x",
- code: ".author.svelte-1xlsy1x{font-size:var(--author-avatar-font-size);text-transform:uppercase;color:var(--author-avatar-color);background-color:var(--author-avatar-background-color);aspect-ratio:1;flex:0 0 var(--author-avatar-size);border-radius:var(--author-avatar-border-radius);justify-content:center;align-items:center;display:flex}"
-};
-function Zn(e, t) {
- we(t, !0), se(e, ds);
- const r = q(t, "initials", 7);
- var n = cs(), o = C(n, !0);
- return x(n), j(() => F(o, r())), $(e, n), _e({
- get initials() {
- return r();
- },
- set initials(a) {
- r(a), E();
- }
- });
-}
-ae(Zn, { initials: {} }, [], [], !0);
-function Wt(e, t = !0) {
- const r = new Date(e * 1e3), o = (/* @__PURE__ */ new Date()).getTime() - r.getTime(), a = Math.floor(o / (1e3 * 60)), s = Math.floor(o / (1e3 * 60 * 60)), i = Math.floor(o / (1e3 * 60 * 60 * 24));
- return t && i <= 3 ? a < 1 ? O("ui.time.just_now", "just now") : a === 1 ? O("ui.time.minute_ago", "a minute ago") : a < 60 ? ur("ui.time.minutes_ago", "{count} minutes ago", { count: a.toString() }) : s === 1 ? O("ui.time.hour_ago", "an hour ago") : s < 24 ? ur("ui.time.hours_ago", "{count} hours ago", { count: s.toString() }) : i === 1 ? O("ui.time.yesterday", "yesterday") : ur("ui.time.days_ago", "{count} days ago", { count: i.toString() }) : r.toLocaleString(void 0, { dateStyle: "short", timeStyle: "short" });
-}
-function Jn(e) {
- return new Date(e * 1e3).toISOString();
-}
-function Xt(e) {
- const t = {
- "&": "&",
- "<": "<",
- ">": ">",
- """: '"',
- "'": "'",
- "/": "/",
- "`": "`",
- "=": "="
- };
- return e.replace(/&[#\w]+;/g, (r) => t[r] || r);
-}
-var fs = /* @__PURE__ */ W(' ');
-const vs = {
- hash: "svelte-1nsulj7",
- code: ".reply.svelte-1nsulj7{gap:var(--reply-gap);flex-direction:row;align-items:start;display:flex}.reply__content.svelte-1nsulj7{padding:var(--reply-content-padding);background-color:var(--reply-content-background);border-radius:var(--reply-content-border-radius)}.reply__content.svelte-1nsulj7 header:where(.svelte-1nsulj7){gap:var(--reply-header-gap);margin-bottom:var(--reply-header-margin-bottom);justify-content:flex-start;align-items:center;display:flex}.reply__content.svelte-1nsulj7 header:where(.svelte-1nsulj7) time:where(.svelte-1nsulj7){font-size:var(--reply-timestamp-font-size);color:var(--reply-timestamp-color)}@media (prefers-color-scheme:dark){.reply__content.svelte-1nsulj7{background-color:var(--reply-content-background-dark)}}.reply__text.svelte-1nsulj7{white-space:pre-line}"
-};
-function Kn(e, t) {
- we(t, !1), se(e, vs);
- let r = q(t, "reply", 12);
- Fn();
- var n = fs(), o = C(n);
- const a = /* @__PURE__ */ me(() => r().author.substring(0, 1));
- Zn(o, {
- get initials() {
- return h(a);
- }
- });
- var s = V(o, 2), i = C(s), u = C(i), c = C(u, !0);
- x(u);
- var v = V(u, 2), m = C(v, !0);
- x(v), x(i);
- var l = V(i, 2), f = C(l, !0);
- return x(l), x(s), x(n), j(
- (d, p, w, I, _, k) => {
- G(n, "data-id", r().id), G(n, "aria-label", `${d ?? ""} ${r().author ?? ""}: ${p ?? ""}`), F(c, r().author), G(v, "datetime", w), G(v, "title", I), F(m, _), F(f, k);
- },
- [
- () => O("ui.reply.aria.label", "Reply by"),
- () => Xt(r().comment),
- () => Jn(r().timestamp),
- () => Wt(r().timestamp, !1),
- () => Wt(r().timestamp),
- () => Xt(r().comment)
- ],
- me
- ), $(e, n), _e({
- get reply() {
- return r();
- },
- set reply(d) {
- r(d), E();
- }
- });
-}
-ae(Kn, { reply: {} }, [], [], !0);
-var hs = /* @__PURE__ */ W(""), ms = /* @__PURE__ */ W(''), gs = /* @__PURE__ */ W(" ", 1), ps = /* @__PURE__ */ W(''), bs = /* @__PURE__ */ W(' ');
-const ws = {
- hash: "svelte-6fqqrp",
- code: '.comment.svelte-6fqqrp{--loop-marker-background:var(--comment-marker-background);--loop-marker-color:var(--comment-marker-color);--marker-size:var(--comment-avatar-size);position:relative}.comment.svelte-6fqqrp>:where(.svelte-6fqqrp){z-index:1;position:relative}.comment.svelte-6fqqrp:after{content:"";left:var(--comment-line-offset);width:var(--comment-line-width);background-color:var(--comment-line-background);z-index:0;height:calc(100% - 4rem);position:absolute;top:1.5rem}.comment.svelte-6fqqrp:not([open]):after{height:calc(100% - 2.75rem)}.comment__header.svelte-6fqqrp{font-size:var(--comment-header-font-size);padding:var(--comment-header-padding);align-items:flex-start;gap:var(--comment-header-gap);cursor:pointer;border-radius:var(--comment-header-border-radius);display:flex}.comment__header.svelte-6fqqrp:focus-visible{outline:2px solid var(--comment-header-outline-color);outline-offset:var(--comment-header-outline-offset)}.comment__header.svelte-6fqqrp .comment__replies-count{bottom:0;left:var(--space-s);min-width:var(--comment-avatar-size);position:absolute}.comment__header.svelte-6fqqrp header:where(.svelte-6fqqrp){gap:var(--comment-author-gap);margin-bottom:var(--comment-author-margin-bottom);justify-content:flex-start;align-items:center;display:flex}.comment__header.svelte-6fqqrp header:where(.svelte-6fqqrp) time:where(.svelte-6fqqrp){font-size:var(--comment-timestamp-font-size);color:var(--comment-timestamp-color)}.comment__header.svelte-6fqqrp .comment__content:where(.svelte-6fqqrp){padding:var(--comment-content-padding);background-color:var(--comment-content-background);border-radius:var(--comment-content-border-radius);flex:1}@media (prefers-color-scheme:dark){.comment__header.svelte-6fqqrp .comment__content:where(.svelte-6fqqrp){background-color:var(--comment-content-background-dark)}}.comment__header.svelte-6fqqrp .comment__text:where(.svelte-6fqqrp){white-space:pre-line}.comment__replies.svelte-6fqqrp{padding:var(--comment-replies-padding);gap:var(--comment-replies-gap);flex-direction:column;margin:0;list-style:none;display:flex}footer.svelte-6fqqrp{gap:var(--comment-footer-gap);padding:var(--comment-footer-padding);flex-direction:column;display:flex}footer.svelte-6fqqrp .buttons:where(.svelte-6fqqrp){gap:var(--comment-buttons-gap);align-items:flex-end;display:flex}.is-hidden.svelte-6fqqrp{display:none}'
-};
-function Gn(e, t) {
- var z;
- we(t, !0), se(e, ws);
- const r = q(t, "comment", 7), n = q(t, "scrollIntoView", 7), o = q(t, "handleSubmit", 7), a = q(t, "cancel", 7);
- let s = ue(!1), i = ue(J(((z = r().replies) == null ? void 0 : z.length) > 0 && !Z.showResolvedOnly));
- var u = bs();
- let c;
- var v = C(u), m = C(v);
- const l = /* @__PURE__ */ ee(() => r().status), f = /* @__PURE__ */ ee(() => Z.currentCommentId === r().id ? "button--marker-highlighted" : ""), d = /* @__PURE__ */ ee(() => `${O("ui.comment.maker.aria.label", "Jump to marker")} ${r().id}`);
- te(m, {
- get style() {
- return `button--marker button--marker-${h(l) ?? ""} ${h(f) ?? ""}`;
- },
- onclick: () => n()(r().id),
- onmouseenter: () => Z.pulseMarkerId = r().id,
- onmouseout: () => Z.pulseMarkerId = 0,
- get ariaLabel() {
- return h(d);
- },
- children: (S, D) => {
- ge();
- var X = le();
- j(() => F(X, r().id)), $(S, X);
- },
- $$slots: { default: !0 }
- });
- var p = V(m, 2), w = C(p), I = C(w), _ = C(I, !0);
- x(I);
- var k = V(I, 2), y = C(k, !0);
- x(k), x(w);
- var g = V(w, 2), R = C(g, !0);
- x(g), x(p);
- var M = V(p, 2);
- {
- var b = (S) => {
- const D = /* @__PURE__ */ ee(() => `${O("ui.comment.replies.aria.label", "Show replies")} ${r().id}`);
- te(S, {
- style: "button--solid button--small comment__replies-count",
- get ariaLabel() {
- return h(D);
- },
- onclick: () => {
- T(i, !h(i));
- },
- children: (X, ce) => {
- ge();
- var ye = le();
- j(() => {
- var ne;
- return F(ye, ((ne = r().replies) == null ? void 0 : ne.length) > 0 ? `+${r().replies.length}` : "+");
- }), $(X, ye);
- },
- $$slots: { default: !0 }
- });
- };
- $e(M, (S) => {
- h(i) || S(b);
- });
- }
- x(v);
- var P = V(v, 2);
- {
- var N = (S) => {
- var D = ms();
- Tr(D, 21, () => r().replies, (X) => X.id, (X, ce) => {
- var ye = hs(), ne = C(ye);
- Kn(ne, {
- get reply() {
- return h(ce);
- }
- }), x(ye), $(X, ye);
- }), x(D), $(S, D);
- };
- $e(P, (S) => {
- var D;
- ((D = r().replies) == null ? void 0 : D.length) > 0 && S(N);
- });
- }
- var L = V(P, 2), K = C(L);
- {
- var Q = (S) => {
- Ar(S, {
- handleSubmit: (D) => {
- T(s, !1), o()(D);
- },
- cancel: () => {
- T(s, !1), a()();
- },
- get parentId() {
- return r().id;
- }
- });
- }, re = (S) => {
- var D = ps(), X = C(D);
- {
- var ce = (ne) => {
- var He = gs(), nr = Oe(He);
- te(nr, {
- style: "button--solid",
- onclick: () => T(s, !0),
- children: (gt, so) => {
- ge();
- var pt = le();
- j((or) => F(pt, or), [() => O("ui.reply.submit", "Reply")]), $(gt, pt);
- },
- $$slots: { default: !0 }
- });
- var It = V(nr, 2);
- te(It, {
- onclick: () => ns(r()),
- children: (gt, so) => {
- ge();
- var pt = le();
- j((or) => F(pt, or), [
- () => O("ui.comment.mark.solved", "Resolve")
- ]), $(gt, pt);
- },
- $$slots: { default: !0 }
- }), $(ne, He);
- }, ye = (ne) => {
- te(ne, {
- onclick: () => os(r()),
- children: (He, nr) => {
- ge();
- var It = le();
- j((gt) => F(It, gt), [
- () => O("ui.comment.mark.unsolved", "Reopen")
- ]), $(He, It);
- },
- $$slots: { default: !0 }
- });
- };
- $e(X, (ne) => {
- r().status === "OPEN" ? ne(ce) : ne(ye, !1);
- });
- }
- x(D), $(S, D);
- };
- $e(K, (S) => {
- h(s) ? S(Q) : S(re, !1);
- });
- }
- return x(L), x(u), j(
- (S, D, X, ce, ye, ne, He) => {
- G(u, "id", `comment-${r().id ?? ""}`), c = ot(u, 1, `comment comment--${r().status ?? ""}`, "svelte-6fqqrp", c, S), G(v, "aria-label", `${D ?? ""} ${r().author ?? ""}: ${X ?? ""}`), F(_, r().author), G(k, "datetime", ce), G(k, "title", ye), F(y, ne), F(R, He);
- },
- [
- () => ({
- "comment--current": Z.currentCommentId === r().id
- }),
- () => O("ui.comment.summary.aria.label", "Comment by"),
- () => Xt(r().comment),
- () => Jn(r().timestamp),
- () => Wt(r().timestamp, !1),
- () => Wt(r().timestamp),
- () => Xt(r().comment)
- ]
- ), za("open", "toggle", u, (S) => T(i, S), () => h(i)), $(e, u), _e({
- get comment() {
- return r();
- },
- set comment(S) {
- r(S), E();
- },
- get scrollIntoView() {
- return n();
- },
- set scrollIntoView(S) {
- n(S), E();
- },
- get handleSubmit() {
- return o();
- },
- set handleSubmit(S) {
- o(S), E();
- },
- get cancel() {
- return a();
- },
- set cancel(S) {
- a(S), E();
- }
- });
-}
-ae(
- Gn,
- {
- comment: {},
- scrollIntoView: {},
- handleSubmit: {},
- cancel: {}
- },
- [],
- [],
- !0
-);
-var _s = /* @__PURE__ */ qt('');
-const ys = {
- hash: "svelte-1q9atlb",
- code: "svg.svelte-1q9atlb{width:var(--size,var(--icon-size));height:var(--size,var(--icon-size))}"
-};
-function ks(e) {
- se(e, ys);
- var t = _s();
- $(e, t);
-}
-ae(ks, {}, [], [], !0);
-var xs = /* @__PURE__ */ qt('');
-const $s = {
- hash: "svelte-1q9atlb",
- code: "svg.svelte-1q9atlb{width:var(--size,var(--icon-size));height:var(--size,var(--icon-size))}"
-};
-function Qn(e) {
- se(e, $s);
- var t = xs();
- $(e, t);
-}
-ae(Qn, {}, [], [], !0);
-var it = Sa(() => Z), Cs = /* @__PURE__ */ W(''), zs = /* @__PURE__ */ W(''), Ss = /* @__PURE__ */ W('');
-const Es = {
- hash: "svelte-15wfo7b",
- code: ".context-menu-container.svelte-15wfo7b{bottom:var(--context-menu-container-bottom);right:var(--context-menu-container-right);z-index:var(--context-menu-container-z-index);position:absolute}.context-menu-trigger.svelte-15wfo7b{width:var(--context-menu-trigger-size);height:var(--context-menu-trigger-size);border-radius:var(--context-menu-trigger-border-radius);justify-content:center;align-items:center;display:flex}.context-menu.svelte-15wfo7b{background:var(--context-menu-background);border-radius:var(--context-menu-border-radius);box-shadow:var(--context-menu-shadow);padding:var(--context-menu-padding);min-width:var(--context-menu-min-width);border:0;margin:0;position:fixed}.context-menu.svelte-15wfo7b::backdrop{background:var(--context-menu-backdrop-background)}.menu-section.svelte-15wfo7b{gap:var(--context-menu-section-gap);flex-direction:column;display:flex}.menu-section-title.svelte-15wfo7b{font-size:var(--context-menu-title-font-size);font-weight:var(--context-menu-title-font-weight);color:var(--context-menu-title-color);margin-bottom:var(--context-menu-title-margin-bottom);text-transform:uppercase;letter-spacing:var(--context-menu-title-letter-spacing);padding:0}.filter-options.svelte-15wfo7b{gap:var(--context-menu-filter-gap);flex-direction:column;display:flex}.filter-dot.svelte-15wfo7b{width:var(--context-menu-filter-dot-size);height:var(--context-menu-filter-dot-size);border-radius:var(--context-menu-filter-dot-border-radius);margin-right:var(--context-menu-filter-dot-margin-right);display:inline-block}.filter-dot--open.svelte-15wfo7b{background:var(--context-menu-filter-dot-open-background)}.filter-dot--resolved.svelte-15wfo7b{background:var(--context-menu-filter-dot-resolved-background)}"
-};
-function eo(e, t) {
- we(t, !1), se(e, Es);
- let r = Vr(), n = Vr();
- const o = () => {
- h(r).matches(":popover-open") ? h(r).hidePopover() : (h(r).showPopover(), a());
- }, a = () => {
- if (!h(n) || !h(r)) return;
- const b = h(n).getBoundingClientRect(), P = h(r).getBoundingClientRect(), N = b.top - P.height - 8, L = b.left - P.width + b.width;
- Mt(r, h(r).style.position = "fixed"), Mt(r, h(r).style.top = `${Math.max(8, N)}px`), Mt(r, h(r).style.left = `${Math.max(8, L)}px`), Mt(r, h(r).style.margin = "0");
- }, s = () => {
- h(r).hidePopover();
- }, i = (b) => {
- it(it().showResolvedOnly = b), s();
- };
- Fn();
- var u = Ss(), c = C(u), v = C(c);
- const m = /* @__PURE__ */ me(() => O("ui.panel.menu.open", "Open menu")), l = /* @__PURE__ */ me(() => {
- var b;
- return (b = h(r)) != null && b.matches(":popover-open") ? "true" : "false";
- });
- te(v, {
- onclick: o,
- get ariaLabel() {
- return h(m);
- },
- style: "button--icon",
- id: "context-menu-trigger",
- ariaHaspopup: "menu",
- get ariaExpanded() {
- return h(l);
- },
- ariaControls: "context-menu",
- $$slots: {
- icon: (b, P) => {
- Qn(b);
- }
- }
- }), x(c), tt(c, (b) => T(n, b), () => h(n));
- var f = V(c, 2), d = C(f), p = C(d), w = C(p, !0);
- x(p);
- var I = V(p, 2), _ = C(I);
- const k = /* @__PURE__ */ me(() => it().showResolvedOnly ? "" : "button--menu-item-active"), y = /* @__PURE__ */ me(() => it().showResolvedOnly ? O("ui.panel.filter.open.inactive", "Show open comments") : O("ui.panel.filter.open.active", "Show open comments (currently selected)"));
- te(_, {
- get style() {
- return `button--menu-item ${h(k) ?? ""}`;
- },
- onclick: () => i(!1),
- get ariaLabel() {
- return h(y);
- },
- children: (b, P) => {
- ge();
- var N = le();
- j((L) => F(N, L), [() => O("ui.panel.filter.open", "Open")], me), $(b, N);
- },
- $$slots: {
- default: !0,
- icon: (b, P) => {
- var N = Cs();
- $(b, N);
- }
- }
- });
- var g = V(_, 2);
- const R = /* @__PURE__ */ me(() => it().showResolvedOnly ? "button--menu-item-active" : ""), M = /* @__PURE__ */ me(() => it().showResolvedOnly ? O("ui.panel.filter.resolved.active", "Show resolved comments (currently selected)") : O("ui.panel.filter.resolved.inactive", "Show resolved comments"));
- te(g, {
- get style() {
- return `button--menu-item ${h(R) ?? ""}`;
- },
- onclick: () => i(!0),
- get ariaLabel() {
- return h(M);
- },
- children: (b, P) => {
- ge();
- var N = le();
- j(
- (L) => F(N, L),
- [
- () => O("ui.panel.filter.resolved", "Resolved")
- ],
- me
- ), $(b, N);
- },
- $$slots: {
- default: !0,
- icon: (b, P) => {
- var N = zs();
- $(b, N);
- }
- }
- }), x(I), x(d), x(f), tt(f, (b) => T(r, b), () => h(r)), x(u), j(
- (b) => F(w, b),
- [
- () => O("ui.panel.menu.filter.title", "Show Comments")
- ],
- me
- ), $(e, u), _e();
-}
-ae(eo, {}, [], [], !0);
-var qs = /* @__PURE__ */ qt('');
-const Is = {
- hash: "svelte-1q9atlb",
- code: "svg.svelte-1q9atlb{width:var(--size,var(--icon-size));height:var(--size,var(--icon-size))}"
-};
-function to(e) {
- se(e, Is);
- var t = qs();
- $(e, t);
-}
-ae(to, {}, [], [], !0);
-var Ms = /* @__PURE__ */ W('
'), Ps = /* @__PURE__ */ W('
'), Ns = /* @__PURE__ */ W(''), Rs = /* @__PURE__ */ W('');
-const Ts = {
- hash: "svelte-1nwyuw",
- code: ".panel.svelte-1nwyuw{position:var(--panel-position);right:var(--panel-right);top:var(--panel-top);transform:var(--panel-transform-closed);width:var(--panel-width);max-width:none;height:var(--panel-height);color:var(--panel-color);border-radius:var(--panel-border-radius);border-top-left-radius:var(--panel-border-top-left-radius);transition:var(--panel-transition);z-index:var(--panel-z-index);cursor:auto;background:0 0;border:0;flex-direction:column;justify-content:flex-start;align-items:flex-start;margin:0;padding:0;display:flex;bottom:auto;left:auto}@media screen and (max-width:600px){.panel.svelte-1nwyuw{width:var(--panel-mobile-width)}}.panel.open.svelte-1nwyuw{transform:var(--panel-transform-open);box-shadow:var(--panel-shadow)}.panel.open.svelte-1nwyuw header:where(.svelte-1nwyuw){transform:var(--panel-header-transform-open)}.panel.svelte-1nwyuw header:where(.svelte-1nwyuw){transform:var(--panel-header-transform-closed);border-top-left-radius:var(--panel-header-border-radius);border-bottom-left-radius:var(--panel-header-border-radius);gap:var(--panel-header-gap);-webkit-backdrop-filter:var(--panel-header-backdrop-filter);backdrop-filter:var(--panel-header-backdrop-filter);background:var(--panel-header-background);box-shadow:var(--shadow-l),var(--shadow-light-edge),var(--shadow-dark-edge);transition:transform var(--transition-duration)var(--transition-easing);border-top-right-radius:0;border-bottom-right-radius:0;flex-direction:column;display:flex;position:absolute}.panel.svelte-1nwyuw header:where(.svelte-1nwyuw):hover,.panel.svelte-1nwyuw header:where(.svelte-1nwyuw):focus-visible{transform:var(--panel-header-transform-hover)}.threads.svelte-1nwyuw{overscroll-behavior:contain;padding:var(--panel-threads-padding);box-sizing:border-box;background-color:var(--panel-threads-background);width:100%;-webkit-backdrop-filter:var(--panel-threads-backdrop);backdrop-filter:var(--panel-threads-backdrop);z-index:2;border-radius:var(--panel-threads-border-radius);border-top-left-radius:var(--panel-threads-border-top-left-radius);scrollbar-width:var(--panel-threads-scrollbar-width);scrollbar-gutter:stable;flex-direction:column;flex:100%;margin:0;list-style:none;display:flex;overflow-y:auto}.threads.svelte-1nwyuw li:where(.svelte-1nwyuw)+li:where(.svelte-1nwyuw){margin-top:var(--panel-threads-item-margin)}.threads.svelte-1nwyuw .no-threads:where(.svelte-1nwyuw){text-align:center;padding:var(--panel-no-threads-padding);font-size:var(--panel-no-threads-font-size);color:var(--panel-no-threads-color);margin-block:auto}"
-};
-function ro(e, t) {
- we(t, !0), se(e, Ts);
- const r = q(t, "scrollIntoView", 7), n = q(t, "handleSubmit", 7), o = q(t, "cancel", 7);
- let a;
- const s = /* @__PURE__ */ ee(() => Z.showResolvedOnly ? qe.comments.filter((_) => _.status === "RESOLVED") : qe.comments.filter((_) => _.status === "OPEN"));
- et(() => {
- a && (Z.open && !a.open ? a.show() : !Z.open && a.open && a.close());
- }), rr(() => {
- const _ = (k) => {
- k.key === "Escape" && Z.open && (Z.open = !1);
- };
- return document.addEventListener("keydown", _), () => document.removeEventListener("keydown", _);
- });
- function i() {
- Z.open = !1;
- }
- var u = Rs();
- let c;
- var v = C(u), m = C(v);
- const l = /* @__PURE__ */ ee(() => O("ui.panel.open", "Open comments"));
- te(m, {
- onclick: () => Z.open = !Z.open,
- style: "button--header",
- get ariaLabel() {
- return h(l);
- },
- $$slots: {
- icon: (_, k) => {
- to(_);
- }
- }
- }), x(v);
- var f = V(v, 2), d = C(f);
- {
- var p = (_) => {
- var k = Ms(), y = C(k), g = C(y, !0);
- x(y), x(k), j((R) => F(g, R), [
- () => O("ui.panel.no.comments", "No comments yet.")
- ]), $(_, k);
- }, w = (_, k) => {
- {
- var y = (R) => {
- var M = Ps(), b = C(M), P = C(b, !0);
- x(b), x(M), j((N) => F(P, N), [
- () => O("ui.panel.no.resolved", "No resolved comments yet.")
- ]), $(R, M);
- }, g = (R) => {
- var M = Rr(), b = Oe(M);
- Tr(b, 17, () => h(s), (P) => P.id, (P, N) => {
- var L = Ns(), K = C(L);
- Gn(K, {
- get comment() {
- return h(N);
- },
- get scrollIntoView() {
- return r();
- },
- get cancel() {
- return o();
- },
- get handleSubmit() {
- return n();
- }
- }), x(L), $(P, L);
- }), $(R, M);
- };
- $e(
- _,
- (R) => {
- h(s).length === 0 && Z.showResolvedOnly ? R(y) : R(g, !1);
- },
- k
- );
- }
- };
- $e(d, (_) => {
- h(s).length === 0 ? _(p) : _(w, !1);
- });
- }
- x(f);
- var I = V(f, 2);
- return eo(I, {}), x(u), tt(u, (_) => a = _, () => a), j(
- (_) => {
- c = ot(u, 1, "panel svelte-1nwyuw", null, c, _), f.inert = !Z.open;
- },
- [() => ({ open: Z.open })]
- ), Ze("close", u, i), $(e, u), _e({
- get scrollIntoView() {
- return r();
- },
- set scrollIntoView(_) {
- r(_), E();
- },
- get handleSubmit() {
- return n();
- },
- set handleSubmit(_) {
- n(_), E();
- },
- get cancel() {
- return o();
- },
- set cancel(_) {
- o(_), E();
- }
- });
-}
-ae(
- ro,
- {
- scrollIntoView: {},
- handleSubmit: {},
- cancel: {}
- },
- [],
- [],
- !0
-);
-function Os(e) {
- const t = e.target;
- if (!t)
- throw new Error("No target element found in event");
- const r = As();
- for (const n of r)
- try {
- const o = n.generator(t);
- if (o && Hs(o, t))
- return o;
- } catch (o) {
- console.warn(`Strategy ${n.name} failed:`, o);
- }
- return Ls(t);
-}
-function As() {
- return [
- {
- name: "ID",
- priority: 1,
- generator: (e) => e.id && Lr(e.id) ? `#${CSS.escape(e.id)}` : null
- },
- {
- name: "Unique Attributes",
- priority: 2,
- generator: (e) => {
- const t = ["data-testid", "data-id", "name", "for"];
- for (const r of t) {
- const n = e.getAttribute(r);
- if (n) {
- const o = `${e.tagName.toLowerCase()}[${r}="${CSS.escape(n)}"]`;
- if (Fe(o))
- return o;
- }
- }
- return null;
- }
- },
- {
- name: "Semantic Attributes",
- priority: 3,
- generator: (e) => {
- const t = [
- "aria-label",
- "aria-labelledby",
- "role",
- "type",
- "placeholder",
- "title",
- "alt"
- ], n = [e.tagName.toLowerCase()];
- for (const o of t) {
- const a = e.getAttribute(o);
- a && n.push(`[${o}="${CSS.escape(a)}"]`);
- }
- if (n.length > 1) {
- const o = n.join("");
- if (Fe(o))
- return o;
- }
- return null;
- }
- },
- {
- name: "Structural Attributes",
- priority: 4,
- generator: (e) => {
- const t = ["href", "src", "action", "value"], r = e.tagName.toLowerCase();
- for (const n of t) {
- const o = e.getAttribute(n);
- if (o && o.length > 0) {
- const a = `${r}[${n}="${CSS.escape(o)}"]`;
- if (Fe(a))
- return a;
- }
- }
- return null;
- }
- },
- {
- name: "Class Combinations",
- priority: 5,
- generator: (e) => {
- const t = Ut(e);
- if (t.length === 0)
- return null;
- const r = e.tagName.toLowerCase();
- for (const n of t) {
- const o = `${r}.${CSS.escape(n)}`;
- if (Fe(o))
- return o;
- }
- if (t.length >= 2) {
- const n = t.slice(0, 3).map((a) => `.${CSS.escape(a)}`).join(""), o = `${r}${n}`;
- if (Fe(o))
- return o;
- }
- return null;
- }
- },
- {
- name: "Parent Context",
- priority: 6,
- generator: (e) => {
- const t = e.parentElement;
- if (!t) return null;
- const r = Ds(t);
- if (!r) return null;
- const n = e.tagName.toLowerCase(), o = no(e);
- if (o > 0) {
- const s = `${r} > ${n}:nth-of-type(${o})`;
- if (Fe(s))
- return s;
- }
- const a = Ut(e);
- if (a.length > 0) {
- const s = `${r} > ${n}.${CSS.escape(a[0])}`;
- if (Fe(s))
- return s;
- }
- return null;
- }
- }
- ];
-}
-function Ut(e) {
- const t = Array.from(e.classList), r = [
- /^(is-|has-|js-)/,
- // State prefixes
- /^(active|disabled|loading|selected|hover|focus)/,
- // State classes
- /^[a-z]+-[0-9]+$/,
- // Generated classes like 'item-123'
- /^(sm-|md-|lg-|xl-)/,
- // Responsive utilities
- /^(m-|p-|w-|h-|text-|bg-)/,
- // Tailwind-like utilities
- /^[a-f0-9]{6,}$/
- // Hash-like classes
- ];
- return t.filter((n) => !r.some((o) => o.test(n)));
-}
-function Ls(e) {
- const t = [];
- let r = e;
- for (; r && r !== document.body && t.length < 5; ) {
- const n = js(r);
- t.unshift(n);
- const o = t.join(" > ");
- if (Fe(o))
- return o;
- r = r.parentElement;
- }
- return t.join(" > ");
-}
-function Ds(e) {
- if (e.id && Lr(e.id))
- return `#${CSS.escape(e.id)}`;
- const t = ["data-testid", "data-id", "name"];
- for (const o of t) {
- const a = e.getAttribute(o);
- if (a)
- return `[${o}="${CSS.escape(a)}"]`;
- }
- const r = e.tagName.toLowerCase(), n = Ut(e);
- return n.length > 0 ? `${r}.${CSS.escape(n[0])}` : null;
-}
-function js(e) {
- const t = e.tagName.toLowerCase();
- if (e.id && Lr(e.id))
- return `#${CSS.escape(e.id)}`;
- const r = Ut(e);
- if (r.length > 0)
- return `${t}.${CSS.escape(r[0])}`;
- const n = no(e);
- return n > 1 ? `${t}:nth-of-type(${n})` : t;
-}
-function no(e) {
- let t = 1, r = e.previousElementSibling;
- for (; r; )
- r.tagName === e.tagName && t++, r = r.previousElementSibling;
- return t;
-}
-function Hs(e, t) {
- try {
- const r = document.querySelectorAll(e);
- return r.length === 1 && r[0] === t;
- } catch (r) {
- return console.warn(`Invalid selector: ${e}`, r), !1;
- }
-}
-function Fe(e) {
- try {
- return document.querySelectorAll(e).length === 1;
- } catch {
- return !1;
- }
-}
-function Lr(e) {
- return ![
- /^[a-f0-9]{8,}$/,
- // Hex strings
- /^(ember|react|vue)[0-9]+/,
- // Framework generated
- /^[0-9]+$/,
- // Pure numbers
- /^temp-/
- // Temporary prefixes
- ].some((r) => r.test(e));
-}
-const Fs = (e, t) => {
- const r = e.pageX, n = e.pageY, o = t.getBoundingClientRect(), a = o.left + window.scrollX, s = o.top + window.scrollY, i = r - a, u = n - s;
- let c = i / t.offsetWidth * 100, v = u / t.offsetHeight * 100;
- return c = Number(c.toFixed(2)), v = Number(v.toFixed(2)), {
- selectorOffsetX: c,
- selectorOffsetY: v
- };
-}, Vs = (e) => {
- const t = Os(e), r = document.querySelector(t);
- if (!r) return;
- const { selectorOffsetX: n, selectorOffsetY: o } = Fs(e, r);
- let a = e.pageX, s = e.pageY;
- return a = Number(a.toFixed(2)), s = Number(s.toFixed(2)), {
- selector: t,
- selectorOffsetX: n,
- selectorOffsetY: o,
- pagePositionX: a,
- pagePositionY: s
- };
-}, Bs = () => {
- const e = document.body, t = document.documentElement;
- return Math.max(
- e.scrollWidth,
- e.offsetWidth,
- t.clientWidth,
- t.scrollWidth,
- t.offsetWidth
- );
-}, Ys = (e, t) => {
- let r = 0, n = 0;
- if (!e || !t) return { left: r, top: n };
- r = e.pagePositionX, n = e.pagePositionY;
- const o = t.offsetWidth, a = t.offsetHeight, s = Bs(), i = Xn();
- return r + o > s && (r = s - o), n + a > i && (n = i - a), r = Math.max(0, r), n = Math.max(0, n), { left: r, top: n };
-};
-var Ws = /* @__PURE__ */ W("");
-const Xs = {
- hash: "svelte-iihm64",
- code: "dialog.svelte-iihm64{--loop-textarea-font-size:var(--comment-dialog-textarea-font-size);position:var(--comment-dialog-position);top:var(--top);left:var(--left);max-width:var(--comment-dialog-max-width);border-radius:var(--comment-dialog-border-radius);visibility:hidden;width:100%;max-height:none;box-shadow:var(--comment-dialog-shadow);border:0;margin:0;padding:0;overflow:hidden}dialog.is-visible.svelte-iihm64{visibility:visible}dialog.svelte-iihm64::backdrop{background-color:var(--comment-dialog-backdrop-background)}"
-};
-function oo(e, t) {
- we(t, !0), se(e, Xs);
- const r = q(t, "handleSubmit", 7), n = q(t, "showModal", 7), o = q(t, "newMarker", 7), a = q(t, "cancel", 7);
- let s, i = ue(J({ left: 0, top: 0 })), u = ue(!1);
- et(() => {
- n() ? (s.showModal(), T(i, J(Ys(o(), s))), T(u, !0)) : (s.close(), T(u, !1));
- });
- var c = Ws();
- let v;
- var m = C(c);
- return Ar(m, {
- get handleSubmit() {
- return r();
- },
- get cancel() {
- return a();
- }
- }), x(c), tt(c, (l) => s = l, () => s), j(
- (l) => {
- _a(c, `--left: ${h(i).left ?? ""}px; --top: ${h(i).top ?? ""}px;`), v = ot(c, 1, "svelte-iihm64", null, v, l);
- },
- [() => ({ "is-visible": h(u) })]
- ), Ze("close", c, function(...l) {
- var f;
- (f = a()) == null || f.apply(this, l);
- }), $(e, c), _e({
- get handleSubmit() {
- return r();
- },
- set handleSubmit(l) {
- r(l), E();
- },
- get showModal() {
- return n();
- },
- set showModal(l) {
- n(l), E();
- },
- get newMarker() {
- return o();
- },
- set newMarker(l) {
- o(l), E();
- },
- get cancel() {
- return a();
- },
- set cancel(l) {
- a(l), E();
- }
- });
-}
-ae(
- oo,
- {
- handleSubmit: {},
- showModal: {},
- newMarker: {},
- cancel: {}
- },
- [],
- [],
- !0
-);
-var Us = /* @__PURE__ */ W('
', 1), Zs = /* @__PURE__ */ W(''), Js = /* @__PURE__ */ W('');
-const Ks = {
- hash: "svelte-1y3js7r",
- code: "dialog.svelte-1y3js7r{-webkit-backdrop-filter:var(--welcome-dialog-backdrop-filter);backdrop-filter:var(--welcome-dialog-backdrop-filter);border:var(--welcome-dialog-border);border-radius:var(--welcome-dialog-border-radius);box-shadow:var(--welcome-dialog-shadow);width:100%;max-width:var(--welcome-dialog-max-width);background:var(--welcome-dialog-background);padding:0}dialog.svelte-1y3js7r::backdrop{background:var(--welcome-dialog-backdrop-background);-webkit-backdrop-filter:var(--welcome-dialog-backdrop-backdrop-filter);backdrop-filter:var(--welcome-dialog-backdrop-backdrop-filter)}form.svelte-1y3js7r{padding:var(--welcome-dialog-form-padding)}h2.svelte-1y3js7r{margin:var(--welcome-dialog-title-margin);font-size:var(--welcome-dialog-title-font-size);color:var(--welcome-dialog-title-color);font-weight:var(--welcome-dialog-title-font-weight)}.welcome-text.svelte-1y3js7r{margin:var(--welcome-dialog-text-margin);font-size:var(--welcome-dialog-text-font-size);color:var(--welcome-dialog-text-color);line-height:var(--welcome-dialog-text-line-height)}.name-section.svelte-1y3js7r{margin-bottom:var(--welcome-dialog-name-section-margin)}.name-section.no-welcome.svelte-1y3js7r{border-top:none;padding-top:0}input.svelte-1y3js7r{border:var(--welcome-dialog-input-border);border-radius:var(--welcome-dialog-input-border-radius);width:100%;padding:var(--welcome-dialog-input-padding);box-sizing:border-box;font-family:var(--welcome-dialog-input-font-family);font-size:var(--welcome-dialog-input-font-size);color:var(--welcome-dialog-input-color);background:var(--welcome-dialog-input-background)}input.svelte-1y3js7r:focus-visible{outline-color:var(--welcome-dialog-input-outline-color);outline-offset:var(--welcome-dialog-input-outline-offset)}footer.svelte-1y3js7r{gap:var(--welcome-dialog-footer-gap);display:flex}footer.svelte-1y3js7r button{flex:1}"
-};
-function ao(e, t) {
- we(t, !0), se(e, Ks);
- const r = q(t, "headline", 7), n = q(t, "text", 7), o = q(t, "authenticated", 7), a = q(t, "welcomeEnabled", 7, !0), s = q(t, "onDismiss", 7);
- let i, u = ue(""), c = ue(!1);
- const v = () => i == null ? void 0 : i.showModal(), m = () => i == null ? void 0 : i.close();
- async function l(b) {
- var P;
- if (b.preventDefault(), o())
- (P = s()) == null || P(), i == null || i.close();
- else if (h(u).trim() && !h(c)) {
- T(c, !0);
- try {
- await as(h(u).trim()), lt.set(h(u).trim()), i == null || i.close();
- } catch (N) {
- console.error("Failed to set guest name:", N);
- } finally {
- T(c, !1);
- }
- }
- }
- function f() {
- i == null || i.close();
- }
- function d() {
- T(u, ""), T(c, !1);
- }
- var p = Js(), w = C(p), I = C(w);
- {
- var _ = (b) => {
- var P = Us(), N = Oe(P), L = C(N, !0);
- x(N);
- var K = V(N, 2), Q = C(K, !0);
- x(K), j(() => {
- F(L, r()), F(Q, n());
- }), $(b, P);
- };
- $e(I, (b) => {
- a() && b(_);
- });
- }
- var k = V(I, 2);
- {
- var y = (b) => {
- var P = Zs();
- let N;
- var L = C(P), K = C(L);
- xa(K), x(L), x(P), j(
- (Q, re) => {
- N = ot(P, 1, "name-section svelte-1y3js7r", null, N, Q), G(K, "placeholder", re);
- },
- [
- () => ({ "no-welcome": !a() }),
- () => O("ui.welcome.guest.name.placeholder", "Enter your name")
- ]
- ), Hn(K, () => h(u), (Q) => T(u, Q)), $(b, P);
- };
- $e(k, (b) => {
- o() || b(y);
- });
- }
- var g = V(k, 2), R = C(g);
- te(R, {
- type: "submit",
- style: "button--solid",
- get disabled() {
- return h(c);
- },
- children: (b, P) => {
- var N = Rr(), L = Oe(N);
- {
- var K = (re) => {
- var z = le();
- j((S) => F(z, S), [
- () => h(c) ? "Saving..." : O("ui.welcome.continue", "Continue")
- ]), $(re, z);
- }, Q = (re) => {
- var z = le();
- j((S) => F(z, S), [() => O("ui.welcome.continue", "Continue")]), $(re, z);
- };
- $e(L, (re) => {
- o() ? re(Q, !1) : re(K);
- });
- }
- $(b, N);
- },
- $$slots: { default: !0 }
- });
- var M = V(R, 2);
- return te(M, {
- onclick: f,
- get disabled() {
- return h(c);
- },
- children: (b, P) => {
- ge();
- var N = le();
- j((L) => F(N, L), [() => O("ui.welcome.dismiss", "Dismiss")]), $(b, N);
- },
- $$slots: { default: !0 }
- }), x(g), x(w), x(p), tt(p, (b) => i = b, () => i), Ze("close", p, d), Ze("submit", w, l), $(e, p), _e({
- showModal: v,
- close: m,
- get headline() {
- return r();
- },
- set headline(b) {
- r(b), E();
- },
- get text() {
- return n();
- },
- set text(b) {
- n(b), E();
- },
- get authenticated() {
- return o();
- },
- set authenticated(b) {
- o(b), E();
- },
- get welcomeEnabled() {
- return a();
- },
- set welcomeEnabled(b = !0) {
- a(b), E();
- },
- get onDismiss() {
- return s();
- },
- set onDismiss(b) {
- s(b), E();
- }
- });
-}
-ae(
- ao,
- {
- headline: {},
- text: {},
- authenticated: {},
- welcomeEnabled: {},
- onDismiss: {}
- },
- [],
- ["showModal", "close"],
- !0
-);
-var Gs = /* @__PURE__ */ W(" ", 1), Qs = /* @__PURE__ */ W(" ", 1);
-function ei(e, t) {
- we(t, !0);
- const r = q(t, "position", 7), n = q(t, "language", 7), o = q(t, "apibase", 7), a = q(t, "pageId", 7), s = q(t, "authenticated", 7), i = q(t, "welcome-enabled", 7), u = q(t, "welcome-headline", 7), c = q(t, "welcome-text", 7), v = q(t, "translations", 7);
- let m = ue(!1), l = ue(!1), f, d = /* @__PURE__ */ ee(() => s() === "true"), p = /* @__PURE__ */ ee(() => i() === "true");
- const w = /* @__PURE__ */ ee(() => qe.comments.filter((z) => z.status !== "RESOLVED")), I = "loop-welcome-dismissed", _ = () => h(d) ? sessionStorage.getItem(I) === "true" : !1, k = () => {
- h(d) && sessionStorage.setItem(I, "true");
- };
- let y = ue(null);
- const g = (z) => {
- var D;
- const S = (D = t.$$host.shadowRoot) == null ? void 0 : D.getElementById(`marker-${z}`);
- S && S.scrollIntoView({ behavior: "smooth", block: "center" });
- }, R = (z) => {
- var ce;
- const S = z.target, D = S.nodeName === "KIRBY-LOOP" || ((ce = S.parentElement) == null ? void 0 : ce.closest("loop"));
- if (!Je.open || D) return;
- if (!h(d) && !lt.get()) {
- f == null || f.showModal();
- return;
- }
- const X = Vs(z);
- X && (T(y, J(X)), T(l, !0));
- }, M = () => {
- T(l, !1), cr();
- }, b = (z) => {
- if (z.preventDefault(), !h(d) && !lt.get()) {
- f == null || f.showModal();
- return;
- }
- const { text: S, parentId: D } = ct;
- if (D)
- ss({ parentId: D, comment: S }), cr();
- else {
- if (!h(y)) return;
- const X = n() || "", ce = {
- url: window.location.href,
- comment: S,
- parentId: null,
- lang: X,
- pageId: a(),
- ...h(y)
- };
- T(l, !1), rs(ce), cr();
- }
- };
- rr(async () => {
- const z = JSON.parse(v() || "{}");
- Ra(z), T(m, J(await ts(a()))), lt.get(), h(p) && h(m) ? (h(d) && !_() || !h(d) && !lt.get()) && (f == null || f.showModal()) : !h(d) && !lt.get() && h(m) && (f == null || f.showModal());
- }), et(() => {
- Je.open ? document.body.style.setProperty("cursor", `url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z' stroke='black' stroke-width='1.5'/%3E%3Cpath d='M12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23Z' stroke='white' stroke-width='0.75'/%3E%3Cpath d='M15 12H12M12 12H9M12 12V9M12 12V15' stroke='white' stroke-width='3' stroke-linecap='round'/%3E%3Cpath d='M15 12H12M12 12H9M12 12V9M12 12V15' stroke='black' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E"), auto`) : document.body.style.removeProperty("cursor"), document.documentElement.classList.toggle("loop-overlay-open", Je.open);
- });
- var P = Qs();
- Ze("click", pn, R);
- var N = Oe(P);
- {
- var L = (z) => {
- var S = Gs(), D = Oe(S);
- Wn(D, {
- get position() {
- return r();
- },
- get commentsCount() {
- return h(w).length;
- }
- });
- var X = V(D, 2);
- ro(X, { scrollIntoView: g, handleSubmit: b, cancel: M });
- var ce = V(X, 2);
- Tr(ce, 17, () => h(w), (ne) => ne.id, (ne, He) => {
- Un(ne, {
- get comment() {
- return h(He);
- }
- });
- });
- var ye = V(ce, 2);
- oo(ye, {
- handleSubmit: b,
- get showModal() {
- return h(l);
- },
- get newMarker() {
- return h(y);
- },
- cancel: M
- }), $(z, S);
- };
- $e(N, (z) => {
- h(m) && z(L);
- });
- }
- var K = V(N, 2);
- const Q = /* @__PURE__ */ ee(() => u() || ""), re = /* @__PURE__ */ ee(() => c() || "");
- return tt(
- ao(K, {
- get headline() {
- return h(Q);
- },
- get text() {
- return h(re);
- },
- get authenticated() {
- return h(d);
- },
- get welcomeEnabled() {
- return h(p);
- },
- onDismiss: k
- }),
- (z) => f = z,
- () => f
- ), $(e, P), _e({
- get position() {
- return r();
- },
- set position(z) {
- r(z), E();
- },
- get language() {
- return n();
- },
- set language(z) {
- n(z), E();
- },
- get apibase() {
- return o();
- },
- set apibase(z) {
- o(z), E();
- },
- get pageId() {
- return a();
- },
- set pageId(z) {
- a(z), E();
- },
- get authenticated() {
- return s();
- },
- set authenticated(z) {
- s(z), E();
- },
- get "welcome-enabled"() {
- return i();
- },
- set "welcome-enabled"(z) {
- i(z), E();
- },
- get "welcome-headline"() {
- return u();
- },
- set "welcome-headline"(z) {
- u(z), E();
- },
- get "welcome-text"() {
- return c();
- },
- set "welcome-text"(z) {
- c(z), E();
- },
- get translations() {
- return v();
- },
- set translations(z) {
- v(z), E();
- }
- });
-}
-customElements.define("kirby-loop", ae(
- ei,
- {
- position: {},
- language: {},
- apibase: {},
- pageId: {},
- authenticated: {},
- "welcome-enabled": {},
- "welcome-headline": {},
- "welcome-text": {},
- translations: {}
- },
- [],
- [],
- !0
-));
-export {
- ei as default
-};
diff --git a/site/plugins/loop/biome.json b/site/plugins/loop/biome.json
deleted file mode 100644
index 30a68d1..0000000
--- a/site/plugins/loop/biome.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "overrides": [
- {
- "include": ["*.svelte", "*.astro", "*.vue"],
- "linter": {
- "rules": {
- "style": {
- "useConst": "off",
- "useImportType": "off"
- }
- }
- }
- }
- ]
-}
diff --git a/site/plugins/loop/composer.json b/site/plugins/loop/composer.json
deleted file mode 100755
index 0ef6dea..0000000
--- a/site/plugins/loop/composer.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "name": "moinframe/kirby-loop",
- "description": "Interactive feedback tool for Kirby CMS websites that allows users to add contextual comments directly on page elements",
- "homepage": "https://github.com/moinframe/kirby-loop",
- "license": "MIT",
- "type": "kirby-plugin",
- "version": "1.0.1",
- "keywords": [
- "kirby",
- "cms",
- "plugin",
- "feedback",
- "comments",
- "review",
- "loop"
- ],
- "authors": [
- {
- "name": "Justus Kraft",
- "email": "justus@moinfra.me",
- "homepage": "https://moinfra.me"
- }
- ],
- "require": {
- "getkirby/composer-installer": "^1.1",
- "getkirby/cms": "^4.0||^5.0"
- },
- "autoload": {
- "psr-4": {
- "Moinframe\\Loop\\": "src/"
- }
- },
- "support": {
- "docs": "https://moinfra.me/docs/moinframe-loop",
- "source": "https://github.com/moinframe/kirby-loop",
- "issues": "https://github.com/moinframe/kirby-loop/issues"
- },
- "scripts": {
- "analyse": "vendor/bin/phpstan analyse"
- },
- "config": {
- "optimize-autoloader": true,
- "allow-plugins": {
- "getkirby/composer-installer": true
- }
- },
- "extra": {
- "installer-name": "loop"
- },
- "require-dev": {
- "phpstan/phpstan": "^2.1",
- "phpstan/phpstan-strict-rules": "^2.0",
- "phpstan/phpstan-deprecation-rules": "^2.0"
- }
-}
diff --git a/site/plugins/loop/composer.lock b/site/plugins/loop/composer.lock
deleted file mode 100644
index b6d4f74..0000000
--- a/site/plugins/loop/composer.lock
+++ /dev/null
@@ -1,1358 +0,0 @@
-{
- "_readme": [
- "This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
- "This file is @generated automatically"
- ],
- "content-hash": "7c44133100ded43f96ca00fffaa78947",
- "packages": [
- {
- "name": "christian-riesen/base32",
- "version": "1.6.0",
- "source": {
- "type": "git",
- "url": "https://github.com/ChristianRiesen/base32.git",
- "reference": "2e82dab3baa008e24a505649b0d583c31d31e894"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/2e82dab3baa008e24a505649b0d583c31d31e894",
- "reference": "2e82dab3baa008e24a505649b0d583c31d31e894",
- "shasum": ""
- },
- "require": {
- "php": "^7.2 || ^8.0"
- },
- "require-dev": {
- "friendsofphp/php-cs-fixer": "^2.17",
- "phpstan/phpstan": "^0.12",
- "phpunit/phpunit": "^8.5.13 || ^9.5"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Base32\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Christian Riesen",
- "email": "chris.riesen@gmail.com",
- "homepage": "http://christianriesen.com",
- "role": "Developer"
- }
- ],
- "description": "Base32 encoder/decoder according to RFC 4648",
- "homepage": "https://github.com/ChristianRiesen/base32",
- "keywords": [
- "base32",
- "decode",
- "encode",
- "rfc4648"
- ],
- "support": {
- "issues": "https://github.com/ChristianRiesen/base32/issues",
- "source": "https://github.com/ChristianRiesen/base32/tree/1.6.0"
- },
- "time": "2021-02-26T10:19:33+00:00"
- },
- {
- "name": "claviska/simpleimage",
- "version": "4.2.1",
- "source": {
- "type": "git",
- "url": "https://github.com/claviska/SimpleImage.git",
- "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/ec6d5021e5a7153a2520d64c59b86b6f3c4157c5",
- "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5",
- "shasum": ""
- },
- "require": {
- "ext-gd": "*",
- "league/color-extractor": "0.4.*",
- "php": ">=8.0"
- },
- "require-dev": {
- "laravel/pint": "^1.5",
- "phpstan/phpstan": "^1.10"
- },
- "type": "library",
- "autoload": {
- "psr-0": {
- "claviska": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Cory LaViska",
- "homepage": "http://www.abeautifulsite.net/",
- "role": "Developer"
- }
- ],
- "description": "A PHP class that makes working with images as simple as possible.",
- "support": {
- "issues": "https://github.com/claviska/SimpleImage/issues",
- "source": "https://github.com/claviska/SimpleImage/tree/4.2.1"
- },
- "funding": [
- {
- "url": "https://github.com/claviska",
- "type": "github"
- }
- ],
- "time": "2024-11-22T13:25:03+00:00"
- },
- {
- "name": "composer/semver",
- "version": "3.4.3",
- "source": {
- "type": "git",
- "url": "https://github.com/composer/semver.git",
- "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
- "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
- "shasum": ""
- },
- "require": {
- "php": "^5.3.2 || ^7.0 || ^8.0"
- },
- "require-dev": {
- "phpstan/phpstan": "^1.11",
- "symfony/phpunit-bridge": "^3 || ^7"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "3.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Composer\\Semver\\": "src"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nils Adermann",
- "email": "naderman@naderman.de",
- "homepage": "http://www.naderman.de"
- },
- {
- "name": "Jordi Boggiano",
- "email": "j.boggiano@seld.be",
- "homepage": "http://seld.be"
- },
- {
- "name": "Rob Bast",
- "email": "rob.bast@gmail.com",
- "homepage": "http://robbast.nl"
- }
- ],
- "description": "Semver library that offers utilities, version constraint parsing and validation.",
- "keywords": [
- "semantic",
- "semver",
- "validation",
- "versioning"
- ],
- "support": {
- "irc": "ircs://irc.libera.chat:6697/composer",
- "issues": "https://github.com/composer/semver/issues",
- "source": "https://github.com/composer/semver/tree/3.4.3"
- },
- "funding": [
- {
- "url": "https://packagist.com",
- "type": "custom"
- },
- {
- "url": "https://github.com/composer",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/composer/composer",
- "type": "tidelift"
- }
- ],
- "time": "2024-09-19T14:15:21+00:00"
- },
- {
- "name": "filp/whoops",
- "version": "2.18.3",
- "source": {
- "type": "git",
- "url": "https://github.com/filp/whoops.git",
- "reference": "59a123a3d459c5a23055802237cb317f609867e5"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/filp/whoops/zipball/59a123a3d459c5a23055802237cb317f609867e5",
- "reference": "59a123a3d459c5a23055802237cb317f609867e5",
- "shasum": ""
- },
- "require": {
- "php": "^7.1 || ^8.0",
- "psr/log": "^1.0.1 || ^2.0 || ^3.0"
- },
- "require-dev": {
- "mockery/mockery": "^1.0",
- "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3",
- "symfony/var-dumper": "^4.0 || ^5.0"
- },
- "suggest": {
- "symfony/var-dumper": "Pretty print complex values better with var-dumper available",
- "whoops/soap": "Formats errors as SOAP responses"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.7-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Whoops\\": "src/Whoops/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Filipe Dobreira",
- "homepage": "https://github.com/filp",
- "role": "Developer"
- }
- ],
- "description": "php error handling for cool kids",
- "homepage": "https://filp.github.io/whoops/",
- "keywords": [
- "error",
- "exception",
- "handling",
- "library",
- "throwable",
- "whoops"
- ],
- "support": {
- "issues": "https://github.com/filp/whoops/issues",
- "source": "https://github.com/filp/whoops/tree/2.18.3"
- },
- "funding": [
- {
- "url": "https://github.com/denis-sokolov",
- "type": "github"
- }
- ],
- "time": "2025-06-16T00:02:10+00:00"
- },
- {
- "name": "getkirby/cms",
- "version": "5.0.1",
- "source": {
- "type": "git",
- "url": "https://github.com/getkirby/kirby.git",
- "reference": "ddd11f9b4a23e0b5d306b146ca5d6b7a1c1909a9"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/getkirby/kirby/zipball/ddd11f9b4a23e0b5d306b146ca5d6b7a1c1909a9",
- "reference": "ddd11f9b4a23e0b5d306b146ca5d6b7a1c1909a9",
- "shasum": ""
- },
- "require": {
- "christian-riesen/base32": "1.6.0",
- "claviska/simpleimage": "4.2.1",
- "composer/semver": "3.4.3",
- "ext-ctype": "*",
- "ext-curl": "*",
- "ext-dom": "*",
- "ext-filter": "*",
- "ext-hash": "*",
- "ext-iconv": "*",
- "ext-json": "*",
- "ext-libxml": "*",
- "ext-mbstring": "*",
- "ext-openssl": "*",
- "ext-simplexml": "*",
- "filp/whoops": "2.18.3",
- "getkirby/composer-installer": "^1.2.1",
- "laminas/laminas-escaper": "2.17.0",
- "michelf/php-smartypants": "1.8.1",
- "php": "~8.2.0 || ~8.3.0 || ~8.4.0",
- "phpmailer/phpmailer": "6.10.0",
- "symfony/polyfill-intl-idn": "1.32.0",
- "symfony/polyfill-mbstring": "1.32.0",
- "symfony/yaml": "7.3.1"
- },
- "replace": {
- "symfony/polyfill-php72": "*"
- },
- "suggest": {
- "ext-PDO": "Support for using databases",
- "ext-apcu": "Support for the Apcu cache driver",
- "ext-exif": "Support for exif information from images",
- "ext-fileinfo": "Improved mime type detection for files",
- "ext-intl": "Improved i18n number formatting",
- "ext-memcached": "Support for the Memcached cache driver",
- "ext-redis": "Support for the Redis cache driver",
- "ext-sodium": "Support for the crypto class and more robust session handling",
- "ext-zip": "Support for ZIP archive file functions",
- "ext-zlib": "Sanitization and validation for svgz files"
- },
- "type": "kirby-cms",
- "extra": {
- "unused": [
- "symfony/polyfill-intl-idn"
- ]
- },
- "autoload": {
- "files": [
- "config/setup.php",
- "config/helpers.php"
- ],
- "psr-4": {
- "Kirby\\": "src/"
- },
- "classmap": [
- "dependencies/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "proprietary"
- ],
- "authors": [
- {
- "name": "Kirby Team",
- "email": "support@getkirby.com",
- "homepage": "https://getkirby.com"
- }
- ],
- "description": "The Kirby core",
- "homepage": "https://getkirby.com",
- "keywords": [
- "cms",
- "core",
- "kirby"
- ],
- "support": {
- "email": "support@getkirby.com",
- "forum": "https://forum.getkirby.com",
- "issues": "https://github.com/getkirby/kirby/issues",
- "source": "https://github.com/getkirby/kirby"
- },
- "funding": [
- {
- "url": "https://getkirby.com/buy",
- "type": "custom"
- }
- ],
- "time": "2025-07-03T11:23:56+00:00"
- },
- {
- "name": "getkirby/composer-installer",
- "version": "1.2.1",
- "source": {
- "type": "git",
- "url": "https://github.com/getkirby/composer-installer.git",
- "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/getkirby/composer-installer/zipball/c98ece30bfba45be7ce457e1102d1b169d922f3d",
- "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d",
- "shasum": ""
- },
- "require": {
- "composer-plugin-api": "^1.0 || ^2.0"
- },
- "require-dev": {
- "composer/composer": "^1.8 || ^2.0"
- },
- "type": "composer-plugin",
- "extra": {
- "class": "Kirby\\ComposerInstaller\\Plugin"
- },
- "autoload": {
- "psr-4": {
- "Kirby\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "description": "Kirby's custom Composer installer for the Kirby CMS and for Kirby plugins",
- "homepage": "https://getkirby.com",
- "support": {
- "issues": "https://github.com/getkirby/composer-installer/issues",
- "source": "https://github.com/getkirby/composer-installer/tree/1.2.1"
- },
- "funding": [
- {
- "url": "https://getkirby.com/buy",
- "type": "custom"
- }
- ],
- "time": "2020-12-28T12:54:39+00:00"
- },
- {
- "name": "laminas/laminas-escaper",
- "version": "2.17.0",
- "source": {
- "type": "git",
- "url": "https://github.com/laminas/laminas-escaper.git",
- "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/df1ef9503299a8e3920079a16263b578eaf7c3ba",
- "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba",
- "shasum": ""
- },
- "require": {
- "ext-ctype": "*",
- "ext-mbstring": "*",
- "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
- },
- "conflict": {
- "zendframework/zend-escaper": "*"
- },
- "require-dev": {
- "infection/infection": "^0.29.8",
- "laminas/laminas-coding-standard": "~3.0.1",
- "phpunit/phpunit": "^10.5.45",
- "psalm/plugin-phpunit": "^0.19.2",
- "vimeo/psalm": "^6.6.2"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Laminas\\Escaper\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs",
- "homepage": "https://laminas.dev",
- "keywords": [
- "escaper",
- "laminas"
- ],
- "support": {
- "chat": "https://laminas.dev/chat",
- "docs": "https://docs.laminas.dev/laminas-escaper/",
- "forum": "https://discourse.laminas.dev",
- "issues": "https://github.com/laminas/laminas-escaper/issues",
- "rss": "https://github.com/laminas/laminas-escaper/releases.atom",
- "source": "https://github.com/laminas/laminas-escaper"
- },
- "funding": [
- {
- "url": "https://funding.communitybridge.org/projects/laminas-project",
- "type": "community_bridge"
- }
- ],
- "time": "2025-05-06T19:29:36+00:00"
- },
- {
- "name": "league/color-extractor",
- "version": "0.4.0",
- "source": {
- "type": "git",
- "url": "https://github.com/thephpleague/color-extractor.git",
- "reference": "21fcac6249c5ef7d00eb83e128743ee6678fe505"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/thephpleague/color-extractor/zipball/21fcac6249c5ef7d00eb83e128743ee6678fe505",
- "reference": "21fcac6249c5ef7d00eb83e128743ee6678fe505",
- "shasum": ""
- },
- "require": {
- "ext-gd": "*",
- "php": "^7.3 || ^8.0"
- },
- "replace": {
- "matthecat/colorextractor": "*"
- },
- "require-dev": {
- "friendsofphp/php-cs-fixer": "~2",
- "phpunit/phpunit": "^9.5"
- },
- "suggest": {
- "ext-curl": "To download images from remote URLs if allow_url_fopen is disabled for security reasons"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "League\\ColorExtractor\\": "src"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Mathieu Lechat",
- "email": "math.lechat@gmail.com",
- "homepage": "http://matthecat.com",
- "role": "Developer"
- }
- ],
- "description": "Extract colors from an image as a human would do.",
- "homepage": "https://github.com/thephpleague/color-extractor",
- "keywords": [
- "color",
- "extract",
- "human",
- "image",
- "palette"
- ],
- "support": {
- "issues": "https://github.com/thephpleague/color-extractor/issues",
- "source": "https://github.com/thephpleague/color-extractor/tree/0.4.0"
- },
- "time": "2022-09-24T15:57:16+00:00"
- },
- {
- "name": "michelf/php-smartypants",
- "version": "1.8.1",
- "source": {
- "type": "git",
- "url": "https://github.com/michelf/php-smartypants.git",
- "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/michelf/php-smartypants/zipball/47d17c90a4dfd0ccf1f87e25c65e6c8012415aad",
- "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.0"
- },
- "type": "library",
- "autoload": {
- "psr-0": {
- "Michelf": ""
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Michel Fortin",
- "email": "michel.fortin@michelf.ca",
- "homepage": "https://michelf.ca/",
- "role": "Developer"
- },
- {
- "name": "John Gruber",
- "homepage": "https://daringfireball.net/"
- }
- ],
- "description": "PHP SmartyPants",
- "homepage": "https://michelf.ca/projects/php-smartypants/",
- "keywords": [
- "dashes",
- "quotes",
- "spaces",
- "typographer",
- "typography"
- ],
- "support": {
- "issues": "https://github.com/michelf/php-smartypants/issues",
- "source": "https://github.com/michelf/php-smartypants/tree/1.8.1"
- },
- "time": "2016-12-13T01:01:17+00:00"
- },
- {
- "name": "phpmailer/phpmailer",
- "version": "v6.10.0",
- "source": {
- "type": "git",
- "url": "https://github.com/PHPMailer/PHPMailer.git",
- "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144",
- "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144",
- "shasum": ""
- },
- "require": {
- "ext-ctype": "*",
- "ext-filter": "*",
- "ext-hash": "*",
- "php": ">=5.5.0"
- },
- "require-dev": {
- "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
- "doctrine/annotations": "^1.2.6 || ^1.13.3",
- "php-parallel-lint/php-console-highlighter": "^1.0.0",
- "php-parallel-lint/php-parallel-lint": "^1.3.2",
- "phpcompatibility/php-compatibility": "^9.3.5",
- "roave/security-advisories": "dev-latest",
- "squizlabs/php_codesniffer": "^3.7.2",
- "yoast/phpunit-polyfills": "^1.0.4"
- },
- "suggest": {
- "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
- "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
- "ext-openssl": "Needed for secure SMTP sending and DKIM signing",
- "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
- "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
- "league/oauth2-google": "Needed for Google XOAUTH2 authentication",
- "psr/log": "For optional PSR-3 debug logging",
- "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)",
- "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "PHPMailer\\PHPMailer\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "LGPL-2.1-only"
- ],
- "authors": [
- {
- "name": "Marcus Bointon",
- "email": "phpmailer@synchromedia.co.uk"
- },
- {
- "name": "Jim Jagielski",
- "email": "jimjag@gmail.com"
- },
- {
- "name": "Andy Prevost",
- "email": "codeworxtech@users.sourceforge.net"
- },
- {
- "name": "Brent R. Matzelle"
- }
- ],
- "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
- "support": {
- "issues": "https://github.com/PHPMailer/PHPMailer/issues",
- "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.10.0"
- },
- "funding": [
- {
- "url": "https://github.com/Synchro",
- "type": "github"
- }
- ],
- "time": "2025-04-24T15:19:31+00:00"
- },
- {
- "name": "psr/log",
- "version": "3.0.2",
- "source": {
- "type": "git",
- "url": "https://github.com/php-fig/log.git",
- "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
- "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
- "shasum": ""
- },
- "require": {
- "php": ">=8.0.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Psr\\Log\\": "src"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "PHP-FIG",
- "homepage": "https://www.php-fig.org/"
- }
- ],
- "description": "Common interface for logging libraries",
- "homepage": "https://github.com/php-fig/log",
- "keywords": [
- "log",
- "psr",
- "psr-3"
- ],
- "support": {
- "source": "https://github.com/php-fig/log/tree/3.0.2"
- },
- "time": "2024-09-11T13:17:53+00:00"
- },
- {
- "name": "symfony/deprecation-contracts",
- "version": "v3.6.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
- "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
- "shasum": ""
- },
- "require": {
- "php": ">=8.1"
- },
- "type": "library",
- "extra": {
- "thanks": {
- "url": "https://github.com/symfony/contracts",
- "name": "symfony/contracts"
- },
- "branch-alias": {
- "dev-main": "3.6-dev"
- }
- },
- "autoload": {
- "files": [
- "function.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "A generic function and convention to trigger deprecation notices",
- "homepage": "https://symfony.com",
- "support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2024-09-25T14:21:43+00:00"
- },
- {
- "name": "symfony/polyfill-ctype",
- "version": "v1.32.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
- "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
- "shasum": ""
- },
- "require": {
- "php": ">=7.2"
- },
- "provide": {
- "ext-ctype": "*"
- },
- "suggest": {
- "ext-ctype": "For best performance"
- },
- "type": "library",
- "extra": {
- "thanks": {
- "url": "https://github.com/symfony/polyfill",
- "name": "symfony/polyfill"
- }
- },
- "autoload": {
- "files": [
- "bootstrap.php"
- ],
- "psr-4": {
- "Symfony\\Polyfill\\Ctype\\": ""
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Gert de Pagter",
- "email": "BackEndTea@gmail.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill for ctype functions",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "ctype",
- "polyfill",
- "portable"
- ],
- "support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2024-09-09T11:45:10+00:00"
- },
- {
- "name": "symfony/polyfill-intl-idn",
- "version": "v1.32.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-intl-idn.git",
- "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3",
- "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3",
- "shasum": ""
- },
- "require": {
- "php": ">=7.2",
- "symfony/polyfill-intl-normalizer": "^1.10"
- },
- "suggest": {
- "ext-intl": "For best performance"
- },
- "type": "library",
- "extra": {
- "thanks": {
- "url": "https://github.com/symfony/polyfill",
- "name": "symfony/polyfill"
- }
- },
- "autoload": {
- "files": [
- "bootstrap.php"
- ],
- "psr-4": {
- "Symfony\\Polyfill\\Intl\\Idn\\": ""
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Laurent Bassin",
- "email": "laurent@bassin.info"
- },
- {
- "name": "Trevor Rowbotham",
- "email": "trevor.rowbotham@pm.me"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "idn",
- "intl",
- "polyfill",
- "portable",
- "shim"
- ],
- "support": {
- "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2024-09-10T14:38:51+00:00"
- },
- {
- "name": "symfony/polyfill-intl-normalizer",
- "version": "v1.32.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "3833d7255cc303546435cb650316bff708a1c75c"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
- "reference": "3833d7255cc303546435cb650316bff708a1c75c",
- "shasum": ""
- },
- "require": {
- "php": ">=7.2"
- },
- "suggest": {
- "ext-intl": "For best performance"
- },
- "type": "library",
- "extra": {
- "thanks": {
- "url": "https://github.com/symfony/polyfill",
- "name": "symfony/polyfill"
- }
- },
- "autoload": {
- "files": [
- "bootstrap.php"
- ],
- "psr-4": {
- "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
- },
- "classmap": [
- "Resources/stubs"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill for intl's Normalizer class and related functions",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "intl",
- "normalizer",
- "polyfill",
- "portable",
- "shim"
- ],
- "support": {
- "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2024-09-09T11:45:10+00:00"
- },
- {
- "name": "symfony/polyfill-mbstring",
- "version": "v1.32.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
- "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
- "shasum": ""
- },
- "require": {
- "ext-iconv": "*",
- "php": ">=7.2"
- },
- "provide": {
- "ext-mbstring": "*"
- },
- "suggest": {
- "ext-mbstring": "For best performance"
- },
- "type": "library",
- "extra": {
- "thanks": {
- "url": "https://github.com/symfony/polyfill",
- "name": "symfony/polyfill"
- }
- },
- "autoload": {
- "files": [
- "bootstrap.php"
- ],
- "psr-4": {
- "Symfony\\Polyfill\\Mbstring\\": ""
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill for the Mbstring extension",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "mbstring",
- "polyfill",
- "portable",
- "shim"
- ],
- "support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2024-12-23T08:48:59+00:00"
- },
- {
- "name": "symfony/yaml",
- "version": "v7.3.1",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/yaml.git",
- "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/0c3555045a46ab3cd4cc5a69d161225195230edb",
- "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb",
- "shasum": ""
- },
- "require": {
- "php": ">=8.2",
- "symfony/deprecation-contracts": "^2.5|^3.0",
- "symfony/polyfill-ctype": "^1.8"
- },
- "conflict": {
- "symfony/console": "<6.4"
- },
- "require-dev": {
- "symfony/console": "^6.4|^7.0"
- },
- "bin": [
- "Resources/bin/yaml-lint"
- ],
- "type": "library",
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\Yaml\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Loads and dumps YAML files",
- "homepage": "https://symfony.com",
- "support": {
- "source": "https://github.com/symfony/yaml/tree/v7.3.1"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2025-06-03T06:57:57+00:00"
- }
- ],
- "packages-dev": [
- {
- "name": "phpstan/phpstan",
- "version": "2.1.17",
- "source": {
- "type": "git",
- "url": "https://github.com/phpstan/phpstan.git",
- "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053",
- "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053",
- "shasum": ""
- },
- "require": {
- "php": "^7.4|^8.0"
- },
- "conflict": {
- "phpstan/phpstan-shim": "*"
- },
- "bin": [
- "phpstan",
- "phpstan.phar"
- ],
- "type": "library",
- "autoload": {
- "files": [
- "bootstrap.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "description": "PHPStan - PHP Static Analysis Tool",
- "keywords": [
- "dev",
- "static analysis"
- ],
- "support": {
- "docs": "https://phpstan.org/user-guide/getting-started",
- "forum": "https://github.com/phpstan/phpstan/discussions",
- "issues": "https://github.com/phpstan/phpstan/issues",
- "security": "https://github.com/phpstan/phpstan/security/policy",
- "source": "https://github.com/phpstan/phpstan-src"
- },
- "funding": [
- {
- "url": "https://github.com/ondrejmirtes",
- "type": "github"
- },
- {
- "url": "https://github.com/phpstan",
- "type": "github"
- }
- ],
- "time": "2025-05-21T20:55:28+00:00"
- },
- {
- "name": "phpstan/phpstan-deprecation-rules",
- "version": "2.0.3",
- "source": {
- "type": "git",
- "url": "https://github.com/phpstan/phpstan-deprecation-rules.git",
- "reference": "468e02c9176891cc901143da118f09dc9505fc2f"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/468e02c9176891cc901143da118f09dc9505fc2f",
- "reference": "468e02c9176891cc901143da118f09dc9505fc2f",
- "shasum": ""
- },
- "require": {
- "php": "^7.4 || ^8.0",
- "phpstan/phpstan": "^2.1.15"
- },
- "require-dev": {
- "php-parallel-lint/php-parallel-lint": "^1.2",
- "phpstan/phpstan-phpunit": "^2.0",
- "phpunit/phpunit": "^9.6"
- },
- "type": "phpstan-extension",
- "extra": {
- "phpstan": {
- "includes": [
- "rules.neon"
- ]
- }
- },
- "autoload": {
- "psr-4": {
- "PHPStan\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
- "support": {
- "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues",
- "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.3"
- },
- "time": "2025-05-14T10:56:57+00:00"
- },
- {
- "name": "phpstan/phpstan-strict-rules",
- "version": "2.0.4",
- "source": {
- "type": "git",
- "url": "https://github.com/phpstan/phpstan-strict-rules.git",
- "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/3e139cbe67fafa3588e1dbe27ca50f31fdb6236a",
- "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a",
- "shasum": ""
- },
- "require": {
- "php": "^7.4 || ^8.0",
- "phpstan/phpstan": "^2.0.4"
- },
- "require-dev": {
- "php-parallel-lint/php-parallel-lint": "^1.2",
- "phpstan/phpstan-deprecation-rules": "^2.0",
- "phpstan/phpstan-phpunit": "^2.0",
- "phpunit/phpunit": "^9.6"
- },
- "type": "phpstan-extension",
- "extra": {
- "phpstan": {
- "includes": [
- "rules.neon"
- ]
- }
- },
- "autoload": {
- "psr-4": {
- "PHPStan\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "description": "Extra strict and opinionated rules for PHPStan",
- "support": {
- "issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
- "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.4"
- },
- "time": "2025-03-18T11:42:40+00:00"
- }
- ],
- "aliases": [],
- "minimum-stability": "stable",
- "stability-flags": {},
- "prefer-stable": false,
- "prefer-lowest": false,
- "platform": {},
- "platform-dev": {},
- "plugin-api-version": "2.6.0"
-}
diff --git a/site/plugins/loop/docs/01-installation.md b/site/plugins/loop/docs/01-installation.md
deleted file mode 100644
index de692b7..0000000
--- a/site/plugins/loop/docs/01-installation.md
+++ /dev/null
@@ -1,90 +0,0 @@
----
-title: Installation
----
-
-This guide covers all installation methods for the Kirby Loop plugin.
-
-## Prerequisites
-
-Before installing the plugin, ensure your system meets these requirements:
-
-- **Kirby CMS**: Version 4.0 or higher
-- **PHP**: Version 8.3 or higher
-- **SQLite**: Support enabled (usually included by default in PHP)
-
-## Installation Methods
-
-### Method 1: Composer (Recommended)
-
-Composer is the preferred installation method
-
-```bash
-composer require moinframe/kirby-loop
-```
-
-### Method 2: Manual Installation
-
-For environments where Composer isn't available or preferred:
-
-1. **Download the plugin**
- - Visit the [GitHub releases page](https://github.com/moinframe/kirby-loop/releases)
- - Download the latest version as a ZIP file
-
-2. **Extract and place**
- - Unzip the downloaded archive
- - Rename the folder to `loop` (remove version numbers)
- - Move the folder to `/site/plugins/loop`
-
-3. **Verify installation**
- - The plugin folder should contain `index.php` and other plugin files
- - Your final structure should be: `/site/plugins/loop/index.php`
-
-### Method 3: Git Submodule
-
-For projects using Git version control, submodules provide a clean way to include the plugin:
-
-```bash
-git submodule add https://github.com/moinframe/kirby-loop.git site/plugins/loop
-```
-
-## Next Steps
-
-After successful installation:
-
-1. **Configuration**: See [Configuration Guide](https://moinfra.me/docs/moinframe-loop/02-configuration) for customization options
-2. **Multi-language**: If using multiple languages, review [Multi-language Setup](https://moinfra.me/docs/moinframe-loop/03-multi-language)
-3. **API Integration**: For custom implementations, check the [API Reference](https://moinfra.me/docs/moinframe-loop/05-api)
-
-## Updating
-
-### Composer Updates
-```bash
-composer update moinframe/kirby-loop
-```
-
-### Manual Updates
-1. Download the new version
-2. Replace the plugin folder (backup first!)
-3. Clear any caches
-
-### Git Submodule Updates
-```bash
-git submodule update --remote site/plugins/loop
-git add site/plugins/loop
-git commit -m "Update loop plugin"
-```
-
-## Uninstallation
-
-To remove the plugin:
-
-1. **Remove plugin files**:
- - Composer: `composer remove moinframe/kirby-loop`
- - Manual: Delete `/site/plugins/loop/` folder
- - Git submodule: `git submodule deinit site/plugins/loop`
-
-2. **Clean up data** (optional):
- - Delete `/site/logs/loop/` directory to remove all comments
- - Remove configuration from `site/config/config.php`
-
-3. **Clear caches**: Clear any site caches to ensure complete removal
diff --git a/site/plugins/loop/docs/02-configuration.md b/site/plugins/loop/docs/02-configuration.md
deleted file mode 100644
index 044e11f..0000000
--- a/site/plugins/loop/docs/02-configuration.md
+++ /dev/null
@@ -1,252 +0,0 @@
----
-title: Configuration
----
-
-You can customize the plugin's look and behavior by adding configuration options.
-Add configuration options to your `site/config/config.php` file:
-
-```php
- [
- 'auto-inject' => true,
- ...
- ]
-];
-```
-
-## Configuration Options
-
-### Enable/Disable Tool
-
-**Option**: `moinframe.loop.enabled`
-**Type**: `boolean|callable`
-**Default**: `true`
-
-Controls whether loop is enabled globally or conditionally.
-
-```php
-// Simple boolean enable/disable
-'moinframe.loop.enabled' => false, // Disables globally
-
-// Use a callback for dynamic control
-'moinframe.loop.enabled' => function($page) {
- // Only enable for specific templates
- return in_array($page->template()->name(), ['article', 'blog']);
-},
-
-// Filter by page status
-'moinframe.loop.enabled' => function($page) {
- return $page->status() === 'published';
-},
-
-// Complex conditions
-'moinframe.loop.enabled' => function($page) {
- return $page->template()->name() === 'article'
- && $page->status() === 'published'
- && !$page->archived()->toBool();
-}
-```
-
-**Callback function receives:**
-- `$page` - The current Kirby page object
-
-**Common use cases:**
-- Disable feedback on specific page templates
-- Enable only for published content
-- Conditional enabling based on page fields or metadata
-
-**Note**: This option is checked both during auto-injection and manual snippet usage.
-
-### Auto-Injection
-
-**Option**: `moinframe.loop.auto-inject`
-**Type**: `boolean`
-**Default**: `true`
-
-Controls whether loop is automatically injected into all pages.
-
-```php
-// Disable auto-injection (requires manual snippet placement)
-'moinframe.loop.auto-inject' => false,
-```
-
-When disabled, you must manually add the snippet to your templates:
-
-```php
-
-```
-
-**Use cases for disabling auto-injection:**
-- Custom page templates where you want precise control
-- JavaScript-based routing (Swup, Taxi.js) that needs manual initialization
-- Conditional loading based on user roles or page types
-
-### Position
-
-**Option**: `moinframe.loop.position`
-**Type**: `string`
-**Default**: `'top'`
-**Values**: `'top'` | `'bottom'`
-
-Sets the position of loop header on the page.
-
-```php
-// Position header at bottom of page
-'moinframe.loop.position' => 'bottom',
-```
-
-### Database Path
-
-**Option**: `moinframe.loop.database`
-**Type**: `string|null`
-**Default**: `null` (uses `site/logs/loop/comments.sqlite`)
-
-Customize the SQLite database location.
-
-```php
-// Custom database path
-'moinframe.loop.database' => '/custom/path/comments.sqlite',
-
-// Alternative locations
-'moinframe.loop.database' => kirby()->root('content') . '/feedback.sqlite',
-'moinframe.loop.database' => '/var/www/data/feedback.sqlite',
-```
-
-**Important considerations:**
-- Path must be absolute
-- Directory must exist and be writable
-- Consider backup strategies for custom locations
-- Ensure path is outside web root for security
-
-### Public Access
-
-**Option**: `moinframe.loop.public`
-**Type**: `boolean`
-**Default**: `false`
-
-Controls whether loop requires authentication.
-
-```php
-// Allow public access (no authentication required)
-'moinframe.loop.public' => true,
-```
-
-**Security implications:**
-- `false` (default): Only authenticated panel users can see/use the tool
-- `true`: Anyone can add comments
-
-**Recommended for public access:**
-- Internal staging environments
-- Client review sites with controlled access
-- Public beta feedback collection
-
-### Language Override
-
-**Option**: `moinframe.loop.language`
-**Type**: `string|null`
-**Default**: `null` (auto-detect from Kirby)
-
-Force a specific UI language regardless of the current page language.
-
-```php
-// Force German UI
-'moinframe.loop.language' => 'de',
-
-// Force English UI
-'moinframe.loop.language' => 'en',
-```
-
-**When to use:**
-- Single-language sites with non-English content but English-speaking editors
-- Multi-language sites where editors prefer consistent UI language
-
-### Theme
-
-**Option**: `moinframe.loop.theme`
-**Type**: `string`
-**Default**: `'default'`
-**Values**: `'default'` | `'dark'` | custom theme name
-
-Sets the visual theme for the loop interface.
-
-```php
-// Use dark theme
-'moinframe.loop.theme' => 'dark',
-
-// Use custom theme
-'moinframe.loop.theme' => 'custom',
-```
-
-**Available themes:**
-- `'default'` - Light theme with clean, bright interface
-- `'dark'` - Dark theme for low-light environments
-- Custom theme names - See [Theming Guide](https://moinfra.me/docs/moinframe-loop/04-theming) for creating custom themes
-
-### Welcome Dialog
-
-The welcome dialog introduces new users to loop functionality.
-
-#### Enable/Disable Welcome Dialog
-
-**Option**: `moinframe.loop.welcome.enabled`
-**Type**: `boolean`
-**Default**: `true`
-
-```php
-// Disable welcome dialog
-'moinframe.loop.welcome.enabled' => false,
-```
-
-#### Custom Welcome Headline
-
-**Option**: `moinframe.loop.welcome.headline`
-**Type**: `string|null`
-**Default**: `null` (uses default translation)
-
-```php
-// Custom welcome headline
-'moinframe.loop.welcome.headline' => 'Welcome to Our Review Tool!',
-```
-
-#### Custom Welcome Text
-
-**Option**: `moinframe.loop.welcome.text`
-**Type**: `string|null`
-**Default**: `null` (uses default translation)
-
-```php
-// Custom welcome message
-'moinframe.loop.welcome.text' => 'Click anywhere on the page to leave feedback. Use the toggle button to switch between navigation and comment modes.',
-```
-
-## Manual Snippet Usage
-
-When auto-injection is disabled, you have full control over when and where loop appears.
-
-### Basic Usage
-
-```php
-
-```
-
-### Conditional Loading
-
-```php
-user() && $kirby->user()->role()->isAdmin()): ?>
-
-
-```
-
-> [!TIPP]
-> Manual snippets also respect the `enabled` configuration option. If you've set up conditional enabling via the `enabled` option, you don't need to duplicate that logic in your template - the snippet will automatically check the enabled status.
-
-
-## Caching Behavior
-
-> [!WARNING]
-> Pages with loop automatically have Kirby's page **cache** **disabled**. This is necessary for CSRF token validation and User authentication checks.
diff --git a/site/plugins/loop/docs/03-multi-language.md b/site/plugins/loop/docs/03-multi-language.md
deleted file mode 100644
index 5736afb..0000000
--- a/site/plugins/loop/docs/03-multi-language.md
+++ /dev/null
@@ -1,94 +0,0 @@
----
-title: Multi-Language
----
-
-Kirby Loop provides comprehensive support for multi-language Kirby sites, including automatic language detection and customizable UI translations.
-
-## How Multi-Language Support Works
-
-The plugin automatically detects and adapts to your Kirby site's language configuration. No additional configuration is required - the plugin works automatically with Kirby's multi-language setup.
-
-- **Single-language sites**: Uses the en translations
-- **Multi-language sites**: Detects the current page language and adapts accordingly
-
-
-
-## UI Language Override
-
-### Forcing a Specific UI Language
-
-By default, loop UI adapts to the current page language. You can override this behavior:
-
-```php
-// Always show German UI regardless of page language
-'moinframe.loop.language' => 'de',
-
-// Always show English UI regardless of page language
-'moinframe.loop.language' => 'en',
-```
-
-### Use Cases for Language Override
-
-**Consistent Editor Experience:**
-```php
-// Editors prefer English UI even on German pages
-'moinframe.loop.language' => 'en',
-```
-
-**Single-Language website with non english content:**
-```php
-// German content site with German-speaking editors
-'moinframe.loop.language' => 'de',
-```
-
-
-## Built-in Translations
-
-The plugin includes complete translations for:
-- English (en) - Default
-- German (de)
-
-## Custom Translations
-
-### Adding New Languages
-
-To add support for additional languages, create or extend your Kirby language files:
-
-```php
-// site/languages/fr.php
- 'fr',
- 'default' => false,
- 'direction' => 'ltr',
- 'locale' => 'fr_FR',
- 'name' => 'Français',
- 'translations' => [
- // UI Elements
- 'moinframe.loop.ui.header.title' => 'Commentaires',
- ...
- ]
-];
-```
-
-### Overriding Existing Translations
-
-Customize existing translations by adding them to your language files:
-
-```php
-// site/languages/en.php - Override English defaults
-return [
- 'code' => 'en',
- 'default' => true,
- 'translations' => [
- 'moinframe.loop.ui.header.title' => 'Page Feedback',
- 'moinframe.loop.ui.comment.placeholder' => 'What needs attention?',
- 'moinframe.loop.ui.welcome.headline' => 'Welcome to Our Review Tool',
- ]
-];
-```
-
-### Translation Key Reference
-
-For a complete list of available translation keys, see the [plugin's index file](https://github.com/moinframe/kirby-loop/blob/main/index.php).
diff --git a/site/plugins/loop/docs/04-theming.md b/site/plugins/loop/docs/04-theming.md
deleted file mode 100644
index 9a01bc2..0000000
--- a/site/plugins/loop/docs/04-theming.md
+++ /dev/null
@@ -1,110 +0,0 @@
----
-title: Theming
----
-
-Kirby Loop comes with built-in theming support, allowing you to customize the visual appearance to match your brand or provide different user experiences. The plugin includes a default (light) theme and a dark theme, with support for creating custom themes.
-
-## Configuration
-
-### Setting a Theme
-
-Configure the theme in your `site/config/config.php`:
-
-```php
-return [
- // Set theme: 'default', 'dark', or custom theme name
- 'moinframe.loop.theme' => 'dark',
-];
-```
-
-**Available options:**
-- `'default'` - Light theme (default)
-- `'dark'` - Dark theme
-- Custom theme name
-
-## Creating Custom Themes
-
-Custom themes are CSS files that override the default color and styling tokens. The theming system uses CSS custom properties (variables) for easy customization.
-
-
-### Basic Custom Theme
-
-Here's a minimal custom theme example:
-
-```css
-/* frontend/src/styles/theme-custom.css */
-kirby-loop[theme="custom"] {
- /* Accent color */
- --color-accent-l: 0.6;
- --color-accent-c: 0.15;
- --color-accent-h: 280; /* Purple accent */
-
- /* Neutral color lightness values */
- --color-neutral-l-0: 0.98;
- --color-neutral-l-100: 0.92;
- --color-neutral-l-200: 0.86;
- --color-neutral-l-300: 0.7;
- --color-neutral-l-400: 0.6;
- --color-neutral-l-500: 0.5;
- --color-neutral-l-600: 0.4;
- --color-neutral-l-700: 0.3;
- --color-neutral-l-800: 0.15;
- --color-neutral-l-900: 0.05;
- --color-neutral-l-1000: 0;
-}
-```
-
-### Configure Your Custom Theme
-
-Set your custom theme in the configuration:
-
-```php
-// site/config/config.php
-return [
- 'moinframe.loop.theme' => 'custom',
-];
-```
-
-## Theme Architecture
-
-### Color System
-
-The theming system uses OKLCH color space for consistent, perceptually uniform colors:
-
-```css
-/* Accent colors */
---color-accent-l: 0.7; /* Lightness (0-1) */
---color-accent-c: 0.12; /* Chroma/saturation (0-0.4) */
---color-accent-h: 220; /* Hue (0-360) */
-
-/* Neutral colors */
---color-neutral-l-0: 1; /* Lightest */
---color-neutral-l-100: 0.95;
---color-neutral-l-200: 0.9;
-/* ... */
---color-neutral-l-900: 0.05;
---color-neutral-l-1000: 0; /* Darkest */
-```
-
-### Advanced Customization
-
-You can override any design token in your custom theme:
-
-```css
-kirby-loop[theme="custom"] {
- /* Colors */
- --color-accent-l: 0.65;
- --color-accent-c: 0.18;
- --color-accent-h: 15; /* Orange accent */
-
- /* Shadows with custom opacity */
- --shadow-s: 0 0.1em 0.25em oklch(var(--color-neutral-l-900) var(--color-neutral-c) var(--color-neutral-h) / 0.15);
-
- /* Custom border radius */
- --border-radius: 0.5rem;
- --border-radius-rounded: 1rem;
-
- /* Custom fonts */
- --font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
-}
-```
diff --git a/site/plugins/loop/docs/05-api.md b/site/plugins/loop/docs/05-api.md
deleted file mode 100644
index 8dc4bd0..0000000
--- a/site/plugins/loop/docs/05-api.md
+++ /dev/null
@@ -1,325 +0,0 @@
----
-title: API Reference
----
-
-Kirby Loop provides a RESTful API for managing comments and feedback. All endpoints include CSRF protection.
-
-## Authentication
-
-All API endpoints require authentication, controlled by the `moinframe.loop.public` configuration option:
-
-- **Default (private)**: Only authenticated Kirby users can access the API
-- **Public mode**: Anyone can access the API
-
-## CSRF Protection
-
-All API requests must include a valid CSRF token in the request header:
-
-```javascript
-fetch('/loop/comments/page-id', {
- headers: {
- 'X-CSRF-Token': ''
- }
-});
-```
-
-## Base URL Structure
-
-### Single Language Sites
-```
-/loop/comments/{pageId}
-/loop/comment/new
-/loop/comment/reply
-/loop/comment/resolve
-/loop/comment/unresolve
-/loop/guest/name
-```
-
-### Multi-Language Sites
-```
-/{language}/loop/comments/{pageId}
-/{language}/loop/comment/new
-/{language}/loop/comment/reply
-/{language}/loop/comment/resolve
-/{language}/loop/comment/unresolve
-/{language}/loop/guest/name
-```
-
-Where `{language}` is the language code (e.g., `en`, `de`).
-
-## Endpoints
-
-### GET /loop/comments/{pageId}
-
-Retrieve all comments for a specific page.
-
-**Parameters:**
-- `pageId` (string): The page ID or 'home' for the homepage
-
-**Response:**
-```json
-{
- "status": "ok",
- "comments": [
- {
- "id": 1,
- "author": "John Doe",
- "url": "https://example.com/page",
- "page": "page-uuid",
- "comment": "This needs to be updated",
- "selector": ".header h1",
- "selectorOffsetX": 10,
- "selectorOffsetY": 20,
- "pagePositionX": 150,
- "pagePositionY": 300,
- "timestamp": 1640995200,
- "lang": "en",
- "status": "OPEN",
- "replies": [
- {
- "id": 1,
- "author": "jane.smith",
- "comment": "I'll fix this",
- "parentId": 1,
- "timestamp": 1640995800
- }
- ]
- }
- ]
-}
-```
-
-**Error Responses:**
-- `400`: Page not found
-- `401`: Unauthorized (if authentication required)
-- `403`: CSRF token invalid
-
-### POST /loop/comment/new
-
-Create a new comment on a page.
-
-**Request Body:**
-```json
-{
- "comment": "This section needs clarification",
- "url": "https://example.com/page",
- "selector": ".content p:nth-child(3)",
- "selectorOffsetX": 15,
- "selectorOffsetY": 25,
- "pagePositionX": 200,
- "pagePositionY": 450,
- "pageId": "projects/project-alpha"
-}
-```
-
-**Required Fields:**
-- `comment` (string): The comment text (HTML stripped and sanitized)
-- `url` (string): The full URL where the comment was made
-- `selector` (string): CSS selector for the commented element
-- `selectorOffsetX` (number): X offset within the selected element
-- `selectorOffsetY` (number): Y offset within the selected element
-- `pagePositionX` (number): X position on the page
-- `pagePositionY` (number): Y position on the page
-- `pageId` (string): Kirby page ID or 'home'
-
-**Response:**
-```json
-{
- "status": "ok",
- "comment": {
- "id": 15,
- "author": "John Doe",
- "url": "https://example.com/page",
- "page": "page-uuid",
- "comment": "This section needs clarification",
- "selector": ".content p:nth-child(3)",
- "selectorOffsetX": 15,
- "selectorOffsetY": 25,
- "pagePositionX": 200,
- "pagePositionY": 450,
- "timestamp": 1640995200,
- "lang": "en",
- "status": "OPEN",
- "replies": []
- }
-}
-```
-
-**Error Responses:**
-- `400`: Missing required fields, invalid selector format, or invalid data
-- `401`: Unauthorized
-- `403`: CSRF token invalid or disabled
-- `404`: Page not found
-
-### POST /loop/comment/reply
-
-Add a reply to an existing comment.
-
-**Request Body:**
-```json
-{
- "comment": "I'll handle this update",
- "parentId": 15
-}
-```
-
-**Required Fields:**
-- `comment` (string): The reply text (HTML stripped and sanitized)
-- `parentId` (number): ID of the parent comment
-
-**Response:**
-```json
-{
- "status": "ok",
- "reply": {
- "id": 3,
- "author": "John Doe",
- "comment": "I'll handle this update",
- "parentId": 15,
- "timestamp": 1640995800
- }
-}
-```
-
-**Error Responses:**
-- `400`: Missing required fields
-- `401`: Unauthorized
-- `403`: CSRF token invalid or disabled
-
-### POST /loop/comment/resolve
-
-Mark a comment as resolved.
-
-**Request Body:**
-```json
-{
- "id": 15
-}
-```
-
-**Required Fields:**
-- `id` (number): The comment ID to resolve
-
-**Response:**
-```json
-{
- "status": "ok",
- "success": true
-}
-```
-
-**Error Responses:**
-- `400`: Missing comment ID
-- `401`: Unauthorized
-- `403`: CSRF token invalid or disabled
-
-### POST /loop/comment/unresolve
-
-Mark a resolved comment as unresolved.
-
-**Request Body:**
-```json
-{
- "id": 15
-}
-```
-
-**Required Fields:**
-- `id` (number): The comment ID to unresolve
-
-**Response:**
-```json
-{
- "status": "ok",
- "success": true
-}
-```
-
-**Error Responses:**
-- `400`: Missing comment ID
-- `401`: Unauthorized
-- `403`: CSRF token invalid or disabled
-
-### POST /loop/guest/name
-
-Set a guest name for non-authenticated users (when public mode is enabled).
-
-**Request Body:**
-```json
-{
- "name": "John Doe"
-}
-```
-
-**Required Fields:**
-- `name` (string): The guest user's name
-
-**Response:**
-```json
-{
- "status": "ok",
- "name": "John Doe"
-}
-```
-
-**Error Responses:**
-- `400`: Missing or empty name
-- `401`: Unauthorized
-- `403`: CSRF token invalid or disabled
-
-## Data Models
-
-### Comment Object
-
-```typescript
-interface Comment {
- id: number;
- author: string; // Resolved display name (user name, email prefix, or guest name)
- url: string; // Full URL where comment was made
- page: string; // Page UUID
- comment: string; // Sanitized comment text
- selector: string; // CSS selector for target element
- selectorOffsetX: number; // X offset within element (float)
- selectorOffsetY: number; // Y offset within element (float)
- pagePositionX: number; // X position on page (float)
- pagePositionY: number; // Y position on page (float)
- timestamp: number; // Unix timestamp
- lang: string; // Language code
- status: string; // Status: OPEN, RESOLVED
- replies: Reply[]; // Array of replies
-}
-```
-
-### Reply Object
-
-```typescript
-interface Reply {
- id: number;
- author: string; // Resolved display name (user name, email prefix, or guest name)
- comment: string; // Sanitized reply text
- parentId: number; // Parent comment ID
- timestamp: number; // Unix timestamp
-}
-```
-
-## Error Handling
-
-The api endpoints return consistent error responses. For more details, switch on the debug mode in your Kirby Installation.
-
-```json
-{
- "status": "error",
- "message": "Human-readable error message",
- "code": "ERROR_CODE" // Optional error code
-}
-```
-
-### Common Error Codes
-
-- `CSRF_INVALID`: CSRF token is missing or invalid
-- `PAGE_NOT_FOUND`: Specified page doesn't exist
-- `FIELD_REQUIRED`: Required field is missing
-- `UNAUTHORIZED`: Authentication required but not provided
-- `INVALID_SELECTOR`: Invalid selector format
-- `INVALID_NAME`: Invalid guest name
-- `DISABLED`: Tool is disabled
diff --git a/site/plugins/loop/frontend/.vscode/extensions.json b/site/plugins/loop/frontend/.vscode/extensions.json
deleted file mode 100644
index bdef820..0000000
--- a/site/plugins/loop/frontend/.vscode/extensions.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "recommendations": ["svelte.svelte-vscode"]
-}
diff --git a/site/plugins/loop/frontend/package.json b/site/plugins/loop/frontend/package.json
deleted file mode 100644
index 66d4d43..0000000
--- a/site/plugins/loop/frontend/package.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "name": "loop-frontend",
- "private": true,
- "version": "0.0.0",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "build": "vite build",
- "preview": "vite preview",
- "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"
- },
- "devDependencies": {
- "@sveltejs/vite-plugin-svelte": "^5.0.3",
- "@tsconfig/svelte": "^5.0.4",
- "@types/node": "^22.13.10",
- "browserslist": "^4.24.4",
- "lightningcss": "^1.29.3",
- "svelte": "^5.20.2",
- "svelte-check": "^4.1.4",
- "terser": "^5.39.0",
- "typescript": "~5.7.2",
- "vite": "^6.2.0",
- "vite-plugin-css-injected-by-js": "^3.5.2",
- "vite-plugin-ejs": "^1.7.0",
- "vitest": "^3.0.9"
- }
-}
diff --git a/site/plugins/loop/frontend/pnpm-lock.yaml b/site/plugins/loop/frontend/pnpm-lock.yaml
deleted file mode 100644
index be3376e..0000000
--- a/site/plugins/loop/frontend/pnpm-lock.yaml
+++ /dev/null
@@ -1,1505 +0,0 @@
-lockfileVersion: '9.0'
-
-settings:
- autoInstallPeers: true
- excludeLinksFromLockfile: false
-
-importers:
-
- .:
- devDependencies:
- '@sveltejs/vite-plugin-svelte':
- specifier: ^5.0.3
- version: 5.0.3(svelte@5.23.2)(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0))
- '@tsconfig/svelte':
- specifier: ^5.0.4
- version: 5.0.4
- '@types/node':
- specifier: ^22.13.10
- version: 22.13.10
- browserslist:
- specifier: ^4.24.4
- version: 4.24.4
- lightningcss:
- specifier: ^1.29.3
- version: 1.29.3
- svelte:
- specifier: ^5.20.2
- version: 5.23.2
- svelte-check:
- specifier: ^4.1.4
- version: 4.1.5(svelte@5.23.2)(typescript@5.7.3)
- terser:
- specifier: ^5.39.0
- version: 5.39.0
- typescript:
- specifier: ~5.7.2
- version: 5.7.3
- vite:
- specifier: ^6.2.0
- version: 6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)
- vite-plugin-css-injected-by-js:
- specifier: ^3.5.2
- version: 3.5.2(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0))
- vite-plugin-ejs:
- specifier: ^1.7.0
- version: 1.7.0(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0))
- vitest:
- specifier: ^3.0.9
- version: 3.0.9(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)
-
-packages:
-
- '@ampproject/remapping@2.3.0':
- resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
- engines: {node: '>=6.0.0'}
-
- '@esbuild/aix-ppc64@0.25.1':
- resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [aix]
-
- '@esbuild/android-arm64@0.25.1':
- resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [android]
-
- '@esbuild/android-arm@0.25.1':
- resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [android]
-
- '@esbuild/android-x64@0.25.1':
- resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [android]
-
- '@esbuild/darwin-arm64@0.25.1':
- resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [darwin]
-
- '@esbuild/darwin-x64@0.25.1':
- resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [darwin]
-
- '@esbuild/freebsd-arm64@0.25.1':
- resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [freebsd]
-
- '@esbuild/freebsd-x64@0.25.1':
- resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [freebsd]
-
- '@esbuild/linux-arm64@0.25.1':
- resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [linux]
-
- '@esbuild/linux-arm@0.25.1':
- resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [linux]
-
- '@esbuild/linux-ia32@0.25.1':
- resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [linux]
-
- '@esbuild/linux-loong64@0.25.1':
- resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==}
- engines: {node: '>=18'}
- cpu: [loong64]
- os: [linux]
-
- '@esbuild/linux-mips64el@0.25.1':
- resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==}
- engines: {node: '>=18'}
- cpu: [mips64el]
- os: [linux]
-
- '@esbuild/linux-ppc64@0.25.1':
- resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [linux]
-
- '@esbuild/linux-riscv64@0.25.1':
- resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==}
- engines: {node: '>=18'}
- cpu: [riscv64]
- os: [linux]
-
- '@esbuild/linux-s390x@0.25.1':
- resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==}
- engines: {node: '>=18'}
- cpu: [s390x]
- os: [linux]
-
- '@esbuild/linux-x64@0.25.1':
- resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [linux]
-
- '@esbuild/netbsd-arm64@0.25.1':
- resolution: {integrity: sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [netbsd]
-
- '@esbuild/netbsd-x64@0.25.1':
- resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [netbsd]
-
- '@esbuild/openbsd-arm64@0.25.1':
- resolution: {integrity: sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openbsd]
-
- '@esbuild/openbsd-x64@0.25.1':
- resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [openbsd]
-
- '@esbuild/sunos-x64@0.25.1':
- resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [sunos]
-
- '@esbuild/win32-arm64@0.25.1':
- resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [win32]
-
- '@esbuild/win32-ia32@0.25.1':
- resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [win32]
-
- '@esbuild/win32-x64@0.25.1':
- resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [win32]
-
- '@jridgewell/gen-mapping@0.3.8':
- resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/resolve-uri@3.1.2':
- resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/set-array@1.2.1':
- resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/source-map@0.3.6':
- resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
-
- '@jridgewell/sourcemap-codec@1.5.0':
- resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
-
- '@jridgewell/trace-mapping@0.3.25':
- resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
-
- '@rollup/rollup-android-arm-eabi@4.36.0':
- resolution: {integrity: sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==}
- cpu: [arm]
- os: [android]
-
- '@rollup/rollup-android-arm64@4.36.0':
- resolution: {integrity: sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==}
- cpu: [arm64]
- os: [android]
-
- '@rollup/rollup-darwin-arm64@4.36.0':
- resolution: {integrity: sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==}
- cpu: [arm64]
- os: [darwin]
-
- '@rollup/rollup-darwin-x64@4.36.0':
- resolution: {integrity: sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==}
- cpu: [x64]
- os: [darwin]
-
- '@rollup/rollup-freebsd-arm64@4.36.0':
- resolution: {integrity: sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==}
- cpu: [arm64]
- os: [freebsd]
-
- '@rollup/rollup-freebsd-x64@4.36.0':
- resolution: {integrity: sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==}
- cpu: [x64]
- os: [freebsd]
-
- '@rollup/rollup-linux-arm-gnueabihf@4.36.0':
- resolution: {integrity: sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==}
- cpu: [arm]
- os: [linux]
-
- '@rollup/rollup-linux-arm-musleabihf@4.36.0':
- resolution: {integrity: sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==}
- cpu: [arm]
- os: [linux]
-
- '@rollup/rollup-linux-arm64-gnu@4.36.0':
- resolution: {integrity: sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==}
- cpu: [arm64]
- os: [linux]
-
- '@rollup/rollup-linux-arm64-musl@4.36.0':
- resolution: {integrity: sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==}
- cpu: [arm64]
- os: [linux]
-
- '@rollup/rollup-linux-loongarch64-gnu@4.36.0':
- resolution: {integrity: sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==}
- cpu: [loong64]
- os: [linux]
-
- '@rollup/rollup-linux-powerpc64le-gnu@4.36.0':
- resolution: {integrity: sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==}
- cpu: [ppc64]
- os: [linux]
-
- '@rollup/rollup-linux-riscv64-gnu@4.36.0':
- resolution: {integrity: sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==}
- cpu: [riscv64]
- os: [linux]
-
- '@rollup/rollup-linux-s390x-gnu@4.36.0':
- resolution: {integrity: sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==}
- cpu: [s390x]
- os: [linux]
-
- '@rollup/rollup-linux-x64-gnu@4.36.0':
- resolution: {integrity: sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==}
- cpu: [x64]
- os: [linux]
-
- '@rollup/rollup-linux-x64-musl@4.36.0':
- resolution: {integrity: sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==}
- cpu: [x64]
- os: [linux]
-
- '@rollup/rollup-win32-arm64-msvc@4.36.0':
- resolution: {integrity: sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==}
- cpu: [arm64]
- os: [win32]
-
- '@rollup/rollup-win32-ia32-msvc@4.36.0':
- resolution: {integrity: sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==}
- cpu: [ia32]
- os: [win32]
-
- '@rollup/rollup-win32-x64-msvc@4.36.0':
- resolution: {integrity: sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==}
- cpu: [x64]
- os: [win32]
-
- '@sveltejs/acorn-typescript@1.0.5':
- resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==}
- peerDependencies:
- acorn: ^8.9.0
-
- '@sveltejs/vite-plugin-svelte-inspector@4.0.1':
- resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22}
- peerDependencies:
- '@sveltejs/vite-plugin-svelte': ^5.0.0
- svelte: ^5.0.0
- vite: ^6.0.0
-
- '@sveltejs/vite-plugin-svelte@5.0.3':
- resolution: {integrity: sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22}
- peerDependencies:
- svelte: ^5.0.0
- vite: ^6.0.0
-
- '@tsconfig/svelte@5.0.4':
- resolution: {integrity: sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q==}
-
- '@types/estree@1.0.6':
- resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
-
- '@types/node@22.13.10':
- resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==}
-
- '@vitest/expect@3.0.9':
- resolution: {integrity: sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==}
-
- '@vitest/mocker@3.0.9':
- resolution: {integrity: sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==}
- peerDependencies:
- msw: ^2.4.9
- vite: ^5.0.0 || ^6.0.0
- peerDependenciesMeta:
- msw:
- optional: true
- vite:
- optional: true
-
- '@vitest/pretty-format@3.0.9':
- resolution: {integrity: sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==}
-
- '@vitest/runner@3.0.9':
- resolution: {integrity: sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==}
-
- '@vitest/snapshot@3.0.9':
- resolution: {integrity: sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==}
-
- '@vitest/spy@3.0.9':
- resolution: {integrity: sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==}
-
- '@vitest/utils@3.0.9':
- resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==}
-
- acorn@8.14.1:
- resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
- engines: {node: '>=0.4.0'}
- hasBin: true
-
- ansi-styles@4.3.0:
- resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
- engines: {node: '>=8'}
-
- aria-query@5.3.2:
- resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
- engines: {node: '>= 0.4'}
-
- assertion-error@2.0.1:
- resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
- engines: {node: '>=12'}
-
- async@3.2.6:
- resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
-
- axobject-query@4.1.0:
- resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
- engines: {node: '>= 0.4'}
-
- balanced-match@1.0.2:
- resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
-
- brace-expansion@1.1.11:
- resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
-
- brace-expansion@2.0.1:
- resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
-
- browserslist@4.24.4:
- resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==}
- engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
- hasBin: true
-
- buffer-from@1.1.2:
- resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
-
- cac@6.7.14:
- resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
- engines: {node: '>=8'}
-
- caniuse-lite@1.0.30001706:
- resolution: {integrity: sha512-3ZczoTApMAZwPKYWmwVbQMFpXBDds3/0VciVoUwPUbldlYyVLmRVuRs/PcUZtHpbLRpzzDvrvnFuREsGt6lUug==}
-
- chai@5.2.0:
- resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==}
- engines: {node: '>=12'}
-
- chalk@4.1.2:
- resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
- engines: {node: '>=10'}
-
- check-error@2.1.1:
- resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
- engines: {node: '>= 16'}
-
- chokidar@4.0.3:
- resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
- engines: {node: '>= 14.16.0'}
-
- clsx@2.1.1:
- resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
- engines: {node: '>=6'}
-
- color-convert@2.0.1:
- resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
- engines: {node: '>=7.0.0'}
-
- color-name@1.1.4:
- resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
-
- commander@2.20.3:
- resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
-
- concat-map@0.0.1:
- resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
-
- debug@4.4.0:
- resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
- engines: {node: '>=6.0'}
- peerDependencies:
- supports-color: '*'
- peerDependenciesMeta:
- supports-color:
- optional: true
-
- deep-eql@5.0.2:
- resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
- engines: {node: '>=6'}
-
- deepmerge@4.3.1:
- resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
- engines: {node: '>=0.10.0'}
-
- detect-libc@2.0.3:
- resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
- engines: {node: '>=8'}
-
- ejs@3.1.10:
- resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==}
- engines: {node: '>=0.10.0'}
- hasBin: true
-
- electron-to-chromium@1.5.122:
- resolution: {integrity: sha512-EML1wnwkY5MFh/xUnCvY8FrhUuKzdYhowuZExZOfwJo+Zu9OsNCI23Cgl5y7awy7HrUHSwB1Z8pZX5TI34lsUg==}
-
- es-module-lexer@1.6.0:
- resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==}
-
- esbuild@0.25.1:
- resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==}
- engines: {node: '>=18'}
- hasBin: true
-
- escalade@3.2.0:
- resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
- engines: {node: '>=6'}
-
- esm-env@1.2.2:
- resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
-
- esrap@1.4.5:
- resolution: {integrity: sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g==}
-
- estree-walker@3.0.3:
- resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
-
- expect-type@1.2.0:
- resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==}
- engines: {node: '>=12.0.0'}
-
- fdir@6.4.3:
- resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==}
- peerDependencies:
- picomatch: ^3 || ^4
- peerDependenciesMeta:
- picomatch:
- optional: true
-
- filelist@1.0.4:
- resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
-
- fsevents@2.3.3:
- resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
- engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
- os: [darwin]
-
- has-flag@4.0.0:
- resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
- engines: {node: '>=8'}
-
- is-reference@3.0.3:
- resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
-
- jake@10.9.2:
- resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==}
- engines: {node: '>=10'}
- hasBin: true
-
- kleur@4.1.5:
- resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
- engines: {node: '>=6'}
-
- lightningcss-darwin-arm64@1.29.3:
- resolution: {integrity: sha512-fb7raKO3pXtlNbQbiMeEu8RbBVHnpyqAoxTyTRMEWFQWmscGC2wZxoHzZ+YKAepUuKT9uIW5vL2QbFivTgprZg==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [darwin]
-
- lightningcss-darwin-x64@1.29.3:
- resolution: {integrity: sha512-KF2XZ4ZdmDGGtEYmx5wpzn6u8vg7AdBHaEOvDKu8GOs7xDL/vcU2vMKtTeNe1d4dogkDdi3B9zC77jkatWBwEQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [darwin]
-
- lightningcss-freebsd-x64@1.29.3:
- resolution: {integrity: sha512-VUWeVf+V1UM54jv9M4wen9vMlIAyT69Krl9XjI8SsRxz4tdNV/7QEPlW6JASev/pYdiynUCW0pwaFquDRYdxMw==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [freebsd]
-
- lightningcss-linux-arm-gnueabihf@1.29.3:
- resolution: {integrity: sha512-UhgZ/XVNfXQVEJrMIWeK1Laj8KbhjbIz7F4znUk7G4zeGw7TRoJxhb66uWrEsonn1+O45w//0i0Fu0wIovYdYg==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm]
- os: [linux]
-
- lightningcss-linux-arm64-gnu@1.29.3:
- resolution: {integrity: sha512-Pqau7jtgJNmQ/esugfmAT1aCFy/Gxc92FOxI+3n+LbMHBheBnk41xHDhc0HeYlx9G0xP5tK4t0Koy3QGGNqypw==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [linux]
-
- lightningcss-linux-arm64-musl@1.29.3:
- resolution: {integrity: sha512-dxakOk66pf7KLS7VRYFO7B8WOJLecE5OPL2YOk52eriFd/yeyxt2Km5H0BjLfElokIaR+qWi33gB8MQLrdAY3A==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [linux]
-
- lightningcss-linux-x64-gnu@1.29.3:
- resolution: {integrity: sha512-ySZTNCpbfbK8rqpKJeJR2S0g/8UqqV3QnzcuWvpI60LWxnFN91nxpSSwCbzfOXkzKfar9j5eOuOplf+klKtINg==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [linux]
-
- lightningcss-linux-x64-musl@1.29.3:
- resolution: {integrity: sha512-3pVZhIzW09nzi10usAXfIGTTSTYQ141dk88vGFNCgawIzayiIzZQxEcxVtIkdvlEq2YuFsL9Wcj/h61JHHzuFQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [linux]
-
- lightningcss-win32-arm64-msvc@1.29.3:
- resolution: {integrity: sha512-VRnkAvtIkeWuoBJeGOTrZxsNp4HogXtcaaLm8agmbYtLDOhQdpgxW6NjZZjDXbvGF+eOehGulXZ3C1TiwHY4QQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [win32]
-
- lightningcss-win32-x64-msvc@1.29.3:
- resolution: {integrity: sha512-IszwRPu2cPnDQsZpd7/EAr0x2W7jkaWqQ1SwCVIZ/tSbZVXPLt6k8s6FkcyBjViCzvB5CW0We0QbbP7zp2aBjQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [win32]
-
- lightningcss@1.29.3:
- resolution: {integrity: sha512-GlOJwTIP6TMIlrTFsxTerwC0W6OpQpCGuX1ECRLBUVRh6fpJH3xTqjCjRgQHTb4ZXexH9rtHou1Lf03GKzmhhQ==}
- engines: {node: '>= 12.0.0'}
-
- locate-character@3.0.0:
- resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
-
- loupe@3.1.3:
- resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==}
-
- magic-string@0.30.17:
- resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
-
- minimatch@3.1.2:
- resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
-
- minimatch@5.1.6:
- resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
- engines: {node: '>=10'}
-
- mri@1.2.0:
- resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
- engines: {node: '>=4'}
-
- ms@2.1.3:
- resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
-
- nanoid@3.3.11:
- resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
-
- node-releases@2.0.19:
- resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
-
- pathe@2.0.3:
- resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
-
- pathval@2.0.0:
- resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
- engines: {node: '>= 14.16'}
-
- picocolors@1.1.1:
- resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
-
- postcss@8.5.3:
- resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
- engines: {node: ^10 || ^12 || >=14}
-
- readdirp@4.1.2:
- resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
- engines: {node: '>= 14.18.0'}
-
- rollup@4.36.0:
- resolution: {integrity: sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==}
- engines: {node: '>=18.0.0', npm: '>=8.0.0'}
- hasBin: true
-
- sade@1.8.1:
- resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
- engines: {node: '>=6'}
-
- siginfo@2.0.0:
- resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
-
- source-map-js@1.2.1:
- resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
- engines: {node: '>=0.10.0'}
-
- source-map-support@0.5.21:
- resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
-
- source-map@0.6.1:
- resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
- engines: {node: '>=0.10.0'}
-
- stackback@0.0.2:
- resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
-
- std-env@3.8.1:
- resolution: {integrity: sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==}
-
- supports-color@7.2.0:
- resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
- engines: {node: '>=8'}
-
- svelte-check@4.1.5:
- resolution: {integrity: sha512-Gb0T2IqBNe1tLB9EB1Qh+LOe+JB8wt2/rNBDGvkxQVvk8vNeAoG+vZgFB/3P5+zC7RWlyBlzm9dVjZFph/maIg==}
- engines: {node: '>= 18.0.0'}
- hasBin: true
- peerDependencies:
- svelte: ^4.0.0 || ^5.0.0-next.0
- typescript: '>=5.0.0'
-
- svelte@5.23.2:
- resolution: {integrity: sha512-PHP1o0aYJNMatiZ+0nq1W/Z1W1/l5Z94B9nhMIo7gsuTBbxC454g4O5SQMjQpZBUZi5ANYUrXJOE4gPzcN/VQw==}
- engines: {node: '>=18'}
-
- terser@5.39.0:
- resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==}
- engines: {node: '>=10'}
- hasBin: true
-
- tinybench@2.9.0:
- resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
-
- tinyexec@0.3.2:
- resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
-
- tinypool@1.0.2:
- resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==}
- engines: {node: ^18.0.0 || >=20.0.0}
-
- tinyrainbow@2.0.0:
- resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
- engines: {node: '>=14.0.0'}
-
- tinyspy@3.0.2:
- resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
- engines: {node: '>=14.0.0'}
-
- typescript@5.7.3:
- resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==}
- engines: {node: '>=14.17'}
- hasBin: true
-
- undici-types@6.20.0:
- resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
-
- update-browserslist-db@1.1.3:
- resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
- hasBin: true
- peerDependencies:
- browserslist: '>= 4.21.0'
-
- vite-node@3.0.9:
- resolution: {integrity: sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
- hasBin: true
-
- vite-plugin-css-injected-by-js@3.5.2:
- resolution: {integrity: sha512-2MpU/Y+SCZyWUB6ua3HbJCrgnF0KACAsmzOQt1UvRVJCGF6S8xdA3ZUhWcWdM9ivG4I5az8PnQmwwrkC2CAQrQ==}
- peerDependencies:
- vite: '>2.0.0-0'
-
- vite-plugin-ejs@1.7.0:
- resolution: {integrity: sha512-JNP3zQDC4mSbfoJ3G73s5mmZITD8NGjUmLkq4swxyahy/W0xuokK9U9IJGXw7KCggq6UucT6hJ0p+tQrNtqTZw==}
- peerDependencies:
- vite: '>=5.0.0'
-
- vite@6.2.2:
- resolution: {integrity: sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
- hasBin: true
- peerDependencies:
- '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
- jiti: '>=1.21.0'
- less: '*'
- lightningcss: ^1.21.0
- sass: '*'
- sass-embedded: '*'
- stylus: '*'
- sugarss: '*'
- terser: ^5.16.0
- tsx: ^4.8.1
- yaml: ^2.4.2
- peerDependenciesMeta:
- '@types/node':
- optional: true
- jiti:
- optional: true
- less:
- optional: true
- lightningcss:
- optional: true
- sass:
- optional: true
- sass-embedded:
- optional: true
- stylus:
- optional: true
- sugarss:
- optional: true
- terser:
- optional: true
- tsx:
- optional: true
- yaml:
- optional: true
-
- vitefu@1.0.6:
- resolution: {integrity: sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==}
- peerDependencies:
- vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0
- peerDependenciesMeta:
- vite:
- optional: true
-
- vitest@3.0.9:
- resolution: {integrity: sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
- hasBin: true
- peerDependencies:
- '@edge-runtime/vm': '*'
- '@types/debug': ^4.1.12
- '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
- '@vitest/browser': 3.0.9
- '@vitest/ui': 3.0.9
- happy-dom: '*'
- jsdom: '*'
- peerDependenciesMeta:
- '@edge-runtime/vm':
- optional: true
- '@types/debug':
- optional: true
- '@types/node':
- optional: true
- '@vitest/browser':
- optional: true
- '@vitest/ui':
- optional: true
- happy-dom:
- optional: true
- jsdom:
- optional: true
-
- why-is-node-running@2.3.0:
- resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
- engines: {node: '>=8'}
- hasBin: true
-
- zimmerframe@1.1.2:
- resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
-
-snapshots:
-
- '@ampproject/remapping@2.3.0':
- dependencies:
- '@jridgewell/gen-mapping': 0.3.8
- '@jridgewell/trace-mapping': 0.3.25
-
- '@esbuild/aix-ppc64@0.25.1':
- optional: true
-
- '@esbuild/android-arm64@0.25.1':
- optional: true
-
- '@esbuild/android-arm@0.25.1':
- optional: true
-
- '@esbuild/android-x64@0.25.1':
- optional: true
-
- '@esbuild/darwin-arm64@0.25.1':
- optional: true
-
- '@esbuild/darwin-x64@0.25.1':
- optional: true
-
- '@esbuild/freebsd-arm64@0.25.1':
- optional: true
-
- '@esbuild/freebsd-x64@0.25.1':
- optional: true
-
- '@esbuild/linux-arm64@0.25.1':
- optional: true
-
- '@esbuild/linux-arm@0.25.1':
- optional: true
-
- '@esbuild/linux-ia32@0.25.1':
- optional: true
-
- '@esbuild/linux-loong64@0.25.1':
- optional: true
-
- '@esbuild/linux-mips64el@0.25.1':
- optional: true
-
- '@esbuild/linux-ppc64@0.25.1':
- optional: true
-
- '@esbuild/linux-riscv64@0.25.1':
- optional: true
-
- '@esbuild/linux-s390x@0.25.1':
- optional: true
-
- '@esbuild/linux-x64@0.25.1':
- optional: true
-
- '@esbuild/netbsd-arm64@0.25.1':
- optional: true
-
- '@esbuild/netbsd-x64@0.25.1':
- optional: true
-
- '@esbuild/openbsd-arm64@0.25.1':
- optional: true
-
- '@esbuild/openbsd-x64@0.25.1':
- optional: true
-
- '@esbuild/sunos-x64@0.25.1':
- optional: true
-
- '@esbuild/win32-arm64@0.25.1':
- optional: true
-
- '@esbuild/win32-ia32@0.25.1':
- optional: true
-
- '@esbuild/win32-x64@0.25.1':
- optional: true
-
- '@jridgewell/gen-mapping@0.3.8':
- dependencies:
- '@jridgewell/set-array': 1.2.1
- '@jridgewell/sourcemap-codec': 1.5.0
- '@jridgewell/trace-mapping': 0.3.25
-
- '@jridgewell/resolve-uri@3.1.2': {}
-
- '@jridgewell/set-array@1.2.1': {}
-
- '@jridgewell/source-map@0.3.6':
- dependencies:
- '@jridgewell/gen-mapping': 0.3.8
- '@jridgewell/trace-mapping': 0.3.25
-
- '@jridgewell/sourcemap-codec@1.5.0': {}
-
- '@jridgewell/trace-mapping@0.3.25':
- dependencies:
- '@jridgewell/resolve-uri': 3.1.2
- '@jridgewell/sourcemap-codec': 1.5.0
-
- '@rollup/rollup-android-arm-eabi@4.36.0':
- optional: true
-
- '@rollup/rollup-android-arm64@4.36.0':
- optional: true
-
- '@rollup/rollup-darwin-arm64@4.36.0':
- optional: true
-
- '@rollup/rollup-darwin-x64@4.36.0':
- optional: true
-
- '@rollup/rollup-freebsd-arm64@4.36.0':
- optional: true
-
- '@rollup/rollup-freebsd-x64@4.36.0':
- optional: true
-
- '@rollup/rollup-linux-arm-gnueabihf@4.36.0':
- optional: true
-
- '@rollup/rollup-linux-arm-musleabihf@4.36.0':
- optional: true
-
- '@rollup/rollup-linux-arm64-gnu@4.36.0':
- optional: true
-
- '@rollup/rollup-linux-arm64-musl@4.36.0':
- optional: true
-
- '@rollup/rollup-linux-loongarch64-gnu@4.36.0':
- optional: true
-
- '@rollup/rollup-linux-powerpc64le-gnu@4.36.0':
- optional: true
-
- '@rollup/rollup-linux-riscv64-gnu@4.36.0':
- optional: true
-
- '@rollup/rollup-linux-s390x-gnu@4.36.0':
- optional: true
-
- '@rollup/rollup-linux-x64-gnu@4.36.0':
- optional: true
-
- '@rollup/rollup-linux-x64-musl@4.36.0':
- optional: true
-
- '@rollup/rollup-win32-arm64-msvc@4.36.0':
- optional: true
-
- '@rollup/rollup-win32-ia32-msvc@4.36.0':
- optional: true
-
- '@rollup/rollup-win32-x64-msvc@4.36.0':
- optional: true
-
- '@sveltejs/acorn-typescript@1.0.5(acorn@8.14.1)':
- dependencies:
- acorn: 8.14.1
-
- '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.23.2)(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)))(svelte@5.23.2)(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0))':
- dependencies:
- '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.23.2)(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0))
- debug: 4.4.0
- svelte: 5.23.2
- vite: 6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)
- transitivePeerDependencies:
- - supports-color
-
- '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.23.2)(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0))':
- dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.23.2)(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)))(svelte@5.23.2)(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0))
- debug: 4.4.0
- deepmerge: 4.3.1
- kleur: 4.1.5
- magic-string: 0.30.17
- svelte: 5.23.2
- vite: 6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)
- vitefu: 1.0.6(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0))
- transitivePeerDependencies:
- - supports-color
-
- '@tsconfig/svelte@5.0.4': {}
-
- '@types/estree@1.0.6': {}
-
- '@types/node@22.13.10':
- dependencies:
- undici-types: 6.20.0
-
- '@vitest/expect@3.0.9':
- dependencies:
- '@vitest/spy': 3.0.9
- '@vitest/utils': 3.0.9
- chai: 5.2.0
- tinyrainbow: 2.0.0
-
- '@vitest/mocker@3.0.9(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0))':
- dependencies:
- '@vitest/spy': 3.0.9
- estree-walker: 3.0.3
- magic-string: 0.30.17
- optionalDependencies:
- vite: 6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)
-
- '@vitest/pretty-format@3.0.9':
- dependencies:
- tinyrainbow: 2.0.0
-
- '@vitest/runner@3.0.9':
- dependencies:
- '@vitest/utils': 3.0.9
- pathe: 2.0.3
-
- '@vitest/snapshot@3.0.9':
- dependencies:
- '@vitest/pretty-format': 3.0.9
- magic-string: 0.30.17
- pathe: 2.0.3
-
- '@vitest/spy@3.0.9':
- dependencies:
- tinyspy: 3.0.2
-
- '@vitest/utils@3.0.9':
- dependencies:
- '@vitest/pretty-format': 3.0.9
- loupe: 3.1.3
- tinyrainbow: 2.0.0
-
- acorn@8.14.1: {}
-
- ansi-styles@4.3.0:
- dependencies:
- color-convert: 2.0.1
-
- aria-query@5.3.2: {}
-
- assertion-error@2.0.1: {}
-
- async@3.2.6: {}
-
- axobject-query@4.1.0: {}
-
- balanced-match@1.0.2: {}
-
- brace-expansion@1.1.11:
- dependencies:
- balanced-match: 1.0.2
- concat-map: 0.0.1
-
- brace-expansion@2.0.1:
- dependencies:
- balanced-match: 1.0.2
-
- browserslist@4.24.4:
- dependencies:
- caniuse-lite: 1.0.30001706
- electron-to-chromium: 1.5.122
- node-releases: 2.0.19
- update-browserslist-db: 1.1.3(browserslist@4.24.4)
-
- buffer-from@1.1.2: {}
-
- cac@6.7.14: {}
-
- caniuse-lite@1.0.30001706: {}
-
- chai@5.2.0:
- dependencies:
- assertion-error: 2.0.1
- check-error: 2.1.1
- deep-eql: 5.0.2
- loupe: 3.1.3
- pathval: 2.0.0
-
- chalk@4.1.2:
- dependencies:
- ansi-styles: 4.3.0
- supports-color: 7.2.0
-
- check-error@2.1.1: {}
-
- chokidar@4.0.3:
- dependencies:
- readdirp: 4.1.2
-
- clsx@2.1.1: {}
-
- color-convert@2.0.1:
- dependencies:
- color-name: 1.1.4
-
- color-name@1.1.4: {}
-
- commander@2.20.3: {}
-
- concat-map@0.0.1: {}
-
- debug@4.4.0:
- dependencies:
- ms: 2.1.3
-
- deep-eql@5.0.2: {}
-
- deepmerge@4.3.1: {}
-
- detect-libc@2.0.3: {}
-
- ejs@3.1.10:
- dependencies:
- jake: 10.9.2
-
- electron-to-chromium@1.5.122: {}
-
- es-module-lexer@1.6.0: {}
-
- esbuild@0.25.1:
- optionalDependencies:
- '@esbuild/aix-ppc64': 0.25.1
- '@esbuild/android-arm': 0.25.1
- '@esbuild/android-arm64': 0.25.1
- '@esbuild/android-x64': 0.25.1
- '@esbuild/darwin-arm64': 0.25.1
- '@esbuild/darwin-x64': 0.25.1
- '@esbuild/freebsd-arm64': 0.25.1
- '@esbuild/freebsd-x64': 0.25.1
- '@esbuild/linux-arm': 0.25.1
- '@esbuild/linux-arm64': 0.25.1
- '@esbuild/linux-ia32': 0.25.1
- '@esbuild/linux-loong64': 0.25.1
- '@esbuild/linux-mips64el': 0.25.1
- '@esbuild/linux-ppc64': 0.25.1
- '@esbuild/linux-riscv64': 0.25.1
- '@esbuild/linux-s390x': 0.25.1
- '@esbuild/linux-x64': 0.25.1
- '@esbuild/netbsd-arm64': 0.25.1
- '@esbuild/netbsd-x64': 0.25.1
- '@esbuild/openbsd-arm64': 0.25.1
- '@esbuild/openbsd-x64': 0.25.1
- '@esbuild/sunos-x64': 0.25.1
- '@esbuild/win32-arm64': 0.25.1
- '@esbuild/win32-ia32': 0.25.1
- '@esbuild/win32-x64': 0.25.1
-
- escalade@3.2.0: {}
-
- esm-env@1.2.2: {}
-
- esrap@1.4.5:
- dependencies:
- '@jridgewell/sourcemap-codec': 1.5.0
-
- estree-walker@3.0.3:
- dependencies:
- '@types/estree': 1.0.6
-
- expect-type@1.2.0: {}
-
- fdir@6.4.3: {}
-
- filelist@1.0.4:
- dependencies:
- minimatch: 5.1.6
-
- fsevents@2.3.3:
- optional: true
-
- has-flag@4.0.0: {}
-
- is-reference@3.0.3:
- dependencies:
- '@types/estree': 1.0.6
-
- jake@10.9.2:
- dependencies:
- async: 3.2.6
- chalk: 4.1.2
- filelist: 1.0.4
- minimatch: 3.1.2
-
- kleur@4.1.5: {}
-
- lightningcss-darwin-arm64@1.29.3:
- optional: true
-
- lightningcss-darwin-x64@1.29.3:
- optional: true
-
- lightningcss-freebsd-x64@1.29.3:
- optional: true
-
- lightningcss-linux-arm-gnueabihf@1.29.3:
- optional: true
-
- lightningcss-linux-arm64-gnu@1.29.3:
- optional: true
-
- lightningcss-linux-arm64-musl@1.29.3:
- optional: true
-
- lightningcss-linux-x64-gnu@1.29.3:
- optional: true
-
- lightningcss-linux-x64-musl@1.29.3:
- optional: true
-
- lightningcss-win32-arm64-msvc@1.29.3:
- optional: true
-
- lightningcss-win32-x64-msvc@1.29.3:
- optional: true
-
- lightningcss@1.29.3:
- dependencies:
- detect-libc: 2.0.3
- optionalDependencies:
- lightningcss-darwin-arm64: 1.29.3
- lightningcss-darwin-x64: 1.29.3
- lightningcss-freebsd-x64: 1.29.3
- lightningcss-linux-arm-gnueabihf: 1.29.3
- lightningcss-linux-arm64-gnu: 1.29.3
- lightningcss-linux-arm64-musl: 1.29.3
- lightningcss-linux-x64-gnu: 1.29.3
- lightningcss-linux-x64-musl: 1.29.3
- lightningcss-win32-arm64-msvc: 1.29.3
- lightningcss-win32-x64-msvc: 1.29.3
-
- locate-character@3.0.0: {}
-
- loupe@3.1.3: {}
-
- magic-string@0.30.17:
- dependencies:
- '@jridgewell/sourcemap-codec': 1.5.0
-
- minimatch@3.1.2:
- dependencies:
- brace-expansion: 1.1.11
-
- minimatch@5.1.6:
- dependencies:
- brace-expansion: 2.0.1
-
- mri@1.2.0: {}
-
- ms@2.1.3: {}
-
- nanoid@3.3.11: {}
-
- node-releases@2.0.19: {}
-
- pathe@2.0.3: {}
-
- pathval@2.0.0: {}
-
- picocolors@1.1.1: {}
-
- postcss@8.5.3:
- dependencies:
- nanoid: 3.3.11
- picocolors: 1.1.1
- source-map-js: 1.2.1
-
- readdirp@4.1.2: {}
-
- rollup@4.36.0:
- dependencies:
- '@types/estree': 1.0.6
- optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.36.0
- '@rollup/rollup-android-arm64': 4.36.0
- '@rollup/rollup-darwin-arm64': 4.36.0
- '@rollup/rollup-darwin-x64': 4.36.0
- '@rollup/rollup-freebsd-arm64': 4.36.0
- '@rollup/rollup-freebsd-x64': 4.36.0
- '@rollup/rollup-linux-arm-gnueabihf': 4.36.0
- '@rollup/rollup-linux-arm-musleabihf': 4.36.0
- '@rollup/rollup-linux-arm64-gnu': 4.36.0
- '@rollup/rollup-linux-arm64-musl': 4.36.0
- '@rollup/rollup-linux-loongarch64-gnu': 4.36.0
- '@rollup/rollup-linux-powerpc64le-gnu': 4.36.0
- '@rollup/rollup-linux-riscv64-gnu': 4.36.0
- '@rollup/rollup-linux-s390x-gnu': 4.36.0
- '@rollup/rollup-linux-x64-gnu': 4.36.0
- '@rollup/rollup-linux-x64-musl': 4.36.0
- '@rollup/rollup-win32-arm64-msvc': 4.36.0
- '@rollup/rollup-win32-ia32-msvc': 4.36.0
- '@rollup/rollup-win32-x64-msvc': 4.36.0
- fsevents: 2.3.3
-
- sade@1.8.1:
- dependencies:
- mri: 1.2.0
-
- siginfo@2.0.0: {}
-
- source-map-js@1.2.1: {}
-
- source-map-support@0.5.21:
- dependencies:
- buffer-from: 1.1.2
- source-map: 0.6.1
-
- source-map@0.6.1: {}
-
- stackback@0.0.2: {}
-
- std-env@3.8.1: {}
-
- supports-color@7.2.0:
- dependencies:
- has-flag: 4.0.0
-
- svelte-check@4.1.5(svelte@5.23.2)(typescript@5.7.3):
- dependencies:
- '@jridgewell/trace-mapping': 0.3.25
- chokidar: 4.0.3
- fdir: 6.4.3
- picocolors: 1.1.1
- sade: 1.8.1
- svelte: 5.23.2
- typescript: 5.7.3
- transitivePeerDependencies:
- - picomatch
-
- svelte@5.23.2:
- dependencies:
- '@ampproject/remapping': 2.3.0
- '@jridgewell/sourcemap-codec': 1.5.0
- '@sveltejs/acorn-typescript': 1.0.5(acorn@8.14.1)
- '@types/estree': 1.0.6
- acorn: 8.14.1
- aria-query: 5.3.2
- axobject-query: 4.1.0
- clsx: 2.1.1
- esm-env: 1.2.2
- esrap: 1.4.5
- is-reference: 3.0.3
- locate-character: 3.0.0
- magic-string: 0.30.17
- zimmerframe: 1.1.2
-
- terser@5.39.0:
- dependencies:
- '@jridgewell/source-map': 0.3.6
- acorn: 8.14.1
- commander: 2.20.3
- source-map-support: 0.5.21
-
- tinybench@2.9.0: {}
-
- tinyexec@0.3.2: {}
-
- tinypool@1.0.2: {}
-
- tinyrainbow@2.0.0: {}
-
- tinyspy@3.0.2: {}
-
- typescript@5.7.3: {}
-
- undici-types@6.20.0: {}
-
- update-browserslist-db@1.1.3(browserslist@4.24.4):
- dependencies:
- browserslist: 4.24.4
- escalade: 3.2.0
- picocolors: 1.1.1
-
- vite-node@3.0.9(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0):
- dependencies:
- cac: 6.7.14
- debug: 4.4.0
- es-module-lexer: 1.6.0
- pathe: 2.0.3
- vite: 6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)
- transitivePeerDependencies:
- - '@types/node'
- - jiti
- - less
- - lightningcss
- - sass
- - sass-embedded
- - stylus
- - sugarss
- - supports-color
- - terser
- - tsx
- - yaml
-
- vite-plugin-css-injected-by-js@3.5.2(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)):
- dependencies:
- vite: 6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)
-
- vite-plugin-ejs@1.7.0(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)):
- dependencies:
- ejs: 3.1.10
- vite: 6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)
-
- vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0):
- dependencies:
- esbuild: 0.25.1
- postcss: 8.5.3
- rollup: 4.36.0
- optionalDependencies:
- '@types/node': 22.13.10
- fsevents: 2.3.3
- lightningcss: 1.29.3
- terser: 5.39.0
-
- vitefu@1.0.6(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)):
- optionalDependencies:
- vite: 6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)
-
- vitest@3.0.9(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0):
- dependencies:
- '@vitest/expect': 3.0.9
- '@vitest/mocker': 3.0.9(vite@6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0))
- '@vitest/pretty-format': 3.0.9
- '@vitest/runner': 3.0.9
- '@vitest/snapshot': 3.0.9
- '@vitest/spy': 3.0.9
- '@vitest/utils': 3.0.9
- chai: 5.2.0
- debug: 4.4.0
- expect-type: 1.2.0
- magic-string: 0.30.17
- pathe: 2.0.3
- std-env: 3.8.1
- tinybench: 2.9.0
- tinyexec: 0.3.2
- tinypool: 1.0.2
- tinyrainbow: 2.0.0
- vite: 6.2.2(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)
- vite-node: 3.0.9(@types/node@22.13.10)(lightningcss@1.29.3)(terser@5.39.0)
- why-is-node-running: 2.3.0
- optionalDependencies:
- '@types/node': 22.13.10
- transitivePeerDependencies:
- - jiti
- - less
- - lightningcss
- - msw
- - sass
- - sass-embedded
- - stylus
- - sugarss
- - supports-color
- - terser
- - tsx
- - yaml
-
- why-is-node-running@2.3.0:
- dependencies:
- siginfo: 2.0.0
- stackback: 0.0.2
-
- zimmerframe@1.1.2: {}
diff --git a/site/plugins/loop/frontend/src/App.svelte b/site/plugins/loop/frontend/src/App.svelte
deleted file mode 100644
index 83a68df..0000000
--- a/site/plugins/loop/frontend/src/App.svelte
+++ /dev/null
@@ -1,216 +0,0 @@
-
-
-
-
-
-
-{#if showLoop}
-
-
-
-
- {#each visibleComments as comment (comment.id)}
-
- {/each}
-
-
-{/if}
-
-
diff --git a/site/plugins/loop/frontend/src/composables/decodeHTMLEntities.ts b/site/plugins/loop/frontend/src/composables/decodeHTMLEntities.ts
deleted file mode 100644
index e65ea71..0000000
--- a/site/plugins/loop/frontend/src/composables/decodeHTMLEntities.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Decodes HTML entities in a string
- * @param text The text that may contain HTML entities
- * @returns The decoded text
- */
-export function decodeHTMLEntities(text: string): string {
- const entityMap: Record = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- ''': "'",
- '/': '/',
- '`': '`',
- '=': '='
- };
-
- return text.replace(/&[#\w]+;/g, (entity) => entityMap[entity] || entity);
-}
diff --git a/site/plugins/loop/frontend/src/composables/formatDate.ts b/site/plugins/loop/frontend/src/composables/formatDate.ts
deleted file mode 100644
index 3b8177c..0000000
--- a/site/plugins/loop/frontend/src/composables/formatDate.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { t, tt } from "../store/translations.svelte";
-
-export function formatDate(timestamp: number, humanize = true): string {
- const date = new Date(timestamp * 1000);
- const now = new Date();
- const diffInMs = now.getTime() - date.getTime();
- const diffInMinutes = Math.floor(diffInMs / (1000 * 60));
- const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60));
- const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
-
- // Show relative time for up to 3 days
- if (humanize && diffInDays <= 3) {
- if (diffInMinutes < 1) {
- return t("ui.time.just_now", "just now");
- } else if (diffInMinutes === 1) {
- return t("ui.time.minute_ago", "a minute ago");
- } else if (diffInMinutes < 60) {
- return tt("ui.time.minutes_ago", "{count} minutes ago", { count: diffInMinutes.toString() });
- } else if (diffInHours === 1) {
- return t("ui.time.hour_ago", "an hour ago");
- } else if (diffInHours < 24) {
- return tt("ui.time.hours_ago", "{count} hours ago", { count: diffInHours.toString() });
- } else if (diffInDays === 1) {
- return t("ui.time.yesterday", "yesterday");
- } else {
- return tt("ui.time.days_ago", "{count} days ago", { count: diffInDays.toString() });
- }
- }
-
- return date.toLocaleString(undefined, { dateStyle: "short", timeStyle: "short" });
-}
diff --git a/site/plugins/loop/frontend/src/composables/formatDateISO.ts b/site/plugins/loop/frontend/src/composables/formatDateISO.ts
deleted file mode 100644
index ba5c164..0000000
--- a/site/plugins/loop/frontend/src/composables/formatDateISO.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export function formatDateISO(timestamp: number): string {
- return new Date(timestamp * 1000).toISOString();
-}
\ No newline at end of file
diff --git a/site/plugins/loop/frontend/src/composables/getDialogPosition.ts b/site/plugins/loop/frontend/src/composables/getDialogPosition.ts
deleted file mode 100644
index 1e54920..0000000
--- a/site/plugins/loop/frontend/src/composables/getDialogPosition.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { getDocumentHeight } from "./getDocumentHeight";
-import { getDocumentWidth } from "./getDocumentWidth";
-
-export const getDialogPosition = (marker: { pagePositionX: number, pagePositionY: number } | null,
- dialogElement: HTMLDialogElement | null): { left: number, top: number } => {
- // Default position (fallback)
- let left = 0;
- let top = 0;
-
- if (!marker || !dialogElement) return { left, top };
-
- // Get marker position
- left = marker.pagePositionX;
- top = marker.pagePositionY;
-
- // Get dialog dimensions
- const dialogWidth = dialogElement.offsetWidth;
- const dialogHeight = dialogElement.offsetHeight;
-
- // Get document dimensions
- const documentWidth = getDocumentWidth();
- const documentHeight = getDocumentHeight();
-
- // Ensure dialog doesn't go off-screen to the right
- if (left + dialogWidth > documentWidth) {
- left = documentWidth - dialogWidth;
- }
-
- // Ensure dialog doesn't go off-screen to the bottom
- if (top + dialogHeight > documentHeight) {
- top = documentHeight - dialogHeight;
- }
-
- // Ensure dialog doesn't go off-screen to the left or top
- left = Math.max(0, left);
- top = Math.max(0, top);
-
- return { left, top };
-}
diff --git a/site/plugins/loop/frontend/src/composables/getDocumentHeight.ts b/site/plugins/loop/frontend/src/composables/getDocumentHeight.ts
deleted file mode 100644
index 8171a77..0000000
--- a/site/plugins/loop/frontend/src/composables/getDocumentHeight.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-// Get the entire document height, including scrollable area
-export const getDocumentHeight = (): number => {
- const body = document.body;
- const html = document.documentElement;
-
- return Math.max(
- body.scrollHeight,
- body.offsetHeight,
- html.clientHeight,
- html.scrollHeight,
- html.offsetHeight
- );
-};
diff --git a/site/plugins/loop/frontend/src/composables/getDocumentWidth.ts b/site/plugins/loop/frontend/src/composables/getDocumentWidth.ts
deleted file mode 100644
index 1eb49d6..0000000
--- a/site/plugins/loop/frontend/src/composables/getDocumentWidth.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-// Get the entire document width, including scrollable area
-export const getDocumentWidth = (): number => {
- const body = document.body;
- const html = document.documentElement;
-
- return Math.max(
- body.scrollWidth,
- body.offsetWidth,
- html.clientWidth,
- html.scrollWidth,
- html.offsetWidth
- );
-};
diff --git a/site/plugins/loop/frontend/src/composables/getSelectorOffset.ts b/site/plugins/loop/frontend/src/composables/getSelectorOffset.ts
deleted file mode 100644
index 4a35241..0000000
--- a/site/plugins/loop/frontend/src/composables/getSelectorOffset.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-export const getSelectorOffset = (e: MouseEvent, element: HTMLElement): { selectorOffsetX: number, selectorOffsetY: number } => {
- // Get absolute click position (relative to the document)
- const clickX = e.pageX;
- const clickY = e.pageY;
-
- // Get element's position relative to the document
- const rect = element.getBoundingClientRect();
- const elementX = rect.left + window.scrollX;
- const elementY = rect.top + window.scrollY;
-
- // Calculate relative offsets
- const offsetXRel = clickX - elementX;
- const offsetYRel = clickY - elementY;
-
- // Convert to percentages
- let offsetX = (offsetXRel / element.offsetWidth) * 100;
- let offsetY = (offsetYRel / element.offsetHeight) * 100;
-
- // Round to 2 decimal places
- offsetX = Number(offsetX.toFixed(2));
- offsetY = Number(offsetY.toFixed(2));
-
- return {
- selectorOffsetX: offsetX, selectorOffsetY: offsetY
- };
-}
diff --git a/site/plugins/loop/frontend/src/composables/setNewMarker.ts b/site/plugins/loop/frontend/src/composables/setNewMarker.ts
deleted file mode 100644
index 09802a4..0000000
--- a/site/plugins/loop/frontend/src/composables/setNewMarker.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useGenerateSelector } from "./useGenerateSelector";
-import { getSelectorOffset } from "./getSelectorOffset";
-
-export const setNewMarker = (e: MouseEvent) => {
-
- const selector = useGenerateSelector(e);
- const element: HTMLElement | null = document.querySelector(selector);
-
- // error out, if no selector found
- if (!element) return;
-
- const { selectorOffsetX, selectorOffsetY } = getSelectorOffset(e, element);
-
- // Store absolute position on the page
- let pagePositionX = e.pageX;
- let pagePositionY = e.pageY;
-
- // Round to 2 digits
- pagePositionX = Number(pagePositionX.toFixed(2));
- pagePositionY = Number(pagePositionY.toFixed(2));
-
- return {
- selector,
- selectorOffsetX,
- selectorOffsetY,
- pagePositionX,
- pagePositionY
- }
-
-}
-
-export default setNewMarker;
diff --git a/site/plugins/loop/frontend/src/composables/useGenerateSelector.ts b/site/plugins/loop/frontend/src/composables/useGenerateSelector.ts
deleted file mode 100644
index 8c06b59..0000000
--- a/site/plugins/loop/frontend/src/composables/useGenerateSelector.ts
+++ /dev/null
@@ -1,352 +0,0 @@
-/**
- * CSS Selector Generator Composable
- * Generates reliable, unique CSS selectors for clicked DOM elements
- */
-
-type SelectorStrategy = {
- name: string;
- generator: (element: Element) => string | null;
- priority: number;
-};
-
-/**
- * Main composable function to generate CSS selector from click event
- * @param event - Mouse event from user click
- * @returns CSS selector string that uniquely identifies the clicked element
- */
-export function useGenerateSelector(event: MouseEvent): string {
- const target = event.target as Element;
-
- if (!target) {
- throw new Error('No target element found in event');
- }
-
- // Try each strategy in priority order
- const strategies = getSelectorStrategies();
-
- for (const strategy of strategies) {
- try {
- const selector = strategy.generator(target);
-
- if (selector && validateSelector(selector, target)) {
- return selector;
- }
- } catch (error) {
- console.warn(`Strategy ${strategy.name} failed:`, error);
- }
- }
-
- // Ultimate fallback - generate a path selector
- return generatePathSelector(target);
-}
-
-/**
- * Define selector generation strategies in priority order
- */
-function getSelectorStrategies(): SelectorStrategy[] {
- return [
- {
- name: 'ID',
- priority: 1,
- generator: (element: Element) => {
- if (element.id && isValidId(element.id)) {
- return `#${CSS.escape(element.id)}`;
- }
- return null;
- }
- },
- {
- name: 'Unique Attributes',
- priority: 2,
- generator: (element: Element) => {
- const uniqueAttrs = ['data-testid', 'data-id', 'name', 'for'];
-
- for (const attr of uniqueAttrs) {
- const value = element.getAttribute(attr);
- if (value) {
- const selector = `${element.tagName.toLowerCase()}[${attr}="${CSS.escape(value)}"]`;
- if (isUniqueSelector(selector)) {
- return selector;
- }
- }
- }
- return null;
- }
- },
- {
- name: 'Semantic Attributes',
- priority: 3,
- generator: (element: Element) => {
- const semanticAttrs = [
- 'aria-label',
- 'aria-labelledby',
- 'role',
- 'type',
- 'placeholder',
- 'title',
- 'alt'
- ];
-
- const tagName = element.tagName.toLowerCase();
- const selectors: string[] = [tagName];
-
- for (const attr of semanticAttrs) {
- const value = element.getAttribute(attr);
- if (value) {
- selectors.push(`[${attr}="${CSS.escape(value)}"]`);
- }
- }
-
- if (selectors.length > 1) {
- const selector = selectors.join('');
- if (isUniqueSelector(selector)) {
- return selector;
- }
- }
-
- return null;
- }
- },
- {
- name: 'Structural Attributes',
- priority: 4,
- generator: (element: Element) => {
- const structuralAttrs = ['href', 'src', 'action', 'value'];
- const tagName = element.tagName.toLowerCase();
-
- for (const attr of structuralAttrs) {
- const value = element.getAttribute(attr);
- if (value && value.length > 0) {
- const selector = `${tagName}[${attr}="${CSS.escape(value)}"]`;
- if (isUniqueSelector(selector)) {
- return selector;
- }
- }
- }
-
- return null;
- }
- },
- {
- name: 'Class Combinations',
- priority: 5,
- generator: (element: Element) => {
- const classes = getStableClasses(element);
-
- if (classes.length === 0) {
- return null;
- }
-
- const tagName = element.tagName.toLowerCase();
-
- // Try single class first
- for (const className of classes) {
- const selector = `${tagName}.${CSS.escape(className)}`;
- if (isUniqueSelector(selector)) {
- return selector;
- }
- }
-
- // Try combinations of classes
- if (classes.length >= 2) {
- const classSelector = classes.slice(0, 3).map(c => `.${CSS.escape(c)}`).join('');
- const selector = `${tagName}${classSelector}`;
- if (isUniqueSelector(selector)) {
- return selector;
- }
- }
-
- return null;
- }
- },
- {
- name: 'Parent Context',
- priority: 6,
- generator: (element: Element) => {
- const parent = element.parentElement;
- if (!parent) return null;
-
- // Try to get a unique selector for parent
- const parentSelector = getSimpleSelector(parent);
- if (!parentSelector) return null;
-
- const tagName = element.tagName.toLowerCase();
- const siblingIndex = getSiblingIndex(element);
-
- if (siblingIndex > 0) {
- const selector = `${parentSelector} > ${tagName}:nth-of-type(${siblingIndex})`;
- if (isUniqueSelector(selector)) {
- return selector;
- }
- }
-
- // Try with classes
- const classes = getStableClasses(element);
- if (classes.length > 0) {
- const selector = `${parentSelector} > ${tagName}.${CSS.escape(classes[0])}`;
- if (isUniqueSelector(selector)) {
- return selector;
- }
- }
-
- return null;
- }
- }
- ];
-}
-
-/**
- * Get stable classes (excluding utility/state classes)
- */
-function getStableClasses(element: Element): string[] {
- const classes = Array.from(element.classList);
-
- // Filter out common utility/state classes
- const unstablePatterns = [
- /^(is-|has-|js-)/, // State prefixes
- /^(active|disabled|loading|selected|hover|focus)/, // State classes
- /^[a-z]+-[0-9]+$/, // Generated classes like 'item-123'
- /^(sm-|md-|lg-|xl-)/, // Responsive utilities
- /^(m-|p-|w-|h-|text-|bg-)/, // Tailwind-like utilities
- /^[a-f0-9]{6,}$/, // Hash-like classes
- ];
-
- return classes.filter(className => {
- return !unstablePatterns.some(pattern => pattern.test(className));
- });
-}
-
-/**
- * Generate a path-based selector as fallback
- */
-function generatePathSelector(element: Element): string {
- const path: string[] = [];
- let current: Element | null = element;
-
- while (current && current !== document.body && path.length < 5) {
- const selector = getElementSelector(current);
- path.unshift(selector);
-
- // Check if this partial path is unique
- const partialSelector = path.join(' > ');
- if (isUniqueSelector(partialSelector)) {
- return partialSelector;
- }
-
- current = current.parentElement;
- }
-
- return path.join(' > ');
-}
-
-/**
- * Get a simple selector for an element
- */
-function getSimpleSelector(element: Element): string | null {
- // Try ID first
- if (element.id && isValidId(element.id)) {
- return `#${CSS.escape(element.id)}`;
- }
-
- // Try unique attributes
- const uniqueAttrs = ['data-testid', 'data-id', 'name'];
- for (const attr of uniqueAttrs) {
- const value = element.getAttribute(attr);
- if (value) {
- return `[${attr}="${CSS.escape(value)}"]`;
- }
- }
-
- // Try tag + first stable class
- const tagName = element.tagName.toLowerCase();
- const classes = getStableClasses(element);
-
- if (classes.length > 0) {
- return `${tagName}.${CSS.escape(classes[0])}`;
- }
-
- return null;
-}
-
-/**
- * Get selector for element in path
- */
-function getElementSelector(element: Element): string {
- const tagName = element.tagName.toLowerCase();
-
- // Use ID if available
- if (element.id && isValidId(element.id)) {
- return `#${CSS.escape(element.id)}`;
- }
-
- // Use classes if available
- const classes = getStableClasses(element);
- if (classes.length > 0) {
- return `${tagName}.${CSS.escape(classes[0])}`;
- }
-
- // Use nth-of-type for siblings
- const index = getSiblingIndex(element);
- if (index > 1) {
- return `${tagName}:nth-of-type(${index})`;
- }
-
- return tagName;
-}
-
-/**
- * Get sibling index for nth-of-type
- */
-function getSiblingIndex(element: Element): number {
- let index = 1;
- let sibling = element.previousElementSibling;
-
- while (sibling) {
- if (sibling.tagName === element.tagName) {
- index++;
- }
- sibling = sibling.previousElementSibling;
- }
-
- return index;
-}
-
-/**
- * Validate that a selector uniquely identifies the target element
- */
-function validateSelector(selector: string, target: Element): boolean {
- try {
- const matches = document.querySelectorAll(selector);
- return matches.length === 1 && matches[0] === target;
- } catch (error) {
- console.warn(`Invalid selector: ${selector}`, error);
- return false;
- }
-}
-
-/**
- * Check if a selector matches exactly one element
- */
-function isUniqueSelector(selector: string): boolean {
- try {
- const matches = document.querySelectorAll(selector);
- return matches.length === 1;
- } catch (error) {
- return false;
- }
-}
-
-/**
- * Check if ID is valid (not auto-generated)
- */
-function isValidId(id: string): boolean {
- // Skip IDs that look auto-generated
- const invalidPatterns = [
- /^[a-f0-9]{8,}$/, // Hex strings
- /^(ember|react|vue)[0-9]+/, // Framework generated
- /^[0-9]+$/, // Pure numbers
- /^temp-/, // Temporary prefixes
- ];
-
- return !invalidPatterns.some(pattern => pattern.test(id));
-}
diff --git a/site/plugins/loop/frontend/src/composables/useResizeHandler.ts b/site/plugins/loop/frontend/src/composables/useResizeHandler.ts
deleted file mode 100644
index 719df77..0000000
--- a/site/plugins/loop/frontend/src/composables/useResizeHandler.ts
+++ /dev/null
@@ -1,130 +0,0 @@
-/**
- * Performance-optimized resize handler with debouncing and RAF
- */
-
-interface ResizeCallback {
- (): void;
-}
-
-interface ResizeHandlerOptions {
- /** Debounce delay in milliseconds (default: 100) */
- debounceDelay?: number;
- /** Whether to use requestAnimationFrame (default: true) */
- useRAF?: boolean;
-}
-
-class ResizeHandler {
- private callbacks = new Set();
- private debounceTimer: number | null = null;
- private rafId: number | null = null;
- private isListening = false;
- private options: Required;
-
- constructor(options: ResizeHandlerOptions = {}) {
- this.options = {
- debounceDelay: options.debounceDelay ?? 100,
- useRAF: options.useRAF ?? true,
- };
- }
-
- private handleResize = () => {
- // Clear existing timers
- if (this.debounceTimer) {
- clearTimeout(this.debounceTimer);
- }
- if (this.rafId) {
- cancelAnimationFrame(this.rafId);
- }
-
- // Debounce the resize event
- this.debounceTimer = window.setTimeout(() => {
- if (this.options.useRAF) {
- // Use RAF for smooth updates
- this.rafId = requestAnimationFrame(() => {
- this.executeCallbacks();
- });
- } else {
- this.executeCallbacks();
- }
- }, this.options.debounceDelay);
- };
-
- private executeCallbacks() {
- this.callbacks.forEach(callback => {
- try {
- callback();
- } catch (error) {
- console.error('Error in resize callback:', error);
- }
- });
- }
-
- private startListening() {
- if (!this.isListening) {
- window.addEventListener('resize', this.handleResize, { passive: true });
- this.isListening = true;
- }
- }
-
- private stopListening() {
- if (this.isListening) {
- window.removeEventListener('resize', this.handleResize);
- this.isListening = false;
- }
- }
-
- /**
- * Add a callback to be executed on resize
- */
- subscribe(callback: ResizeCallback): () => void {
- this.callbacks.add(callback);
- this.startListening();
-
- // Return unsubscribe function
- return () => {
- this.callbacks.delete(callback);
- if (this.callbacks.size === 0) {
- this.stopListening();
- }
- };
- }
-
- /**
- * Clean up all resources
- */
- destroy() {
- this.callbacks.clear();
- this.stopListening();
- if (this.debounceTimer) {
- clearTimeout(this.debounceTimer);
- }
- if (this.rafId) {
- cancelAnimationFrame(this.rafId);
- }
- }
-}
-
-// Singleton instance for global use
-const globalResizeHandler = new ResizeHandler();
-
-/**
- * Svelte composable for handling window resize events with performance optimization
- * @param callback Function to call on resize
- * @param options Configuration options
- * @returns Cleanup function
- */
-export function useResizeHandler(
- callback: ResizeCallback,
- options?: ResizeHandlerOptions
-): () => void {
- if (options) {
- // Create a new handler with custom options
- const handler = new ResizeHandler(options);
- return handler.subscribe(callback);
- } else {
- // Use the global handler
- return globalResizeHandler.subscribe(callback);
- }
-}
-
-export default useResizeHandler;
\ No newline at end of file
diff --git a/site/plugins/loop/frontend/src/lib/Author.svelte b/site/plugins/loop/frontend/src/lib/Author.svelte
deleted file mode 100644
index 0be9b17..0000000
--- a/site/plugins/loop/frontend/src/lib/Author.svelte
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- {initials}
-
-
-
diff --git a/site/plugins/loop/frontend/src/lib/Button.svelte b/site/plugins/loop/frontend/src/lib/Button.svelte
deleted file mode 100644
index 75f7063..0000000
--- a/site/plugins/loop/frontend/src/lib/Button.svelte
+++ /dev/null
@@ -1,256 +0,0 @@
-
-
-
-
-
diff --git a/site/plugins/loop/frontend/src/lib/Comment.svelte b/site/plugins/loop/frontend/src/lib/Comment.svelte
deleted file mode 100644
index cd74c17..0000000
--- a/site/plugins/loop/frontend/src/lib/Comment.svelte
+++ /dev/null
@@ -1,227 +0,0 @@
-
-
-
-
-
diff --git a/site/plugins/loop/frontend/src/lib/CommentDialog.svelte b/site/plugins/loop/frontend/src/lib/CommentDialog.svelte
deleted file mode 100644
index 5e21a08..0000000
--- a/site/plugins/loop/frontend/src/lib/CommentDialog.svelte
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
diff --git a/site/plugins/loop/frontend/src/lib/CommentForm.svelte b/site/plugins/loop/frontend/src/lib/CommentForm.svelte
deleted file mode 100644
index d8d9cd1..0000000
--- a/site/plugins/loop/frontend/src/lib/CommentForm.svelte
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-
-
-
diff --git a/site/plugins/loop/frontend/src/lib/ContextMenu.svelte b/site/plugins/loop/frontend/src/lib/ContextMenu.svelte
deleted file mode 100644
index f90fb93..0000000
--- a/site/plugins/loop/frontend/src/lib/ContextMenu.svelte
+++ /dev/null
@@ -1,187 +0,0 @@
-
-
-
-
-
diff --git a/site/plugins/loop/frontend/src/lib/Header.svelte b/site/plugins/loop/frontend/src/lib/Header.svelte
deleted file mode 100644
index 711bef3..0000000
--- a/site/plugins/loop/frontend/src/lib/Header.svelte
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/site/plugins/loop/frontend/src/lib/Marker.svelte b/site/plugins/loop/frontend/src/lib/Marker.svelte
deleted file mode 100644
index 96bcd52..0000000
--- a/site/plugins/loop/frontend/src/lib/Marker.svelte
+++ /dev/null
@@ -1,179 +0,0 @@
-
-
-{#if comment}
-
-{/if}
-
-
diff --git a/site/plugins/loop/frontend/src/lib/Panel.svelte b/site/plugins/loop/frontend/src/lib/Panel.svelte
deleted file mode 100644
index 8cfbc4e..0000000
--- a/site/plugins/loop/frontend/src/lib/Panel.svelte
+++ /dev/null
@@ -1,181 +0,0 @@
-
-
-
-
-
diff --git a/site/plugins/loop/frontend/src/lib/Reply.svelte b/site/plugins/loop/frontend/src/lib/Reply.svelte
deleted file mode 100644
index 8902dab..0000000
--- a/site/plugins/loop/frontend/src/lib/Reply.svelte
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
- {reply.author}
-
-
-
{decodeHTMLEntities(reply.comment)}
-
-
-
-
diff --git a/site/plugins/loop/frontend/src/lib/WelcomeDialog.svelte b/site/plugins/loop/frontend/src/lib/WelcomeDialog.svelte
deleted file mode 100644
index 2c9b412..0000000
--- a/site/plugins/loop/frontend/src/lib/WelcomeDialog.svelte
+++ /dev/null
@@ -1,168 +0,0 @@
-
-
-
-
-
diff --git a/site/plugins/loop/frontend/src/main.ts b/site/plugins/loop/frontend/src/main.ts
deleted file mode 100644
index 04ebb82..0000000
--- a/site/plugins/loop/frontend/src/main.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import App from './App.svelte'
-import "./styles/variables.css"
-import "./styles/app.css"
-
-export default App;
diff --git a/site/plugins/loop/frontend/src/store/api.svelte.ts b/site/plugins/loop/frontend/src/store/api.svelte.ts
deleted file mode 100644
index bdc5c71..0000000
--- a/site/plugins/loop/frontend/src/store/api.svelte.ts
+++ /dev/null
@@ -1,111 +0,0 @@
-import type { Comment, CommentPayload, Reply, ReplyPayload } from '../types';
-
-export const store: { comments: Comment[] } = $state({
- comments: []
-});
-
-const apiPrefix = 'loop';
-const KirbyLoop = document.querySelector('kirby-loop');
-const csrfToken = KirbyLoop?.getAttribute('csrf-token') || '';
-const apiBase = KirbyLoop?.getAttribute('apibase') || '/';
-const headers = {
- 'Content-Type': 'application/json',
- 'X-CSRF-Token': csrfToken || ''
-};
-
-const buildApiUrl = (endpoint: string): string => {
- const url = new URL(`${apiBase}/${apiPrefix}/${endpoint}`, window.location.origin);
-
- // Add token query params from current page if they exist
- const currentParams = new URLSearchParams(window.location.search);
- const token = currentParams.get('token') || currentParams.get('_token');
- if (token) {
- url.searchParams.set(currentParams.has('token') ? 'token' : '_token', token);
- }
-
- return url.toString();
-};
-
-export const getComments = async (pageId: string): Promise => {
- const url = buildApiUrl(`comments/${pageId}`);
- const response = await fetch(url, {
- headers
- });
- const data = await response.json();
- if (data.status === 'ok') {
- store.comments = data.comments;
- }
- return data.status === 'ok';
-}
-
-export const addComment = async (comment: CommentPayload) => {
- const url = buildApiUrl('comment/new');
- const response = await fetch(url, {
- method: 'POST',
- headers,
- body: JSON.stringify(comment)
- });
- const data: { comment: Comment, status: string } = await response.json();
- if (data.status === 'ok') {
- store.comments = [data.comment, ...store.comments];
- }
-}
-
-export const resolveComment = async (comment: Comment) => {
- const url = buildApiUrl('comment/resolve');
- const response = await fetch(url, {
- method: 'POST',
- headers,
- body: JSON.stringify({ id: comment.id })
- });
- const data: { success: boolean } = await response.json();
- if (data.success) {
- const commentIndex = store.comments.findIndex(c => c.id === comment.id);
- if (commentIndex !== -1) {
- store.comments[commentIndex].status = 'RESOLVED';
- }
- }
- return data.success;
-}
-
-export const unresolveComment = async (comment: Comment) => {
- const url = buildApiUrl('comment/unresolve');
- const response = await fetch(url, {
- method: 'POST',
- headers,
- body: JSON.stringify({ id: comment.id })
- });
- const data: { success: boolean } = await response.json();
- if (data.success) {
- const commentIndex = store.comments.findIndex(c => c.id === comment.id);
- if (commentIndex !== -1) {
- store.comments[commentIndex].status = 'OPEN';
- }
- }
- return data.success;
-}
-
-export const setGuestName = async (name: string) => {
- const response = await fetch(buildApiUrl('guest/name'), {
- method: 'POST',
- headers,
- body: JSON.stringify({ name })
- });
- return await response.json();
-}
-
-export const addReply = async (reply: ReplyPayload) => {
- const url = buildApiUrl('comment/reply');
- const response = await fetch(url, {
- method: 'POST',
- headers,
- body: JSON.stringify(reply)
- });
- const data: { reply: Reply, status: string } = await response.json();
- if (data.status === 'ok') {
- const parent = store.comments.find(c => c.id === data.reply.parentId)
- if (parent) parent.replies = [...parent.replies, data.reply];
- }
-}
-
-export default store;
diff --git a/site/plugins/loop/frontend/src/store/form.svelte.ts b/site/plugins/loop/frontend/src/store/form.svelte.ts
deleted file mode 100644
index a33d7a5..0000000
--- a/site/plugins/loop/frontend/src/store/form.svelte.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import type { FormData } from '../types';
-
-export const formData: FormData = $state({
- text: "",
- parentId: null
-});
-
-export const reset = () => {
- formData.text = ""
- formData.parentId = null
-}
diff --git a/site/plugins/loop/frontend/src/store/translations.svelte.ts b/site/plugins/loop/frontend/src/store/translations.svelte.ts
deleted file mode 100644
index 5edeb33..0000000
--- a/site/plugins/loop/frontend/src/store/translations.svelte.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-let translations = $state>({});
-
-export const t = (key: string, fallback?: string): string => {
- return translations[key] || fallback || key;
-};
-
-export const tt = (key: string, fallback: string, replacements: Record): string => {
- let text = translations[key] || fallback || key;
-
- for (const [placeholder, value] of Object.entries(replacements)) {
- text = text.replace(`{${placeholder}}`, value);
- }
-
- return text;
-};
-
-export const setTranslations = (newTranslations: Record) => {
- translations = newTranslations;
-};
\ No newline at end of file
diff --git a/site/plugins/loop/frontend/src/store/ui.svelte.ts b/site/plugins/loop/frontend/src/store/ui.svelte.ts
deleted file mode 100644
index 305b47c..0000000
--- a/site/plugins/loop/frontend/src/store/ui.svelte.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-export const panel = $state({
- open: false,
- currentCommentId: 0,
- showResolvedOnly: false,
- pulseMarkerId: 0
-});
-export const overlay = $state({ open: false });
-
-// Guest name management
-let guestNameValue = $state("");
-
-export const guestName = {
- get value() {
- return guestNameValue;
- },
- set(name: string) {
- guestNameValue = name;
- if (typeof window !== 'undefined') {
- sessionStorage.setItem('loop-guest-name', name);
- }
- },
- get() {
- if (!guestNameValue && typeof window !== 'undefined') {
- guestNameValue = sessionStorage.getItem('loop-guest-name') || "";
- }
- return guestNameValue;
- },
- clear() {
- guestNameValue = "";
- if (typeof window !== 'undefined') {
- sessionStorage.removeItem('loop-guest-name');
- }
- }
-};
diff --git a/site/plugins/loop/frontend/src/styles/app.css b/site/plugins/loop/frontend/src/styles/app.css
deleted file mode 100644
index fd92a49..0000000
--- a/site/plugins/loop/frontend/src/styles/app.css
+++ /dev/null
@@ -1,13 +0,0 @@
-kirby-loop {
- font-family: var(--font-family);
- line-height: var(--line-height);
- font-weight: var(--font-weight-normal);
- font-size: var(--font-size-7);
- box-sizing: border-box;
-}
-
-html.loop-overlay-open {
- a {
- pointer-events: none;
- }
-}
diff --git a/site/plugins/loop/frontend/src/styles/theme-dark.css b/site/plugins/loop/frontend/src/styles/theme-dark.css
deleted file mode 100644
index a7ce655..0000000
--- a/site/plugins/loop/frontend/src/styles/theme-dark.css
+++ /dev/null
@@ -1,35 +0,0 @@
-kirby-loop[theme="dark"] {
- /* Accent lightness values */
- --color-accent-l: 0.85;
-
- /* Neutral lightness values */
- --color-neutral-l-0: 0;
- --color-neutral-l-100: 0.1;
- --color-neutral-l-200: 0.2;
- --color-neutral-l-300: 0.45;
- --color-neutral-l-400: 0.5;
- --color-neutral-l-600: 0.55;
- --color-neutral-l-500: 0.6;
- --color-neutral-l-700: 0.7;
- --color-neutral-l-800: 0.8;
- --color-neutral-l-900: 0.95;
- --color-neutral-l-1000: 1;
-
- /* Shadow tokens */
- --shadow-s: 0 0.1em 0.25em oklch(var(--color-neutral-l-100) var(--color-neutral-c) var(--color-neutral-h) / 0.1);
- --shadow-m: 0 2px 8px oklch(var(--color-neutral-l-100) var(--color-neutral-c) var(--color-neutral-h) / 0.08),
- 0 8px 16px oklch(var(--color-neutral-l-100) var(--color-neutral-c) var(--color-neutral-h) / 0.12),
- 0 16px 24px oklch(var(--color-neutral-l-100) var(--color-neutral-c) var(--color-neutral-h) / 0.08);
- --shadow-l: 0 4px 16px oklch(var(--color-neutral-l-100) var(--color-neutral-c) var(--color-neutral-h) / 0.08),
- 0 12px 32px oklch(var(--color-neutral-l-100) var(--color-neutral-c) var(--color-neutral-h) / 0.12),
- 0 24px 48px oklch(var(--color-neutral-l-100) var(--color-neutral-c) var(--color-neutral-h) / 0.16),
- 0 48px 80px oklch(var(--color-neutral-l-100) var(--color-neutral-c) var(--color-neutral-h) / 0.08);
- --shadow-light-edge: inset 1px 1px 1px oklch(var(--color-neutral-l-1000) var(--color-neutral-c) var(--color-neutral-h) / 0.3);
- --shadow-dark-edge: inset -1px -1px 1px oklch(var(--color-neutral-l-100) var(--color-neutral-c) var(--color-neutral-h) / 0.3);
-
- /* Background tokens */
- --background-glass: linear-gradient(135deg, transparent, var(--color-base-background-o-50));
-
- /* Panel */
- --panel-threads-background: oklch(var(--color-neutral-l-200) var(--color-neutral-c) var(--color-neutral-h) / 0.99)
-}
\ No newline at end of file
diff --git a/site/plugins/loop/frontend/src/styles/theme-default.css b/site/plugins/loop/frontend/src/styles/theme-default.css
deleted file mode 100644
index f8f39bc..0000000
--- a/site/plugins/loop/frontend/src/styles/theme-default.css
+++ /dev/null
@@ -1,23 +0,0 @@
-kirby-loop {
- /* Color Customization */
- --color-neutral-h: 900;
- --color-neutral-c: 0;
- --color-accent-h: 900;
- --color-accent-c: 0.18;
- --color-accent-l: 0.75;
- --color-accent-dark-factor: 0.4;
- --color-accent-light-factor: 1.2;
-
- /* Neutral lightness values */
- --color-neutral-l-0: 1;
- --color-neutral-l-100: 0.95;
- --color-neutral-l-200: 0.9;
- --color-neutral-l-300: 0.7;
- --color-neutral-l-400: 0.6;
- --color-neutral-l-600: 0.4;
- --color-neutral-l-500: 0.5;
- --color-neutral-l-700: 0.3;
- --color-neutral-l-800: 0.2;
- --color-neutral-l-900: 0.1;
- --color-neutral-l-1000: 0;
-}
\ No newline at end of file
diff --git a/site/plugins/loop/frontend/src/styles/variables.css b/site/plugins/loop/frontend/src/styles/variables.css
deleted file mode 100644
index 19d41d1..0000000
--- a/site/plugins/loop/frontend/src/styles/variables.css
+++ /dev/null
@@ -1,421 +0,0 @@
-@import "./theme-default.css";
-@import "./theme-dark.css";
-
-kirby-loop {
- /* Colors */
- --color-base: var(--color-neutral-900);
- --color-base-background: var(--color-neutral-0);
-
- --color-base-background-o-5: oklch(var(--color-neutral-l-0) var(--color-neutral-c) var(--color-neutral-h) / 0.05);
- --color-base-background-o-10: oklch(var(--color-neutral-l-0) var(--color-neutral-c) var(--color-neutral-h) / 0.1);
- --color-base-background-o-20: oklch(var(--color-neutral-l-0) var(--color-neutral-c) var(--color-neutral-h) / 0.2);
- --color-base-background-o-50: oklch(var(--color-neutral-l-0) var(--color-neutral-c) var(--color-neutral-h) / 0.5);
- --color-base-background-o-60: oklch(var(--color-neutral-l-0) var(--color-neutral-c) var(--color-neutral-h) / 0.6);
- --color-base-background-o-75: oklch(var(--color-neutral-l-0) var(--color-neutral-c) var(--color-neutral-h) / 0.75);
- --color-base-background-o-95: oklch(var(--color-neutral-l-0) var(--color-neutral-c) var(--color-neutral-h) / 0.95);
-
- --color-accent-light: oklch(calc(var(--color-accent-l) * var(--color-accent-light-factor)) var(--color-accent-c) var(--color-accent-h));
- --color-accent: oklch(var(--color-accent-l) var(--color-accent-c) var(--color-accent-h));
- --color-accent-dark: oklch(calc(var(--color-accent-l) * var(--color-accent-dark-factor)) var(--color-accent-c) var(--color-accent-h));
-
- --color-neutral-0: oklch(var(--color-neutral-l-0) var(--color-neutral-c) var(--color-neutral-h));
- --color-neutral-100: oklch(var(--color-neutral-l-100) var(--color-neutral-c) var(--color-neutral-h));
- --color-neutral-200: oklch(var(--color-neutral-l-200) var(--color-neutral-c) var(--color-neutral-h));
- --color-neutral-300: oklch(var(--color-neutral-l-300) var(--color-neutral-c) var(--color-neutral-h));
- --color-neutral-400: oklch(var(--color-neutral-l-400) var(--color-neutral-c) var(--color-neutral-h));
- --color-neutral-500: oklch(var(--color-neutral-l-500) var(--color-neutral-c) var(--color-neutral-h));
- --color-neutral-600: oklch(var(--color-neutral-l-600) var(--color-neutral-c) var(--color-neutral-h));
- --color-neutral-700: oklch(var(--color-neutral-l-700) var(--color-neutral-c) var(--color-neutral-h));
- --color-neutral-800: oklch(var(--color-neutral-l-800) var(--color-neutral-c) var(--color-neutral-h));
- --color-neutral-900: oklch(var(--color-neutral-l-900) var(--color-neutral-c) var(--color-neutral-h));
- --color-neutral-1000: oklch(var(--color-neutral-l-1000) var(--color-neutral-c) var(--color-neutral-h));
-
- --color-success: oklch(0.65 0.15 150);
- --color-warning: oklch(0.75 0.15 80);
- --color-error: oklch(0.65 0.18 25);
- --color-info: oklch(0.65 0.15 220);
-
- --font-family: -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- Roboto,
- Helvetica,
- Arial,
- sans-serif,
- "Apple Color Emoji",
- "Segoe UI Emoji",
- "Segoe UI Symbol",
- sans-serif;
-
- --line-height: 1.4;
-
- --font-weight-light: 300;
- --font-weight-normal: 400;
- --font-weight-medium: 550;
- --font-weight-bold: 700;
-
- --font-size-3: clamp(1.9531rem, 1.4262rem + 1.7565vw, 3.5339rem);
- --font-size-4: clamp(1.5625rem, 1.2503rem + 1.0408vw, 2.4992rem);
- --font-size-5: clamp(1.25rem, 1.0775rem + 0.575vw, 1.7675rem);
- --font-size-6: clamp(1rem, 0.9167rem + 0.2778vw, 1.25rem);
- --font-size-7: clamp(0.8rem, 0.772rem + 0.0934vw, 0.884rem);
- --font-size-8: clamp(0.6252rem, 0.6449rem + -0.0165vw, 0.64rem);
-
- --border-radius-s: 0.125rem;
- --border-radius: 0.25rem;
- --border-radius-rounded: 4096px;
-
- --space-2xs: clamp(0.25rem, 0.2292rem + 0.0694vw, 0.3125rem);
- --space-xs: clamp(0.5rem, 0.4583rem + 0.1389vw, 0.625rem);
- --space-s: clamp(1rem, 0.9167rem + 0.2778vw, 1.25rem);
- --space-m: clamp(1.5rem, 1.375rem + 0.4167vw, 1.875rem);
- --space-l: clamp(2rem, 1.8333rem + 0.5556vw, 2.5rem);
- --space-2xs-xs: clamp(0.25rem, 0.125rem + 0.4167vw, 0.625rem);
- --space-xs-s: clamp(0.5rem, 0.25rem + 0.8333vw, 1.25rem);
- --space-s-m: clamp(1rem, 0.7083rem + 0.9722vw, 1.875rem);
- --space-m-l: clamp(1.5rem, 1.1667rem + 1.1111vw, 2.5rem);
- --space-s-l: clamp(1rem, 0.5rem + 1.6667vw, 2.5rem);
-
- /* Shadow tokens */
- --shadow-s: 0 0.1em 0.25em oklch(var(--color-neutral-l-900) var(--color-neutral-c) var(--color-neutral-h) / 0.1);
- --shadow-m: 0 2px 8px oklch(var(--color-neutral-l-900) var(--color-neutral-c) var(--color-neutral-h) / 0.08),
- 0 8px 16px oklch(var(--color-neutral-l-900) var(--color-neutral-c) var(--color-neutral-h) / 0.12),
- 0 16px 24px oklch(var(--color-neutral-l-900) var(--color-neutral-c) var(--color-neutral-h) / 0.08);
- --shadow-l: 0 4px 16px oklch(var(--color-neutral-l-900) var(--color-neutral-c) var(--color-neutral-h) / 0.08),
- 0 12px 32px oklch(var(--color-neutral-l-900) var(--color-neutral-c) var(--color-neutral-h) / 0.12),
- 0 24px 48px oklch(var(--color-neutral-l-900) var(--color-neutral-c) var(--color-neutral-h) / 0.16),
- 0 48px 80px oklch(var(--color-neutral-l-900) var(--color-neutral-c) var(--color-neutral-h) / 0.08);
- --shadow-light-edge: inset 1px 1px 1px oklch(var(--color-neutral-l-0) var(--color-neutral-c) var(--color-neutral-h) / 0.3);
- --shadow-dark-edge: inset 0 -1px 1px oklch(var(--color-neutral-l-900) var(--color-neutral-c) var(--color-neutral-h) / 0.3);
-
- /* Backdrop tokens */
- --backdrop-blur: blur(6px);
- --backdrop-glass: var(--backdrop-blur) saturate(1.4) brightness(1.2);
-
- /* Background tokens */
- --background-glass: linear-gradient(135deg, transparent, var(--color-base-background-o-95));
- --background-glass-frosted: linear-gradient(0deg, var(--color-base-background-o-75) 0%, var(--color-base-background-o-95) 50%);
-
- /* Opacity tokens */
- --opacity-subtle: 0.5;
- --opacity-medium: 0.7;
- --opacity-strong: 0.9;
-
- /* Outline tokens */
- --outline-color: var(--color-accent);
- --outline-offset: 0.25rem;
-
- /* Transition tokens */
- --transition-duration: 0.2s;
- --transition-duration-jump: 0.4s;
- --transition-easing-jump: cubic-bezier(0.44, 1.2, 0.64, 1);
- --transition-easing: cubic-bezier(0, 0, 0.2, 1);
-
- /* Z-index tokens */
- --z-loop-marker: 9998;
- --z-loop-panel: 9999;
- --z-loop-dialog: 10000;
-
- /* Author */
- --author-avatar-color: var(--color-neutral-600);
- --author-avatar-background-color: var(--color-neutral-100);
- --author-avatar-size: 2.5rem;
- --author-avatar-border-radius: var(--border-radius-rounded);
- --author-avatar-font-size: var(--font-size-6);
-
- /* Button */
- --button-background: transparent;
- --button-color: var(--color-neutral-600);
- --button-border-radius: var(--border-radius);
- --button-padding: 0 var(--space-xs);
- --button-gap: var(--space-2xs);
- --button-font-size: var(--font-size-7);
- --button-font-weight: var(--font-weight-medium);
- --button-height: 2.25rem;
- --button-transition: var(--transition-duration) var(--transition-easing);
- --button-outline-color: var(--outline-color);
- --button-outline-offset: var(--outline-offset);
-
- --button-hover-color: var(--color-neutral-900);
- --button-hover-background: var(--color-neutral-200);
-
- --button-header-background: transparent;
- --button-header-height: 3rem;
- --button-header-padding: 0 var(--space-s);
- --button-header-hover-background: var(--color-base-background-o-95);
- --button-header-blend-mode: multiply;
-
- --button-panel-background: transparent;
- --button-panel-padding: 0 calc(var(--space-s) * 0.4);
-
- --button-solid-background: var(--color-neutral-100);
- --button-solid-hover-color: var(--color-neutral-900);
- --button-solid-hover-background: var(--color-neutral-200);
-
- --button-small-height: 1.5rem;
- --button-small-font-size: var(--font-size-7);
-
- --button-icon-background: var(--color-neutral-0);
- --button-icon-color: var(--color-neutral-500);
- --button-icon-height: 3rem;
- --button-icon-shadow: var(--shadow-s);
- --button-icon-border-radius: var(--border-radius-rounded);
- --button-icon-font-size: var(--font-size-6);
- --button-icon-hover-background: var(--color-neutral-200);
- --button-icon-hover-color: var(--color-neutral-900);
-
- --button-marker-background: var(--color-accent);
- --button-marker-color: var(--color-accent-dark);
- --button-marker-font-weight: var(--font-weight-bold);
- --button-marker-border-radius: var(--border-radius-rounded);
- --button-marker-highlighted-background: var(--color-accent);
- --button-marker-highlighted-color: var(--color-accent-dark);
-
- --button-filter-background: transparent;
- --button-filter-color: var(--color-neutral-500);
- --button-filter-height: 1.75rem;
- --button-filter-font-size: var(--font-size-8);
- --button-filter-padding: 0 var(--space-xs);
- --button-filter-border-radius: calc(var(--border-radius) - 2px);
- --button-filter-hover-color: var(--color-neutral-700);
- --button-filter-hover-background: var(--color-neutral-200);
- --button-filter-active-background: var(--color-base-background);
- --button-filter-active-color: var(--color-base);
- --button-filter-active-font-weight: var(--font-weight-medium);
-
- --button-menu-item-background: transparent;
- --button-menu-item-color: var(--color-neutral-700);
- --button-menu-item-padding: var(--space-2xs) var(--space-xs);
- --button-menu-item-border-radius: calc(var(--border-radius) - 2px);
- --button-menu-item-font-size: var(--font-size-7);
- --button-menu-item-gap: var(--space-2xs);
- --button-menu-item-hover-background: var(--color-neutral-100);
- --button-menu-item-hover-color: var(--color-neutral-900);
- --button-menu-item-active-background: var(--color-accent-light);
- --button-menu-item-active-color: var(--color-accent-dark);
- --button-menu-item-active-font-weight: var(--font-weight-medium);
-
- --button-active-background: var(--color-accent);
- --button-active-color: var(--color-accent-dark);
-
- --button-disabled-opacity: var(--opacity-subtle);
- --button-disabled-hover-color: var(--color-neutral-700);
- --button-disabled-hover-background: var(--color-neutral-100);
-
- /* Comment */
- --comment-avatar-size: 2.5rem;
- --comment-marker-background: var(--color-neutral-200);
- --comment-marker-color: var(--color-neutral-800);
- --comment-line-background: var(--color-neutral-100);
- --comment-line-width: 0.1rem;
- --comment-line-offset: calc(var(--space-s) + var(--comment-avatar-size) / 2);
-
- --comment-header-font-size: var(--font-size-7);
- --comment-header-padding: var(--space-s);
- --comment-header-gap: var(--space-s);
- --comment-header-outline-color: var(--outline-color);
- --comment-header-outline-offset: -2px;
- --comment-header-border-radius: var(--border-radius);
-
- --comment-content-padding: var(--space-xs);
- --comment-content-background: var(--color-neutral-100);
- --comment-content-background-dark: var(--color-neutral-200);
- --comment-content-border-radius: var(--border-radius);
-
- --comment-author-gap: var(--space-xs);
- --comment-author-margin-bottom: var(--space-2xs);
- --comment-timestamp-font-size: var(--font-size-8);
- --comment-timestamp-color: var(--color-neutral-300);
-
- --comment-replies-padding: 0 var(--space-s);
- --comment-replies-gap: var(--space-s);
-
- --comment-footer-padding: var(--space-s);
- --comment-footer-gap: var(--space-s);
- --comment-buttons-gap: var(--space-xs);
-
- /* CommentDialog */
- --comment-dialog-position: absolute;
- --comment-dialog-max-width: 300px;
- --comment-dialog-border-radius: var(--border-radius);
- --comment-dialog-shadow: var(--shadow-s);
- --comment-dialog-backdrop-background: transparent;
- --comment-dialog-textarea-font-size: var(--font-size-6);
-
- /* CommentForm */
- --comment-form-background: var(--color-base-background);
- --comment-form-color: var(--color-base);
- --comment-form-border: 1px solid var(--color-neutral-200);
- --comment-form-border-radius: var(--border-radius);
-
- --comment-form-textarea-height: 15ch;
- --comment-form-textarea-padding: var(--space-s);
- --comment-form-textarea-background: var(--color-base-background);
- --comment-form-textarea-font-family: var(--font-family);
- --comment-form-textarea-font-size: var(--font-size-7);
-
- --comment-form-footer-padding: var(--space-xs);
- --comment-form-footer-gap: var(--space-xs);
-
- --comment-form-hint-font-size: var(--font-size-8);
- --comment-form-hint-color: var(--color-neutral-300);
- --comment-form-hint-padding: 0 var(--space-xs) var(--space-xs) var(--space-xs);
-
- /* ContextMenu */
- --context-menu-container-bottom: var(--space-s);
- --context-menu-container-right: var(--space-s);
- --context-menu-container-z-index: 10;
-
- --context-menu-trigger-size: 2.5rem;
- --context-menu-trigger-border-radius: var(--border-radius-rounded);
-
- --context-menu-background: var(--color-base-background);
- --context-menu-border-radius: var(--border-radius);
- --context-menu-shadow: var(--shadow-s);
- --context-menu-padding: var(--space-xs);
- --context-menu-min-width: 12rem;
- --context-menu-backdrop-background: transparent;
-
- --context-menu-section-gap: var(--space-2xs);
-
- --context-menu-title-font-size: var(--font-size-8);
- --context-menu-title-font-weight: var(--font-weight-medium);
- --context-menu-title-color: var(--color-neutral-500);
- --context-menu-title-margin-bottom: var(--space-2xs);
- --context-menu-title-letter-spacing: 0.05em;
-
- --context-menu-filter-gap: 1px;
-
- --context-menu-filter-dot-size: 0.5em;
- --context-menu-filter-dot-border-radius: 50%;
- --context-menu-filter-dot-margin-right: var(--space-2xs);
- --context-menu-filter-dot-open-background: var(--color-accent);
- --context-menu-filter-dot-resolved-background: var(--color-neutral-400);
-
- /* Header */
- --header-position: fixed;
- --header-top: var(--space-xs);
- --header-transform: translateX(-50%);
- --header-color: var(--color-base);
- --header-border-radius: var(--border-radius-rounded);
- --header-z-index: 9999;
- --header-bottom-position: var(--space-xs);
- --header-backdrop-filter: var(--backdrop-glass);
- --header-background: var(--background-glass);
-
- --header-count-size: 2rem;
- --header-count-border-radius: var(--border-radius-rounded);
- --header-count-backdrop-filter: var(--backdrop-glass);
- --header-count-background: var(--background-glass);
-
- /* Marker */
- --marker-size: 2rem;
- --marker-position: absolute;
- --marker-z-index: var(--z-loop-marker);
- --marker-transform: translate(-50%, -50%);
- --marker-border-radius: var(--border-radius-rounded);
-
- /* Panel */
- --panel-width: 380px;
- --panel-mobile-width: 85svw;
- --panel-position: fixed;
- --panel-right: var(--space-xs);
- --panel-top: var(--space-xs);
- --panel-height: calc(100svh - var(--space-xs) * 2);
- --panel-transform-closed: translateX(calc(100% + var(--space-xs)));
- --panel-transform-open: translateX(0);
- --panel-color: var(--color-base);
- --panel-border-radius: var(--border-radius);
- --panel-border-top-left-radius: 0;
- --panel-transition: var(--transition-duration-jump) var(--transition-easing-jump);
- --panel-z-index: var(--z-loop-panel);
- --panel-shadow: var(--shadow-m);
-
- --panel-header-transform-closed: translate(-95%);
- --panel-header-transform-open: translate(calc(-100% + 1px));
- --panel-header-transform-hover: translate(calc(-100% + 1px));
- --panel-header-border-radius: var(--border-radius-rounded);
- --panel-header-gap: var(--space-xs);
- --panel-header-backdrop-filter: var(--backdrop-glass);
- --panel-header-background: var(--background-glass);
-
- --panel-threads-background: var(--color-base-background-o-95);
- --panel-threads-backdrop: var(--backdrop-blur);
- --panel-threads-border-radius: var(--border-radius);
- --panel-threads-border-top-left-radius: 0;
- --panel-threads-padding: 0 0 var(--space-s) 0;
- --panel-threads-item-margin: var(--space-s);
- --panel-threads-scrollbar-width: thin;
-
- --panel-no-threads-padding: var(--space-s) var(--space-l);
- --panel-no-threads-font-size: var(--font-size-6);
- --panel-no-threads-color: var(--color-neutral-300);
-
- /* Reply */
- --reply-gap: var(--space-s);
- --reply-content-padding: var(--space-xs);
- --reply-content-background: var(--color-neutral-100);
- --reply-content-background-dark: var(--color-neutral-200);
- --reply-content-border-radius: var(--border-radius);
-
- --reply-header-gap: var(--space-xs);
- --reply-header-margin-bottom: var(--space-2xs);
- --reply-timestamp-font-size: var(--font-size-8);
- --reply-timestamp-color: var(--color-neutral-300);
-
- /* WelcomeDialog */
- --welcome-dialog-background: var(--background-glass-frosted);
- --welcome-dialog-backdrop-filter: var(--backdrop-glass);
- --welcome-dialog-border: 0px;
- --welcome-dialog-border-radius: var(--border-radius);
- --welcome-dialog-shadow: var(--shadow-l), var(--shadow-light-edge),
- var(--shadow-dark-edge);
- --welcome-dialog-max-width: 500px;
-
- --welcome-dialog-backdrop-background: var(--color-base-background-o-10);
- --welcome-dialog-backdrop-backdrop-filter: none;
-
- --welcome-dialog-form-padding: var(--space-l);
-
- --welcome-dialog-title-margin: 0 0 var(--space-s) 0;
- --welcome-dialog-title-font-size: var(--font-size-4);
- --welcome-dialog-title-color: var(--color-base);
- --welcome-dialog-title-font-weight: var(--font-weight-bold);
-
- --welcome-dialog-text-margin: 0 0 var(--space-m) 0;
- --welcome-dialog-text-font-size: var(--font-size-6);
- --welcome-dialog-text-color: var(--color-neutral-600);
- --welcome-dialog-text-line-height: var(--line-height);
-
- --welcome-dialog-name-section-margin: var(--space-l);
-
- --welcome-dialog-input-border: 1px solid var(--color-neutral-300);
- --welcome-dialog-input-border-radius: var(--border-radius-s);
- --welcome-dialog-input-padding: var(--space-xs);
- --welcome-dialog-input-font-family: var(--font-family);
- --welcome-dialog-input-font-size: var(--font-size-6);
- --welcome-dialog-input-color: var(--color-base);
- --welcome-dialog-input-background: var(--color-base-background);
- --welcome-dialog-input-outline-color: var(--outline-color);
- --welcome-dialog-input-outline-offset: var(--outline-offset);
-
- --welcome-dialog-footer-gap: var(--space-xs);
-
- /* Icon */
- --icon-size: 1em;
-}
-
-/* Dark theme overrides */
-kirby-loop[data-theme="dark"] {
- --color-neutral-l-0: 0;
- --color-neutral-l-100: 0.1;
- --color-neutral-l-200: 0.2;
- --color-neutral-l-300: 0.3;
- --color-neutral-l-400: 0.4;
- --color-neutral-l-500: 0.5;
- --color-neutral-l-600: 0.6;
- --color-neutral-l-700: 0.7;
- --color-neutral-l-800: 0.9;
- --color-neutral-l-900: 0.95;
- --color-neutral-l-1000: 1;
-}
diff --git a/site/plugins/loop/frontend/src/types.ts b/site/plugins/loop/frontend/src/types.ts
deleted file mode 100644
index 0c726fa..0000000
--- a/site/plugins/loop/frontend/src/types.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-// TypeScript interfaces for loop
-
-export interface LoopProps {
- position: 'top' | 'bottom';
- language?: string;
- apibase?: string;
- pageId: string;
- authenticated?: 'true' | 'false';
- 'welcome-enabled'?: 'true' | 'false';
- 'welcome-headline'?: string;
- 'welcome-text'?: string;
- translations?: string;
-}
-
-export interface Comment {
- id: number;
- author: string;
- url: string;
- page: string;
- comment: string;
- selector: string;
- selectorOffsetX: number;
- selectorOffsetY: number;
- pagePositionX: number;
- pagePositionY: number;
- status: string;
- timestamp: number;
- lang: string;
- replies: Reply[];
-}
-
-export interface Reply {
- id?: number;
- author: string;
- comment: string;
- parentId: number | null;
- timestamp: number;
-}
-
-export interface CommentPayload {
- url: string;
- comment: string;
- selector: string;
- selectorOffsetX: number;
- selectorOffsetY: number;
- pagePositionX: number;
- pagePositionY: number;
- parentId: number | null;
- lang: string;
- pageId: string;
-}
-
-export interface ReplyPayload {
- comment: string;
- parentId: number | null;
-}
-
-export interface MarkerPosition {
- selector: string;
- selectorOffsetX: number;
- selectorOffsetY: number;
- pagePositionX: number;
- pagePositionY: number;
-}
-
-export interface ApiResponse {
- status: 'ok' | 'error';
- message?: string;
- code?: string;
- data?: T;
-}
-
-export interface CommentsResponse extends ApiResponse {
- comments: Comment[];
-}
-
-export interface CommentResponse extends ApiResponse {
- comment: Comment;
-}
-
-export interface ReplyResponse extends ApiResponse {
- reply: Reply;
-}
-
-// Store interfaces
-export interface FormData {
- text: string;
- parentId: number | null;
-}
-
-export interface UIState {
- open: boolean;
- sidebarOpen: boolean;
-}
-
-export interface APIStore {
- comments: Comment[];
- loading: boolean;
- error: string | null;
-}
diff --git a/site/plugins/loop/frontend/src/vite-env.d.ts b/site/plugins/loop/frontend/src/vite-env.d.ts
deleted file mode 100644
index 4078e74..0000000
--- a/site/plugins/loop/frontend/src/vite-env.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-///
-///
diff --git a/site/plugins/loop/frontend/svelte.config.js b/site/plugins/loop/frontend/svelte.config.js
deleted file mode 100644
index bb11984..0000000
--- a/site/plugins/loop/frontend/svelte.config.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
-
-export default {
- // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
- // for more information about preprocessors
- preprocess: vitePreprocess(),
- compilerOptions: {
- customElement: true,
- },
-};
diff --git a/site/plugins/loop/frontend/tsconfig.app.json b/site/plugins/loop/frontend/tsconfig.app.json
deleted file mode 100644
index 55a2f9b..0000000
--- a/site/plugins/loop/frontend/tsconfig.app.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "extends": "@tsconfig/svelte/tsconfig.json",
- "compilerOptions": {
- "target": "ESNext",
- "useDefineForClassFields": true,
- "module": "ESNext",
- "resolveJsonModule": true,
- /**
- * Typecheck JS in `.svelte` and `.js` files by default.
- * Disable checkJs if you'd like to use dynamic types in JS.
- * Note that setting allowJs false does not prevent the use
- * of JS in `.svelte` files.
- */
- "allowJs": true,
- "checkJs": true,
- "isolatedModules": true,
- "moduleDetection": "force"
- },
- "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
-}
diff --git a/site/plugins/loop/frontend/tsconfig.json b/site/plugins/loop/frontend/tsconfig.json
deleted file mode 100644
index 1ffef60..0000000
--- a/site/plugins/loop/frontend/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "files": [],
- "references": [
- { "path": "./tsconfig.app.json" },
- { "path": "./tsconfig.node.json" }
- ]
-}
diff --git a/site/plugins/loop/frontend/tsconfig.node.json b/site/plugins/loop/frontend/tsconfig.node.json
deleted file mode 100644
index db0becc..0000000
--- a/site/plugins/loop/frontend/tsconfig.node.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "compilerOptions": {
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
- "target": "ES2022",
- "lib": ["ES2023"],
- "module": "ESNext",
- "skipLibCheck": true,
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "isolatedModules": true,
- "moduleDetection": "force",
- "noEmit": true,
-
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true,
- "noUncheckedSideEffectImports": true
- },
- "include": ["vite.config.ts"]
-}
diff --git a/site/plugins/loop/frontend/vite.config.ts b/site/plugins/loop/frontend/vite.config.ts
deleted file mode 100644
index f7c36cd..0000000
--- a/site/plugins/loop/frontend/vite.config.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-///
-
-import { defineConfig, loadEnv } from "vite";
-import { svelte } from "@sveltejs/vite-plugin-svelte";
-import { resolve, dirname } from "node:path";
-import { fileURLToPath } from "node:url";
-import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
-import { ViteEjsPlugin } from 'vite-plugin-ejs'
-import { browserslistToTargets } from 'lightningcss';
-import browserslist from "browserslist"
-
-// Isomorphic dirname
-const _dirname =
- typeof __dirname !== "undefined"
- ? __dirname
- : dirname(fileURLToPath(import.meta.url));
-
-// Config
-export default defineConfig(({ mode }) => {
- const env = loadEnv(mode, process.cwd());
- return {
- base: env.VITE_DEMO_BASE,
- compilerOptions: {
- hmr: !process.env.VITEST && mode !== 'production',
- },
- build: {
- cssMinify: 'lightningcss',
- minify: true,
- lib: {
- entry: resolve(_dirname, "src/main.ts"),
- name: "Loop",
- fileName: "loop",
- formats: ["es"],
- },
- outDir: "../assets",
- },
- css: {
- transformer: 'lightningcss',
- lightningcss: {
- drafts: {
- customMedia: true
- },
- targets: browserslistToTargets(browserslist(["last 2 versions", ">= 0.4%", "not dead", "Firefox ESR", "not op_mini all", "not and_uc > 0"]))
- }
- },
- define: {
- APP_VERSION: JSON.stringify(process.env.npm_package_version),
- },
- plugins: [
- svelte({ compilerOptions: { customElement: true } }),
- cssInjectedByJsPlugin(),
- ViteEjsPlugin((viteConfig) => ({
- // viteConfig is the current Vite resolved config
- env: viteConfig.env,
- }))
- ],
- test: {
- globals: true,
- environment: "jsdom",
- },
- server: {
- allowedHosts: ['kirby-loop.test'],
- cors: {
- // Allow ddev and .test domains
- origin: /https?:\/\/([A-Za-z0-9\-\.]+)?(\.(ddev\.site|test))(?::\d+)?$/,
- },
- }
- }
-});
diff --git a/site/plugins/loop/index.php b/site/plugins/loop/index.php
deleted file mode 100644
index 1426882..0000000
--- a/site/plugins/loop/index.php
+++ /dev/null
@@ -1,333 +0,0 @@
- 'src/App.php',
-// 'moinframe\\loop\\Database' => 'src/Database.php',
-// 'moinframe\\loop\\Middleware' => 'src/Middleware.php',
-// 'moinframe\\loop\\Options' => 'src/Options.php',
-// 'moinframe\\loop\\Routes' => 'src/Routes.php',
-// 'moinframe\\loop\\Models\\Comment' => 'src/Models/Comment.php',
-// 'moinframe\\loop\\Models\\Reply' => 'src/Models/Reply.php',
-// 'moinframe\\loop\\Enums\\CommentStatus' => 'src/Enums/CommentStatus.php',
-// ], __DIR__);
-
-// Kirby::plugin('moinframe/loop', [
-// 'translations' => [
-// 'en' => [
-// // General errors
-// 'moinframe.loop.csrf.invalid' => 'Invalid CSRF token',
-// 'moinframe.loop.field.required' => 'Missing required field: {field}',
-
-// // Page errors
-// 'moinframe.loop.page.not.found' => 'Page with id {pageId} not found',
-// 'moinframe.loop.page.path.not.found' => 'Page not found: {path}',
-
-// // Comment validation
-// 'moinframe.loop.comment.required' => 'Comment text is required',
-// 'moinframe.loop.comment.max.length' => 'Comment text must be less than 5000 characters',
-// 'moinframe.loop.comment.validation.failed' => 'Comment validation failed: {errors}',
-// 'moinframe.loop.comment.validation.error' => 'Comment validation failed: {errors}',
-// 'moinframe.loop.comment.creation.failed' => 'Comment creation failed: {error}',
-// 'moinframe.loop.comment.add.failed' => 'Failed to add comment: {error}',
-
-// // Reply validation
-// 'moinframe.loop.reply.validation.failed' => 'Reply validation failed: {errors}',
-// 'moinframe.loop.reply.validation.error' => 'Reply validation failed: {errors}',
-// 'moinframe.loop.reply.creation.failed' => 'Reply creation failed: {error}',
-// 'moinframe.loop.reply.add.failed' => 'Failed to add reply: {error}',
-// 'moinframe.loop.reply.index.error' => 'Reply {index}: {error}',
-
-// // Author validation
-// 'moinframe.loop.author.required' => 'Author is required',
-// 'moinframe.loop.author.max.length' => 'Author name must be less than 255 characters',
-
-// // Page field validation
-// 'moinframe.loop.page.required' => 'Page identifier is required',
-// 'moinframe.loop.page.max.length' => 'Page identifier must be less than 255 characters',
-
-// // Selector validation
-// 'moinframe.loop.selector.required' => 'Element selector is required',
-// 'moinframe.loop.selector.max.length' => 'Element selector must be less than 1000 characters',
-// 'moinframe.loop.selector.offset.x.min' => 'Selector offset X must be non-negative',
-// 'moinframe.loop.selector.offset.y.min' => 'Selector offset Y must be non-negative',
-
-// // URL validation
-// 'moinframe.loop.url.format.invalid' => 'URL format is invalid',
-// 'moinframe.loop.url.max.length' => 'URL must be less than 2048 characters',
-
-// // Position validation
-// 'moinframe.loop.page.position.x.min' => 'Page position X must be non-negative',
-// 'moinframe.loop.page.position.y.min' => 'Page position Y must be non-negative',
-
-// // Other field validation
-// 'moinframe.loop.timestamp.min' => 'Timestamp must be non-negative',
-// 'moinframe.loop.parent.id.min' => 'Parent ID must be non-negative',
-// 'moinframe.loop.parent.id.required' => 'Valid parent comment ID is required',
-
-// // Welcome dialog
-// 'moinframe.loop.welcome.headline' => 'Welcome! 👋',
-// 'moinframe.loop.welcome.text' => 'We\'re excited to hear your thoughts! This page has an interactive feedback system that lets you comment directly on any element. Simply use the action bar at the {position} of your screen to switch between browsing and commenting mode. When in commenting mode, click anywhere on the page to leave your feedback.',
-
-// // Frontend UI translations
-// 'moinframe.loop.ui.comment.placeholder' => 'Enter your comment...',
-// 'moinframe.loop.ui.comment.submit' => 'Submit',
-// 'moinframe.loop.ui.comment.cancel' => 'Cancel',
-// 'moinframe.loop.ui.comment.keyboardHint' => '⌘+Enter or Ctrl+Enter to submit',
-// 'moinframe.loop.ui.comment.replies.aria.label' => 'Show replies',
-// 'moinframe.loop.ui.reply.placeholder' => 'Write a reply...',
-// 'moinframe.loop.ui.reply.submit' => 'Reply',
-// 'moinframe.loop.ui.panel.no.comments' => 'No comments. Add your first comment to get started.',
-// 'moinframe.loop.ui.header.browse.mode' => 'Browse',
-// 'moinframe.loop.ui.header.comment.mode' => 'Comment',
-// 'moinframe.loop.ui.header.aria.count' => 'unresolved comments',
-// 'moinframe.loop.ui.welcome.guest.name.placeholder' => 'Enter your name',
-// 'moinframe.loop.ui.welcome.continue' => 'Continue',
-// 'moinframe.loop.ui.welcome.dismiss' => 'Dismiss',
-// 'moinframe.loop.ui.header.position.top' => 'top',
-// 'moinframe.loop.ui.header.position.bottom' => 'bottom',
-// 'moinframe.loop.ui.comment.mark.solved' => 'Resolve',
-// 'moinframe.loop.ui.comment.mark.unsolved' => 'Reopen',
-// 'moinframe.loop.ui.comment.maker.aria.label' => 'Jump to marker',
-// 'moinframe.loop.ui.comment.summary.aria.label' => 'Comment by',
-// 'moinframe.loop.ui.reply.aria.label' => 'Reply by',
-// 'moinframe.loop.ui.panel.open' => 'Open comments',
-// 'moinframe.loop.ui.panel.show.resolved' => 'Show Resolved Only',
-// 'moinframe.loop.ui.panel.show.all' => 'Show All Comments',
-// 'moinframe.loop.ui.panel.filter.open' => 'Open',
-// 'moinframe.loop.ui.panel.filter.resolved' => 'Resolved',
-// 'moinframe.loop.ui.panel.filter.open.active' => 'Show open comments (currently selected)',
-// 'moinframe.loop.ui.panel.filter.open.inactive' => 'Show open comments',
-// 'moinframe.loop.ui.panel.filter.resolved.active' => 'Show resolved comments (currently selected)',
-// 'moinframe.loop.ui.panel.filter.resolved.inactive' => 'Show resolved comments',
-// 'moinframe.loop.ui.panel.menu.open' => 'Open menu',
-// 'moinframe.loop.ui.panel.menu.filter.title' => 'Show Comments',
-// 'moinframe.loop.ui.panel.no.resolved' => 'No resolved comments yet.',
-
-// // Time formatting
-// 'moinframe.loop.ui.time.just_now' => 'just now',
-// 'moinframe.loop.ui.time.minute_ago' => 'a minute ago',
-// 'moinframe.loop.ui.time.minutes_ago' => '{count} minutes ago',
-// 'moinframe.loop.ui.time.hour_ago' => 'an hour ago',
-// 'moinframe.loop.ui.time.hours_ago' => '{count} hours ago',
-// 'moinframe.loop.ui.time.yesterday' => 'yesterday',
-// 'moinframe.loop.ui.time.days_ago' => '{count} days ago'
-// ],
-// 'de' => [
-// // General errors
-// 'moinframe.loop.csrf.invalid' => 'Ungültiges CSRF-Token',
-// 'moinframe.loop.field.required' => 'Pflichtfeld fehlt: {field}',
-
-// // Page errors
-// 'moinframe.loop.page.not.found' => 'Seite mit ID {pageId} nicht gefunden',
-// 'moinframe.loop.page.path.not.found' => 'Seite nicht gefunden: {path}',
-
-// // Comment validation
-// 'moinframe.loop.comment.required' => 'Kommentartext ist erforderlich',
-// 'moinframe.loop.comment.max.length' => 'Kommentartext darf maximal 5000 Zeichen lang sein',
-// 'moinframe.loop.comment.validation.failed' => 'Kommentar-Validierung fehlgeschlagen: {errors}',
-// 'moinframe.loop.comment.validation.error' => 'Kommentar-Validierung fehlgeschlagen: {errors}',
-// 'moinframe.loop.comment.creation.failed' => 'Kommentar-Erstellung fehlgeschlagen: {error}',
-// 'moinframe.loop.comment.add.failed' => 'Kommentar konnte nicht hinzugefügt werden: {error}',
-
-// // Reply validation
-// 'moinframe.loop.reply.validation.failed' => 'Antwort-Validierung fehlgeschlagen: {errors}',
-// 'moinframe.loop.reply.validation.error' => 'Antwort-Validierung fehlgeschlagen: {errors}',
-// 'moinframe.loop.reply.creation.failed' => 'Antwort-Erstellung fehlgeschlagen: {error}',
-// 'moinframe.loop.reply.add.failed' => 'Antwort konnte nicht hinzugefügt werden: {error}',
-// 'moinframe.loop.reply.index.error' => 'Antwort {index}: {error}',
-
-// // Author validation
-// 'moinframe.loop.author.required' => 'Autor ist erforderlich',
-// 'moinframe.loop.author.max.length' => 'Autorname darf maximal 255 Zeichen lang sein',
-
-// // Page field validation
-// 'moinframe.loop.page.required' => 'Seiten-Identifikator ist erforderlich',
-// 'moinframe.loop.page.max.length' => 'Seiten-Identifikator darf maximal 255 Zeichen lang sein',
-
-// // Selector validation
-// 'moinframe.loop.selector.required' => 'Element-Selektor ist erforderlich',
-// 'moinframe.loop.selector.max.length' => 'Element-Selektor darf maximal 1000 Zeichen lang sein',
-// 'moinframe.loop.selector.offset.x.min' => 'Selektor-Offset X muss nicht-negativ sein',
-// 'moinframe.loop.selector.offset.y.min' => 'Selektor-Offset Y muss nicht-negativ sein',
-
-// // URL validation
-// 'moinframe.loop.url.format.invalid' => 'URL-Format ist ungültig',
-// 'moinframe.loop.url.max.length' => 'URL darf maximal 2048 Zeichen lang sein',
-
-// // Position validation
-// 'moinframe.loop.page.position.x.min' => 'Seitenposition X muss nicht-negativ sein',
-// 'moinframe.loop.page.position.y.min' => 'Seitenposition Y muss nicht-negativ sein',
-
-// // Other field validation
-// 'moinframe.loop.timestamp.min' => 'Zeitstempel muss nicht-negativ sein',
-// 'moinframe.loop.parent.id.min' => 'Eltern-ID muss nicht-negativ sein',
-// 'moinframe.loop.parent.id.required' => 'Gültige Eltern-Kommentar-ID ist erforderlich',
-
-// // Welcome dialog
-// 'moinframe.loop.welcome.headline' => 'Willkommen! 👋',
-// 'moinframe.loop.welcome.text' => 'Wir freuen uns auf Ihr Feedback! Diese Seite verfügt über ein interaktives Feedback-System, mit dem Sie direkt zu jedem Element kommentieren können. Verwenden Sie einfach die Aktionsleiste {position} an ihrem Bildschirm, um zwischen Browse- und Kommentar-Modus zu wechseln. Im Kommentar-Modus klicken Sie einfach irgendwo auf die Seite, um Ihr Feedback zu hinterlassen.',
-
-// // Frontend UI translations
-// 'moinframe.loop.ui.comment.placeholder' => 'Ihr Kommentar...',
-// 'moinframe.loop.ui.comment.submit' => 'Senden',
-// 'moinframe.loop.ui.comment.cancel' => 'Abbrechen',
-// 'moinframe.loop.ui.comment.keyboardHint' => '⌘+Enter oder Strg+Enter zum Senden',
-// 'moinframe.loop.ui.comment.replies.aria.label' => 'Antworten anzeigen',
-// 'moinframe.loop.ui.comment.maker.aria.label' => 'Springe zu Marker',
-// 'moinframe.loop.ui.comment.summary.aria.label' => 'Kommentar von',
-// 'moinframe.loop.ui.reply.aria.label' => 'Antwort von',
-// 'moinframe.loop.ui.reply.placeholder' => 'Antwort schreiben...',
-// 'moinframe.loop.ui.reply.submit' => 'Antworten',
-// 'moinframe.loop.ui.panel.no.comments' => 'Keine Kommentare. Fügen Sie Ihren ersten Kommentar hinzu, um zu beginnen.',
-// 'moinframe.loop.ui.panel.open' => 'Kommentare öffnen',
-// 'moinframe.loop.ui.header.browse.mode' => 'Navigieren',
-// 'moinframe.loop.ui.header.comment.mode' => 'Kommentieren',
-// 'moinframe.loop.ui.header.aria.count' => 'offene Kommentare',
-// 'moinframe.loop.ui.welcome.guest.name.placeholder' => 'Geben Sie Ihren Namen ein',
-// 'moinframe.loop.ui.welcome.continue' => 'Weiter',
-// 'moinframe.loop.ui.welcome.dismiss' => 'Schließen',
-// 'moinframe.loop.ui.header.position.top' => 'oben',
-// 'moinframe.loop.ui.header.position.bottom' => 'unten',
-// 'moinframe.loop.ui.comment.mark.solved' => 'Erledigt',
-// 'moinframe.loop.ui.comment.mark.unsolved' => 'Wieder öffnen',
-// 'moinframe.loop.ui.panel.show.resolved' => 'Nur erledigte anzeigen',
-// 'moinframe.loop.ui.panel.show.all' => 'Alle Kommentare anzeigen',
-// 'moinframe.loop.ui.panel.filter.open' => 'Offen',
-// 'moinframe.loop.ui.panel.filter.resolved' => 'Erledigt',
-// 'moinframe.loop.ui.panel.filter.open.active' => 'Offene Kommentare anzeigen (aktuell ausgewählt)',
-// 'moinframe.loop.ui.panel.filter.open.inactive' => 'Offene Kommentare anzeigen',
-// 'moinframe.loop.ui.panel.filter.resolved.active' => 'Erledigte Kommentare anzeigen (aktuell ausgewählt)',
-// 'moinframe.loop.ui.panel.filter.resolved.inactive' => 'Erledigte Kommentare anzeigen',
-// 'moinframe.loop.ui.panel.menu.open' => 'Menü öffnen',
-// 'moinframe.loop.ui.panel.menu.filter.title' => 'Kommentare anzeigen',
-// 'moinframe.loop.ui.panel.no.resolved' => 'Noch keine erledigten Kommentare.',
-
-// // Time formatting
-// 'moinframe.loop.ui.time.just_now' => 'gerade eben',
-// 'moinframe.loop.ui.time.minute_ago' => 'vor einer Minute',
-// 'moinframe.loop.ui.time.minutes_ago' => 'vor {count} Minuten',
-// 'moinframe.loop.ui.time.hour_ago' => 'vor einer Stunde',
-// 'moinframe.loop.ui.time.hours_ago' => 'vor {count} Stunden',
-// 'moinframe.loop.ui.time.yesterday' => 'gestern',
-// 'moinframe.loop.ui.time.days_ago' => 'vor {count} Tagen'
-// ],
-// 'fr' => [
-// // General errors
-// 'moinframe.loop.csrf.invalid' => 'Jeton CSRF invalide',
-// 'moinframe.loop.field.required' => 'Champ obligatoire manquant : {field}',
-
-// // Page errors
-// 'moinframe.loop.page.not.found' => 'Page avec l\'id {pageId} introuvable',
-// 'moinframe.loop.page.path.not.found' => 'Page introuvable : {path}',
-
-// // Comment validation
-// 'moinframe.loop.comment.required' => 'Le texte du commentaire est requis',
-// 'moinframe.loop.comment.max.length' => 'Le commentaire ne doit pas dépasser 5000 caractères',
-// 'moinframe.loop.comment.validation.failed' => 'Validation du commentaire échouée : {errors}',
-// 'moinframe.loop.comment.validation.error' => 'Validation du commentaire échouée : {errors}',
-// 'moinframe.loop.comment.creation.failed' => 'Création du commentaire échouée : {error}',
-// 'moinframe.loop.comment.add.failed' => 'Impossible d\'ajouter le commentaire : {error}',
-
-// // Reply validation
-// 'moinframe.loop.reply.validation.failed' => 'Validation de la réponse échouée : {errors}',
-// 'moinframe.loop.reply.validation.error' => 'Validation de la réponse échouée : {errors}',
-// 'moinframe.loop.reply.creation.failed' => 'Création de la réponse échouée : {error}',
-// 'moinframe.loop.reply.add.failed' => 'Impossible d\'ajouter la réponse : {error}',
-// 'moinframe.loop.reply.index.error' => 'Réponse {index} : {error}',
-
-// // Author validation
-// 'moinframe.loop.author.required' => 'L\'auteur est requis',
-// 'moinframe.loop.author.max.length' => 'Le nom de l\'auteur ne doit pas dépasser 255 caractères',
-
-// // Page field validation
-// 'moinframe.loop.page.required' => 'L\'identifiant de page est requis',
-// 'moinframe.loop.page.max.length' => 'L\'identifiant de page ne doit pas dépasser 255 caractères',
-
-// // Selector validation
-// 'moinframe.loop.selector.required' => 'Le sélecteur d\'élément est requis',
-// 'moinframe.loop.selector.max.length' => 'Le sélecteur d\'élément ne doit pas dépasser 1000 caractères',
-// 'moinframe.loop.selector.offset.x.min' => 'L\'offset X du sélecteur doit être positif',
-// 'moinframe.loop.selector.offset.y.min' => 'L\'offset Y du sélecteur doit être positif',
-
-// // URL validation
-// 'moinframe.loop.url.format.invalid' => 'Format d\'URL invalide',
-// 'moinframe.loop.url.max.length' => 'L\'URL ne doit pas dépasser 2048 caractères',
-
-// // Position validation
-// 'moinframe.loop.page.position.x.min' => 'La position X doit être positive',
-// 'moinframe.loop.page.position.y.min' => 'La position Y doit être positive',
-
-// // Other field validation
-// 'moinframe.loop.timestamp.min' => 'L\'horodatage doit être positif',
-// 'moinframe.loop.parent.id.min' => 'L\'ID parent doit être positif',
-// 'moinframe.loop.parent.id.required' => 'Un ID de commentaire parent valide est requis',
-
-// // Welcome dialog
-// 'moinframe.loop.welcome.headline' => 'Nouveauté',
-// 'moinframe.loop.welcome.text' => 'Il est désormais possible de commenter directement n\'importe quel élément. Utilisez la barre d\'action en {position} de votre écran pour basculer entre le mode navigation et le mode commentaire. En mode commentaire, cliquez n\'importe où sur la page pour laisser votre message. Cette fonctionnalité n\'est accessible qu\'aux éditeurs connecté au back-office.',
-
-// // Frontend UI translations
-// 'moinframe.loop.ui.comment.placeholder' => 'Votre commentaire...',
-// 'moinframe.loop.ui.comment.submit' => 'Envoyer',
-// 'moinframe.loop.ui.comment.cancel' => 'Annuler',
-// 'moinframe.loop.ui.comment.keyboardHint' => '⌘+Entrée ou Ctrl+Entrée pour envoyer',
-// 'moinframe.loop.ui.comment.replies.aria.label' => 'Afficher les réponses',
-// 'moinframe.loop.ui.comment.maker.aria.label' => 'Aller au marqueur',
-// 'moinframe.loop.ui.comment.summary.aria.label' => 'Commentaire de',
-// 'moinframe.loop.ui.reply.aria.label' => 'Réponse de',
-// 'moinframe.loop.ui.reply.placeholder' => 'Écrire une réponse...',
-// 'moinframe.loop.ui.reply.submit' => 'Répondre',
-// 'moinframe.loop.ui.panel.no.comments' => 'Aucun commentaire. Ajoutez votre premier commentaire pour commencer.',
-// 'moinframe.loop.ui.panel.open' => 'Ouvrir les commentaires',
-// 'moinframe.loop.ui.header.browse.mode' => 'Naviguer',
-// 'moinframe.loop.ui.header.comment.mode' => 'Commenter',
-// 'moinframe.loop.ui.header.aria.count' => 'commentaires non résolus',
-// 'moinframe.loop.ui.welcome.guest.name.placeholder' => 'Entrez votre nom',
-// 'moinframe.loop.ui.welcome.continue' => 'Continuer',
-// 'moinframe.loop.ui.welcome.dismiss' => 'Fermer',
-// 'moinframe.loop.ui.header.position.top' => 'haut',
-// 'moinframe.loop.ui.header.position.bottom' => 'bas',
-// 'moinframe.loop.ui.comment.mark.solved' => 'Résoudre',
-// 'moinframe.loop.ui.comment.mark.unsolved' => 'Rouvrir',
-// 'moinframe.loop.ui.panel.show.resolved' => 'Afficher les résolus uniquement',
-// 'moinframe.loop.ui.panel.show.all' => 'Afficher tous les commentaires',
-// 'moinframe.loop.ui.panel.filter.open' => 'Ouverts',
-// 'moinframe.loop.ui.panel.filter.resolved' => 'Résolus',
-// 'moinframe.loop.ui.panel.filter.open.active' => 'Afficher les commentaires ouverts (sélectionné)',
-// 'moinframe.loop.ui.panel.filter.open.inactive' => 'Afficher les commentaires ouverts',
-// 'moinframe.loop.ui.panel.filter.resolved.active' => 'Afficher les commentaires résolus (sélectionné)',
-// 'moinframe.loop.ui.panel.filter.resolved.inactive' => 'Afficher les commentaires résolus',
-// 'moinframe.loop.ui.panel.menu.open' => 'Ouvrir le menu',
-// 'moinframe.loop.ui.panel.menu.filter.title' => 'Afficher les commentaires',
-// 'moinframe.loop.ui.panel.no.resolved' => 'Aucun commentaire résolu pour le moment.',
-
-// // Time formatting
-// 'moinframe.loop.ui.time.just_now' => 'à l\'instant',
-// 'moinframe.loop.ui.time.minute_ago' => 'il y a une minute',
-// 'moinframe.loop.ui.time.minutes_ago' => 'il y a {count} minutes',
-// 'moinframe.loop.ui.time.hour_ago' => 'il y a une heure',
-// 'moinframe.loop.ui.time.hours_ago' => 'il y a {count} heures',
-// 'moinframe.loop.ui.time.yesterday' => 'hier',
-// 'moinframe.loop.ui.time.days_ago' => 'il y a {count} jours'
-// ]
-// ],
-// 'hooks' => [
-// 'page.render:after' => function (string $contentType, array $data, string $html, \Kirby\Cms\Page $page) {
-// if ($contentType === 'html' && Options::autoInject() && Options::enabled()) {
-// $snippet = snippet('loop/app', ['page' => $page], true);
-// // @phpstan-ignore-next-line
-// $html = str_replace('
- -
-
- {comment.author}
-
-
- {decodeHTMLEntities(comment.comment)}
-
-
- {#if !detailsOpen}
-
- {/if}
-
- - {#if comment.replies?.length > 0} -- {#each comment.replies as reply (reply.id)} --
-
-
- {/each}
-
- {/if} - - -