From f2f6dc696fc515be2b69b2daea4a66c678aceeff Mon Sep 17 00:00:00 2001 From: Ys-sudo Date: Thu, 27 Aug 2026 18:17:44 +0200 Subject: [PATCH] chore: sync superfunky-theme from monorepo Publish the current free package from coded-letter/coded-letter-monorepo@0aebd69d9cf0616630e4f090c3e4579abe17c63d with standalone validation and GitHub release automation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/ci.yml | 55 + .github/workflows/release.yml | 160 ++ .monorepo-source.json | 14 + EXTRACTION_STATUS.md | 7 +- assets/css/theme-source.css | 416 ++++ assets/dist/theme.css | 1 + assets/dist/theme.js | 1 + assets/js/theme.js | 112 + assets/storefront-ui-strings/en.json | 677 ++++++ assets/storefront-ui-strings/ja.json | 677 ++++++ assets/storefront-ui-strings/pl.json | 677 ++++++ assets/submission-form-block.js | 42 + assets/video-hero-block.js | 70 + build/lint-php.mjs | 54 + build/sync-template-assets.mjs | 80 + functions.php | 881 +++++-- inc/account.php | 490 +++- inc/admin-theme.php | 495 ++++ inc/admin-view-links.php | 85 + inc/artifact-invalidation.php | 472 ++++ inc/artifact-protocol.php | 680 ++++++ inc/artifact-renderer.php | 1956 +++++++++++++++ inc/artifact-rest.php | 586 +++++ inc/artifact-store.php | 1906 +++++++++++++++ inc/avatar.php | 443 ++++ inc/backend-preview.php | 421 ++++ inc/build-webhooks.php | 36 +- inc/checkout-context.php | 802 +++++++ inc/community.php | 2324 ++++++++++++++++-- inc/control-center-schema.php | 546 ++++- inc/control-center.php | 808 ++++++- inc/crypto-payments.php | 2 +- inc/custom-404.php | 122 + inc/frontend-theme.php | 187 ++ inc/headless-login.php | 32 + inc/multilingual-content.php | 106 +- inc/native-shortcodes.php | 2658 +++++++++++++++++++++ inc/native-woocommerce.php | 343 +++ inc/navigation-commerce.php | 883 ++++++- inc/notifications.php | 95 + inc/ratings.php | 459 ++++ inc/recent-orders.php | 109 + inc/registration-email-verification.php | 380 +++ inc/saved-lists.php | 690 ++++++ inc/security-hardening.php | 219 +- inc/seo-feeds.php | 262 +- inc/submissions.php | 830 ++++++- inc/superfunky-licence-client.php | 1126 +++++++++ inc/superfunky-release-client.php | 242 ++ inc/superfunky-update-client.php | 625 +++++ inc/web-push.php | 384 +++ package-lock.json | 1700 +++++++++++++ package.json | 21 + parts/footer.html | 36 +- parts/header.html | 31 +- postcss.config.js | 6 + readme.txt | 156 +- screenshot.png | Bin 0 -> 135434 bytes scripts/prepare-wordpress-org-package.mjs | 140 ++ style.css | 20 +- tailwind.config.js | 83 + templates/404.html | 16 + templates/archive.html | 42 + templates/author.html | 12 +- templates/front-page.html | 27 + templates/home.html | 61 + templates/index.html | 36 +- templates/page.html | 15 + templates/search.html | 39 + templates/single-community_post.html | 4 +- templates/single.html | 66 +- theme.json | 251 +- 72 files changed, 27743 insertions(+), 747 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .monorepo-source.json create mode 100644 assets/css/theme-source.css create mode 100644 assets/dist/theme.css create mode 100644 assets/dist/theme.js create mode 100644 assets/js/theme.js create mode 100644 assets/storefront-ui-strings/en.json create mode 100644 assets/storefront-ui-strings/ja.json create mode 100644 assets/storefront-ui-strings/pl.json create mode 100644 assets/submission-form-block.js create mode 100644 assets/video-hero-block.js create mode 100644 build/lint-php.mjs create mode 100644 build/sync-template-assets.mjs create mode 100644 inc/admin-theme.php create mode 100644 inc/admin-view-links.php create mode 100644 inc/artifact-invalidation.php create mode 100644 inc/artifact-protocol.php create mode 100644 inc/artifact-renderer.php create mode 100644 inc/artifact-rest.php create mode 100644 inc/artifact-store.php create mode 100644 inc/avatar.php create mode 100644 inc/backend-preview.php create mode 100644 inc/checkout-context.php create mode 100644 inc/custom-404.php create mode 100644 inc/frontend-theme.php create mode 100644 inc/native-shortcodes.php create mode 100644 inc/native-woocommerce.php create mode 100644 inc/notifications.php create mode 100644 inc/ratings.php create mode 100644 inc/recent-orders.php create mode 100644 inc/registration-email-verification.php create mode 100644 inc/saved-lists.php create mode 100644 inc/superfunky-licence-client.php create mode 100644 inc/superfunky-release-client.php create mode 100644 inc/superfunky-update-client.php create mode 100644 inc/web-push.php create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 screenshot.png create mode 100644 scripts/prepare-wordpress-org-package.mjs create mode 100644 tailwind.config.js create mode 100644 templates/404.html create mode 100644 templates/archive.html create mode 100644 templates/front-page.html create mode 100644 templates/home.html create mode 100644 templates/page.html create mode 100644 templates/search.html diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..033eb92 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: WordPress theme CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262c # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "22" + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 + with: + php-version: "8.1" + coverage: none + - name: Reject symbolic links + run: test -z "$(find . -type l -not -path './.git/*' -print -quit)" + - name: Install theme build dependencies + run: npm ci + - name: Build native theme assets + run: npm run build + - name: Verify committed assets are reproducible + run: git diff --exit-code + - name: Parse all theme PHP + run: npm run lint:php + - name: Prepare the WordPress.org package + run: node scripts/prepare-wordpress-org-package.mjs --out "$RUNNER_TEMP/wordpress-org/funkycommerce-headless" + - name: Validate packaged PHP syntax + run: find "$RUNNER_TEMP/wordpress-org/funkycommerce-headless" -type f -name '*.php' -print0 | xargs -0 -n1 php -l + + wordpress-org-preflight: + if: vars.WPORG_PREFLIGHT_ENABLED == 'true' + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262c # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "22" + - name: Prepare the WordPress.org package + run: node scripts/prepare-wordpress-org-package.mjs --out "$RUNNER_TEMP/wordpress-org/funkycommerce-headless" + - name: Run the WordPress Theme Review checks + uses: WordPress/theme-review-action@84400b232998116c9d1651502575c60262cceef9 + with: + root-folder: ${{ runner.temp }}/wordpress-org/funkycommerce-headless + accessible-ready: false + ui-debug: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9289790 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,160 @@ +name: Publish WordPress theme + +on: + workflow_run: + workflows: ["WordPress theme CI"] + branches: [main] + types: [completed] + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: publish-wordpress-theme + cancel-in-progress: false + +env: + PACKAGE_SLUG: funkycommerce-headless + VERSION_FILE: style.css + WPORG_SLUG: funkycommerce-headless + +jobs: + release: + if: github.event_name == 'workflow_dispatch' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_repository.full_name == github.repository) + runs-on: ubuntu-latest + timeout-minutes: 15 + outputs: + archive: ${{ steps.package.outputs.archive }} + publish: ${{ steps.package.outputs.publish }} + source-sha: ${{ steps.source.outputs.sha }} + version: ${{ steps.package.outputs.version }} + wordpress-org-archive: ${{ steps.package.outputs.wordpress-org-archive }} + steps: + - id: source + name: Resolve the validated source commit + env: + VALIDATED_SHA: ${{ github.event.workflow_run.head_sha }} + run: | + sha="${VALIDATED_SHA:-$GITHUB_SHA}" + echo "sha=$sha" >> "$GITHUB_OUTPUT" + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262c # v4 + with: + ref: ${{ steps.source.outputs.sha }} + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "22" + - id: package + name: Build the release archive + run: | + version="$(grep -m1 -E '^Version:' "$VERSION_FILE" | sed -E 's/^[^:]+:[[:space:]]*//')" + stable="$(grep -m1 -E '^Stable tag:' readme.txt | sed -E 's/^[^:]+:[[:space:]]*//')" + if ! printf '%s' "$version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "::error::Invalid package version: $version" + exit 1 + fi + if [ "$version" != "$stable" ]; then + echo "::error::Version $version does not match readme Stable tag $stable." + exit 1 + fi + archive="$PACKAGE_SLUG-$version.zip" + wordpress_org_archive="$PACKAGE_SLUG-$version-wordpress-org.zip" + if git ls-remote --exit-code --tags origin "refs/tags/v$version" >/dev/null; then + echo "publish=false" >> "$GITHUB_OUTPUT" + echo "version=$version" >> "$GITHUB_OUTPUT" + exit 0 + fi + + public_stage="$RUNNER_TEMP/public-package/$PACKAGE_SLUG" + mkdir -p "$public_stage" + rsync -a ./ "$public_stage/" \ + --exclude '.git*' \ + --exclude '.github' \ + --exclude '.monorepo-source.json' \ + --exclude '.wordpress-org' \ + --exclude 'CODE_OF_CONDUCT.md' \ + --exclude 'CONTRIBUTING.md' \ + --exclude 'EXTRACTION_STATUS.md' \ + --exclude 'README.md' \ + --exclude 'SECURITY.md' \ + --exclude 'docs' \ + --exclude 'scripts' + stage="$RUNNER_TEMP/package/$PACKAGE_SLUG" + node scripts/prepare-wordpress-org-package.mjs --out "$stage" + if find "$stage" -type l -print -quit | grep -q .; then + echo "::error::Release packages must not contain symbolic links." + exit 1 + fi + (cd "$(dirname "$public_stage")" && zip -X -q -r "$GITHUB_WORKSPACE/$archive" "$PACKAGE_SLUG") + (cd "$(dirname "$stage")" && zip -X -q -r "$GITHUB_WORKSPACE/$wordpress_org_archive" "$PACKAGE_SLUG") + echo "archive=$archive" >> "$GITHUB_OUTPUT" + echo "publish=true" >> "$GITHUB_OUTPUT" + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "wordpress-org-archive=$wordpress_org_archive" >> "$GITHUB_OUTPUT" + - name: Publish the GitHub release + if: steps.package.outputs.publish == 'true' + env: + GH_TOKEN: ${{ github.token }} + SOURCE_SHA: ${{ steps.source.outputs.sha }} + VERSION: ${{ steps.package.outputs.version }} + run: | + gh release create "v$VERSION" \ + "${{ steps.package.outputs.archive }}" \ + "${{ steps.package.outputs.wordpress-org-archive }}" \ + --target "$SOURCE_SHA" \ + --title "$PACKAGE_SLUG $VERSION" \ + --generate-notes + - name: Preserve the WordPress.org submission package + if: steps.package.outputs.publish == 'true' + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: funkycommerce-headless-${{ steps.package.outputs.version }} + path: ${{ steps.package.outputs.wordpress-org-archive }} + if-no-files-found: error + + wordpress-org: + if: needs.release.outputs.publish == 'true' && vars.WPORG_DEPLOY_ENABLED == 'true' && false + needs: release + runs-on: ubuntu-latest + timeout-minutes: 15 + environment: wordpress-org + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262c # v4 + with: + ref: ${{ needs.release.outputs.source-sha }} + persist-credentials: false + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "22" + - name: Require WordPress.org credentials + env: + WPORG_SVN_PASSWORD: ${{ secrets.WPORG_SVN_PASSWORD }} + WPORG_USERNAME: ${{ secrets.WPORG_USERNAME }} + run: | + test -n "$WPORG_USERNAME" || { echo "::error::Missing WPORG_USERNAME."; exit 1; } + test -n "$WPORG_SVN_PASSWORD" || { echo "::error::Missing WPORG_SVN_PASSWORD."; exit 1; } + - name: Deploy the approved theme release to WordPress.org + env: + VERSION: ${{ needs.release.outputs.version }} + WPORG_SVN_PASSWORD: ${{ secrets.WPORG_SVN_PASSWORD }} + WPORG_USERNAME: ${{ secrets.WPORG_USERNAME }} + run: | + svn_root="$RUNNER_TEMP/wordpress-org" + stage="$RUNNER_TEMP/package/$PACKAGE_SLUG" + node scripts/prepare-wordpress-org-package.mjs --out "$stage" + svn checkout "https://themes.svn.wordpress.org/$WPORG_SLUG" "$svn_root" + if svn info "https://themes.svn.wordpress.org/$WPORG_SLUG/$VERSION" >/dev/null 2>&1; then + echo "::error::WordPress.org version $VERSION already exists and cannot be overwritten." + exit 1 + fi + mkdir "$svn_root/$VERSION" + rsync -a "$stage/" "$svn_root/$VERSION/" + svn add "$svn_root/$VERSION" + svn commit "$svn_root" \ + --message "Release $VERSION from GitHub commit ${{ needs.release.outputs.source-sha }}" \ + --username "$WPORG_USERNAME" \ + --password "$WPORG_SVN_PASSWORD" \ + --no-auth-cache \ + --non-interactive diff --git a/.monorepo-source.json b/.monorepo-source.json new file mode 100644 index 0000000..7e37c49 --- /dev/null +++ b/.monorepo-source.json @@ -0,0 +1,14 @@ +{ + "productId": "funkycommerce-headless", + "repository": "coded-letter/superfunky-theme", + "source": "coded-letter/coded-letter-monorepo@0aebd69d9cf0616630e4f090c3e4579abe17c63d", + "sourcePath": "workspace/backend/wordpress/themes/free/funkycommerce-headless", + "installSlug": "funkycommerce-headless", + "kind": "theme", + "versionFile": "style.css", + "wordpressOrg": { + "deploySupported": false, + "slug": "funkycommerce-headless", + "status": "blocked-theme-plugin-territory" + } +} diff --git a/EXTRACTION_STATUS.md b/EXTRACTION_STATUS.md index 5cf9f55..e98790a 100644 --- a/EXTRACTION_STATUS.md +++ b/EXTRACTION_STATUS.md @@ -7,6 +7,7 @@ free-tier-only code. | Date | Module | Status | |---|---|---| | 2025-08-03 | Full theme (v0.7.0) | ✅ Complete — all PHP files, templates, and schema published | +| 2026-08-27 | Full theme (v1.2.6) | Complete — synchronized from `coded-letter/coded-letter-monorepo@0aebd69d9cf0616630e4f090c3e4579abe17c63d` | ## Free/Pro Tier Summary @@ -32,6 +33,6 @@ When Pro is not active, `funkycommerce_is_pro()` returns `false` and: ## Process 1. Changes are developed in the private monorepo. -2. Free-tier code is manually extracted and reviewed. -3. Premium plugin references and secrets are verified absent. -4. Code is pushed to this repo with an updated status entry. +2. The catalog-allowlisted exporter replaces the public package files. +3. Premium implementation and secrets are verified absent before and after export. +4. A provenance-stamped pull request is reviewed and independently validated here. diff --git a/assets/css/theme-source.css b/assets/css/theme-source.css new file mode 100644 index 0000000..1c845bb --- /dev/null +++ b/assets/css/theme-source.css @@ -0,0 +1,416 @@ +/* + * Superfunky native shell. + * + * Keep this layer intentionally small: migrated content should inherit WordPress + * block styles, while the shell supplies only structure, rhythm, and polish. + */ + +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --sf-shell-line: color-mix(in srgb, currentColor 13%, transparent); + --sf-shell-surface: color-mix(in srgb, var(--wp--preset--color--surface) 88%, transparent); + --sf-shell-radius: var(--wp--custom--shell--radius, 18px); + --sf-shell-shadow: 0 24px 70px -48px rgb(32 24 48 / 45%); + } + + html { + scroll-behavior: smooth; + } + + body { + min-height: 100vh; + text-rendering: optimizeLegibility; + } + + :where(a, button, input, textarea, select):focus-visible { + outline: 2px solid var(--wp--preset--color--accent); + outline-offset: 3px; + } + + ::selection { + background: var(--wp--preset--color--accent-soft); + color: var(--wp--preset--color--foreground); + } +} + +@layer components { + .sf-skip-link { + background: var(--wp--preset--color--foreground); + border-radius: 999px; + color: var(--wp--preset--color--background); + font-size: 0.8rem; + font-weight: 650; + left: 1rem; + padding: 0.65rem 1rem; + position: fixed; + top: 1rem; + transform: translateY(-6rem); + transition: transform 160ms ease; + z-index: 1000; + } + + .sf-skip-link:focus { + transform: translateY(0); + } + + .sf-shell-header { + backdrop-filter: blur(18px) saturate(140%); + background: var(--sf-shell-surface); + border-bottom: 1px solid var(--sf-shell-line); + position: sticky; + top: var(--wp-admin--admin-bar--height, 0); + z-index: 50; + } + + .sf-shell-header__inner { + min-height: 5.25rem; + padding-block: 1rem; + } + + .sf-shell-identity { + min-width: 0; + } + + .sf-shell-identity .wp-block-site-logo { + flex: 0 0 auto; + } + + .sf-shell-identity .custom-logo { + border-radius: 11px; + } + + .sf-shell-site-title { + font-family: var(--wp--preset--font-family--display); + font-size: clamp(1.05rem, 2vw, 1.25rem); + font-weight: 680; + letter-spacing: -0.035em; + line-height: 1.1; + } + + .sf-shell-site-title a { + color: inherit; + text-decoration: none; + } + + .sf-shell-tagline { + color: var(--wp--preset--color--muted); + font-size: 0.72rem; + line-height: 1.3; + margin-top: 0.25rem; + } + + .sf-shell-navigation { + font-size: 0.84rem; + font-weight: 580; + } + + .sf-shell-navigation .wp-block-navigation-item__content { + border-radius: 999px; + color: inherit; + padding: 0.55rem 0.75rem; + text-decoration: none; + transition: background-color 160ms ease, color 160ms ease; + } + + .sf-shell-navigation .wp-block-navigation-item__content:hover, + .sf-shell-navigation .current-menu-item > .wp-block-navigation-item__content { + background: var(--wp--preset--color--accent-soft); + color: var(--wp--preset--color--accent); + } + + .sf-shell-navigation .wp-block-navigation__responsive-container.is-menu-open { + background: var(--wp--preset--color--background); + color: var(--wp--preset--color--foreground); + padding: 2rem; + } + + .sf-shell-main { + min-height: 55vh; + } + + .sf-shell-hero { + border-bottom: 1px solid var(--sf-shell-line); + overflow: hidden; + position: relative; + } + + .sf-shell-hero::before { + background: + radial-gradient(circle at 15% 25%, color-mix(in srgb, var(--wp--preset--color--accent) 18%, transparent), transparent 38%), + radial-gradient(circle at 85% 10%, color-mix(in srgb, var(--wp--preset--color--highlight) 25%, transparent), transparent 34%); + content: ""; + inset: 0; + pointer-events: none; + position: absolute; + } + + .sf-shell-hero__inner { + padding-block: clamp(5rem, 13vw, 10rem); + position: relative; + } + + .sf-shell-eyebrow { + color: var(--wp--preset--color--accent); + font-size: 0.72rem; + font-weight: 700; + letter-spacing: 0.16em; + text-transform: uppercase; + } + + .sf-shell-hero .wp-block-site-title { + font-size: clamp(3rem, 10vw, 7.5rem); + font-weight: 680; + letter-spacing: -0.07em; + line-height: 0.9; + max-width: 11ch; + } + + .sf-shell-hero .wp-block-site-tagline { + color: var(--wp--preset--color--muted); + font-size: clamp(1.05rem, 2vw, 1.35rem); + line-height: 1.5; + max-width: 42rem; + } + + .sf-shell-content { + padding-block: clamp(3.5rem, 8vw, 7rem); + } + + .sf-shell-card { + background: var(--wp--preset--color--surface); + border: 1px solid var(--sf-shell-line); + border-radius: var(--sf-shell-radius); + box-shadow: var(--sf-shell-shadow); + height: 100%; + overflow: hidden; + transition: border-color 180ms ease, transform 180ms ease; + } + + .sf-shell-card:hover { + border-color: color-mix(in srgb, var(--wp--preset--color--accent) 34%, transparent); + transform: translateY(-3px); + } + + .sf-shell-card .wp-block-post-featured-image { + margin: 0; + } + + .sf-shell-card__body { + padding: clamp(1.25rem, 3vw, 2rem); + } + + .sf-shell-card .wp-block-post-title a { + color: inherit; + text-decoration: none; + } + + .sf-shell-meta { + color: var(--wp--preset--color--muted); + font-size: 0.75rem; + letter-spacing: 0.02em; + } + + .sf-shell-empty { + background: var(--wp--preset--color--surface); + border: 1px dashed var(--sf-shell-line); + border-radius: var(--sf-shell-radius); + padding: clamp(2rem, 6vw, 4rem); + text-align: center; + } + + .sf-shell-footer { + background: var(--wp--preset--color--foreground); + color: var(--wp--preset--color--background); + } + + .sf-shell-footer a { + color: inherit; + } + + .sf-shell-footer__inner { + gap: 2rem; + padding-block: clamp(3rem, 7vw, 5rem); + } + + .sf-shell-footer__brand { + max-width: 28rem; + } + + .sf-shell-footer__brand .wp-block-site-tagline, + .sf-shell-footer__meta { + color: color-mix(in srgb, var(--wp--preset--color--background) 62%, transparent); + } + + .sf-shell-footer__navigation { + font-size: 0.82rem; + } + + .sf-shell-footer__navigation .wp-block-navigation-item__content { + text-decoration: none; + } + + .sf-shell-footer__meta { + border-top: 1px solid color-mix(in srgb, var(--wp--preset--color--background) 15%, transparent); + padding-block: 1.25rem 2rem; + } +} + +@media (max-width: 600px) { + .sf-shell-tagline { + display: none; + } + + .sf-shell-header__inner { + min-height: 4.5rem; + } +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + scroll-behavior: auto !important; + transition-duration: 0.01ms !important; + } +} +.funkycommerce-native-video-hero { + position: relative; + display: flex; + overflow: hidden; + align-items: flex-end; + min-height: 24rem; + padding: clamp(2rem, 6vw, 5rem); + border-radius: 1.5rem; + background: #09090b; + color: #fff; +} + +.funkycommerce-native-video-hero-media, +.funkycommerce-native-video-hero-poster, +.funkycommerce-native-video-hero-overlay { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + border: 0; + object-fit: cover; +} + +.funkycommerce-native-video-hero-overlay { + background: #000; +} + +.funkycommerce-native-video-hero-content { + position: relative; + z-index: 1; + max-width: 48rem; +} + +.funkycommerce-native-video-hero-controls { + position: absolute; + right: 1.25rem; + bottom: 1.25rem; + z-index: 2; + display: flex; + gap: 0.5rem; +} + +.funkycommerce-native-video-hero-control { + display: inline-grid; + width: 2.75rem; + height: 2.75rem; + place-items: center; + border: 0; + border-radius: 9999px; + background: rgb(0 0 0 / 60%); + color: #fff; + cursor: pointer; +} + +.funkycommerce-native-video-hero--center { + justify-content: center; + text-align: center; +} + +.funkycommerce-native-video-hero--right { + justify-content: flex-end; + text-align: right; +} + +.funkycommerce-native-video-hero--glow .funkycommerce-native-video-hero-overlay { + background: radial-gradient(circle at center, rgb(124 58 237 / 70%), #000 75%); +} + +.funkycommerce-native-video-hero--fullbleed { + left: 50%; + width: 100vw; + margin-inline: -50vw; + border-radius: 0; +} + +.funkycommerce-native-video-hero--fullbleed .funkycommerce-native-video-hero-content { + width: min(100%, var(--wp--style--global--wide-size, 1200px)); + margin-inline: auto; + padding-inline: clamp(1rem, 3vw, 2rem); +} + +.funkycommerce-native-video-hero--fullbleed .funkycommerce-native-video-hero-controls { + right: max(1.25rem, calc((100vw - var(--wp--style--global--wide-size, 1200px)) / 2 + 1.25rem)); +} + +.funkycommerce-native-video-hero--split { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + background: #fff; + color: #18181b; +} + +.funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-content { + order: -1; + align-self: center; + padding-right: 2rem; +} + +.funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-media, +.funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-poster, +.funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-overlay { + left: 50%; + width: 50%; +} + +.funkycommerce-native-video-hero--minimal { + background: #fff; + color: #18181b; + text-align: center; + justify-content: center; +} + +.funkycommerce-native-video-hero--minimal :is(.funkycommerce-native-video-hero-media, .funkycommerce-native-video-hero-poster) { + opacity: 0.15; +} + +.funkycommerce-native-video-hero--strip { + min-height: 0 !important; + padding-block: 1.5rem; +} + +@media (max-width: 640px) { + .funkycommerce-native-video-hero--split { + grid-template-columns: 1fr; + } + + .funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-media, + .funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-poster, + .funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-overlay { + left: 0; + width: 100%; + } +} diff --git a/assets/dist/theme.css b/assets/dist/theme.css new file mode 100644 index 0000000..1dfd03c --- /dev/null +++ b/assets/dist/theme.css @@ -0,0 +1 @@ +*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }:root{--sf-shell-line:color-mix(in srgb,currentColor 13%,transparent);--sf-shell-surface:color-mix(in srgb,var(--wp--preset--color--surface) 88%,transparent);--sf-shell-radius:var(--wp--custom--shell--radius,18px);--sf-shell-shadow:0 24px 70px -48px rgba(32,24,48,.45)}html{scroll-behavior:smooth}body{min-height:100vh;text-rendering:optimizeLegibility}:where(a,button,input,textarea,select):focus-visible{outline:2px solid var(--wp--preset--color--accent);outline-offset:3px}::-moz-selection{background:var(--wp--preset--color--accent-soft);color:var(--wp--preset--color--foreground)}::selection{background:var(--wp--preset--color--accent-soft);color:var(--wp--preset--color--foreground)}.sf-skip-link{background:var(--wp--preset--color--foreground);border-radius:999px;color:var(--wp--preset--color--background);font-size:.8rem;font-weight:650;left:1rem;padding:.65rem 1rem;position:fixed;top:1rem;transform:translateY(-6rem);transition:transform .16s ease;z-index:1000}.sf-skip-link:focus{transform:translateY(0)}.sf-shell-header{backdrop-filter:blur(18px) saturate(140%);background:var(--sf-shell-surface);border-bottom:1px solid var(--sf-shell-line);position:sticky;top:var(--wp-admin--admin-bar--height,0);z-index:50}.sf-shell-header__inner{min-height:5.25rem;padding-block:1rem}.sf-shell-identity{min-width:0}.sf-shell-identity .wp-block-site-logo{flex:0 0 auto}.sf-shell-identity .custom-logo{border-radius:11px}.sf-shell-site-title{font-family:var(--wp--preset--font-family--display);font-size:clamp(1.05rem,2vw,1.25rem);font-weight:680;letter-spacing:-.035em;line-height:1.1}.sf-shell-site-title a{color:inherit;text-decoration:none}.sf-shell-tagline{color:var(--wp--preset--color--muted);font-size:.72rem;line-height:1.3;margin-top:.25rem}.sf-shell-navigation{font-size:.84rem;font-weight:580}.sf-shell-navigation .wp-block-navigation-item__content{border-radius:999px;color:inherit;padding:.55rem .75rem;text-decoration:none;transition:background-color .16s ease,color .16s ease}.sf-shell-navigation .current-menu-item>.wp-block-navigation-item__content,.sf-shell-navigation .wp-block-navigation-item__content:hover{background:var(--wp--preset--color--accent-soft);color:var(--wp--preset--color--accent)}.sf-shell-navigation .wp-block-navigation__responsive-container.is-menu-open{background:var(--wp--preset--color--background);color:var(--wp--preset--color--foreground);padding:2rem}.sf-shell-main{min-height:55vh}.sf-shell-hero{border-bottom:1px solid var(--sf-shell-line);overflow:hidden;position:relative}.sf-shell-hero:before{background:radial-gradient(circle at 15% 25%,color-mix(in srgb,var(--wp--preset--color--accent) 18%,transparent),transparent 38%),radial-gradient(circle at 85% 10%,color-mix(in srgb,var(--wp--preset--color--highlight) 25%,transparent),transparent 34%);content:"";inset:0;pointer-events:none;position:absolute}.sf-shell-hero__inner{padding-block:clamp(5rem,13vw,10rem);position:relative}.sf-shell-eyebrow{color:var(--wp--preset--color--accent);font-size:.72rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase}.sf-shell-hero .wp-block-site-title{font-size:clamp(3rem,10vw,7.5rem);font-weight:680;letter-spacing:-.07em;line-height:.9;max-width:11ch}.sf-shell-hero .wp-block-site-tagline{color:var(--wp--preset--color--muted);font-size:clamp(1.05rem,2vw,1.35rem);line-height:1.5;max-width:42rem}.sf-shell-content{padding-block:clamp(3.5rem,8vw,7rem)}.sf-shell-card{background:var(--wp--preset--color--surface);border:1px solid var(--sf-shell-line);border-radius:var(--sf-shell-radius);box-shadow:var(--sf-shell-shadow);height:100%;overflow:hidden;transition:border-color .18s ease,transform .18s ease}.sf-shell-card:hover{border-color:color-mix(in srgb,var(--wp--preset--color--accent) 34%,transparent);transform:translateY(-3px)}.sf-shell-card .wp-block-post-featured-image{margin:0}.sf-shell-card__body{padding:clamp(1.25rem,3vw,2rem)}.sf-shell-card .wp-block-post-title a{color:inherit;text-decoration:none}.sf-shell-meta{color:var(--wp--preset--color--muted);font-size:.75rem;letter-spacing:.02em}.sf-shell-empty{background:var(--wp--preset--color--surface);border:1px dashed var(--sf-shell-line);border-radius:var(--sf-shell-radius);padding:clamp(2rem,6vw,4rem);text-align:center}.sf-shell-footer{background:var(--wp--preset--color--foreground);color:var(--wp--preset--color--background)}.sf-shell-footer a{color:inherit}.sf-shell-footer__inner{gap:2rem;padding-block:clamp(3rem,7vw,5rem)}.sf-shell-footer__brand{max-width:28rem}.sf-shell-footer__brand .wp-block-site-tagline,.sf-shell-footer__meta{color:color-mix(in srgb,var(--wp--preset--color--background) 62%,transparent)}.sf-shell-footer__navigation{font-size:.82rem}.sf-shell-footer__navigation .wp-block-navigation-item__content{text-decoration:none}.sf-shell-footer__meta{border-top:1px solid color-mix(in srgb,var(--wp--preset--color--background) 15%,transparent);padding-block:1.25rem 2rem}.sticky{position:sticky}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.border{border-width:1px}@media (max-width:600px){.sf-shell-tagline{display:none}.sf-shell-header__inner{min-height:4.5rem}}@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}*,:after,:before{scroll-behavior:auto!important;transition-duration:.01ms!important}}.funkycommerce-native-video-hero{position:relative;display:flex;overflow:hidden;align-items:flex-end;min-height:24rem;padding:clamp(2rem,6vw,5rem);border-radius:1.5rem;background:#09090b;color:#fff}.funkycommerce-native-video-hero-media,.funkycommerce-native-video-hero-overlay,.funkycommerce-native-video-hero-poster{position:absolute;inset:0;width:100%;height:100%;border:0;-o-object-fit:cover;object-fit:cover}.funkycommerce-native-video-hero-overlay{background:#000}.funkycommerce-native-video-hero-content{position:relative;z-index:1;max-width:48rem}.funkycommerce-native-video-hero-controls{position:absolute;right:1.25rem;bottom:1.25rem;z-index:2;display:flex;gap:.5rem}.funkycommerce-native-video-hero-control{display:inline-grid;width:2.75rem;height:2.75rem;place-items:center;border:0;border-radius:9999px;background:rgba(0,0,0,.6);color:#fff;cursor:pointer}.funkycommerce-native-video-hero--center{justify-content:center;text-align:center}.funkycommerce-native-video-hero--right{justify-content:flex-end;text-align:right}.funkycommerce-native-video-hero--glow .funkycommerce-native-video-hero-overlay{background:radial-gradient(circle at center,rgba(124,58,237,.7),#000 75%)}.funkycommerce-native-video-hero--fullbleed{left:50%;width:100vw;margin-inline:-50vw;border-radius:0}.funkycommerce-native-video-hero--fullbleed .funkycommerce-native-video-hero-content{width:min(100%,var(--wp--style--global--wide-size,1200px));margin-inline:auto;padding-inline:clamp(1rem,3vw,2rem)}.funkycommerce-native-video-hero--fullbleed .funkycommerce-native-video-hero-controls{right:max(1.25rem,calc((100vw - var(--wp--style--global--wide-size, 1200px))/2 + 1.25rem))}.funkycommerce-native-video-hero--split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);background:#fff;color:#18181b}.funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-content{order:-1;align-self:center;padding-right:2rem}.funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-media,.funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-overlay,.funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-poster{left:50%;width:50%}.funkycommerce-native-video-hero--minimal{background:#fff;color:#18181b;text-align:center;justify-content:center}.funkycommerce-native-video-hero--minimal :is(.funkycommerce-native-video-hero-media,.funkycommerce-native-video-hero-poster){opacity:.15}.funkycommerce-native-video-hero--strip{min-height:0!important;padding-block:1.5rem}@media (max-width:640px){.funkycommerce-native-video-hero--split{grid-template-columns:1fr}.funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-media,.funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-overlay,.funkycommerce-native-video-hero--split .funkycommerce-native-video-hero-poster{left:0;width:100%}} \ No newline at end of file diff --git a/assets/dist/theme.js b/assets/dist/theme.js new file mode 100644 index 0000000..6c7df37 --- /dev/null +++ b/assets/dist/theme.js @@ -0,0 +1 @@ +(()=>{function p(){var s,u,l,m,y,f;let n=document.querySelector("[data-funky-spotify-embed]");if(!n)return;let e=((u=(s=window.FunkyCommerceThemeSettings)==null?void 0:s.spotify)==null?void 0:u.embedUrl)||"",i=((m=(l=window.FunkyCommerceThemeSettings)==null?void 0:l.spotify)==null?void 0:m.title)||"",r=((f=(y=window.FunkyCommerceThemeSettings)==null?void 0:y.spotify)==null?void 0:f.description)||"",c=n.dataset.funkySpotifyEmbed||e,t;try{t=new URL(c)}catch(a){return}if(t.protocol!=="https:"||t.hostname!=="open.spotify.com"||!/^\/embed\/(?:track|album|playlist|artist|show|episode)\/[A-Za-z0-9]{10,64}$/.test(t.pathname)||n.querySelector("iframe"))return;let o=document.createElement("iframe");o.title=i||"Spotify player",o.src=t.href,o.loading="lazy",o.allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture",o.referrerPolicy="strict-origin-when-cross-origin",o.style.border="0",o.style.borderRadius="12px",o.style.height="352px",o.style.width="100%";let d=[];if(i){let a=document.createElement("h2");a.textContent=i,d.push(a)}if(r){let a=document.createElement("p");a.textContent=r,d.push(a)}d.push(o),n.replaceChildren(...d),n.hidden=!1}p();function v(){document.querySelectorAll(".funkycommerce-native-video-hero").forEach(n=>{let e=n.querySelector(".funkycommerce-native-video-hero-media"),i=n.querySelector(".funkycommerce-native-video-hero-playback"),r=n.querySelector(".funkycommerce-native-video-hero-mute");if(!e||!i||!r)return;let c=()=>{let t=n.dataset.videoMuted==="true";if(e instanceof HTMLVideoElement){e.muted=t;return}if(!(e instanceof HTMLIFrameElement)||!e.contentWindow)return;let o=n.dataset.videoProvider==="youtube"?{event:"command",func:t?"mute":"unMute",args:[]}:{method:"setVolume",value:t?0:1};e.contentWindow.postMessage(JSON.stringify(o),"*"),!t&&n.dataset.videoProvider==="youtube"&&e.contentWindow.postMessage(JSON.stringify({event:"command",func:"playVideo",args:[]}),"*")};e instanceof HTMLIFrameElement&&e.addEventListener("load",c),c(),i.addEventListener("click",()=>{let t=n.dataset.videoPlaying==="true";e instanceof HTMLVideoElement?t?e.pause():e.play():e instanceof HTMLIFrameElement&&(t?(e.dataset.videoSrc=e.src,e.removeAttribute("src")):e.dataset.videoSrc&&(e.src=e.dataset.videoSrc));let o=!t;n.dataset.videoPlaying=String(o),i.textContent=o?"\u275A\u275A":"\u25B6",i.setAttribute("aria-label",o?"Pause background video":"Play background video")}),r.addEventListener("click",()=>{let t=n.dataset.videoMuted!=="true";n.dataset.videoMuted=String(t),c(),r.textContent=t?"\u{1F507}":"\u{1F50A}",r.setAttribute("aria-label",t?"Unmute background video":"Mute background video")})})}v();})(); diff --git a/assets/js/theme.js b/assets/js/theme.js new file mode 100644 index 0000000..9791cf4 --- /dev/null +++ b/assets/js/theme.js @@ -0,0 +1,112 @@ +function mountSpotifyPlayer() { + const slot = document.querySelector("[data-funky-spotify-embed]"); + if (!slot) return; + + const localizedUrl = window.FunkyCommerceThemeSettings?.spotify?.embedUrl || ""; + const title = window.FunkyCommerceThemeSettings?.spotify?.title || ""; + const description = window.FunkyCommerceThemeSettings?.spotify?.description || ""; + const candidate = slot.dataset.funkySpotifyEmbed || localizedUrl; + let embedUrl; + try { + embedUrl = new URL(candidate); + } catch { + return; + } + if ( + embedUrl.protocol !== "https:" || + embedUrl.hostname !== "open.spotify.com" || + !/^\/embed\/(?:track|album|playlist|artist|show|episode)\/[A-Za-z0-9]{10,64}$/.test(embedUrl.pathname) + ) { + return; + } + if (slot.querySelector("iframe")) return; + + const iframe = document.createElement("iframe"); + iframe.title = title || "Spotify player"; + iframe.src = embedUrl.href; + iframe.loading = "lazy"; + iframe.allow = "autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"; + iframe.referrerPolicy = "strict-origin-when-cross-origin"; + iframe.style.border = "0"; + iframe.style.borderRadius = "12px"; + iframe.style.height = "352px"; + iframe.style.width = "100%"; + const content = []; + if (title) { + const heading = document.createElement("h2"); + heading.textContent = title; + content.push(heading); + } + if (description) { + const copy = document.createElement("p"); + copy.textContent = description; + content.push(copy); + } + content.push(iframe); + slot.replaceChildren(...content); + slot.hidden = false; +} + +mountSpotifyPlayer(); + +function mountVideoHeroControls() { + document.querySelectorAll(".funkycommerce-native-video-hero").forEach((hero) => { + const media = hero.querySelector(".funkycommerce-native-video-hero-media"); + const playbackControl = hero.querySelector(".funkycommerce-native-video-hero-playback"); + const muteControl = hero.querySelector(".funkycommerce-native-video-hero-mute"); + if (!media || !playbackControl || !muteControl) return; + + const applyMuted = () => { + const muted = hero.dataset.videoMuted === "true"; + if (media instanceof HTMLVideoElement) { + media.muted = muted; + return; + } + if (!(media instanceof HTMLIFrameElement) || !media.contentWindow) return; + const message = hero.dataset.videoProvider === "youtube" + ? { event: "command", func: muted ? "mute" : "unMute", args: [] } + : { method: "setVolume", value: muted ? 0 : 1 }; + media.contentWindow.postMessage(JSON.stringify(message), "*"); + if (!muted && hero.dataset.videoProvider === "youtube") { + media.contentWindow.postMessage( + JSON.stringify({ event: "command", func: "playVideo", args: [] }), + "*", + ); + } + }; + + if (media instanceof HTMLIFrameElement) { + media.addEventListener("load", applyMuted); + } + applyMuted(); + + playbackControl.addEventListener("click", () => { + const playing = hero.dataset.videoPlaying === "true"; + if (media instanceof HTMLVideoElement) { + if (playing) media.pause(); + else void media.play(); + } else if (media instanceof HTMLIFrameElement) { + if (playing) { + media.dataset.videoSrc = media.src; + media.removeAttribute("src"); + } else if (media.dataset.videoSrc) { + media.src = media.dataset.videoSrc; + } + } + const next = !playing; + hero.dataset.videoPlaying = String(next); + playbackControl.textContent = next ? "\u275A\u275A" : "\u25B6"; + playbackControl.setAttribute("aria-label", next ? "Pause background video" : "Play background video"); + }); + + muteControl.addEventListener("click", () => { + const next = hero.dataset.videoMuted !== "true"; + hero.dataset.videoMuted = String(next); + applyMuted(); + muteControl.textContent = next ? "\uD83D\uDD07" : "\uD83D\uDD0A"; + muteControl.setAttribute("aria-label", next ? "Unmute background video" : "Mute background video"); + }); + }); +} + +mountVideoHeroControls(); diff --git a/assets/storefront-ui-strings/en.json b/assets/storefront-ui-strings/en.json new file mode 100644 index 0000000..279e346 --- /dev/null +++ b/assets/storefront-ui-strings/en.json @@ -0,0 +1,677 @@ +{ + "cart.title": "Your cart", + "cart.aria": "Shopping cart", + "cart.close": "Close cart", + "cart.empty.heading": "Your cart is empty", + "cart.empty.body": "Looks like you haven't added anything yet.", + "cart.empty.body_alt": "Add products from the shop to see them here.", + "cart.you_might_like": "You might like", + "cart.subtotal": "Subtotal", + "cart.shipping_notice": "Shipping and taxes calculated at checkout.", + "cart.view_cart": "View cart", + "cart.checkout": "Checkout", + "cart.continue_shopping": "Continue shopping", + "cart.add": "Add to cart", + "cart.added": "Added ✓", + "cart.remove": "Remove", + "cart.ready": "Ready to check out?", + "cart.item_singular": "item", + "cart.item_plural": "items", + "cart.continue_checkout": "Continue to checkout", + "cart.shipping": "Shipping", + "cart.tax": "Tax", + "cart.digital_delivery": "Digital delivery", + "cart.free": "Free", + "cart.shipping_destination": "Shipping destination preview", + "cart.shipping_destination_aria": "Preview shipping destination", + "cart.empty.cart_title": "Your cart is empty", + "checkout.title": "Checkout", + "checkout.progress_aria": "Checkout progress", + "checkout.step.cart": "Cart", + "checkout.step.checkout": "Checkout", + "checkout.step.confirmation": "Confirmation", + "checkout.billing.title": "Billing details", + "checkout.field.first_name": "First name", + "checkout.field.last_name": "Last name", + "checkout.field.company": "Company name", + "checkout.field.country": "Country / region", + "checkout.field.address1": "Street address", + "checkout.field.address1_helper": "House number and street name", + "checkout.field.address2": "Apartment, suite, unit etc.", + "checkout.field.optional": "Optional", + "checkout.field.city": "Town / city", + "checkout.field.state": "State / county", + "checkout.field.postcode": "Postcode / ZIP", + "checkout.field.phone": "Phone", + "checkout.field.email": "Email address", + "checkout.field.phone_error": "Phone is not a valid phone number", + "checkout.field.email_error": "Email is not a valid email address", + "checkout.field.country_placeholder": "Select a country…", + "checkout.digital_notice": "Digital delivery. Your purchase is delivered instantly via the website after payment — no shipping address or shipping method needed.", + "checkout.account.title": "Account", + "checkout.account.create": "Create an account?", + "checkout.field.username": "Username", + "checkout.field.password": "Password", + "checkout.marketing_consent": "Keep me posted about new drops, offers, and restocks by email.", + "checkout.guest_notice": "An account will be created with your email address during checkout.", + "checkout.delivery.title": "Delivery", + "checkout.ship_to_different": "Ship to a different address?", + "checkout.shipping_method": "Shipping method", + "checkout.shipping_loading": "Loading live shipping methods…", + "checkout.subtotal": "Subtotal", + "checkout.discount": "Discount", + "checkout.shipping": "Shipping", + "checkout.tax": "Tax", + "checkout.free": "Free", + "checkout.free_shipping_nudge": "left to free standard shipping — back to cart", + "checkout.back_to_cart": "back to cart", + "checkout.order_notes.title": "Order notes", + "checkout.order_notes.label": "Notes about your order", + "checkout.order_notes.helper": "Optional — e.g. delivery instructions", + "checkout.coupon.title": "Have a coupon?", + "checkout.coupon.label": "Coupon code", + "checkout.coupon.apply": "Apply code", + "checkout.coupon.applying": "Applying…", + "checkout.coupon.applied": "Coupon “{code}” applied", + "checkout.coupon.remove": "Remove", + "checkout.coupon.error": "Failed to apply coupon", + "checkout.payment.title": "Payment method", + "checkout.payment.online": "Pay online", + "checkout.payment.online_desc": "Cards and other Stripe-supported online payment methods.", + "checkout.payment.blik": "BLIK", + "checkout.payment.blik_desc": "Pay instantly in PLN with a 6-digit BLIK code via Stripe.", + "checkout.payment.blik_label": "BLIK code", + "checkout.payment.blik_error": "BLIK code must be 6 digits", + "checkout.payment.blik_expired": "Code expired — place the order again to get a fresh authorization window.", + "checkout.payment.bacs": "Direct bank transfer", + "checkout.payment.bacs_desc": "Pay from your bank using the store's transfer instructions after placing the order.", + "checkout.payment.instructions": "Payment instructions", + "checkout.payment.cod": "Payment upon delivery", + "checkout.payment.cod_desc": "Pay in cash when your order arrives.", + "checkout.payment.cheque": "Bank cheque", + "checkout.payment.cheque_desc": "Mail a cheque — your order ships once it clears.", + "checkout.payment.unavailable_digital": "Not available for digital orders", + "checkout.payment.unavailable": "Not available at this time", + "checkout.payment.ssl": "Transactions secured with SSL encryption", + "checkout.payment.unavailable_backend": "Crypto checkout is not currently available on this backend.", + "checkout.payment.method_unavailable": "The selected payment method is not currently available.", + "checkout.summary.title": "Review order", + "checkout.cta": "Place order", + "checkout.terms": "I have read and agree to the website's terms and conditions", + "checkout.privacy": "I consent to my personal data being processed as described in the privacy policy", + "checkout.terms_link": "terms and conditions", + "checkout.privacy_link": "privacy policy", + "checkout.error.billing_required": "Complete the required billing details before placing the order.", + "checkout.error.method_unavailable": "Selected payment method is unavailable.", + "order_success.breadcrumb": "Order confirmed", + "order_success.section": "Order confirmation", + "order_success.section_digital": "Digital order confirmation", + "order_success.heading": "Order successful", + "order_success.thank_you": "Thank you, {name} — your order #{number} is confirmed. A receipt has been sent to your email.", + "order_success.thank_you_digital": "Thank you, {name} — your digital order #{number} is confirmed. Your downloads are ready below and a receipt has been sent to your email.", + "order_success.customer_fallback": "there", + "order_success.empty.heading": "No completed order found", + "order_success.empty.message": "This page shows the most recent order completed in this browser session.", + "order_success.refreshing": "Refreshing order details", + "order_success.refresh_error": "Live details could not be refreshed. Showing the order captured at checkout.", + "order_success.order_label": "Order #{number}", + "order_success.quantity": "Quantity: {quantity}", + "order_success.shipping_to": "Shipping to", + "order_success.delivery": "Delivery", + "order_success.details.order": "Order details", + "order_success.details.delivery_payment": "Delivery and payment", + "order_success.receipt": "Receipt", + "order_success.currency": "Currency", + "order_success.account_login_error": "Your order was created, but automatic account setup did not finish: {error} Sign in and check My Orders; contact support if the order is missing.", + "order_success.delivery_note": "Your order is now in the store workflow.", + "order_success.native_note": "Continue to the native order page for the latest payment and status details.", + "order_success.payment.cod": "Cash on delivery", + "order_success.payment.cheque": "Cheque payment", + "order_success.payment.bacs": "Direct bank transfer", + "order_success.payment.crypto": "Cryptocurrency payment", + "order_success.payment.blik": "BLIK", + "order_success.payment.card": "Card payment", + "order_success.row.subtotal": "Subtotal", + "order_success.row.discount": "Discount", + "order_success.row.discount_with_codes": "Discount ({codes})", + "order_success.row.shipping": "Shipping", + "order_success.row.tax": "Tax", + "order_success.row.free": "Free", + "order_success.row.digital_delivery": "Digital delivery", + "order_success.row.total": "Total paid", + "order_success.cta.shopping": "Continue shopping", + "order_success.cta.native": "Open native order page", + "order_success.cta.track": "Track my order", + "order_success.cta.orders": "View my orders", + "order_success.cta.private": "Open private order page", + "order_success.cta.pdf": "Save receipt as PDF", + "order_success.cta.pdf_hint": "Opens your browser print dialog, where you can save this receipt as a PDF.", + "order_success.cta.help": "Get order help", + "order_success.promo": "Thanks for being a returning customer — here's 15% off your next order.", + "order_success.support": "Questions about your order?", + "order_success.contact": "Contact us", + "order_success.downloads": "Your Downloads", + "order_success.downloads_note": "Files are also available anytime from your account's Downloads tab — no need to save this page.", + "order_success.download.available": "Available", + "order_success.download.unavailable": "Unavailable", + "order_success.download.expires": "Expires:", + "order_success.download.remaining": "Downloads remaining:", + "order_success.download.unlimited": "Unlimited", + "order_success.download.cta": "Download", + "order_success.support.trouble": "Trouble accessing a download?", + "order_status.pending": "Pending payment", + "order_status.processing": "Processing", + "order_status.on-hold": "On hold", + "order_status.completed": "Completed", + "order_status.cancelled": "Cancelled", + "order_status.failed": "Failed", + "order_status.refunded": "Refunded", + "order_details.private_title": "Private order", + "order_details.heading": "Order #{number}", + "order_details.guest_expires": "This private guest link expires {date}.", + "order_details.loading": "Loading order", + "order_details.unavailable": "Order not found, unavailable, or the 24-hour guest link has expired.", + "order_details.back_account": "Back to account", + "auth.title.login": "Welcome back", + "auth.title.register": "Create your account", + "auth.title.forgot": "Forgotten password", + "auth.breadcrumb.login": "Sign in", + "auth.breadcrumb.register": "Register", + "auth.breadcrumb.forgot": "Forgot password", + "auth.desc.login": "Sign in to track orders, manage your wishlist, and check out faster.", + "auth.desc.register": "Join Superfunky for personalized recommendations and faster checkout.", + "auth.desc.forgot": "We'll email you a secure link to get back into your account.", + "auth.tab.login": "Login", + "auth.tab.register": "Register", + "auth.tab.forgot": "Forgot", + "auth.password_updated": "Your password was updated. Sign in with the new password.", + "auth.or_continue": "Or continue with", + "auth.continue_with": "Continue with {provider}", + "auth.brand_tagline": "A modern storefront experience connected to your site account.", + "auth.login.username": "Username or email", + "auth.login.password": "Password", + "auth.login.remember": "Remember me", + "auth.login.forgot_link": "Forgot password?", + "auth.login.error": "Sign-in failed.", + "auth.login.cta": "Sign in", + "auth.login.cta_loading": "Signing in…", + "auth.register.first_name": "First name", + "auth.register.last_name": "Last name", + "auth.register.username": "Username", + "auth.register.username_placeholder": "e.g. funk_rider.99", + "auth.register.username_helper": "Used for your community profile URL — letters, numbers, underscores, hyphens, dots", + "auth.register.email": "Email", + "auth.register.password": "Password", + "auth.register.password_helper": "At least 8 characters", + "auth.register.confirm": "Confirm password", + "auth.register.newsletter": "I'd like to receive occasional emails about new drops, offers, and restocks. You can unsubscribe anytime.", + "auth.register.error": "The account could not be created.", + "auth.register.cta": "Create account", + "auth.register.cta_loading": "Creating account…", + "auth.register.success.heading": "Account created.", + "auth.register.success.body": "You can now sign in with your email and password.", + "auth.register.success.cta": "Continue to sign in", + "auth.forgot.field": "Username or email", + "auth.forgot.error": "The reset email could not be sent.", + "auth.forgot.cta": "Send reset link", + "auth.forgot.cta_loading": "Sending…", + "auth.forgot.success": "If an account exists for {identity}, a reset link is on its way.", + "auth.reset.breadcrumb": "Reset password", + "auth.reset.error.mismatch": "The passwords do not match.", + "auth.reset.error.failed": "The password could not be reset.", + "auth.oauth.error": "Provider sign-in failed.", + "auth.oauth.callback_error": "The provider callback is incomplete or invalid.", + "validation.passwords_mismatch": "Passwords don't match", + "validation.password.min_length": "Use at least 8 characters.", + "validation.password.uppercase": "Include an uppercase letter.", + "validation.password.lowercase": "Include a lowercase letter.", + "validation.password.number": "Include a number.", + "validation.password.special": "Include a special character.", + "validation.required": "{label} is required.", + "validation.min_length": "{label} must be at least {min} characters.", + "validation.max_length": "{label} must be at most {max} characters.", + "validation.email": "{label} is not a valid email address.", + "validation.phone": "{label} is not a valid phone number.", + "validation.username_chars": "{label} may only contain letters, numbers, underscores, hyphens, and dots.", + "account.tab.dashboard": "Dashboard", + "account.tab.orders": "Orders", + "account.tab.addresses": "Addresses", + "account.tab.community": "Community", + "account.logout": "Log out", + "account.signin_register": "Sign in or register", + "account.guest.name": "Guest account", + "account.guest.email_placeholder": "Sign in to load your account", + "account.loading": "Loading your account", + "account.empty_state": "Sign in to load your profile and account summary.", + "account.greeting": "Hi, {name} 👋", + "account.role": "{role} role", + "account.verified": "Verified account", + "account.orders.empty": "No orders yet.", + "account.orders.loading": "Loading your orders…", + "account.orders.sign_in": "Sign in to view your order history.", + "account.orders.view_details": "View details", + "account.addresses.loading": "Loading your saved addresses…", + "account.addresses.sign_in": "Sign in to manage billing and shipping addresses.", + "account.addresses.save": "Save address", + "account.addresses.save_error": "The address could not be saved.", + "account.guest.benefit1_eyebrow": "Your personal storefront", + "account.guest.benefit1_title": "Bring your account experience together", + "account.guest.benefit2_eyebrow": "Private order history", + "account.guest.benefit2_title": "Track every purchase in one place", + "account.guest.benefit3_eyebrow": "Faster checkout", + "account.guest.benefit3_title": "Save billing and shipping details", + "account.guest.benefit4_eyebrow": "Community and marketplace", + "account.guest.benefit4_title": "Unlock the tools assigned to your role", + "account.guest.cta.login": "Log in", + "account.guest.cta.register": "Create an account", + "product.add_to_cart": "Add to cart", + "product.added": "Added ✓", + "product.buy_now": "Buy now", + "product.choose_options": "Choose available options", + "product.select_options": "Select options", + "product.grouped": "Grouped product", + "product.add_wishlist": "Add to wishlist", + "product.remove_wishlist": "Remove from wishlist", + "product.save_reading": "Save to reading list", + "product.remove_reading": "Remove from reading list", + "product.unavailable": "Product unavailable", + "product.loading": "Loading product", + "product.related_loading": "Loading related products", + "search.placeholder": "Search products, stories, people, and tags…", + "search.open": "Open search", + "search.close": "Close search", + "header.theme.toggle": "Toggle color mode", + "header.theme.light": "Switch to light mode", + "header.theme.dark": "Switch to dark mode", + "header.push.enable": "Enable push notifications", + "header.push.disable": "Disable push notifications", + "header.push.enabled": "Push notifications enabled", + "header.account": "Account", + "header.reading_list": "Reading list", + "header.wishlist": "Wishlist", + "header.cart": "Cart", + "header.sync_error": "{label} (sync error)", + "header.sync_error_detail": "{label} — {message}", + "header.menu.open": "Open menu", + "header.menu.site": "Site menu", + "header.menu.title": "Menu", + "header.menu.close": "Close menu", + "header.navigation.main": "Main navigation", + "header.navigation.mobile": "Mobile navigation", + "search.aria": "Search results", + "search.results": "Search results", + "search.loading": "Searching the site…", + "search.no_results": "No results for “{query}”", + "search.unavailable": "Search is unavailable", + "search.type.product": "Product", + "search.type.post": "Post", + "search.type.page": "Page", + "search.type.post_category": "Post category", + "search.type.post_tag": "Post tag", + "search.type.product_category": "Product category", + "search.type.product_tag": "Product tag", + "search.type.product_brand": "Product brand", + "search.type.author": "Author", + "search.type.community_post": "Community post", + "search.type.community_author": "Community member", + "search.type.community_tag": "Community tag", + "search.group.catalog": "Catalog", + "search.group.editorial": "Editorial", + "search.group.community": "Community", + "search.group.pages": "Pages", + "nav.home": "Home", + "nav.main_aria": "Main navigation", + "nav.mobile_aria": "Mobile navigation", + "nav.site_aria": "Site menu", + "nav.open_menu": "Open menu", + "nav.close_menu": "Close menu", + "nav.account": "Account", + "nav.cart": "Cart", + "nav.wishlist": "Wishlist", + "nav.reading_list": "Reading list", + "nav.toggle_dark": "Toggle dark mode", + "nav.select_currency": "Select currency", + "nav.select_language": "Select language", + "nav.close_newsletter": "Close newsletter signup", + "nav.close_quickview": "Close quick view", + "nav.breadcrumb": "Breadcrumb", + "image.close": "Close image viewer", + "image.next": "Next image", + "image.prev": "Previous image", + "image.viewer_aria": "Image viewer", + "cookie.title": "Cookie consent", + "cookie.settings": "Cookie settings", + "cookie.manage": "Manage cookie preferences", + "wishlist.loading": "Loading your saved products", + "wishlist.empty": "No listings yet.", + "reading_list.loading": "Loading your saved articles", + "reading_list.empty": "No articles yet.", + "community.share": "Share a new post", + "community.write": "Write a new article", + "community.list_product": "List a new product", + "community.profile.sections": "Profile sections", + "community.copy_link": "Copy link", + "loading.page": "Loading page", + "loading.content": "Loading content", + "loading.post": "Loading post", + "loading.product": "Loading product", + "loading.community_post": "Loading community post", + "loading.community_feed": "Loading community feed", + "loading.community_profile": "Loading community profile", + "error.page_unavailable": "Page unavailable", + "error.post_unavailable": "Post unavailable", + "error.product_unavailable": "Product unavailable", + "error.community_post_unavailable": "Community post unavailable", + "error.author_unavailable": "Author unavailable", + "error.archive_unavailable": "Archive unavailable", + "error.content_unavailable": "Content unavailable", + "error.like_failed": "The like could not be updated.", + "error.review_failed": "The review could not be submitted.", + "error.reply_failed": "The reply could not be submitted.", + "error.article_failed": "The article could not be published.", + "error.post_failed": "The post could not be published.", + "error.product_listing_failed": "The product could not be listed.", + "error.account_load": "The account could not be loaded", + "notification.dismiss": "Dismiss notification", + "notification.push.enable_error": "Couldn't enable push notifications.", + "notification.push.disable_error": "Couldn't disable push notifications.", + "newsletter.join": "Join the mailing list", + "newsletter.email": "Email", + "newsletter.email_placeholder": "you@example.com", + "newsletter.subscribe": "Subscribe", + "newsletter.subscribing": "Subscribing…", + "newsletter.subscribed": "Thank you. You are subscribed.", + "newsletter.signup_error": "The newsletter signup could not be saved.", + "newsletter.unsubscribe": "Unsubscribe", + "newsletter.consent": "Please accept the privacy note to continue.", + "newsletter.email_invalid": "Please enter a valid email address.", + "footer.newsletter.title": "Get product drops and offers first", + "footer.newsletter.description": "Subscribe for curated picks, launch alerts and monthly updates. No spam, unsubscribe anytime.", + "footer.newsletter.privacy": "I agree to receive marketing emails and accept the privacy policy.", + "footer.assistant.title": "AI shopping assistant — ask about sizing, orders, or recommendations", + "footer.assistant.tab": "AI shopping assistant", + "footer.spotify.tab": "Spotify player", + "footer.assistant_spotify.aria": "Assistant / Spotify", + "footer.radio.title": "Superfunky Radio", + "footer.radio.description": "Instrumental jazz-hop for browsing — swap for any track, album, or podcast link.", + "footer.nav.expand": "Expand {label}", + "footer.nav.collapse": "Collapse {label}", + "cookie.banner.accept_all": "Accept all", + "cookie.banner.description": "{providerName} uses cookies for the proper functioning of our website, as well as for analytics and advertising purposes. Learn more in our", + "cookie.banner.policy_link": "Cookies Policy", + "cookie.category.functional": "Functional", + "cookie.category.functional_desc": "Always on", + "cookie.category.functional_title": "Required for core features — cart, wishlist, and remembering this choice.", + "cookie.category.marketing": "Marketing", + "cookie.category.marketing_desc": "Ads", + "cookie.category.tracking": "Tracking", + "cookie.category.tracking_desc": "Analytics", + "cookie.category.performance": "Performance", + "cookie.category.performance_desc": "Layout", + "cookie.item.delete": "Delete", + "cookie.item.delete_aria": "Delete {name}", + "cookie.item.expires_in": "Expires in {lifetime}", + "cookie.item.required": "Required", + "cookie.manager.accept": "Accept", + "cookie.manager.close": "Close", + "cookie.manager.decline": "Decline", + "cookie.manager.nothing_to_show": "Nothing left to show.", + "cookie.manager.save": "Save", + "cookie.manager.subtitle": "Choose which optional cookies we may use.", + "cookie.manager.tab_cookies": "Cookies list", + "cookie.manager.tab_preferences": "Preferences", + "filters.all_authors": "All authors", + "filters.all_brands": "All brands", + "filters.all_categories": "All categories", + "filters.all_tags": "All tags", + "filters.aria_label": "{title} filters", + "filters.author_aria": "Filter by author", + "filters.brand_aria": "Filter by brand", + "filters.browse": "Browse", + "filters.category_aria": "Filter by category", + "filters.clear": "Clear filters", + "filters.default_empty_social": "No posts yet — be the first to share something here.", + "filters.default_title_posts": "Latest posts", + "filters.default_title_products": "Products", + "filters.default_title_social": "Community feed", + "filters.end_reached": "You've reached the end.", + "filters.interested_in": "Interested in", + "filters.layout_compact": "Compact grid (profile-style)", + "filters.layout_grid3": "3 columns", + "filters.layout_grid4": "4 columns", + "filters.layout_label": "Layout", + "filters.layout_list": "List", + "filters.layout_masonry": "Masonry", + "filters.load_mode_infinite": "Infinite scroll", + "filters.load_mode_pages": "Pages", + "filters.load_more": "Load more", + "filters.loading_more": "Loading more…", + "filters.next": "Next →", + "filters.no_posts_match": "No posts match these filters.", + "filters.no_products_match": "No products match these filters.", + "filters.pagination_aria": "{title} pagination", + "filters.prev": "← Prev", + "filters.rating_3": "3+ stars", + "filters.rating_4": "4+ stars", + "filters.rating_45": "4.5+ stars", + "filters.rating_any": "Any rating", + "filters.rating_aria": "Filter by minimum rating", + "filters.search_feed": "Search feed", + "filters.search_posts": "Search posts", + "filters.search_products": "Search products", + "filters.showing_label": "Showing", + "filters.showing_suffix": "of {total}", + "filters.sort_default_order": "Default order", + "filters.sort_featured": "Featured", + "filters.sort_feed_aria": "Sort feed", + "filters.sort_name": "Name", + "filters.sort_newest": "Newest", + "filters.sort_oldest": "Oldest", + "filters.sort_popular": "Most liked", + "filters.sort_posts_aria": "Sort posts", + "filters.sort_price_asc": "Price: low to high", + "filters.sort_price_desc": "Price: high to low", + "filters.sort_products_aria": "Sort products", + "filters.sort_rating": "Highest rated", + "filters.sort_title": "Title", + "filters.tag_aria": "Filter by tag", + "image.show_label": "Show {label}", + "newsletter.default_body": "Join our insider list for early access, private offers, and curated stories from the Superfunky world.", + "newsletter.default_title": "Be the first to know when the next favorite drops.", + "newsletter.eyebrow": "Stay in the loop", + "newsletter.image_placeholder_body": "Drop in a full-bleed product or editorial image here.", + "newsletter.image_placeholder_title": "Image placeholder", + "newsletter.maybe_later": "Maybe later", + "newsletter.subscribed_body": "Thanks for subscribing — expect a first look at our next drop and exclusive updates soon.", + "newsletter.trust.easy_unsubscribe": "Easy unsubscribe", + "newsletter.trust.no_spam": "No spam", + "newsletter.trust.privacy": "Privacy respected", + "order_success.download.downloading": "Downloading…", + "order_success.download.empty": "This order does not include any downloadable files.", + "order_success.download.error": "File download failed. Please try again or contact support.", + "order_success.download.loading": "Loading secure download links…", + "order_success.download.never": "Never", + "product.cta.gallery_thumbnails_aria": "{name} gallery thumbnails", + "product.cta.learn_more": "Learn more", + "product.cta.quick_view": "Quick view", + "product.cta.quick_view_aria": "Quick view — {name}", + "product.cta.show_photo_aria": "Show photo {index}", + "product.cta.view_product": "View product", + "product.cta.view_product_aria": "View {name}", + "product.cta.view_products": "View products", + "product.image_alt": "Product image", + "product.image_alt_indexed": "Product image {index}", + "product.status.available": "Available", + "product.status.new": "New", + "product.status.promotion": "Promotion", + "product.status.promotion_percent": "{percent}% promotion", + "product.status.sold_out": "Sold out", + "product.variation_unavailable.description": "Choose an in-stock option combination.", + "product.variation_unavailable.title": "Variation unavailable", + "reading_list.browse_blog": "Browse the blog", + "reading_list.cap_error": "Your reading-list limit could not be loaded: {error}", + "reading_list.cap_suffix": "of {cap}", + "reading_list.category_fallback": "Journal", + "reading_list.count": "{count} saved {item}", + "reading_list.empty_hint": "Use the bookmark button on any article in the blog to save it for later reading.", + "reading_list.item_plural": "articles", + "reading_list.item_singular": "article", + "reading_list.load_error": "Your saved articles could not be loaded: {message}", + "reading_list.mark_read": "Mark as read", + "reading_list.read": "Read", + "reading_list.reading_time": "{minutes} min read", + "reading_list.sign_in_suffix": "to sync your reading list across devices.", + "reading_list.sync_error": "Your reading list could not be synced: {error}", + "reading_list.sync_local": "persisted locally in this browser", + "reading_list.sync_synced": "synced to your account", + "reading_list.unread_title": "Unread", + "share.label": "Share", + "share.link_copied": "Link copied!", + "share.on.facebook": "Share on Facebook", + "share.on.linkedin": "Share on LinkedIn", + "share.on.telegram": "Share on Telegram", + "share.on.tiktok": "Share on TikTok", + "share.on.whatsapp": "Share on WhatsApp", + "share.on.x": "Share on X", + "share.via_email": "Share via email", + "wishlist.browse_shop": "Browse the shop", + "wishlist.cap_error": "Your wishlist limit could not be loaded: {error}", + "wishlist.cap_suffix": "of {cap}", + "wishlist.clear_unavailable": "Clear unavailable products", + "wishlist.count": "{count} saved {item}", + "wishlist.empty_hint": "Tap the heart icon on any product card to save it here for later — it stays saved across visits.", + "wishlist.item_plural": "products", + "wishlist.item_singular": "product", + "wishlist.load_error": "Your saved products could not be loaded: {message}", + "wishlist.sign_in_suffix": "to sync your wishlist across devices.", + "wishlist.sync_error": "Your wishlist could not be synced: {error}", + "wishlist.sync_local": "persisted locally in this browser", + "wishlist.sync_synced": "synced to your account", + "wishlist.unavailable_message": "These saved products are no longer available in the current catalog.", + "account.tab.downloads": "Downloads", + "account.avatar.saving": "Saving avatar…", + "account.avatar.change": "Change avatar", + "account.avatar.add": "Add avatar", + "account.avatar.remove": "Remove avatar", + "account.avatar.max_size": "Max file size 690KB", + "account.email.verification_required": "Email verification required", + "account.email.verification_optional": "Email verification optional", + "account.newsletter.subscribed": "Subscribed to newsletter", + "account.newsletter.not_subscribed": "Not subscribed to newsletter", + "account.stat.orders_placed": "Orders placed", + "account.stat.saved_addresses": "Saved addresses", + "account.stat.publishing_role": "Publishing role", + "account.role.member": "Member", + "account.guest.benefit1_description": "Sign in to see your verified profile and private customer tools.", + "account.guest.benefit1_item1": "Review your profile and account status", + "account.guest.benefit1_item2": "See orders and saved delivery details together", + "account.guest.benefit1_item3": "Discover publishing tools enabled for your role", + "account.guest.benefit2_description": "Your order history is private. Sign in to review real order statuses, totals, products, and variation details.", + "account.guest.benefit2_item1": "See current fulfilment status", + "account.guest.benefit2_item2": "Review line items and variations", + "account.guest.benefit2_item3": "Keep past purchases available for reference", + "account.guest.benefit3_description": "Create an account to securely manage your customer profile and checkout addresses.", + "account.guest.benefit3_item1": "Edit billing and shipping separately", + "account.guest.benefit3_item2": "Reuse accurate customer details", + "account.guest.benefit3_item3": "Keep address data private to your account", + "account.guest.benefit4_description": "Sign in to manage your public profile and access Creator, Collaborator, or administrator publishing actions when permitted.", + "account.guest.benefit4_item1": "Control public profile visibility", + "account.guest.benefit4_item2": "Publish community posts when eligible", + "account.guest.benefit4_item3": "List products and write articles when eligible", + "account.guest.benefit5_eyebrow": "Secure digital library", + "account.guest.benefit5_title": "Keep purchased files available", + "account.guest.benefit5_description": "Sign in to access the downloads available to your account.", + "account.guest.benefit5_item1": "Use signed download links", + "account.guest.benefit5_item2": "Review expiry and remaining limits", + "account.guest.benefit5_item3": "Keep purchases tied to your account", + "community.profile.unavailable": "Community profile unavailable", + "community.profile.private_badge": "Private", + "community.role.collaborator": "Collaborator", + "community.role.creator": "Creator", + "community.profile.own_badge": "That's you", + "community.follow.following": "Following", + "community.follow.requested": "Requested", + "community.follow.cta": "Follow", + "community.stat.posts": "Posts", + "community.stat.articles": "Articles", + "community.stat.followers": "Followers", + "community.stat.following": "Following", + "community.stat.listings": "Listings", + "community.feed.title": "Community feed", + "community.title": "Community", + "community.authors": "Community authors", + "community.tab.posts": "Posts ({count})", + "community.tab.shop": "Shop ({count})", + "community.tab.articles": "Articles ({count})", + "community.tab.followers": "Followers ({count})", + "community.tab.following": "Following ({count})", + "community.profile.private": "This profile is private", + "community.feed.following_title": "Posts from followed profiles", + "community.feed.empty_following": "No posts from followed profiles yet.", + "community.shop.title": "Your shop", + "community.shop.manage": "Manage your listings", + "community.remove": "Remove", + "community.shop.empty": "No listings yet.", + "community.articles.title": "Your articles", + "community.articles.manage": "Manage your articles", + "community.articles.empty": "No articles yet.", + "community.posts.empty": "No posts yet.", + "community.media.read_error": "The selected media could not be read.", + "community.translation.search_error": "Translation search failed.", + "community.post.updated": "Post updated", + "community.post.published": "Post published", + "community.changes_live": "Your changes are now live.", + "community.post.live": "Your community post is now live.", + "community.modal.close": "Close", + "community.post.edit_title": "Edit community post", + "community.post.create_title": "Share a new post", + "community.media.add_more": "Add more media", + "community.media.choose": "Choose images or MP4 videos", + "community.field.title": "Title", + "community.post.title_placeholder": "Give your post a clear title", + "community.field.description": "Description", + "community.post.description_placeholder": "Add context, details, or a story", + "community.field.tags": "Tags", + "community.saving": "Saving…", + "community.publishing": "Publishing…", + "community.save_changes": "Save changes", + "community.post.submit": "Post", + "community.article.updated": "Article updated", + "community.article.published": "Article published", + "community.article.live": "It now appears in the site journal.", + "community.article.delete_confirm": "Permanently delete this article? This action cannot be undone.", + "community.article.deleted": "Article deleted", + "community.article.deleted_body": "The article has been removed.", + "community.article.delete_error": "The article could not be deleted.", + "community.article.edit_title": "Edit article", + "community.article.create_title": "Write a new article", + "community.article.edit_description": "Update the details below — changes publish immediately to the site journal.", + "community.article.create_description": "Collaborator accounts can publish this article directly to the site journal.", + "community.field.slug": "Slug", + "community.field.excerpt": "Excerpt", + "community.field.body": "Body", + "community.publish": "Publish", + "community.deleting": "Deleting…", + "community.delete": "Delete", + "community.product.image_read_error": "An image could not be read.", + "community.product.file_read_error": "A file could not be read.", + "community.product.updated": "Product updated", + "community.product.published": "Product listed", + "community.product.live": "It now appears in your shop and the community marketplace.", + "community.product.edit_title": "Edit product", + "community.product.create_title": "List a new product", + "community.product.edit_description": "Update the details below — changes publish immediately to your marketplace shop.", + "community.product.create_description": "Collaborator accounts can publish this product directly to their marketplace shop.", + "community.product.media.add_more": "Add more product images", + "community.product.media.choose": "Choose product images", + "community.product.field.name": "Product name", + "community.product.field.brand": "Brand", + "community.product.field.subtitle": "Subtitle", + "community.product.field.type": "Product type", + "community.product.field.external_url": "External product URL", + "community.product.field.button_text": "Button text", + "community.product.field.sku": "SKU", + "community.product.field.stock": "Stock quantity", + "community.product.field.category": "Category", + "community.product.submit": "List product" +} diff --git a/assets/storefront-ui-strings/ja.json b/assets/storefront-ui-strings/ja.json new file mode 100644 index 0000000..80ea73d --- /dev/null +++ b/assets/storefront-ui-strings/ja.json @@ -0,0 +1,677 @@ +{ + "cart.title": "カート", + "cart.aria": "ショッピングカート", + "cart.close": "カートを閉じる", + "cart.empty.heading": "カートは空です", + "cart.empty.body": "まだ商品が追加されていません。", + "cart.empty.body_alt": "ショップから商品を追加すると、ここに表示されます。", + "cart.you_might_like": "こちらもおすすめ", + "cart.subtotal": "小計", + "cart.shipping_notice": "送料と税金は購入手続き時に計算されます。", + "cart.view_cart": "カートを見る", + "cart.checkout": "購入手続きへ", + "cart.continue_shopping": "買い物を続ける", + "cart.add": "カートに追加", + "cart.added": "追加済み ✓", + "cart.remove": "削除", + "cart.ready": "購入手続きに進みますか?", + "cart.item_singular": "点", + "cart.item_plural": "点", + "cart.continue_checkout": "購入手続きに進む", + "cart.shipping": "送料", + "cart.tax": "税金", + "cart.digital_delivery": "デジタル配信", + "cart.free": "無料", + "cart.shipping_destination": "配送先のプレビュー", + "cart.shipping_destination_aria": "配送先をプレビュー", + "cart.empty.cart_title": "カートは空です", + "checkout.title": "購入手続き", + "checkout.progress_aria": "購入手続きの進行状況", + "checkout.step.cart": "カート", + "checkout.step.checkout": "購入手続き", + "checkout.step.confirmation": "確認", + "checkout.billing.title": "請求先情報", + "checkout.field.first_name": "名", + "checkout.field.last_name": "姓", + "checkout.field.company": "会社名", + "checkout.field.country": "国/地域", + "checkout.field.address1": "住所", + "checkout.field.address1_helper": "番地・町名", + "checkout.field.address2": "建物名・部屋番号など", + "checkout.field.optional": "任意", + "checkout.field.city": "市区町村", + "checkout.field.state": "都道府県", + "checkout.field.postcode": "郵便番号", + "checkout.field.phone": "電話番号", + "checkout.field.email": "メールアドレス", + "checkout.field.phone_error": "有効な電話番号を入力してください", + "checkout.field.email_error": "有効なメールアドレスを入力してください", + "checkout.field.country_placeholder": "国を選択…", + "checkout.digital_notice": "デジタル配信です。お支払い後、ウェブサイトからすぐに受け取れます。配送先住所や配送方法の指定は不要です。", + "checkout.account.title": "アカウント", + "checkout.account.create": "アカウントを作成しますか?", + "checkout.field.username": "ユーザー名", + "checkout.field.password": "パスワード", + "checkout.marketing_consent": "新商品、キャンペーン、再入荷のお知らせをメールで受け取る。", + "checkout.guest_notice": "購入手続き中に、メールアドレスを使用してアカウントが作成されます。", + "checkout.delivery.title": "配送", + "checkout.ship_to_different": "別の住所に配送しますか?", + "checkout.shipping_method": "配送方法", + "checkout.shipping_loading": "利用可能な配送方法を読み込み中…", + "checkout.subtotal": "小計", + "checkout.discount": "割引", + "checkout.shipping": "送料", + "checkout.tax": "税金", + "checkout.free": "無料", + "checkout.free_shipping_nudge": "で通常配送が無料になります — カートに戻る", + "checkout.back_to_cart": "カートに戻る", + "checkout.order_notes.title": "注文メモ", + "checkout.order_notes.label": "ご注文に関するメモ", + "checkout.order_notes.helper": "任意 — 例:配送時のご要望", + "checkout.coupon.title": "クーポンをお持ちですか?", + "checkout.coupon.label": "クーポンコード", + "checkout.coupon.apply": "コードを適用", + "checkout.coupon.applying": "適用中…", + "checkout.coupon.applied": "クーポン「{code}」を適用しました", + "checkout.coupon.remove": "削除", + "checkout.coupon.error": "クーポンを適用できませんでした", + "checkout.payment.title": "お支払い方法", + "checkout.payment.online": "オンライン決済", + "checkout.payment.online_desc": "カードおよびStripeがサポートするその他のオンライン決済方法。", + "checkout.payment.blik": "BLIK", + "checkout.payment.blik_desc": "Stripeを通じて6桁のBLIKコードを使用し、PLNですぐに支払います。", + "checkout.payment.blik_label": "BLIKコード", + "checkout.payment.blik_error": "BLIKコードは6桁で入力してください", + "checkout.payment.blik_expired": "コードの有効期限が切れました。新しい認証時間を取得するには、もう一度注文してください。", + "checkout.payment.bacs": "銀行振込", + "checkout.payment.bacs_desc": "注文後、ショップの振込案内に従って銀行からお支払いください。", + "checkout.payment.instructions": "お支払い方法のご案内", + "checkout.payment.cod": "代金引換", + "checkout.payment.cod_desc": "商品到着時に現金でお支払いください。", + "checkout.payment.cheque": "銀行小切手", + "checkout.payment.cheque_desc": "小切手を郵送してください。決済完了後に商品を発送します。", + "checkout.payment.unavailable_digital": "デジタル注文では利用できません", + "checkout.payment.unavailable": "現在利用できません", + "checkout.payment.ssl": "取引はSSL暗号化で保護されています", + "checkout.payment.unavailable_backend": "このバックエンドでは現在、暗号資産決済を利用できません。", + "checkout.payment.method_unavailable": "選択したお支払い方法は現在利用できません。", + "checkout.summary.title": "注文内容の確認", + "checkout.cta": "注文を確定する", + "checkout.terms": "ウェブサイトの利用規約を読み、同意します", + "checkout.privacy": "プライバシーポリシーに記載された個人データの取り扱いに同意します", + "checkout.terms_link": "利用規約", + "checkout.privacy_link": "プライバシーポリシー", + "checkout.error.billing_required": "注文を確定する前に、必須の請求先情報を入力してください。", + "checkout.error.method_unavailable": "選択したお支払い方法は利用できません。", + "order_success.breadcrumb": "注文確定", + "order_success.section": "注文確認", + "order_success.section_digital": "デジタル注文の確認", + "order_success.heading": "注文が完了しました", + "order_success.thank_you": "{name}、ありがとうございます。ご注文 #{number} が確定しました。領収書をメールでお送りしました。", + "order_success.thank_you_digital": "{name}、ありがとうございます。デジタル注文 #{number} が確定しました。以下からダウンロードできます。領収書もメールでお送りしました。", + "order_success.customer_fallback": "お客様", + "order_success.empty.heading": "完了した注文が見つかりません", + "order_success.empty.message": "このページには、このブラウザセッションで最後に完了した注文が表示されます。", + "order_success.refreshing": "注文情報を更新中", + "order_success.refresh_error": "最新情報を取得できませんでした。購入手続き時に保存された注文内容を表示しています。", + "order_success.order_label": "注文 #{number}", + "order_success.quantity": "数量:{quantity}", + "order_success.shipping_to": "配送先", + "order_success.delivery": "配送", + "order_success.details.order": "注文詳細", + "order_success.details.delivery_payment": "配送とお支払い", + "order_success.receipt": "領収書", + "order_success.currency": "通貨", + "order_success.account_login_error": "注文は作成されましたが、アカウントの自動設定が完了しませんでした:{error} ログインして「注文履歴」を確認し、注文が見つからない場合はサポートにお問い合わせください。", + "order_success.delivery_note": "ご注文はショップの処理フローに入りました。", + "order_success.native_note": "最新のお支払い状況と注文ステータスは、標準の注文ページでご確認ください。", + "order_success.payment.cod": "代金引換", + "order_success.payment.cheque": "小切手払い", + "order_success.payment.bacs": "銀行振込", + "order_success.payment.crypto": "暗号資産決済", + "order_success.payment.blik": "BLIK", + "order_success.payment.card": "カード決済", + "order_success.row.subtotal": "小計", + "order_success.row.discount": "割引", + "order_success.row.discount_with_codes": "割引({codes})", + "order_success.row.shipping": "送料", + "order_success.row.tax": "税金", + "order_success.row.free": "無料", + "order_success.row.digital_delivery": "デジタル配信", + "order_success.row.total": "お支払い合計", + "order_success.cta.shopping": "買い物を続ける", + "order_success.cta.native": "標準の注文ページを開く", + "order_success.cta.track": "注文を追跡", + "order_success.cta.orders": "注文履歴を見る", + "order_success.cta.private": "非公開の注文ページを開く", + "order_success.cta.pdf": "領収書をPDFで保存", + "order_success.cta.pdf_hint": "ブラウザーの印刷画面を開き、この領収書をPDFとして保存できます。", + "order_success.cta.help": "注文について問い合わせる", + "order_success.promo": "いつもご利用いただきありがとうございます。次回のご注文が15%オフになります。", + "order_success.support": "ご注文についてご不明な点がありますか?", + "order_success.contact": "お問い合わせ", + "order_success.downloads": "ダウンロード", + "order_success.downloads_note": "ファイルはアカウントの「ダウンロード」タブからいつでも入手できます。このページを保存する必要はありません。", + "order_success.download.available": "利用可能", + "order_success.download.unavailable": "利用不可", + "order_success.download.expires": "有効期限:", + "order_success.download.remaining": "残りダウンロード回数:", + "order_success.download.unlimited": "無制限", + "order_success.download.cta": "ダウンロード", + "order_success.support.trouble": "ダウンロードできない場合", + "order_status.pending": "入金待ち", + "order_status.processing": "処理中", + "order_status.on-hold": "保留中", + "order_status.completed": "完了", + "order_status.cancelled": "キャンセル済み", + "order_status.failed": "失敗", + "order_status.refunded": "返金済み", + "order_details.private_title": "非公開の注文", + "order_details.heading": "注文 #{number}", + "order_details.guest_expires": "このゲスト用非公開リンクの有効期限は{date}です。", + "order_details.loading": "注文を読み込み中", + "order_details.unavailable": "注文が見つからないか、利用できない、または24時間有効のゲストリンクが期限切れです。", + "order_details.back_account": "アカウントに戻る", + "auth.title.login": "おかえりなさい", + "auth.title.register": "アカウントを作成", + "auth.title.forgot": "パスワードをお忘れですか?", + "auth.breadcrumb.login": "ログイン", + "auth.breadcrumb.register": "新規登録", + "auth.breadcrumb.forgot": "パスワードを忘れた場合", + "auth.desc.login": "ログインすると、注文の追跡、ウィッシュリストの管理、スムーズな購入手続きができます。", + "auth.desc.register": "Superfunkyに登録して、あなた向けのおすすめやスムーズな購入手続きをご利用ください。", + "auth.desc.forgot": "アカウントに再度アクセスするための安全なリンクをメールでお送りします。", + "auth.tab.login": "ログイン", + "auth.tab.register": "新規登録", + "auth.tab.forgot": "パスワードを忘れた場合", + "auth.password_updated": "パスワードを更新しました。新しいパスワードでログインしてください。", + "auth.or_continue": "または次の方法で続ける", + "auth.continue_with": "{provider}で続ける", + "auth.brand_tagline": "サイトアカウントと連携した、モダンなストア体験。", + "auth.login.username": "ユーザー名またはメールアドレス", + "auth.login.password": "パスワード", + "auth.login.remember": "ログイン状態を保持する", + "auth.login.forgot_link": "パスワードをお忘れですか?", + "auth.login.error": "ログインできませんでした。", + "auth.login.cta": "ログイン", + "auth.login.cta_loading": "ログイン中…", + "auth.register.first_name": "名", + "auth.register.last_name": "姓", + "auth.register.username": "ユーザー名", + "auth.register.username_placeholder": "例:funk_rider.99", + "auth.register.username_helper": "コミュニティプロフィールのURLに使用されます — 英字、数字、アンダースコア、ハイフン、ドット", + "auth.register.email": "メールアドレス", + "auth.register.password": "パスワード", + "auth.register.password_helper": "8文字以上", + "auth.register.confirm": "パスワード(確認)", + "auth.register.newsletter": "新商品、キャンペーン、再入荷に関するメールを不定期で受け取る。いつでも配信を停止できます。", + "auth.register.error": "アカウントを作成できませんでした。", + "auth.register.cta": "アカウントを作成", + "auth.register.cta_loading": "アカウントを作成中…", + "auth.register.success.heading": "アカウントを作成しました。", + "auth.register.success.body": "メールアドレスとパスワードでログインできます。", + "auth.register.success.cta": "ログインへ進む", + "auth.forgot.field": "ユーザー名またはメールアドレス", + "auth.forgot.error": "リセット用メールを送信できませんでした。", + "auth.forgot.cta": "リセット用リンクを送信", + "auth.forgot.cta_loading": "送信中…", + "auth.forgot.success": "{identity}のアカウントが存在する場合は、リセット用リンクを送信しました。", + "auth.reset.breadcrumb": "パスワードをリセット", + "auth.reset.error.mismatch": "パスワードが一致しません。", + "auth.reset.error.failed": "パスワードをリセットできませんでした。", + "auth.oauth.error": "プロバイダー経由でログインできませんでした。", + "auth.oauth.callback_error": "プロバイダーからのコールバックが不完全か、無効です。", + "validation.passwords_mismatch": "パスワードが一致しません", + "validation.password.min_length": "8文字以上で入力してください。", + "validation.password.uppercase": "大文字を1文字以上含めてください。", + "validation.password.lowercase": "小文字を1文字以上含めてください。", + "validation.password.number": "数字を1文字以上含めてください。", + "validation.password.special": "特殊文字を1文字以上含めてください。", + "validation.required": "{label}は必須です。", + "validation.min_length": "{label}は{min}文字以上で入力してください。", + "validation.max_length": "{label}は{max}文字以内で入力してください。", + "validation.email": "{label}に有効なメールアドレスを入力してください。", + "validation.phone": "{label}に有効な電話番号を入力してください。", + "validation.username_chars": "{label}に使用できるのは、英字、数字、アンダースコア、ハイフン、ドットのみです。", + "account.tab.dashboard": "ダッシュボード", + "account.tab.orders": "注文履歴", + "account.tab.addresses": "住所", + "account.tab.community": "コミュニティ", + "account.logout": "ログアウト", + "account.signin_register": "ログインまたは新規登録", + "account.guest.name": "ゲストアカウント", + "account.guest.email_placeholder": "ログインしてアカウントを読み込む", + "account.loading": "アカウントを読み込み中", + "account.empty_state": "ログインすると、プロフィールとアカウント概要を表示できます。", + "account.greeting": "こんにちは、{name}さん 👋", + "account.role": "ロール:{role}", + "account.verified": "認証済みアカウント", + "account.orders.empty": "注文履歴はまだありません。", + "account.orders.loading": "注文履歴を読み込み中…", + "account.orders.sign_in": "注文履歴を表示するにはログインしてください。", + "account.orders.view_details": "詳細を見る", + "account.addresses.loading": "保存済みの住所を読み込み中…", + "account.addresses.sign_in": "請求先と配送先の住所を管理するにはログインしてください。", + "account.addresses.save": "住所を保存", + "account.addresses.save_error": "住所を保存できませんでした。", + "account.guest.benefit1_eyebrow": "あなた専用のストア", + "account.guest.benefit1_title": "アカウント体験をひとつに", + "account.guest.benefit2_eyebrow": "非公開の注文履歴", + "account.guest.benefit2_title": "すべての購入を一か所で追跡", + "account.guest.benefit3_eyebrow": "スムーズな購入手続き", + "account.guest.benefit3_title": "請求先と配送先情報を保存", + "account.guest.benefit4_eyebrow": "コミュニティとマーケットプレイス", + "account.guest.benefit4_title": "ロールに割り当てられたツールを利用", + "account.guest.cta.login": "ログイン", + "account.guest.cta.register": "アカウントを作成", + "product.add_to_cart": "カートに追加", + "product.added": "追加済み ✓", + "product.buy_now": "今すぐ購入", + "product.choose_options": "利用可能なオプションを選ぶ", + "product.select_options": "オプションを選択", + "product.grouped": "グループ商品", + "product.add_wishlist": "ウィッシュリストに追加", + "product.remove_wishlist": "ウィッシュリストから削除", + "product.save_reading": "リーディングリストに保存", + "product.remove_reading": "リーディングリストから削除", + "product.unavailable": "商品を利用できません", + "product.loading": "商品を読み込み中", + "product.related_loading": "関連商品を読み込み中", + "search.placeholder": "商品、ストーリー、ユーザー、タグを検索…", + "search.open": "検索を開く", + "search.close": "検索を閉じる", + "header.theme.toggle": "カラーモードを切り替える", + "header.theme.light": "ライトモードに切り替える", + "header.theme.dark": "ダークモードに切り替える", + "header.push.enable": "プッシュ通知を有効にする", + "header.push.disable": "プッシュ通知を無効にする", + "header.push.enabled": "プッシュ通知は有効です", + "header.account": "アカウント", + "header.reading_list": "リーディングリスト", + "header.wishlist": "ウィッシュリスト", + "header.cart": "カート", + "header.sync_error": "{label}(同期エラー)", + "header.sync_error_detail": "{label} — {message}", + "header.menu.open": "メニューを開く", + "header.menu.site": "サイトメニュー", + "header.menu.title": "メニュー", + "header.menu.close": "メニューを閉じる", + "header.navigation.main": "メインナビゲーション", + "header.navigation.mobile": "モバイルナビゲーション", + "search.aria": "検索結果", + "search.results": "検索結果", + "search.loading": "サイトを検索中…", + "search.no_results": "「{query}」の検索結果はありません", + "search.unavailable": "検索を利用できません", + "search.type.product": "商品", + "search.type.post": "投稿", + "search.type.page": "ページ", + "search.type.post_category": "投稿カテゴリー", + "search.type.post_tag": "投稿タグ", + "search.type.product_category": "商品カテゴリー", + "search.type.product_tag": "商品タグ", + "search.type.product_brand": "商品ブランド", + "search.type.author": "投稿者", + "search.type.community_post": "コミュニティ投稿", + "search.type.community_author": "コミュニティメンバー", + "search.type.community_tag": "コミュニティタグ", + "search.group.catalog": "カタログ", + "search.group.editorial": "記事", + "search.group.community": "コミュニティ", + "search.group.pages": "ページ", + "nav.home": "ホーム", + "nav.main_aria": "メインナビゲーション", + "nav.mobile_aria": "モバイルナビゲーション", + "nav.site_aria": "サイトメニュー", + "nav.open_menu": "メニューを開く", + "nav.close_menu": "メニューを閉じる", + "nav.account": "アカウント", + "nav.cart": "カート", + "nav.wishlist": "ウィッシュリスト", + "nav.reading_list": "リーディングリスト", + "nav.toggle_dark": "ダークモードを切り替える", + "nav.select_currency": "通貨を選択", + "nav.select_language": "言語を選択", + "nav.close_newsletter": "ニュースレター登録を閉じる", + "nav.close_quickview": "クイックビューを閉じる", + "nav.breadcrumb": "パンくずリスト", + "image.close": "画像ビューアーを閉じる", + "image.next": "次の画像", + "image.prev": "前の画像", + "image.viewer_aria": "画像ビューアー", + "cookie.title": "Cookieの同意", + "cookie.settings": "Cookie設定", + "cookie.manage": "Cookieの設定を管理", + "wishlist.loading": "保存済みの商品を読み込み中", + "wishlist.empty": "商品はまだありません。", + "reading_list.loading": "保存済みの記事を読み込み中", + "reading_list.empty": "記事はまだありません。", + "community.share": "新しい投稿をシェア", + "community.write": "新しい記事を書く", + "community.list_product": "新しい商品を出品", + "community.profile.sections": "プロフィールセクション", + "community.copy_link": "リンクをコピー", + "loading.page": "ページを読み込み中", + "loading.content": "コンテンツを読み込み中", + "loading.post": "投稿を読み込み中", + "loading.product": "商品を読み込み中", + "loading.community_post": "コミュニティ投稿を読み込み中", + "loading.community_feed": "コミュニティフィードを読み込み中", + "loading.community_profile": "コミュニティプロフィールを読み込み中", + "error.page_unavailable": "ページを利用できません", + "error.post_unavailable": "投稿を利用できません", + "error.product_unavailable": "商品を利用できません", + "error.community_post_unavailable": "コミュニティ投稿を利用できません", + "error.author_unavailable": "投稿者情報を利用できません", + "error.archive_unavailable": "アーカイブを利用できません", + "error.content_unavailable": "コンテンツを利用できません", + "error.like_failed": "「いいね」を更新できませんでした。", + "error.review_failed": "レビューを送信できませんでした。", + "error.reply_failed": "返信を送信できませんでした。", + "error.article_failed": "記事を公開できませんでした。", + "error.post_failed": "投稿を公開できませんでした。", + "error.product_listing_failed": "商品を出品できませんでした。", + "error.account_load": "アカウントを読み込めませんでした", + "notification.dismiss": "通知を閉じる", + "notification.push.enable_error": "プッシュ通知を有効にできませんでした。", + "notification.push.disable_error": "プッシュ通知を無効にできませんでした。", + "newsletter.join": "メーリングリストに登録", + "newsletter.email": "メールアドレス", + "newsletter.email_placeholder": "you@example.com", + "newsletter.subscribe": "登録", + "newsletter.subscribing": "登録中…", + "newsletter.subscribed": "ありがとうございます。購読登録が完了しました。", + "newsletter.signup_error": "ニュースレターに登録できませんでした。", + "newsletter.unsubscribe": "登録解除", + "newsletter.consent": "続行するには、プライバシーに関する注意事項に同意してください。", + "newsletter.email_invalid": "有効なメールアドレスを入力してください。", + "footer.newsletter.title": "新商品やオファーをいち早くお届け", + "footer.newsletter.description": "厳選アイテム、発売情報、毎月の最新情報をお届けします。迷惑メールは送信せず、いつでも解除できます。", + "footer.newsletter.privacy": "マーケティングメールの受信とプライバシーポリシーに同意します。", + "footer.assistant.title": "AIショッピングアシスタント — サイズ、注文、おすすめについてご相談ください", + "footer.assistant.tab": "AIショッピングアシスタント", + "footer.spotify.tab": "Spotifyプレーヤー", + "footer.assistant_spotify.aria": "アシスタント / Spotify", + "footer.radio.title": "Superfunky Radio", + "footer.radio.description": "ブラウジングに合うインストゥルメンタル・ジャズホップ。お好きな曲、アルバム、ポッドキャストに変更できます。", + "footer.nav.expand": "{label}を展開", + "footer.nav.collapse": "{label}を折りたたむ", + "cookie.banner.accept_all": "すべて同意する", + "cookie.banner.description": "{providerName}は、ウェブサイトを正しく機能させるため、また分析や広告の目的でCookieを使用しています。詳しくは", + "cookie.banner.policy_link": "Cookieポリシーをご覧ください", + "cookie.category.functional": "機能性", + "cookie.category.functional_desc": "常にオン", + "cookie.category.functional_title": "カート、ウィッシュリスト、この選択の記憶など、主要な機能に必要です。", + "cookie.category.marketing": "マーケティング", + "cookie.category.marketing_desc": "広告", + "cookie.category.tracking": "トラッキング", + "cookie.category.tracking_desc": "アクセス解析", + "cookie.category.performance": "パフォーマンス", + "cookie.category.performance_desc": "レイアウト", + "cookie.item.delete": "削除", + "cookie.item.delete_aria": "{name}を削除", + "cookie.item.expires_in": "{lifetime}後に期限切れ", + "cookie.item.required": "必須", + "cookie.manager.accept": "同意する", + "cookie.manager.close": "閉じる", + "cookie.manager.decline": "拒否する", + "cookie.manager.nothing_to_show": "表示するものはこれ以上ありません。", + "cookie.manager.save": "保存", + "cookie.manager.subtitle": "使用を許可する任意のCookieを選択してください。", + "cookie.manager.tab_cookies": "Cookie一覧", + "cookie.manager.tab_preferences": "設定", + "filters.all_authors": "すべての著者", + "filters.all_brands": "すべてのブランド", + "filters.all_categories": "すべてのカテゴリー", + "filters.all_tags": "すべてのタグ", + "filters.aria_label": "{title}のフィルター", + "filters.author_aria": "著者で絞り込む", + "filters.brand_aria": "ブランドで絞り込む", + "filters.browse": "閲覧する", + "filters.category_aria": "カテゴリーで絞り込む", + "filters.clear": "フィルターをクリア", + "filters.default_empty_social": "まだ投稿はありません。最初の投稿をしてみましょう。", + "filters.default_title_posts": "最新の投稿", + "filters.default_title_products": "商品", + "filters.default_title_social": "コミュニティフィード", + "filters.end_reached": "最後まで到達しました。", + "filters.interested_in": "興味のあるカテゴリー", + "filters.layout_compact": "コンパクトグリッド(プロフィール風)", + "filters.layout_grid3": "3列", + "filters.layout_grid4": "4列", + "filters.layout_label": "レイアウト", + "filters.layout_list": "リスト", + "filters.layout_masonry": "マソンリー", + "filters.load_mode_infinite": "無限スクロール", + "filters.load_mode_pages": "ページ", + "filters.load_more": "もっと見る", + "filters.loading_more": "読み込み中…", + "filters.next": "次へ →", + "filters.no_posts_match": "これらのフィルターに一致する投稿はありません。", + "filters.no_products_match": "これらのフィルターに一致する商品はありません。", + "filters.pagination_aria": "{title}のページ送り", + "filters.prev": "← 前へ", + "filters.rating_3": "★3以上", + "filters.rating_4": "★4以上", + "filters.rating_45": "★4.5以上", + "filters.rating_any": "評価を問わない", + "filters.rating_aria": "最低評価で絞り込む", + "filters.search_feed": "フィードを検索", + "filters.search_posts": "投稿を検索", + "filters.search_products": "商品を検索", + "filters.showing_label": "表示中", + "filters.showing_suffix": "/ {total}件", + "filters.sort_default_order": "デフォルトの並び順", + "filters.sort_featured": "おすすめ", + "filters.sort_feed_aria": "フィードを並べ替え", + "filters.sort_name": "名前", + "filters.sort_newest": "新着順", + "filters.sort_oldest": "古い順", + "filters.sort_popular": "人気順", + "filters.sort_posts_aria": "投稿を並べ替え", + "filters.sort_price_asc": "価格が安い順", + "filters.sort_price_desc": "価格が高い順", + "filters.sort_products_aria": "商品を並べ替え", + "filters.sort_rating": "評価が高い順", + "filters.sort_title": "タイトル", + "filters.tag_aria": "タグで絞り込む", + "image.show_label": "{label}を表示", + "newsletter.default_body": "インサイダーリストに登録すると、Superfunkyの世界からの先行アクセス、限定オファー、厳選ストーリーをお届けします。", + "newsletter.default_title": "次のお気に入りが登場したら、いち早くお知らせします。", + "newsletter.eyebrow": "最新情報をチェック", + "newsletter.image_placeholder_body": "ここにフルブリードの商品画像や特集画像を配置します。", + "newsletter.image_placeholder_title": "画像プレースホルダー", + "newsletter.maybe_later": "また今度", + "newsletter.subscribed_body": "ご登録ありがとうございます。次回の新作の先行情報や限定アップデートを近日中にお届けします。", + "newsletter.trust.easy_unsubscribe": "簡単に配信停止", + "newsletter.trust.no_spam": "スパムなし", + "newsletter.trust.privacy": "プライバシーを尊重", + "order_success.download.downloading": "ダウンロード中…", + "order_success.download.empty": "この注文にはダウンロード可能なファイルが含まれていません。", + "order_success.download.error": "ファイルのダウンロードに失敗しました。もう一度お試しいただくか、サポートまでお問い合わせください。", + "order_success.download.loading": "安全なダウンロードリンクを読み込み中…", + "order_success.download.never": "なし", + "product.cta.gallery_thumbnails_aria": "{name}のギャラリーサムネイル", + "product.cta.learn_more": "詳しく見る", + "product.cta.quick_view": "クイックビュー", + "product.cta.quick_view_aria": "クイックビュー — {name}", + "product.cta.show_photo_aria": "写真{index}を表示", + "product.cta.view_product": "商品を見る", + "product.cta.view_product_aria": "{name}を見る", + "product.cta.view_products": "商品一覧を見る", + "product.image_alt": "商品画像", + "product.image_alt_indexed": "商品画像{index}", + "product.status.available": "在庫あり", + "product.status.new": "新着", + "product.status.promotion": "セール", + "product.status.promotion_percent": "{percent}%オフ", + "product.status.sold_out": "売り切れ", + "product.variation_unavailable.description": "在庫のある組み合わせを選択してください。", + "product.variation_unavailable.title": "このバリエーションは選択できません", + "reading_list.browse_blog": "ブログを見る", + "reading_list.cap_error": "リーディングリストの上限を読み込めませんでした: {error}", + "reading_list.cap_suffix": "/ {cap}", + "reading_list.category_fallback": "ジャーナル", + "reading_list.count": "保存済みの{item}: {count}件", + "reading_list.empty_hint": "ブログの記事にあるブックマークボタンを使うと、後で読むために保存できます。", + "reading_list.item_plural": "記事", + "reading_list.item_singular": "記事", + "reading_list.load_error": "保存済みの記事を読み込めませんでした: {message}", + "reading_list.mark_read": "既読にする", + "reading_list.read": "既読", + "reading_list.reading_time": "読了目安 {minutes}分", + "reading_list.sign_in_suffix": "して端末間でリーディングリストを同期しましょう。", + "reading_list.sync_error": "リーディングリストを同期できませんでした: {error}", + "reading_list.sync_local": "このブラウザにローカル保存されています", + "reading_list.sync_synced": "アカウントと同期済み", + "reading_list.unread_title": "未読", + "share.label": "シェア", + "share.link_copied": "リンクをコピーしました!", + "share.on.facebook": "Facebookでシェア", + "share.on.linkedin": "LinkedInでシェア", + "share.on.telegram": "Telegramでシェア", + "share.on.tiktok": "TikTokでシェア", + "share.on.whatsapp": "WhatsAppでシェア", + "share.on.x": "Xでシェア", + "share.via_email": "メールでシェア", + "wishlist.browse_shop": "ショップを見る", + "wishlist.cap_error": "ウィッシュリストの上限を読み込めませんでした: {error}", + "wishlist.cap_suffix": "/ {cap}", + "wishlist.clear_unavailable": "利用できない商品を削除", + "wishlist.count": "保存済みの{item}: {count}件", + "wishlist.empty_hint": "商品カードのハートアイコンをタップすると、後で見返せるようにここに保存されます。次回の訪問時も保存されたままです。", + "wishlist.item_plural": "商品", + "wishlist.item_singular": "商品", + "wishlist.load_error": "保存済みの商品を読み込めませんでした: {message}", + "wishlist.sign_in_suffix": "して端末間でウィッシュリストを同期しましょう。", + "wishlist.sync_error": "ウィッシュリストを同期できませんでした: {error}", + "wishlist.sync_local": "このブラウザにローカル保存されています", + "wishlist.sync_synced": "アカウントと同期済み", + "wishlist.unavailable_message": "保存済みのこれらの商品は、現在のカタログには存在しません。", + "account.tab.downloads": "ダウンロード", + "account.avatar.saving": "アバターを保存中…", + "account.avatar.change": "アバターを変更", + "account.avatar.add": "アバターを追加", + "account.avatar.remove": "アバターを削除", + "account.avatar.max_size": "ファイルサイズの上限は690KBです", + "account.email.verification_required": "メールアドレスの確認が必要です", + "account.email.verification_optional": "メールアドレスの確認は任意です", + "account.newsletter.subscribed": "ニュースレターに登録済み", + "account.newsletter.not_subscribed": "ニュースレター未登録", + "account.stat.orders_placed": "注文件数", + "account.stat.saved_addresses": "保存済みの住所", + "account.stat.publishing_role": "公開権限", + "account.role.member": "メンバー", + "account.guest.benefit1_description": "サインインすると、認証済みプロフィールと非公開の顧客ツールを確認できます。", + "account.guest.benefit1_item1": "プロフィールとアカウントの状態を確認", + "account.guest.benefit1_item2": "注文と保存済みの配送情報をまとめて確認", + "account.guest.benefit1_item3": "自分の役割で利用できる公開ツールを確認", + "account.guest.benefit2_description": "注文履歴は非公開です。サインインすると、実際の注文状況、合計金額、商品、バリエーションの詳細を確認できます。", + "account.guest.benefit2_item1": "現在の発送状況を確認", + "account.guest.benefit2_item2": "注文明細とバリエーションを確認", + "account.guest.benefit2_item3": "過去の購入履歴をいつでも参照可能", + "account.guest.benefit3_description": "アカウントを作成すると、顧客プロフィールと購入時の住所を安全に管理できます。", + "account.guest.benefit3_item1": "請求先と配送先を個別に編集", + "account.guest.benefit3_item2": "正確な顧客情報を再利用", + "account.guest.benefit3_item3": "住所データをアカウント内で非公開に保持", + "account.guest.benefit4_description": "サインインすると公開プロフィールを管理でき、権限があればクリエイター、コラボレーター、管理者の公開機能を利用できます。", + "account.guest.benefit4_item1": "公開プロフィールの表示範囲を管理", + "account.guest.benefit4_item2": "権限があればコミュニティ投稿を公開", + "account.guest.benefit4_item3": "権限があれば商品の出品や記事の執筆が可能", + "account.guest.benefit5_eyebrow": "安全なデジタルライブラリ", + "account.guest.benefit5_title": "購入したファイルにいつでもアクセス", + "account.guest.benefit5_description": "サインインすると、アカウントで利用できるダウンロードにアクセスできます。", + "account.guest.benefit5_item1": "署名付きのダウンロードリンクを利用", + "account.guest.benefit5_item2": "有効期限と残りの回数制限を確認", + "account.guest.benefit5_item3": "購入情報をアカウントに紐づけて保持", + "community.profile.unavailable": "コミュニティプロフィールを利用できません", + "community.profile.private_badge": "非公開", + "community.role.collaborator": "コラボレーター", + "community.role.creator": "クリエイター", + "community.profile.own_badge": "あなたのプロフィールです", + "community.follow.following": "フォロー中", + "community.follow.requested": "リクエスト済み", + "community.follow.cta": "フォローする", + "community.stat.posts": "投稿", + "community.stat.articles": "記事", + "community.stat.followers": "フォロワー", + "community.stat.following": "フォロー中", + "community.stat.listings": "出品数", + "community.feed.title": "コミュニティフィード", + "community.title": "コミュニティ", + "community.authors": "コミュニティの著者", + "community.tab.posts": "投稿 ({count})", + "community.tab.shop": "ショップ ({count})", + "community.tab.articles": "記事 ({count})", + "community.tab.followers": "フォロワー ({count})", + "community.tab.following": "フォロー中 ({count})", + "community.profile.private": "このプロフィールは非公開です", + "community.feed.following_title": "フォロー中のプロフィールの投稿", + "community.feed.empty_following": "フォロー中のプロフィールの投稿はまだありません。", + "community.shop.title": "あなたのショップ", + "community.shop.manage": "出品を管理", + "community.remove": "削除", + "community.shop.empty": "出品はまだありません。", + "community.articles.title": "あなたの記事", + "community.articles.manage": "記事を管理", + "community.articles.empty": "記事はまだありません。", + "community.posts.empty": "投稿はまだありません。", + "community.media.read_error": "選択したメディアを読み込めませんでした。", + "community.translation.search_error": "翻訳の検索に失敗しました。", + "community.post.updated": "投稿を更新しました", + "community.post.published": "投稿を公開しました", + "community.changes_live": "変更内容が反映されました。", + "community.post.live": "コミュニティ投稿が公開されました。", + "community.modal.close": "閉じる", + "community.post.edit_title": "コミュニティ投稿を編集", + "community.post.create_title": "新しい投稿をシェア", + "community.media.add_more": "メディアを追加", + "community.media.choose": "画像またはMP4動画を選択", + "community.field.title": "タイトル", + "community.post.title_placeholder": "投稿にわかりやすいタイトルを付けましょう", + "community.field.description": "説明", + "community.post.description_placeholder": "背景、詳細、あるいはストーリーを追加しましょう", + "community.field.tags": "タグ", + "community.saving": "保存中…", + "community.publishing": "公開中…", + "community.save_changes": "変更を保存", + "community.post.submit": "投稿する", + "community.article.updated": "記事を更新しました", + "community.article.published": "記事を公開しました", + "community.article.live": "サイトのジャーナルに反映されました。", + "community.article.delete_confirm": "この記事を完全に削除しますか?この操作は取り消せません。", + "community.article.deleted": "記事を削除しました", + "community.article.deleted_body": "記事は削除されました。", + "community.article.delete_error": "記事を削除できませんでした。", + "community.article.edit_title": "記事を編集", + "community.article.create_title": "新しい記事を書く", + "community.article.edit_description": "以下の内容を更新してください。変更内容はサイトのジャーナルに即座に公開されます。", + "community.article.create_description": "コラボレーター権限のアカウントは、この記事をサイトのジャーナルに直接公開できます。", + "community.field.slug": "スラッグ", + "community.field.excerpt": "抜粋", + "community.field.body": "本文", + "community.publish": "公開する", + "community.deleting": "削除中…", + "community.delete": "削除", + "community.product.image_read_error": "画像を読み込めませんでした。", + "community.product.file_read_error": "ファイルを読み込めませんでした。", + "community.product.updated": "商品を更新しました", + "community.product.published": "商品を出品しました", + "community.product.live": "あなたのショップとコミュニティマーケットプレイスに反映されました。", + "community.product.edit_title": "商品を編集", + "community.product.create_title": "新しい商品を出品", + "community.product.edit_description": "以下の内容を更新してください。変更内容はマーケットプレイスのショップに即座に公開されます。", + "community.product.create_description": "コラボレーター権限のアカウントは、この商品をマーケットプレイスのショップに直接公開できます。", + "community.product.media.add_more": "商品画像を追加", + "community.product.media.choose": "商品画像を選択", + "community.product.field.name": "商品名", + "community.product.field.brand": "ブランド", + "community.product.field.subtitle": "サブタイトル", + "community.product.field.type": "商品タイプ", + "community.product.field.external_url": "外部商品URL", + "community.product.field.button_text": "ボタンのテキスト", + "community.product.field.sku": "SKU", + "community.product.field.stock": "在庫数", + "community.product.field.category": "カテゴリー", + "community.product.submit": "商品を出品する" +} diff --git a/assets/storefront-ui-strings/pl.json b/assets/storefront-ui-strings/pl.json new file mode 100644 index 0000000..706526b --- /dev/null +++ b/assets/storefront-ui-strings/pl.json @@ -0,0 +1,677 @@ +{ + "cart.title": "Twój koszyk", + "cart.aria": "Koszyk zakupowy", + "cart.close": "Zamknij koszyk", + "cart.empty.heading": "Twój koszyk jest pusty", + "cart.empty.body": "Wygląda na to, że nic jeszcze nie dodałeś.", + "cart.empty.body_alt": "Dodaj produkty ze sklepu, aby je tutaj zobaczyć.", + "cart.you_might_like": "Może ci się spodobać", + "cart.subtotal": "Suma częściowa", + "cart.shipping_notice": "Dostawa i podatki naliczane przy realizacji zamówienia.", + "cart.view_cart": "Zobacz koszyk", + "cart.checkout": "Do kasy", + "cart.continue_shopping": "Kontynuuj zakupy", + "cart.add": "Dodaj do koszyka", + "cart.added": "Dodano ✓", + "cart.remove": "Usuń", + "cart.ready": "Gotowy do realizacji?", + "cart.item_singular": "produkt", + "cart.item_plural": "produkty", + "cart.continue_checkout": "Przejdź do kasy", + "cart.shipping": "Dostawa", + "cart.tax": "Podatek", + "cart.digital_delivery": "Dostawa cyfrowa", + "cart.free": "Gratis", + "cart.shipping_destination": "Podgląd miejsca dostawy", + "cart.shipping_destination_aria": "Podgląd miejsca dostawy", + "cart.empty.cart_title": "Twój koszyk jest pusty", + "checkout.title": "Kasa", + "checkout.progress_aria": "Postęp realizacji zamówienia", + "checkout.step.cart": "Koszyk", + "checkout.step.checkout": "Kasa", + "checkout.step.confirmation": "Potwierdzenie", + "checkout.billing.title": "Dane do rozliczenia", + "checkout.field.first_name": "Imię", + "checkout.field.last_name": "Nazwisko", + "checkout.field.company": "Nazwa firmy", + "checkout.field.country": "Kraj / region", + "checkout.field.address1": "Adres", + "checkout.field.address1_helper": "Numer domu i nazwa ulicy", + "checkout.field.address2": "Mieszkanie, lokal, piętro itp.", + "checkout.field.optional": "Opcjonalnie", + "checkout.field.city": "Miasto", + "checkout.field.state": "Województwo / region", + "checkout.field.postcode": "Kod pocztowy", + "checkout.field.phone": "Telefon", + "checkout.field.email": "Adres e-mail", + "checkout.field.phone_error": "Numer telefonu jest nieprawidłowy", + "checkout.field.email_error": "Adres e-mail jest nieprawidłowy", + "checkout.field.country_placeholder": "Wybierz kraj…", + "checkout.digital_notice": "Dostawa cyfrowa. Zamówienie jest dostępne natychmiast po płatności — nie jest wymagany adres ani metoda dostawy.", + "checkout.account.title": "Konto", + "checkout.account.create": "Założyć konto?", + "checkout.field.username": "Nazwa użytkownika", + "checkout.field.password": "Hasło", + "checkout.marketing_consent": "Chcę otrzymywać e-maile o nowych produktach, ofertach i wznowieniach.", + "checkout.guest_notice": "Konto zostanie utworzone na podany adres e-mail podczas realizacji zamówienia.", + "checkout.delivery.title": "Dostawa", + "checkout.ship_to_different": "Wysłać na inny adres?", + "checkout.shipping_method": "Metoda dostawy", + "checkout.shipping_loading": "Ładowanie metod dostawy…", + "checkout.subtotal": "Suma częściowa", + "checkout.discount": "Rabat", + "checkout.shipping": "Dostawa", + "checkout.tax": "Podatek", + "checkout.free": "Gratis", + "checkout.free_shipping_nudge": "do darmowej standardowej dostawy — wróć do koszyka", + "checkout.back_to_cart": "wróć do koszyka", + "checkout.order_notes.title": "Uwagi do zamówienia", + "checkout.order_notes.label": "Uwagi do twojego zamówienia", + "checkout.order_notes.helper": "Opcjonalnie — np. instrukcje dostawy", + "checkout.coupon.title": "Masz kupon?", + "checkout.coupon.label": "Kod kuponu", + "checkout.coupon.apply": "Zastosuj kod", + "checkout.coupon.applying": "Stosowanie…", + "checkout.coupon.applied": "Kupon „{code}“ zastosowany", + "checkout.coupon.remove": "Usuń", + "checkout.coupon.error": "Nie udało się zastosować kuponu", + "checkout.payment.title": "Metoda płatności", + "checkout.payment.online": "Płatność online", + "checkout.payment.online_desc": "Karty i inne metody płatności obsługiwane przez Stripe.", + "checkout.payment.blik": "BLIK", + "checkout.payment.blik_desc": "Zapłać natychmiast w PLN 6-cyfrowym kodem BLIK przez Stripe.", + "checkout.payment.blik_label": "Kod BLIK", + "checkout.payment.blik_error": "Kod BLIK musi mieć 6 cyfr", + "checkout.payment.blik_expired": "Kod wygasł — złóż zamówienie ponownie, aby uzyskać nowe okno autoryzacji.", + "checkout.payment.bacs": "Przelew bankowy", + "checkout.payment.bacs_desc": "Zapłać przelewem — szczegóły płatności otrzymasz po złożeniu zamówienia.", + "checkout.payment.instructions": "Instrukcje płatności", + "checkout.payment.cod": "Płatność przy odbiorze", + "checkout.payment.cod_desc": "Zapłać gotówką przy dostawie.", + "checkout.payment.cheque": "Czek bankowy", + "checkout.payment.cheque_desc": "Wyślij czek — zamówienie wysyłamy po jego zaksięgowaniu.", + "checkout.payment.unavailable_digital": "Niedostępne dla zamówień cyfrowych", + "checkout.payment.unavailable": "Niedostępne w tej chwili", + "checkout.payment.ssl": "Transakcje zabezpieczone szyfrowaniem SSL", + "checkout.payment.unavailable_backend": "Płatności krypto są obecnie niedostępne.", + "checkout.payment.method_unavailable": "Wybrana metoda płatności jest niedostępna.", + "checkout.summary.title": "Podsumowanie zamówienia", + "checkout.cta": "Złóż zamówienie", + "checkout.terms": "Zapoznałem/am się z regulaminem serwisu i akceptuję go", + "checkout.privacy": "Wyrażam zgodę na przetwarzanie moich danych osobowych zgodnie z polityką prywatności", + "checkout.terms_link": "regulaminem", + "checkout.privacy_link": "polityką prywatności", + "checkout.error.billing_required": "Uzupełnij wymagane dane rozliczeniowe przed złożeniem zamówienia.", + "checkout.error.method_unavailable": "Wybrana metoda płatności jest niedostępna.", + "order_success.breadcrumb": "Zamówienie potwierdzone", + "order_success.section": "Potwierdzenie zamówienia", + "order_success.section_digital": "Potwierdzenie zamówienia cyfrowego", + "order_success.heading": "Zamówienie złożone pomyślnie", + "order_success.thank_you": "Dziękujemy, {name} — twoje zamówienie #{number} zostało potwierdzone. Paragon został wysłany na twój adres e-mail.", + "order_success.thank_you_digital": "Dziękujemy, {name} — twoje cyfrowe zamówienie #{number} zostało potwierdzone. Pliki do pobrania są gotowe poniżej, a paragon wysłaliśmy na twój e-mail.", + "order_success.customer_fallback": "kliencie", + "order_success.empty.heading": "Nie znaleziono ukończonego zamówienia", + "order_success.empty.message": "Ta strona pokazuje ostatnie zamówienie ukończone w tej sesji przeglądarki.", + "order_success.refreshing": "Odświeżanie szczegółów zamówienia", + "order_success.refresh_error": "Nie udało się odświeżyć danych. Wyświetlamy zamówienie zapisane podczas składania.", + "order_success.order_label": "Zamówienie #{number}", + "order_success.quantity": "Ilość: {quantity}", + "order_success.shipping_to": "Wysyłka do", + "order_success.delivery": "Dostawa", + "order_success.details.order": "Szczegóły zamówienia", + "order_success.details.delivery_payment": "Dostawa i płatność", + "order_success.receipt": "Potwierdzenie", + "order_success.currency": "Waluta", + "order_success.account_login_error": "Zamówienie zostało utworzone, ale automatyczna konfiguracja konta nie została ukończona: {error} Zaloguj się i sprawdź Moje zamówienia; jeśli zamówienia brakuje, skontaktuj się z obsługą.", + "order_success.delivery_note": "Twoje zamówienie trafiło do realizacji.", + "order_success.native_note": "Przejdź do natywnej strony zamówienia, aby zobaczyć szczegóły płatności i status.", + "order_success.payment.cod": "Płatność przy odbiorze", + "order_success.payment.cheque": "Płatność czekiem", + "order_success.payment.bacs": "Przelew bankowy", + "order_success.payment.crypto": "Płatność kryptowalutą", + "order_success.payment.blik": "BLIK", + "order_success.payment.card": "Płatność kartą", + "order_success.row.subtotal": "Suma częściowa", + "order_success.row.discount": "Rabat", + "order_success.row.discount_with_codes": "Rabat ({codes})", + "order_success.row.shipping": "Dostawa", + "order_success.row.tax": "Podatek", + "order_success.row.free": "Bezpłatnie", + "order_success.row.digital_delivery": "Dostawa cyfrowa", + "order_success.row.total": "Łącznie zapłacono", + "order_success.cta.shopping": "Kontynuuj zakupy", + "order_success.cta.native": "Otwórz natywną stronę zamówienia", + "order_success.cta.track": "Śledź zamówienie", + "order_success.cta.orders": "Zobacz moje zamówienia", + "order_success.cta.private": "Otwórz prywatną stronę zamówienia", + "order_success.cta.pdf": "Zapisz rachunek jako PDF", + "order_success.cta.pdf_hint": "Otwiera okno drukowania przeglądarki, w którym możesz zapisać rachunek jako PDF.", + "order_success.cta.help": "Uzyskaj pomoc dotyczącą zamówienia", + "order_success.promo": "Dziękujemy za ponowne zakupy — oto 15% zniżki na następne zamówienie.", + "order_success.support": "Pytania dotyczące zamówienia?", + "order_success.contact": "Skontaktuj się z nami", + "order_success.downloads": "Twoje pliki do pobrania", + "order_success.downloads_note": "Pliki są dostępne w każdej chwili w zakładce Pobrane na Twoim koncie.", + "order_success.download.available": "Dostępny", + "order_success.download.unavailable": "Niedostępny", + "order_success.download.expires": "Wygasa:", + "order_success.download.remaining": "Pozostałe pobrania:", + "order_success.download.unlimited": "Nieograniczone", + "order_success.download.cta": "Pobierz", + "order_success.support.trouble": "Problem z pobraniem?", + "order_status.pending": "Oczekuje na płatność", + "order_status.processing": "W realizacji", + "order_status.on-hold": "Wstrzymane", + "order_status.completed": "Zrealizowane", + "order_status.cancelled": "Anulowane", + "order_status.failed": "Nieudane", + "order_status.refunded": "Zwrócone", + "order_details.private_title": "Prywatne zamówienie", + "order_details.heading": "Zamówienie #{number}", + "order_details.guest_expires": "Ten prywatny link dla gościa wygasa {date}.", + "order_details.loading": "Ładowanie zamówienia", + "order_details.unavailable": "Nie znaleziono zamówienia, jest ono niedostępne lub 24-godzinny link dla gościa wygasł.", + "order_details.back_account": "Wróć do konta", + "auth.title.login": "Witaj ponownie", + "auth.title.register": "Utwórz konto", + "auth.title.forgot": "Zapomniane hasło", + "auth.breadcrumb.login": "Zaloguj się", + "auth.breadcrumb.register": "Rejestracja", + "auth.breadcrumb.forgot": "Zapomniane hasło", + "auth.desc.login": "Zaloguj się, aby śledzić zamówienia, zarządzać listą życzeń i realizować zakupy szybciej.", + "auth.desc.register": "Dołącz do Superfunky i korzystaj z spersonalizowanych rekomendacji.", + "auth.desc.forgot": "Wyślemy Ci bezpieczny link do odzyskania dostępu do konta.", + "auth.tab.login": "Logowanie", + "auth.tab.register": "Rejestracja", + "auth.tab.forgot": "Zapomniane hasło", + "auth.password_updated": "Hasło zostało zaktualizowane. Zaloguj się nowym hasłem.", + "auth.or_continue": "Lub kontynuuj z", + "auth.continue_with": "Kontynuuj z {provider}", + "auth.brand_tagline": "Nowoczesne doświadczenie zakupowe połączone z Twoim kontem w serwisie.", + "auth.login.username": "Nazwa użytkownika lub e-mail", + "auth.login.password": "Hasło", + "auth.login.remember": "Zapamiętaj mnie", + "auth.login.forgot_link": "Zapomniane hasło?", + "auth.login.error": "Logowanie nie powiodło się.", + "auth.login.cta": "Zaloguj się", + "auth.login.cta_loading": "Logowanie…", + "auth.register.first_name": "Imię", + "auth.register.last_name": "Nazwisko", + "auth.register.username": "Nazwa użytkownika", + "auth.register.username_placeholder": "np. funk_rider.99", + "auth.register.username_helper": "Używana w adresie URL profilu społeczności — litery, cyfry, podkreślenia, myślniki, kropki", + "auth.register.email": "E-mail", + "auth.register.password": "Hasło", + "auth.register.password_helper": "Co najmniej 8 znaków", + "auth.register.confirm": "Potwierdź hasło", + "auth.register.newsletter": "Chcę otrzymywać okazjonalne e-maile o nowych produktach, ofertach i wznowieniach. Możesz zrezygnować w dowolnym momencie.", + "auth.register.error": "Nie udało się utworzyć konta.", + "auth.register.cta": "Utwórz konto", + "auth.register.cta_loading": "Tworzenie konta…", + "auth.register.success.heading": "Konto utworzone.", + "auth.register.success.body": "Możesz teraz zalogować się swoim adresem e-mail i hasłem.", + "auth.register.success.cta": "Przejdź do logowania", + "auth.forgot.field": "Nazwa użytkownika lub e-mail", + "auth.forgot.error": "Nie udało się wysłać e-maila z resetem hasła.", + "auth.forgot.cta": "Wyślij link resetujący", + "auth.forgot.cta_loading": "Wysyłanie…", + "auth.forgot.success": "Jeśli konto dla {identity} istnieje, link resetujący jest już w drodze.", + "auth.reset.breadcrumb": "Resetowanie hasła", + "auth.reset.error.mismatch": "Hasła nie są identyczne.", + "auth.reset.error.failed": "Nie udało się zresetować hasła.", + "auth.oauth.error": "Logowanie przez dostawcę nie powiodło się.", + "auth.oauth.callback_error": "Wywołanie zwrotne dostawcy jest niekompletne lub nieprawidłowe.", + "validation.passwords_mismatch": "Hasła nie są identyczne", + "validation.password.min_length": "Użyj co najmniej 8 znaków.", + "validation.password.uppercase": "Dodaj wielką literę.", + "validation.password.lowercase": "Dodaj małą literę.", + "validation.password.number": "Dodaj cyfrę.", + "validation.password.special": "Dodaj znak specjalny.", + "validation.required": "{label} jest wymagane.", + "validation.min_length": "{label} musi mieć co najmniej {min} znaki.", + "validation.max_length": "{label} może mieć co najwyżej {max} znaki.", + "validation.email": "{label} nie jest prawidłowym adresem e-mail.", + "validation.phone": "{label} nie jest prawidłowym numerem telefonu.", + "validation.username_chars": "{label} może zawierać tylko litery, cyfry, podkreślenia, myślniki i kropki.", + "account.tab.dashboard": "Panel", + "account.tab.orders": "Zamówienia", + "account.tab.addresses": "Adresy", + "account.tab.community": "Społeczność", + "account.logout": "Wyloguj", + "account.signin_register": "Zaloguj się lub zarejestruj", + "account.guest.name": "Konto gościa", + "account.guest.email_placeholder": "Zaloguj się, aby wczytać konto", + "account.loading": "Ładowanie konta", + "account.empty_state": "Zaloguj się, aby wczytać profil i podsumowanie konta.", + "account.greeting": "Cześć, {name} 👋", + "account.role": "Rola: {role}", + "account.verified": "Konto zweryfikowane", + "account.orders.empty": "Brak zamówień.", + "account.orders.loading": "Ładowanie zamówień…", + "account.orders.sign_in": "Zaloguj się, aby zobaczyć historię zamówień.", + "account.orders.view_details": "Zobacz szczegóły", + "account.addresses.loading": "Ładowanie zapisanych adresów…", + "account.addresses.sign_in": "Zaloguj się, aby zarządzać adresami rozliczeniowymi i wysyłki.", + "account.addresses.save": "Zapisz adres", + "account.addresses.save_error": "Nie udało się zapisać adresu.", + "account.guest.benefit1_eyebrow": "Twoje konto", + "account.guest.benefit1_title": "Zarządzaj wszystkim w jednym miejscu", + "account.guest.benefit2_eyebrow": "Historia zamówień", + "account.guest.benefit2_title": "Śledź każde zamówienie", + "account.guest.benefit3_eyebrow": "Szybsza realizacja", + "account.guest.benefit3_title": "Zapisz dane do rozliczeń i dostawy", + "account.guest.benefit4_eyebrow": "Społeczność i rynek", + "account.guest.benefit4_title": "Odblokuj narzędzia przypisane do Twojej roli", + "account.guest.cta.login": "Zaloguj się", + "account.guest.cta.register": "Utwórz konto", + "product.add_to_cart": "Dodaj do koszyka", + "product.added": "Dodano ✓", + "product.buy_now": "Kup teraz", + "product.choose_options": "Wybierz dostępne opcje", + "product.select_options": "Wybierz opcje", + "product.grouped": "Produkt grupowany", + "product.add_wishlist": "Dodaj do listy życzeń", + "product.remove_wishlist": "Usuń z listy życzeń", + "product.save_reading": "Zapisz na liście lektur", + "product.remove_reading": "Usuń z listy lektur", + "product.unavailable": "Produkt niedostępny", + "product.loading": "Ładowanie produktu", + "product.related_loading": "Ładowanie podobnych produktów", + "search.placeholder": "Szukaj produktów, artykułów, osób i tagów…", + "search.open": "Otwórz wyszukiwanie", + "search.close": "Zamknij wyszukiwanie", + "header.theme.toggle": "Przełącz tryb kolorów", + "header.theme.light": "Przełącz na jasny motyw", + "header.theme.dark": "Przełącz na ciemny motyw", + "header.push.enable": "Włącz powiadomienia push", + "header.push.disable": "Wyłącz powiadomienia push", + "header.push.enabled": "Powiadomienia push są włączone", + "header.account": "Konto", + "header.reading_list": "Czytelnia", + "header.wishlist": "Lista życzeń", + "header.cart": "Koszyk", + "header.sync_error": "{label} (błąd synchronizacji)", + "header.sync_error_detail": "{label} — {message}", + "header.menu.open": "Otwórz menu", + "header.menu.site": "Menu witryny", + "header.menu.title": "Menu", + "header.menu.close": "Zamknij menu", + "header.navigation.main": "Główna nawigacja", + "header.navigation.mobile": "Nawigacja mobilna", + "search.aria": "Wyniki wyszukiwania", + "search.results": "Wyniki wyszukiwania", + "search.loading": "Przeszukiwanie witryny…", + "search.no_results": "Brak wyników dla „{query}”", + "search.unavailable": "Wyszukiwanie jest niedostępne", + "search.type.product": "Produkt", + "search.type.post": "Wpis", + "search.type.page": "Strona", + "search.type.post_category": "Kategoria wpisów", + "search.type.post_tag": "Tag wpisów", + "search.type.product_category": "Kategoria produktów", + "search.type.product_tag": "Tag produktów", + "search.type.product_brand": "Marka produktu", + "search.type.author": "Autor", + "search.type.community_post": "Wpis społeczności", + "search.type.community_author": "Członek społeczności", + "search.type.community_tag": "Tag społeczności", + "search.group.catalog": "Katalog", + "search.group.editorial": "Artykuły", + "search.group.community": "Społeczność", + "search.group.pages": "Strony", + "nav.home": "Strona główna", + "nav.main_aria": "Główna nawigacja", + "nav.mobile_aria": "Nawigacja mobilna", + "nav.site_aria": "Menu witryny", + "nav.open_menu": "Otwórz menu", + "nav.close_menu": "Zamknij menu", + "nav.account": "Konto", + "nav.cart": "Koszyk", + "nav.wishlist": "Lista życzeń", + "nav.reading_list": "Lista lektur", + "nav.toggle_dark": "Przełącz tryb ciemny", + "nav.select_currency": "Wybierz walutę", + "nav.select_language": "Wybierz język", + "nav.close_newsletter": "Zamknij zapis do newslettera", + "nav.close_quickview": "Zamknij szybki podgląd", + "nav.breadcrumb": "Ścieżka nawigacji", + "image.close": "Zamknij przeglądarkę zdjęć", + "image.next": "Następne zdjęcie", + "image.prev": "Poprzednie zdjęcie", + "image.viewer_aria": "Przeglądarka zdjęć", + "cookie.title": "Zgoda na pliki cookie", + "cookie.settings": "Ustawienia plików cookie", + "cookie.manage": "Zarządzaj preferencjami plików cookie", + "wishlist.loading": "Ładowanie zapisanych produktów", + "wishlist.empty": "Brak pozycji.", + "reading_list.loading": "Ładowanie zapisanych artykułów", + "reading_list.empty": "Brak artykułów.", + "community.share": "Udostępnij nowy wpis", + "community.write": "Napisz nowy artykuł", + "community.list_product": "Dodaj nowy produkt", + "community.profile.sections": "Sekcje profilu", + "community.copy_link": "Kopiuj link", + "loading.page": "Ładowanie strony", + "loading.content": "Ładowanie zawartości", + "loading.post": "Ładowanie wpisu", + "loading.product": "Ładowanie produktu", + "loading.community_post": "Ładowanie wpisu społeczności", + "loading.community_feed": "Ładowanie aktualności społeczności", + "loading.community_profile": "Ładowanie profilu społeczności", + "error.page_unavailable": "Strona niedostępna", + "error.post_unavailable": "Wpis niedostępny", + "error.product_unavailable": "Produkt niedostępny", + "error.community_post_unavailable": "Wpis społeczności niedostępny", + "error.author_unavailable": "Autor niedostępny", + "error.archive_unavailable": "Archiwum niedostępne", + "error.content_unavailable": "Zawartość niedostępna", + "error.like_failed": "Nie udało się zaktualizować polubienia.", + "error.review_failed": "Nie udało się wysłać recenzji.", + "error.reply_failed": "Nie udało się wysłać odpowiedzi.", + "error.article_failed": "Nie udało się opublikować artykułu.", + "error.post_failed": "Nie udało się opublikować wpisu.", + "error.product_listing_failed": "Nie udało się dodać produktu.", + "error.account_load": "Nie udało się wczytać konta", + "notification.dismiss": "Zamknij powiadomienie", + "notification.push.enable_error": "Nie udało się włączyć powiadomień push.", + "notification.push.disable_error": "Nie udało się wyłączyć powiadomień push.", + "newsletter.join": "Dołącz do listy mailingowej", + "newsletter.email": "E-mail", + "newsletter.email_placeholder": "twoj@email.pl", + "newsletter.subscribe": "Zapisz się", + "newsletter.subscribing": "Zapisywanie…", + "newsletter.subscribed": "Dziękujemy. Subskrypcja jest aktywna.", + "newsletter.signup_error": "Nie udało się zapisać do newslettera.", + "newsletter.unsubscribe": "Wypisz się", + "newsletter.consent": "Zaakceptuj notę prywatności, aby kontynuować.", + "newsletter.email_invalid": "Wprowadź prawidłowy adres e-mail.", + "footer.newsletter.title": "Otrzymuj premiery produktów i oferty jako pierwszy", + "footer.newsletter.description": "Zapisz się po wybrane propozycje, informacje o premierach i comiesięczne aktualizacje. Bez spamu, możesz zrezygnować w każdej chwili.", + "footer.newsletter.privacy": "Zgadzam się na otrzymywanie wiadomości marketingowych i akceptuję politykę prywatności.", + "footer.assistant.title": "Asystent zakupowy AI — zapytaj o rozmiary, zamówienia lub rekomendacje", + "footer.assistant.tab": "Asystent zakupowy AI", + "footer.spotify.tab": "Odtwarzacz Spotify", + "footer.assistant_spotify.aria": "Asystent / Spotify", + "footer.radio.title": "Superfunky Radio", + "footer.radio.description": "Instrumentalny jazz-hop do przeglądania — wybierz dowolny utwór, album lub podcast.", + "footer.nav.expand": "Rozwiń {label}", + "footer.nav.collapse": "Zwiń {label}", + "cookie.banner.accept_all": "Zaakceptuj wszystkie", + "cookie.banner.description": "{providerName} korzysta z plików cookie w celu prawidłowego działania naszej witryny, a także do celów analitycznych i reklamowych. Dowiedz się więcej w naszej", + "cookie.banner.policy_link": "Polityce cookies", + "cookie.category.functional": "Funkcjonalne", + "cookie.category.functional_desc": "Zawsze aktywne", + "cookie.category.functional_title": "Wymagane dla podstawowych funkcji — koszyka, listy życzeń oraz zapamiętywania tego wyboru.", + "cookie.category.marketing": "Marketingowe", + "cookie.category.marketing_desc": "Reklamy", + "cookie.category.tracking": "Analityczne", + "cookie.category.tracking_desc": "Analityka", + "cookie.category.performance": "Wydajnościowe", + "cookie.category.performance_desc": "Układ", + "cookie.item.delete": "Usuń", + "cookie.item.delete_aria": "Usuń {name}", + "cookie.item.expires_in": "Wygasa za {lifetime}", + "cookie.item.required": "Wymagane", + "cookie.manager.accept": "Akceptuj", + "cookie.manager.close": "Zamknij", + "cookie.manager.decline": "Odrzuć", + "cookie.manager.nothing_to_show": "Nie ma już nic do wyświetlenia.", + "cookie.manager.save": "Zapisz", + "cookie.manager.subtitle": "Wybierz, z których opcjonalnych plików cookie możemy korzystać.", + "cookie.manager.tab_cookies": "Lista plików cookie", + "cookie.manager.tab_preferences": "Preferencje", + "filters.all_authors": "Wszyscy autorzy", + "filters.all_brands": "Wszystkie marki", + "filters.all_categories": "Wszystkie kategorie", + "filters.all_tags": "Wszystkie tagi", + "filters.aria_label": "Filtry: {title}", + "filters.author_aria": "Filtruj według autora", + "filters.brand_aria": "Filtruj według marki", + "filters.browse": "Przeglądaj", + "filters.category_aria": "Filtruj według kategorii", + "filters.clear": "Wyczyść filtry", + "filters.default_empty_social": "Brak postów — bądź pierwszą osobą, która się tu podzieli.", + "filters.default_title_posts": "Najnowsze posty", + "filters.default_title_products": "Produkty", + "filters.default_title_social": "Kanał społeczności", + "filters.end_reached": "To już koniec listy.", + "filters.interested_in": "Zainteresowania", + "filters.layout_compact": "Siatka kompaktowa (styl profilu)", + "filters.layout_grid3": "3 kolumny", + "filters.layout_grid4": "4 kolumny", + "filters.layout_label": "Układ", + "filters.layout_list": "Lista", + "filters.layout_masonry": "Układ murarski", + "filters.load_mode_infinite": "Nieskończone przewijanie", + "filters.load_mode_pages": "Strony", + "filters.load_more": "Wczytaj więcej", + "filters.loading_more": "Wczytywanie kolejnych…", + "filters.next": "Dalej →", + "filters.no_posts_match": "Żadne posty nie pasują do tych filtrów.", + "filters.no_products_match": "Żadne produkty nie pasują do tych filtrów.", + "filters.pagination_aria": "Paginacja: {title}", + "filters.prev": "← Wstecz", + "filters.rating_3": "3+ gwiazdki", + "filters.rating_4": "4+ gwiazdki", + "filters.rating_45": "4,5+ gwiazdki", + "filters.rating_any": "Dowolna ocena", + "filters.rating_aria": "Filtruj według minimalnej oceny", + "filters.search_feed": "Szukaj w kanale", + "filters.search_posts": "Szukaj postów", + "filters.search_products": "Szukaj produktów", + "filters.showing_label": "Wyświetlono", + "filters.showing_suffix": "z {total}", + "filters.sort_default_order": "Domyślna kolejność", + "filters.sort_featured": "Polecane", + "filters.sort_feed_aria": "Sortuj kanał", + "filters.sort_name": "Nazwa", + "filters.sort_newest": "Najnowsze", + "filters.sort_oldest": "Najstarsze", + "filters.sort_popular": "Najbardziej polubione", + "filters.sort_posts_aria": "Sortuj posty", + "filters.sort_price_asc": "Cena: od najniższej", + "filters.sort_price_desc": "Cena: od najwyższej", + "filters.sort_products_aria": "Sortuj produkty", + "filters.sort_rating": "Najwyżej oceniane", + "filters.sort_title": "Tytuł", + "filters.tag_aria": "Filtruj według tagu", + "image.show_label": "Pokaż {label}", + "newsletter.default_body": "Dołącz do naszej listy insiderów, aby zyskać wcześniejszy dostęp, ekskluzywne oferty i wyselekcjonowane historie ze świata Superfunky.", + "newsletter.default_title": "Bądź pierwszy, który dowie się o nowej ulubionej premierze.", + "newsletter.eyebrow": "Bądź na bieżąco", + "newsletter.image_placeholder_body": "Umieść tutaj pełnowymiarowe zdjęcie produktu lub materiał redakcyjny.", + "newsletter.image_placeholder_title": "Miejsce na obraz", + "newsletter.maybe_later": "Może później", + "newsletter.subscribed_body": "Dziękujemy za zapisanie się — wkrótce otrzymasz pierwszy podgląd naszej kolejnej premiery oraz ekskluzywne aktualizacje.", + "newsletter.trust.easy_unsubscribe": "Łatwa rezygnacja", + "newsletter.trust.no_spam": "Bez spamu", + "newsletter.trust.privacy": "Szanujemy prywatność", + "order_success.download.downloading": "Pobieranie…", + "order_success.download.empty": "To zamówienie nie zawiera żadnych plików do pobrania.", + "order_success.download.error": "Pobieranie pliku nie powiodło się. Spróbuj ponownie lub skontaktuj się z pomocą techniczną.", + "order_success.download.loading": "Wczytywanie bezpiecznych linków do pobrania…", + "order_success.download.never": "Nigdy", + "product.cta.gallery_thumbnails_aria": "Miniatury galerii: {name}", + "product.cta.learn_more": "Dowiedz się więcej", + "product.cta.quick_view": "Szybki podgląd", + "product.cta.quick_view_aria": "Szybki podgląd — {name}", + "product.cta.show_photo_aria": "Pokaż zdjęcie {index}", + "product.cta.view_product": "Zobacz produkt", + "product.cta.view_product_aria": "Zobacz {name}", + "product.cta.view_products": "Zobacz produkty", + "product.image_alt": "Zdjęcie produktu", + "product.image_alt_indexed": "Zdjęcie produktu {index}", + "product.status.available": "Dostępny", + "product.status.new": "Nowość", + "product.status.promotion": "Promocja", + "product.status.promotion_percent": "Promocja {percent}%", + "product.status.sold_out": "Wyprzedane", + "product.variation_unavailable.description": "Wybierz kombinację opcji dostępną w magazynie.", + "product.variation_unavailable.title": "Wariant niedostępny", + "reading_list.browse_blog": "Przeglądaj bloga", + "reading_list.cap_error": "Nie udało się wczytać limitu listy lektur: {error}", + "reading_list.cap_suffix": "z {cap}", + "reading_list.category_fallback": "Dziennik", + "reading_list.count": "Zapisano {count} {item}", + "reading_list.empty_hint": "Użyj przycisku zakładki przy dowolnym artykule na blogu, aby zapisać go do przeczytania później.", + "reading_list.item_plural": "artykuły", + "reading_list.item_singular": "artykuł", + "reading_list.load_error": "Nie udało się wczytać zapisanych artykułów: {message}", + "reading_list.mark_read": "Oznacz jako przeczytane", + "reading_list.read": "Przeczytane", + "reading_list.reading_time": "{minutes} min czytania", + "reading_list.sign_in_suffix": ", aby zsynchronizować listę lektur na wszystkich urządzeniach.", + "reading_list.sync_error": "Nie udało się zsynchronizować listy lektur: {error}", + "reading_list.sync_local": "zapisywana lokalnie w tej przeglądarce", + "reading_list.sync_synced": "zsynchronizowana z Twoim kontem", + "reading_list.unread_title": "Nieprzeczytane", + "share.label": "Udostępnij", + "share.link_copied": "Link skopiowany!", + "share.on.facebook": "Udostępnij na Facebooku", + "share.on.linkedin": "Udostępnij na LinkedIn", + "share.on.telegram": "Udostępnij na Telegramie", + "share.on.tiktok": "Udostępnij na TikToku", + "share.on.whatsapp": "Udostępnij na WhatsApp", + "share.on.x": "Udostępnij na X", + "share.via_email": "Udostępnij przez e-mail", + "wishlist.browse_shop": "Przeglądaj sklep", + "wishlist.cap_error": "Nie udało się wczytać limitu listy życzeń: {error}", + "wishlist.cap_suffix": "z {cap}", + "wishlist.clear_unavailable": "Usuń niedostępne produkty", + "wishlist.count": "Zapisano {count} {item}", + "wishlist.empty_hint": "Dotknij ikony serca na dowolnej karcie produktu, aby zapisać go tutaj na później — pozostanie zapisany przy kolejnych wizytach.", + "wishlist.item_plural": "produkty", + "wishlist.item_singular": "produkt", + "wishlist.load_error": "Nie udało się wczytać zapisanych produktów: {message}", + "wishlist.sign_in_suffix": ", aby zsynchronizować listę życzeń na wszystkich urządzeniach.", + "wishlist.sync_error": "Nie udało się zsynchronizować listy życzeń: {error}", + "wishlist.sync_local": "zapisywana lokalnie w tej przeglądarce", + "wishlist.sync_synced": "zsynchronizowana z Twoim kontem", + "wishlist.unavailable_message": "Te zapisane produkty nie są już dostępne w bieżącym katalogu.", + "account.tab.downloads": "Pliki do pobrania", + "account.avatar.saving": "Zapisywanie awatara…", + "account.avatar.change": "Zmień awatar", + "account.avatar.add": "Dodaj awatar", + "account.avatar.remove": "Usuń awatar", + "account.avatar.max_size": "Maksymalny rozmiar pliku to 690 KB", + "account.email.verification_required": "Wymagana weryfikacja adresu e-mail", + "account.email.verification_optional": "Weryfikacja adresu e-mail jest opcjonalna", + "account.newsletter.subscribed": "Zapisano do newslettera", + "account.newsletter.not_subscribed": "Brak zapisu do newslettera", + "account.stat.orders_placed": "Złożone zamówienia", + "account.stat.saved_addresses": "Zapisane adresy", + "account.stat.publishing_role": "Rola publikacyjna", + "account.role.member": "Członek", + "account.guest.benefit1_description": "Zaloguj się, aby zobaczyć swój zweryfikowany profil oraz prywatne narzędzia klienta.", + "account.guest.benefit1_item1": "Przeglądaj swój profil i status konta", + "account.guest.benefit1_item2": "Zobacz zamówienia i zapisane dane dostawy w jednym miejscu", + "account.guest.benefit1_item3": "Odkryj narzędzia publikacyjne dostępne dla Twojej roli", + "account.guest.benefit2_description": "Historia zamówień jest prywatna. Zaloguj się, aby zobaczyć rzeczywiste statusy zamówień, kwoty, produkty i szczegóły wariantów.", + "account.guest.benefit2_item1": "Sprawdź aktualny status realizacji", + "account.guest.benefit2_item2": "Przeglądaj pozycje zamówienia i warianty", + "account.guest.benefit2_item3": "Miej dostęp do wcześniejszych zakupów do wglądu", + "account.guest.benefit3_description": "Utwórz konto, aby bezpiecznie zarządzać profilem klienta oraz adresami do zamówień.", + "account.guest.benefit3_item1": "Edytuj osobno adres rozliczeniowy i dostawy", + "account.guest.benefit3_item2": "Ponownie wykorzystuj dokładne dane klienta", + "account.guest.benefit3_item3": "Zachowaj dane adresowe prywatne w ramach swojego konta", + "account.guest.benefit4_description": "Zaloguj się, aby zarządzać swoim publicznym profilem oraz — jeśli masz uprawnienia — korzystać z funkcji publikacyjnych Twórcy, Współtwórcy lub administratora.", + "account.guest.benefit4_item1": "Kontroluj widoczność publicznego profilu", + "account.guest.benefit4_item2": "Publikuj posty w społeczności, jeśli masz uprawnienia", + "account.guest.benefit4_item3": "Wystawiaj produkty i pisz artykuły, jeśli masz uprawnienia", + "account.guest.benefit5_eyebrow": "Bezpieczna biblioteka cyfrowa", + "account.guest.benefit5_title": "Miej stały dostęp do zakupionych plików", + "account.guest.benefit5_description": "Zaloguj się, aby uzyskać dostęp do plików do pobrania dostępnych na Twoim koncie.", + "account.guest.benefit5_item1": "Korzystaj z podpisanych linków do pobrania", + "account.guest.benefit5_item2": "Sprawdzaj terminy ważności i pozostałe limity", + "account.guest.benefit5_item3": "Miej zakupy powiązane ze swoim kontem", + "community.profile.unavailable": "Profil społeczności niedostępny", + "community.profile.private_badge": "Prywatny", + "community.role.collaborator": "Współtwórca", + "community.role.creator": "Twórca", + "community.profile.own_badge": "To Ty", + "community.follow.following": "Obserwujesz", + "community.follow.requested": "Wysłano prośbę", + "community.follow.cta": "Obserwuj", + "community.stat.posts": "Posty", + "community.stat.articles": "Artykuły", + "community.stat.followers": "Obserwujący", + "community.stat.following": "Obserwowani", + "community.stat.listings": "Oferty", + "community.feed.title": "Kanał społeczności", + "community.title": "Społeczność", + "community.authors": "Autorzy społeczności", + "community.tab.posts": "Posty ({count})", + "community.tab.shop": "Sklep ({count})", + "community.tab.articles": "Artykuły ({count})", + "community.tab.followers": "Obserwujący ({count})", + "community.tab.following": "Obserwowani ({count})", + "community.profile.private": "Ten profil jest prywatny", + "community.feed.following_title": "Posty od obserwowanych profili", + "community.feed.empty_following": "Brak jeszcze postów od obserwowanych profili.", + "community.shop.title": "Twój sklep", + "community.shop.manage": "Zarządzaj swoimi ofertami", + "community.remove": "Usuń", + "community.shop.empty": "Brak jeszcze ofert.", + "community.articles.title": "Twoje artykuły", + "community.articles.manage": "Zarządzaj swoimi artykułami", + "community.articles.empty": "Brak jeszcze artykułów.", + "community.posts.empty": "Brak jeszcze postów.", + "community.media.read_error": "Nie udało się odczytać wybranych plików multimedialnych.", + "community.translation.search_error": "Wyszukiwanie tłumaczeń nie powiodło się.", + "community.post.updated": "Post zaktualizowany", + "community.post.published": "Post opublikowany", + "community.changes_live": "Twoje zmiany są już widoczne.", + "community.post.live": "Twój post w społeczności jest już widoczny.", + "community.modal.close": "Zamknij", + "community.post.edit_title": "Edytuj post w społeczności", + "community.post.create_title": "Udostępnij nowy post", + "community.media.add_more": "Dodaj więcej plików multimedialnych", + "community.media.choose": "Wybierz zdjęcia lub filmy MP4", + "community.field.title": "Tytuł", + "community.post.title_placeholder": "Nadaj postowi jasny tytuł", + "community.field.description": "Opis", + "community.post.description_placeholder": "Dodaj kontekst, szczegóły lub opowieść", + "community.field.tags": "Tagi", + "community.saving": "Zapisywanie…", + "community.publishing": "Publikowanie…", + "community.save_changes": "Zapisz zmiany", + "community.post.submit": "Opublikuj", + "community.article.updated": "Artykuł zaktualizowany", + "community.article.published": "Artykuł opublikowany", + "community.article.live": "Jest już widoczny w dzienniku witryny.", + "community.article.delete_confirm": "Trwale usunąć ten artykuł? Tej czynności nie można cofnąć.", + "community.article.deleted": "Artykuł usunięty", + "community.article.deleted_body": "Artykuł został usunięty.", + "community.article.delete_error": "Nie udało się usunąć artykułu.", + "community.article.edit_title": "Edytuj artykuł", + "community.article.create_title": "Napisz nowy artykuł", + "community.article.edit_description": "Zaktualizuj poniższe dane — zmiany zostaną od razu opublikowane w dzienniku witryny.", + "community.article.create_description": "Konta Współtwórców mogą publikować ten artykuł bezpośrednio w dzienniku witryny.", + "community.field.slug": "Slug", + "community.field.excerpt": "Zajawka", + "community.field.body": "Treść", + "community.publish": "Opublikuj", + "community.deleting": "Usuwanie…", + "community.delete": "Usuń", + "community.product.image_read_error": "Nie udało się odczytać obrazu.", + "community.product.file_read_error": "Nie udało się odczytać pliku.", + "community.product.updated": "Produkt zaktualizowany", + "community.product.published": "Produkt wystawiony", + "community.product.live": "Jest już widoczny w Twoim sklepie oraz na rynku społeczności.", + "community.product.edit_title": "Edytuj produkt", + "community.product.create_title": "Wystaw nowy produkt", + "community.product.edit_description": "Zaktualizuj poniższe dane — zmiany zostaną od razu opublikowane w Twoim sklepie na rynku.", + "community.product.create_description": "Konta Współtwórców mogą publikować ten produkt bezpośrednio w swoim sklepie na rynku.", + "community.product.media.add_more": "Dodaj więcej zdjęć produktu", + "community.product.media.choose": "Wybierz zdjęcia produktu", + "community.product.field.name": "Nazwa produktu", + "community.product.field.brand": "Marka", + "community.product.field.subtitle": "Podtytuł", + "community.product.field.type": "Typ produktu", + "community.product.field.external_url": "Zewnętrzny adres URL produktu", + "community.product.field.button_text": "Tekst przycisku", + "community.product.field.sku": "SKU", + "community.product.field.stock": "Stan magazynowy", + "community.product.field.category": "Kategoria", + "community.product.submit": "Wystaw produkt" +} diff --git a/assets/submission-form-block.js b/assets/submission-form-block.js new file mode 100644 index 0000000..cc6315d --- /dev/null +++ b/assets/submission-form-block.js @@ -0,0 +1,42 @@ +( function ( blocks, blockEditor, components, element, i18n ) { + const el = element.createElement; + const TextControl = components.TextControl; + const ToggleControl = components.ToggleControl; + const InspectorControls = blockEditor.InspectorControls; + const PanelBody = components.PanelBody; + + blocks.registerBlockType( 'funkycommerce/submission-form', { + title: i18n.__( 'FunkyCommerce Form', 'funkycommerce-headless' ), + icon: 'feedback', + category: 'widgets', + description: i18n.__( 'A backend-stored contact form with optional protected attachments.', 'funkycommerce-headless' ), + edit: function ( props ) { + const attributes = props.attributes; + const set = props.setAttributes; + return el( + element.Fragment, + null, + el( + InspectorControls, + null, + el( + PanelBody, + { title: i18n.__( 'Form settings', 'funkycommerce-headless' ) }, + el( TextControl, { label: i18n.__( 'Form ID', 'funkycommerce-headless' ), value: attributes.formId, onChange: function ( value ) { set( { formId: value } ); } } ), + el( TextControl, { label: i18n.__( 'Form name', 'funkycommerce-headless' ), value: attributes.formName, onChange: function ( value ) { set( { formName: value } ); } } ), + el( ToggleControl, { label: i18n.__( 'Allow attachments', 'funkycommerce-headless' ), checked: attributes.uploads, onChange: function ( value ) { set( { uploads: value } ); } } ) + ) + ), + el( + 'div', + { className: props.className }, + el( TextControl, { label: i18n.__( 'Heading', 'funkycommerce-headless' ), value: attributes.title, onChange: function ( value ) { set( { title: value } ); } } ), + el( 'p', null, i18n.__( 'The storefront renders Name, Email, Message, and optional attachment fields.', 'funkycommerce-headless' ) ) + ) + ); + }, + save: function () { + return null; + }, + } ); +} )( window.wp.blocks, window.wp.blockEditor, window.wp.components, window.wp.element, window.wp.i18n ); diff --git a/assets/video-hero-block.js b/assets/video-hero-block.js new file mode 100644 index 0000000..1c305b9 --- /dev/null +++ b/assets/video-hero-block.js @@ -0,0 +1,70 @@ +( function ( blocks, blockEditor, components, element, i18n, serverSideRender ) { + var el = element.createElement; + var InspectorControls = blockEditor.InspectorControls; + var TextControl = components.TextControl; + var TextareaControl = components.TextareaControl; + var RangeControl = components.RangeControl; + var ToggleControl = components.ToggleControl; + var SelectControl = components.SelectControl; + + blocks.registerBlockType( 'funkycommerce/video-hero', { + apiVersion: 3, + title: i18n.__( 'Video hero/banner', 'funkycommerce-headless' ), + icon: 'format-video', + category: 'design', + attributes: { + src: { type: 'string', default: '' }, + variant: { type: 'string', default: 'fullbleed' }, + poster: { type: 'string', default: '' }, + kicker: { type: 'string', default: '' }, + title: { type: 'string', default: 'Video hero' }, + description: { type: 'string', default: '' }, + primaryCtaLabel: { type: 'string', default: '' }, + primaryCtaHref: { type: 'string', default: '' }, + secondaryCtaLabel: { type: 'string', default: '' }, + secondaryCtaHref: { type: 'string', default: '' }, + align: { type: 'string', default: 'left' }, + height: { type: 'string', default: '70vh' }, + overlayOpacity: { type: 'number', default: 55 }, + autoplay: { type: 'boolean', default: true }, + loop: { type: 'boolean', default: true }, + muted: { type: 'boolean', default: true } + }, + edit: function ( props ) { + var a = props.attributes; + function field( name ) { + return function ( value ) { + var update = {}; + update[ name ] = value; + props.setAttributes( update ); + }; + } + return el( element.Fragment, {}, + el( InspectorControls, {}, + el( components.PanelBody, { title: i18n.__( 'Media', 'funkycommerce-headless' ), initialOpen: true }, + el( TextControl, { label: i18n.__( 'Video URL (MP4, WebM, YouTube, or Vimeo)', 'funkycommerce-headless' ), value: a.src, onChange: field( 'src' ) } ), + el( TextControl, { label: i18n.__( 'Poster/fallback image URL', 'funkycommerce-headless' ), value: a.poster, onChange: field( 'poster' ) } ), + el( ToggleControl, { label: i18n.__( 'Autoplay', 'funkycommerce-headless' ), checked: a.autoplay, onChange: field( 'autoplay' ) } ), + el( ToggleControl, { label: i18n.__( 'Loop', 'funkycommerce-headless' ), checked: a.loop, onChange: field( 'loop' ) } ), + el( ToggleControl, { label: i18n.__( 'Muted', 'funkycommerce-headless' ), checked: a.muted, onChange: field( 'muted' ) } ) + ), + el( components.PanelBody, { title: i18n.__( 'Content and appearance', 'funkycommerce-headless' ) }, + el( SelectControl, { label: i18n.__( 'View', 'funkycommerce-headless' ), value: a.variant, options: [ { label: 'Full bleed', value: 'fullbleed' }, { label: 'Atmospheric glow', value: 'glow' }, { label: 'Split media/content', value: 'split' }, { label: 'Minimal editorial', value: 'minimal' }, { label: 'Compact strip', value: 'strip' } ], onChange: field( 'variant' ) } ), + el( TextControl, { label: i18n.__( 'Kicker', 'funkycommerce-headless' ), value: a.kicker, onChange: field( 'kicker' ) } ), + el( TextControl, { label: i18n.__( 'Heading', 'funkycommerce-headless' ), value: a.title, onChange: field( 'title' ) } ), + el( TextareaControl, { label: i18n.__( 'Description', 'funkycommerce-headless' ), value: a.description, onChange: field( 'description' ) } ), + el( TextControl, { label: i18n.__( 'Primary button label', 'funkycommerce-headless' ), value: a.primaryCtaLabel, onChange: field( 'primaryCtaLabel' ) } ), + el( TextControl, { label: i18n.__( 'Primary button URL', 'funkycommerce-headless' ), value: a.primaryCtaHref, onChange: field( 'primaryCtaHref' ) } ), + el( TextControl, { label: i18n.__( 'Secondary button label', 'funkycommerce-headless' ), value: a.secondaryCtaLabel, onChange: field( 'secondaryCtaLabel' ) } ), + el( TextControl, { label: i18n.__( 'Secondary button URL', 'funkycommerce-headless' ), value: a.secondaryCtaHref, onChange: field( 'secondaryCtaHref' ) } ), + el( SelectControl, { label: i18n.__( 'Text alignment', 'funkycommerce-headless' ), value: a.align, options: [ { label: 'Left', value: 'left' }, { label: 'Center', value: 'center' }, { label: 'Right', value: 'right' } ], onChange: field( 'align' ) } ), + el( TextControl, { label: i18n.__( 'Minimum height', 'funkycommerce-headless' ), value: a.height, onChange: field( 'height' ) } ), + el( RangeControl, { label: i18n.__( 'Overlay opacity', 'funkycommerce-headless' ), value: a.overlayOpacity, min: 0, max: 90, onChange: field( 'overlayOpacity' ) } ) + ) + ), + el( serverSideRender, { block: 'funkycommerce/video-hero', attributes: a } ) + ); + }, + save: function () { return null; } + } ); +} )( window.wp.blocks, window.wp.blockEditor, window.wp.components, window.wp.element, window.wp.i18n, window.wp.serverSideRender ); diff --git a/build/lint-php.mjs b/build/lint-php.mjs new file mode 100644 index 0000000..aab11e9 --- /dev/null +++ b/build/lint-php.mjs @@ -0,0 +1,54 @@ +#!/usr/bin/env node +/** + * Lightweight PHP syntax linter for the theme's own PHP files, using the + * php-parser npm package as a stand-in for `php -l` (not available in every + * environment this theme is built in). + */ +import { readFileSync, readdirSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; +import { Engine } from "php-parser"; + +const themeRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url))); + +function phpFiles(directory, prefix = "") { + return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { + const relativePath = path.join(prefix, entry.name); + const absolutePath = path.join(directory, entry.name); + if (entry.isDirectory()) { + return phpFiles(absolutePath, relativePath); + } + return entry.isFile() && entry.name.endsWith(".php") ? [relativePath] : []; + }); +} + +const FILES_TO_LINT = [ + "functions.php", + ...phpFiles(path.join(themeRoot, "inc"), "inc"), +]; + +const parser = new Engine({ + parser: { extractDoc: true, suppressErrors: false }, + ast: { withPositions: true }, +}); + +let hasErrors = false; + +for (const relativePath of FILES_TO_LINT) { + const filePath = path.join(themeRoot, relativePath); + try { + const source = readFileSync(filePath, "utf8"); + parser.parseCode(source, filePath); + console.log(`[lint:php] OK ${relativePath}`); + } catch (error) { + hasErrors = true; + console.error(`[lint:php] FAIL ${relativePath}`); + console.error(` ${error.message}`); + } +} + +if (hasErrors) { + process.exitCode = 1; +} else { + console.log(`[lint:php] All ${FILES_TO_LINT.length} file(s) parsed without syntax errors.`); +} diff --git a/build/sync-template-assets.mjs b/build/sync-template-assets.mjs new file mode 100644 index 0000000..a554596 --- /dev/null +++ b/build/sync-template-assets.mjs @@ -0,0 +1,80 @@ +#!/usr/bin/env node +/** + * Splices the compiled assets/dist/theme.css and assets/dist/theme.js files + * into parts/header.html and parts/footer.html between marker comments, so + * the native WordPress shell renders fully styled/interactive even before + * (or without) the optional inc/frontend-theme.php enqueue is wired up by + * the parent theme's functions.php. + * + * Run automatically as part of `npm run build` (see package.json), after + * build:css and build:js have produced assets/dist/theme.css and + * assets/dist/theme.js. + */ +import { readFileSync, writeFileSync, existsSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +const themeRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url))); + +const CSS_PATH = path.join(themeRoot, "assets/dist/theme.css"); +const JS_PATH = path.join(themeRoot, "assets/dist/theme.js"); +const HEADER_PATH = path.join(themeRoot, "parts/header.html"); +const FOOTER_PATH = path.join(themeRoot, "parts/footer.html"); + +/** + * @param {string} filePath + * @param {string} startMarker + * @param {string} endMarker + * @param {string} injected + */ +function spliceBetweenMarkers(filePath, startMarker, endMarker, injected) { + const original = readFileSync(filePath, "utf8"); + const startIndex = original.indexOf(startMarker); + const endIndex = original.indexOf(endMarker); + + if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) { + throw new Error( + `Could not find markers "${startMarker}" / "${endMarker}" in ${filePath}` + ); + } + + const before = original.slice(0, startIndex + startMarker.length); + const after = original.slice(endIndex); + const next = `${before}\n${injected}\n${after}`; + + writeFileSync(filePath, next, "utf8"); +} + +function run() { + if (!existsSync(CSS_PATH)) { + console.warn( + `[sync-template-assets] Skipping CSS inline: ${CSS_PATH} not found. Run "npm run build:css" first.` + ); + } else { + const css = readFileSync(CSS_PATH, "utf8").trim(); + spliceBetweenMarkers( + HEADER_PATH, + "", + "", + `` + ); + console.log(`[sync-template-assets] Inlined theme.css into ${path.relative(themeRoot, HEADER_PATH)}`); + } + + if (!existsSync(JS_PATH)) { + console.warn( + `[sync-template-assets] Skipping JS inline: ${JS_PATH} not found. Run "npm run build:js" first.` + ); + } else { + const js = readFileSync(JS_PATH, "utf8").trim(); + spliceBetweenMarkers( + FOOTER_PATH, + "", + "", + `` + ); + console.log(`[sync-template-assets] Inlined theme.js into ${path.relative(themeRoot, FOOTER_PATH)}`); + } +} + +run(); diff --git a/functions.php b/functions.php index fca2965..69705c9 100644 --- a/functions.php +++ b/functions.php @@ -9,7 +9,7 @@ exit; } -define( 'FUNKYCOMMERCE_HEADLESS_VERSION', '0.7.2' ); +define( 'FUNKYCOMMERCE_HEADLESS_VERSION', '1.2.6' ); /** * Whether Superfunky Pro is active and licensed. @@ -66,6 +66,11 @@ function funkycommerce_has_woocommerce_graphql() { return funkycommerce_has_woocommerce() && defined( 'WPGRAPHQL_WOOCOMMERCE_VERSION' ); } +function funkycommerce_is_headless_mode() { + $settings = (array) get_option( 'funkycommerce_control_center', array() ); + return (bool) apply_filters( 'funkycommerce_is_headless_mode', 'no' !== ( $settings['headless_mode'] ?? 'yes' ) ); +} + /** * Return a safe base currency when WooCommerce is optional or inactive. */ @@ -77,7 +82,7 @@ function funkycommerce_base_currency() { * Return currency names without requiring WooCommerce. */ function funkycommerce_currency_names() { - return function_exists( 'get_woocommerce_currencies' ) + $currencies = function_exists( 'get_woocommerce_currencies' ) ? get_woocommerce_currencies() : array( 'EUR' => __( 'Euro', 'funkycommerce-headless' ), @@ -85,13 +90,18 @@ function funkycommerce_currency_names() { 'GBP' => __( 'Pound sterling', 'funkycommerce-headless' ), 'PLN' => __( 'Polish złoty', 'funkycommerce-headless' ), ); + + $currencies['BTC'] = $currencies['BTC'] ?? __( 'Bitcoin', 'funkycommerce-headless' ); + $currencies['ETH'] = $currencies['ETH'] ?? __( 'Ethereum', 'funkycommerce-headless' ); + + return $currencies; } /** * Return currency symbols without requiring WooCommerce. */ function funkycommerce_currency_symbols() { - return function_exists( 'get_woocommerce_currency_symbols' ) + $symbols = function_exists( 'get_woocommerce_currency_symbols' ) ? get_woocommerce_currency_symbols() : array( 'EUR' => '€', @@ -99,6 +109,11 @@ function funkycommerce_currency_symbols() { 'GBP' => '£', 'PLN' => 'zł', ); + + $symbols['BTC'] = '₿'; + $symbols['ETH'] = 'Ξ'; + + return $symbols; } /** @@ -117,18 +132,54 @@ function funkycommerce_frontend_url( $path = '' ) { } require_once get_template_directory() . '/inc/headless-login.php'; +require_once get_template_directory() . '/inc/notifications.php'; +require_once get_template_directory() . '/inc/checkout-context.php'; require_once get_template_directory() . '/inc/community.php'; +require_once get_template_directory() . '/inc/avatar.php'; +require_once get_template_directory() . '/inc/registration-email-verification.php'; require_once get_template_directory() . '/inc/account.php'; require_once get_template_directory() . '/inc/navigation-commerce.php'; +require_once get_template_directory() . '/inc/recent-orders.php'; +require_once get_template_directory() . '/inc/web-push.php'; require_once get_template_directory() . '/inc/crypto-payments.php'; require_once get_template_directory() . '/inc/multilingual-content.php'; +require_once get_template_directory() . '/inc/ratings.php'; +require_once get_template_directory() . '/inc/saved-lists.php'; require_once get_template_directory() . '/inc/control-center-schema.php'; +require_once get_template_directory() . '/inc/artifact-protocol.php'; +require_once get_template_directory() . '/inc/artifact-store.php'; +require_once get_template_directory() . '/inc/artifact-rest.php'; +require_once get_template_directory() . '/inc/superfunky-licence-client.php'; +require_once get_template_directory() . '/inc/superfunky-update-client.php'; +Superfunky_Licence_Client::register(); +Superfunky_Update_Client::register_product( + array( + 'access' => 'public', + 'file' => null, + 'name' => 'Superfunky Headless', + 'product_id' => 'funkycommerce-headless', + 'requires_php' => '7.4', + 'slug' => 'funkycommerce-headless', + 'type' => 'theme', + 'url' => 'https://github.com/coded-letter/superfunky-theme', + 'version' => FUNKYCOMMERCE_HEADLESS_VERSION, + ) +); +require_once get_template_directory() . '/inc/admin-theme.php'; require_once get_template_directory() . '/inc/woocommerce-admin.php'; require_once get_template_directory() . '/inc/submissions.php'; require_once get_template_directory() . '/inc/control-center.php'; +require_once get_template_directory() . '/inc/admin-view-links.php'; +require_once get_template_directory() . '/inc/frontend-theme.php'; +require_once get_template_directory() . '/inc/custom-404.php'; +require_once get_template_directory() . '/inc/native-woocommerce.php'; +require_once get_template_directory() . '/inc/native-shortcodes.php'; require_once get_template_directory() . '/inc/build-webhooks.php'; +require_once get_template_directory() . '/inc/artifact-renderer.php'; +require_once get_template_directory() . '/inc/artifact-invalidation.php'; require_once get_template_directory() . '/inc/security-hardening.php'; require_once get_template_directory() . '/inc/seo-feeds.php'; +require_once get_template_directory() . '/inc/backend-preview.php'; /** * Configure the block editor and register menu locations consumed by the storefront. @@ -163,6 +214,10 @@ function funkycommerce_headless_setup() { */ function funkycommerce_headless_component_shortcodes() { return array( + 'funkycommerce_shop', + 'funkycommerce_blog', + 'product_archive', + 'post_archive', 'woocommerce_cart', 'woocommerce_checkout', 'woocommerce_my_account', @@ -172,6 +227,9 @@ function funkycommerce_headless_component_shortcodes() { 'cart', 'checkout', 'account', + 'wishlist', + 'reading_list', + 'auth', 'funkycommerce_wishlist', 'funkycommerce_reading_list', 'funkycommerce_auth', @@ -184,11 +242,17 @@ function funkycommerce_headless_component_shortcodes() { function funkycommerce_headless_content_shortcodes() { return array( 'hero', + 'video-hero', + 'spotify-radio', + 'chat_assistant', 'categories', 'slider', 'carousel', 'grid', + 'sticky-posts', + 'sticky_posts', 'tags', + 'product-tags', 'authors', 'reviews', 'comments', @@ -201,6 +265,10 @@ function funkycommerce_headless_content_shortcodes() { 'related-sections', 'order-success', 'unsubscribe-form', + 'funkycommerce_map', + 'funkycommerce_locations', + 'gml_map', + 'sorted_locations', ); } @@ -224,6 +292,10 @@ function funkycommerce_headless_component_blocks() { function funkycommerce_render_headless_component_marker( $attributes, $content, $tag ) { $schemas = funkycommerce_component_shortcode_schemas(); $schema_key = str_replace( 'woocommerce_', '', str_replace( 'funkycommerce_', '', $tag ) ); + $schema_key = array( + 'shop' => 'product_archive', + 'blog' => 'post_archive', + )[ $schema_key ] ?? $schema_key; $schema = isset( $schemas[ $schema_key ] ) ? $schemas[ $schema_key ] : array(); $defaults = array_map( static function ( $definition ) { @@ -244,6 +316,8 @@ static function ( $definition ) { function funkycommerce_component_shortcode_schemas() { return array( + 'product_archive' => array(), + 'post_archive' => array(), 'cart' => array( 'layout' => array( 'default' => 'classic', 'enum' => array( 'classic', 'editorial' ) ), 'summary_position' => array( 'default' => 'sticky', 'enum' => array( 'sticky', 'static' ) ), @@ -267,16 +341,21 @@ function funkycommerce_component_shortcode_schemas() { 'layout' => array( 'default' => 'cards', 'enum' => array( 'cards', 'editorial-2col' ) ), ), 'account' => array( - 'default_tab' => array( 'default' => 'dashboard', 'enum' => array( 'dashboard', 'orders', 'addresses', 'community' ) ), - 'tabs' => array( 'default' => 'dashboard,orders,addresses,community', 'type' => 'account-tab-list' ), + 'default_tab' => array( 'default' => 'dashboard', 'enum' => array( 'dashboard', 'orders', 'downloads', 'addresses', 'community' ) ), + 'tabs' => array( 'default' => 'dashboard,orders,downloads,addresses,community', 'type' => 'account-tab-list' ), ), 'auth' => array( - 'mode' => array( 'default' => 'login', 'enum' => array( 'login', 'register', 'forgot-password' ) ), - 'layout' => array( 'default' => 'split', 'enum' => array( 'split', 'centered', 'image-bg' ) ), + 'mode' => array( 'default' => 'login', 'enum' => array( 'login', 'register', 'forgot-password', 'combined' ) ), + 'default_mode' => array( 'default' => 'login', 'enum' => array( 'login', 'register', 'forgot-password' ) ), + 'layout' => array( 'default' => 'split', 'enum' => array( 'split', 'centered', 'image-bg' ) ), ), ); } +add_shortcode( 'funkycommerce_shop', 'funkycommerce_render_headless_component_marker' ); +add_shortcode( 'product_archive', 'funkycommerce_render_headless_component_marker' ); +add_shortcode( 'funkycommerce_blog', 'funkycommerce_render_headless_component_marker' ); +add_shortcode( 'post_archive', 'funkycommerce_render_headless_component_marker' ); add_shortcode( 'funkycommerce_cart', 'funkycommerce_render_headless_component_marker' ); add_shortcode( 'cart', 'funkycommerce_render_headless_component_marker' ); add_shortcode( 'funkycommerce_checkout', 'funkycommerce_render_headless_component_marker' ); @@ -284,31 +363,99 @@ function funkycommerce_component_shortcode_schemas() { add_shortcode( 'funkycommerce_account', 'funkycommerce_render_headless_component_marker' ); add_shortcode( 'account', 'funkycommerce_render_headless_component_marker' ); add_shortcode( 'funkycommerce_wishlist', 'funkycommerce_render_headless_component_marker' ); +add_shortcode( 'wishlist', 'funkycommerce_render_headless_component_marker' ); add_shortcode( 'funkycommerce_reading_list', 'funkycommerce_render_headless_component_marker' ); +add_shortcode( 'reading_list', 'funkycommerce_render_headless_component_marker' ); add_shortcode( 'funkycommerce_auth', 'funkycommerce_render_headless_component_marker' ); +add_shortcode( 'auth', 'funkycommerce_render_headless_component_marker' ); + +/** + * Shared collection offset schema definition for content shortcodes. + */ +function funkycommerce_collection_shortcode_offset_definition() { + return array( + 'default' => 0, + 'type' => 'integer', + 'min' => 0, + 'max' => 1000000, + ); +} /** * Return the backend contract for editor-authored storefront content modules. */ function funkycommerce_content_shortcode_schemas() { + // Shared by both the canonical `sticky-posts` tag and its neutral `sticky_posts` + // alias (kept as one definition, unlike the historically hand-duplicated + // funkycommerce_map/gml_map pair, so the two names can never drift apart). + $sticky_posts_schema = array( + 'layout' => array( 'default' => 'grid', 'enum' => array( 'grid', 'carousel', 'compact-list' ) ), + 'card_variant' => array( 'default' => 'default', 'enum' => array( 'default', 'compact', 'editorial', 'minimal' ) ), + 'columns' => array( 'default' => 3, 'type' => 'integer', 'min' => 1, 'max' => 4 ), + 'limit' => array( 'default' => 6, 'type' => 'integer', 'min' => 1, 'max' => 24 ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), + 'autoplay' => array( 'default' => 4000, 'type' => 'integer', 'min' => 0, 'max' => 60000 ), + 'loop' => array( 'default' => 'true', 'type' => 'boolean' ), + 'title' => array( 'default' => __( 'Pinned posts', 'funkycommerce-headless' ) ), + 'subtitle' => array( 'default' => '' ), + ); + return array( 'hero' => array( 'variant' => array( 'default' => 'fullbleed', 'enum' => array( 'glow', 'fullbleed', 'split', 'minimal', 'strip' ) ), 'kicker' => array( 'default' => '' ), 'title' => array( 'default' => __( 'Storefront hero', 'funkycommerce-headless' ) ), + 'h2' => array( 'default' => '' ), + 'heading_level' => array( 'default' => 'h1', 'enum' => array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ) ), 'description' => array( 'default' => '' ), 'image' => array( 'default' => '', 'type' => 'url' ), 'primary_cta_label' => array( 'default' => '' ), 'primary_cta_href' => array( 'default' => '', 'type' => 'url-path' ), + 'primary_cta_target' => array( 'default' => '_self', 'enum' => array( '_self', '_blank' ) ), + 'primary_cta_rel' => array( 'default' => '' ), 'secondary_cta_label' => array( 'default' => '' ), 'secondary_cta_href' => array( 'default' => '', 'type' => 'url-path' ), + 'secondary_cta_target' => array( 'default' => '_self', 'enum' => array( '_self', '_blank' ) ), + 'secondary_cta_rel' => array( 'default' => '' ), 'fullwidth' => array( 'default' => 'false', 'type' => 'boolean' ), 'height' => array( 'default' => '' ), ), + 'video-hero' => array( + 'variant' => array( 'default' => 'fullbleed', 'enum' => array( 'glow', 'fullbleed', 'split', 'minimal', 'strip' ) ), + 'src' => array( 'default' => '', 'type' => 'url' ), + 'poster' => array( 'default' => '', 'type' => 'url' ), + 'kicker' => array( 'default' => '' ), + 'title' => array( 'default' => __( 'Video hero', 'funkycommerce-headless' ) ), + 'description' => array( 'default' => '' ), + 'primary_cta_label' => array( 'default' => '' ), + 'primary_cta_href' => array( 'default' => '', 'type' => 'url-path' ), + 'primary_cta_target' => array( 'default' => '_self', 'enum' => array( '_self', '_blank' ) ), + 'primary_cta_rel' => array( 'default' => '' ), + 'secondary_cta_label' => array( 'default' => '' ), + 'secondary_cta_href' => array( 'default' => '', 'type' => 'url-path' ), + 'secondary_cta_target' => array( 'default' => '_self', 'enum' => array( '_self', '_blank' ) ), + 'secondary_cta_rel' => array( 'default' => '' ), + 'align' => array( 'default' => 'left', 'enum' => array( 'left', 'center', 'right' ) ), + 'height' => array( 'default' => '70vh' ), + 'overlay_opacity' => array( 'default' => 55, 'type' => 'integer', 'min' => 0, 'max' => 90 ), + 'autoplay' => array( 'default' => 'true', 'type' => 'boolean' ), + 'loop' => array( 'default' => 'true', 'type' => 'boolean' ), + 'muted' => array( 'default' => 'true', 'type' => 'boolean' ), + ), + 'spotify-radio' => array( + 'uri' => array( 'default' => 'https://open.spotify.com/playlist/37i9dQZF1DWWQRwui0ExPn' ), + 'content_type' => array( 'default' => 'playlist', 'enum' => array( 'track', 'album', 'playlist', 'artist', 'show', 'episode' ) ), + 'height' => array( 'default' => 400, 'type' => 'integer', 'min' => 152, 'max' => 800 ), + 'theme' => array( 'default' => 'auto', 'enum' => array( 'auto', 'dark', 'light' ) ), + 'title' => array( 'default' => __( 'Superfunky Radio', 'funkycommerce-headless' ) ), + 'description' => array( 'default' => '' ), + ), + 'chat_assistant' => array(), 'categories' => array( 'type' => array( 'default' => 'product', 'enum' => array( 'product', 'post' ) ), 'layout' => array( 'default' => 'cards', 'enum' => array( 'cards', 'compact', 'minimal', 'editorial', 'graphical', 'pills' ) ), 'columns' => array( 'default' => 3, 'type' => 'integer', 'min' => 2, 'max' => 4 ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'limit' => array( 'default' => 3, 'type' => 'integer', 'min' => 1, 'max' => 24 ), 'include' => array( 'default' => '' ), 'orderby' => array( 'default' => 'name', 'enum' => array( 'name', 'count', 'include' ) ), @@ -316,10 +463,11 @@ function funkycommerce_content_shortcode_schemas() { 'title' => array( 'default' => '' ), ), 'slider' => array( - 'type' => array( 'default' => 'product', 'enum' => array( 'campaign', 'product', 'post' ) ), + 'type' => array( 'default' => 'product', 'enum' => array( 'campaign', 'cinematic', 'product', 'post' ) ), 'layout' => array( 'default' => '3/3', 'enum' => array( '3/3', '2/3', '1/3' ) ), 'card_variant' => array( 'default' => 'default', 'enum' => array( 'default', 'compact', 'editorial', 'minimal', 'gallery', 'simple', 'variation', 'expandable' ) ), 'slides' => array( 'default' => 3, 'type' => 'integer', 'min' => 1, 'max' => 12 ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'limit' => array( 'default' => 6, 'type' => 'integer', 'min' => 1, 'max' => 48 ), 'navigation' => array( 'default' => 'both', 'enum' => array( 'dots', 'arrows', 'both', 'none' ) ), 'autoplay' => array( 'default' => 5000, 'type' => 'integer', 'min' => 0, 'max' => 60000 ), @@ -335,13 +483,28 @@ function funkycommerce_content_shortcode_schemas() { 'order' => array( 'default' => 'desc', 'enum' => array( 'asc', 'desc' ) ), 'title' => array( 'default' => '' ), 'subtitle' => array( 'default' => '' ), + 'section_heading_level' => array( 'default' => 'h3', 'enum' => array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ) ), + 'heading_level' => array( 'default' => 'h2', 'enum' => array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ) ), + 'first_heading_level' => array( 'default' => '', 'enum' => array( '', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ) ), 'kicker' => array( 'default' => '' ), 'description' => array( 'default' => '' ), 'image' => array( 'default' => '', 'type' => 'url' ), + 'bgimgs' => array( 'default' => '' ), + 'h1' => array( 'default' => '' ), + 'p' => array( 'default' => '' ), + 'pill' => array( 'default' => '' ), 'titles' => array( 'default' => '' ), 'descriptions' => array( 'default' => '' ), 'images' => array( 'default' => '', 'type' => 'url-list' ), 'kickers' => array( 'default' => '' ), + 'primary_cta_label' => array( 'default' => '' ), + 'primary_cta_href' => array( 'default' => '', 'type' => 'url-path' ), + 'primary_cta_target' => array( 'default' => '_self', 'enum' => array( '_self', '_blank' ) ), + 'primary_cta_rel' => array( 'default' => '' ), + 'secondary_cta_label' => array( 'default' => '' ), + 'secondary_cta_href' => array( 'default' => '', 'type' => 'url-path' ), + 'secondary_cta_target' => array( 'default' => '_self', 'enum' => array( '_self', '_blank' ) ), + 'secondary_cta_rel' => array( 'default' => '' ), 'fullwidth' => array( 'default' => 'false', 'type' => 'boolean' ), 'height' => array( 'default' => '' ), ), @@ -349,6 +512,7 @@ function funkycommerce_content_shortcode_schemas() { 'type' => array( 'default' => 'product', 'enum' => array( 'product', 'post' ) ), 'card_variant' => array( 'default' => 'default', 'enum' => array( 'default', 'compact', 'editorial', 'minimal', 'gallery', 'simple', 'variation', 'expandable' ) ), 'columns' => array( 'default' => 4, 'type' => 'integer', 'min' => 1, 'max' => 6 ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'limit' => array( 'default' => 12, 'type' => 'integer', 'min' => 1, 'max' => 48 ), 'include' => array( 'default' => '' ), 'category' => array( 'default' => '' ), @@ -361,6 +525,7 @@ function funkycommerce_content_shortcode_schemas() { 'loop' => array( 'default' => 'true', 'type' => 'boolean' ), 'title' => array( 'default' => '' ), 'subtitle' => array( 'default' => '' ), + 'section_heading_level' => array( 'default' => 'h3', 'enum' => array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ) ), ), 'grid' => array( 'type' => array( 'default' => 'product', 'enum' => array( 'product', 'post', 'community-article' ) ), @@ -368,6 +533,7 @@ function funkycommerce_content_shortcode_schemas() { 'layout' => array( 'default' => 'standard', 'enum' => array( 'standard', 'compact', 'editorial', 'masonry' ) ), 'columns' => array( 'default' => 3, 'type' => 'integer', 'min' => 1, 'max' => 6 ), 'page_size' => array( 'default' => 12, 'type' => 'integer', 'min' => 1, 'max' => 48 ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'paginated' => array( 'default' => 'true', 'type' => 'boolean' ), 'include' => array( 'default' => '' ), 'category' => array( 'default' => '' ), @@ -381,16 +547,29 @@ function funkycommerce_content_shortcode_schemas() { 'title' => array( 'default' => '' ), 'subtitle' => array( 'default' => '' ), ), + 'sticky-posts' => $sticky_posts_schema, + 'sticky_posts' => $sticky_posts_schema, 'tags' => array( 'layout' => array( 'default' => 'pills', 'enum' => array( 'pills', 'cards', 'compact' ) ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'limit' => array( 'default' => 24, 'type' => 'integer', 'min' => 1, 'max' => 100 ), 'include' => array( 'default' => '' ), 'orderby' => array( 'default' => 'name', 'enum' => array( 'name', 'count', 'include' ) ), 'order' => array( 'default' => 'asc', 'enum' => array( 'asc', 'desc' ) ), 'title' => array( 'default' => __( 'Tags', 'funkycommerce-headless' ) ), ), + 'product-tags' => array( + 'layout' => array( 'default' => 'pills', 'enum' => array( 'pills', 'cards', 'compact' ) ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), + 'limit' => array( 'default' => 24, 'type' => 'integer', 'min' => 1, 'max' => 100 ), + 'include' => array( 'default' => '' ), + 'orderby' => array( 'default' => 'name', 'enum' => array( 'name', 'count', 'include' ) ), + 'order' => array( 'default' => 'asc', 'enum' => array( 'asc', 'desc' ) ), + 'title' => array( 'default' => __( 'Product tags', 'funkycommerce-headless' ) ), + ), 'authors' => array( 'layout' => array( 'default' => 'cards', 'enum' => array( 'cards', 'compact' ) ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'limit' => array( 'default' => 12, 'type' => 'integer', 'min' => 1, 'max' => 100 ), 'include' => array( 'default' => '' ), 'show_bio' => array( 'default' => 'true', 'type' => 'boolean' ), @@ -402,6 +581,7 @@ function funkycommerce_content_shortcode_schemas() { 'reviews' => array( 'layout' => array( 'default' => 'grid-4', 'enum' => array( 'grid-4', 'grid-3', 'grid-5', 'masonry', 'compact' ) ), 'variant' => array( 'default' => 'cards', 'enum' => array( 'cards', 'full', 'compact' ) ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'limit' => array( 'default' => 12, 'type' => 'integer', 'min' => 1, 'max' => 48 ), 'product' => array( 'default' => '' ), 'min_rating' => array( 'default' => 0, 'type' => 'number', 'min' => 0, 'max' => 5 ), @@ -413,6 +593,7 @@ function funkycommerce_content_shortcode_schemas() { 'comments' => array( 'layout' => array( 'default' => 'cards', 'enum' => array( 'cards', 'compact' ) ), 'variant' => array( 'default' => 'cards', 'enum' => array( 'cards', 'full', 'compact' ) ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'limit' => array( 'default' => 12, 'type' => 'integer', 'min' => 1, 'max' => 48 ), 'post' => array( 'default' => '' ), 'min_rating' => array( 'default' => 0, 'type' => 'number', 'min' => 0, 'max' => 5 ), @@ -425,6 +606,7 @@ function funkycommerce_content_shortcode_schemas() { 'layout' => array( 'default' => 'masonry', 'enum' => array( 'masonry', 'grid-3', 'grid-4', 'list', 'compact' ) ), 'load_mode' => array( 'default' => 'manual', 'enum' => array( 'manual', 'infinite' ) ), 'page_size' => array( 'default' => 12, 'type' => 'integer', 'min' => 1, 'max' => 48 ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'show_filters' => array( 'default' => 'true', 'type' => 'boolean' ), 'tags' => array( 'default' => '' ), 'author' => array( 'default' => '' ), @@ -438,6 +620,7 @@ function funkycommerce_content_shortcode_schemas() { 'layout' => array( 'default' => 'gradient', 'enum' => array( 'gradient', 'split', 'image-bg' ) ), 'kicker' => array( 'default' => __( 'Community', 'funkycommerce-headless' ) ), 'title' => array( 'default' => __( 'See how the community styles it', 'funkycommerce-headless' ) ), + 'heading_level' => array( 'default' => 'h1', 'enum' => array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ) ), 'description' => array( 'default' => '' ), 'image' => array( 'default' => '', 'type' => 'url' ), 'show_upload' => array( 'default' => 'true', 'type' => 'boolean' ), @@ -446,6 +629,7 @@ function funkycommerce_content_shortcode_schemas() { 'layout' => array( 'default' => 'grid', 'enum' => array( 'grid', 'compact', 'carousel' ) ), 'card_variant' => array( 'default' => 'default', 'enum' => array( 'default', 'minimal', 'editorial', 'gallery', 'simple', 'variation', 'expandable' ) ), 'columns' => array( 'default' => 4, 'type' => 'integer', 'min' => 1, 'max' => 6 ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'limit' => array( 'default' => 12, 'type' => 'integer', 'min' => 1, 'max' => 48 ), 'min_rating' => array( 'default' => 0, 'type' => 'number', 'min' => 0, 'max' => 5 ), 'title' => array( 'default' => __( 'Shop the community', 'funkycommerce-headless' ) ), @@ -455,6 +639,7 @@ function funkycommerce_content_shortcode_schemas() { 'tags' => array( 'default' => '' ), 'tag_limit' => array( 'default' => 3, 'type' => 'integer', 'min' => 1, 'max' => 12 ), 'post_limit' => array( 'default' => 3, 'type' => 'integer', 'min' => 1, 'max' => 12 ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'min_likes' => array( 'default' => 0, 'type' => 'integer', 'min' => 0, 'max' => 1000000 ), 'date_from' => array( 'default' => '', 'type' => 'date' ), 'date_to' => array( 'default' => '', 'type' => 'date' ), @@ -463,14 +648,18 @@ function funkycommerce_content_shortcode_schemas() { 'community-members' => array( 'layout' => array( 'default' => 'grid', 'enum' => array( 'grid', 'compact', 'list' ) ), 'columns' => array( 'default' => 6, 'type' => 'integer', 'min' => 1, 'max' => 6 ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'limit' => array( 'default' => 12, 'type' => 'integer', 'min' => 1, 'max' => 100 ), 'include' => array( 'default' => '' ), - 'role' => array( 'default' => 'all', 'enum' => array( 'all', 'member', 'creator', 'collaborator' ) ), + 'members' => array( 'default' => '', 'type' => 'community-role-list' ), + 'role' => array( 'default' => 'all', 'type' => 'community-role-list' ), + 'permission' => array( 'default' => 'all', 'type' => 'community-role-list' ), 'show_bio' => array( 'default' => 'false', 'type' => 'boolean' ), 'title' => array( 'default' => __( 'Members to follow', 'funkycommerce-headless' ) ), ), 'testimonials' => array( 'layout' => array( 'default' => 'grid-3', 'enum' => array( 'grid-3', 'carousel', 'compact' ) ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'limit' => array( 'default' => 3, 'type' => 'integer', 'min' => 1, 'max' => 12 ), 'min_rating' => array( 'default' => 4, 'type' => 'number', 'min' => 0, 'max' => 5 ), 'date_from' => array( 'default' => '', 'type' => 'date' ), @@ -492,9 +681,64 @@ function funkycommerce_content_shortcode_schemas() { 'title' => array( 'default' => __( 'We’re sorry to see you go.', 'funkycommerce-headless' ) ), 'description' => array( 'default' => __( 'Confirm your email address and tell us why you’re unsubscribing.', 'funkycommerce-headless' ) ), ), + 'funkycommerce_map' => array( + 'height' => array( 'default' => 500, 'type' => 'integer', 'min' => 240, 'max' => 1000 ), + ), + 'funkycommerce_locations' => array(), + 'gml_map' => array( + 'height' => array( 'default' => 500, 'type' => 'integer', 'min' => 240, 'max' => 1000 ), + ), + 'sorted_locations' => array(), ); } +/** + * Public filter key for a registered WordPress role. + */ +function funkycommerce_community_role_type( $role ) { + $role = sanitize_key( (string) $role ); + if ( 'administrator' === $role ) { + return 'admin'; + } + return $role; +} + +/** + * Resolve role slugs and human-readable role labels accepted by the shortcode. + */ +function funkycommerce_community_role_filter_aliases() { + $aliases = array( + 'all' => 'all', + 'admin' => 'admin', + 'administrator' => 'admin', + 'member' => 'member', + 'creator' => 'creator', + 'collaborator' => 'collaborator', + 'customer' => 'customer', + 'subscriber' => 'subscriber', + 'editor' => 'editor', + 'author' => 'author', + 'contributor' => 'contributor', + 'shop-manager' => 'shop_manager', + 'seo-editor' => 'wpseo_editor', + 'seo-manager' => 'wpseo_manager', + ); + $roles = function_exists( 'wp_roles' ) ? wp_roles()->roles : array(); + foreach ( $roles as $slug => $details ) { + $type = funkycommerce_community_role_type( $slug ); + if ( '' === $type ) { + continue; + } + $aliases[ sanitize_key( (string) $slug ) ] = $type; + $aliases[ sanitize_title( str_replace( '_', ' ', (string) $slug ) ) ] = $type; + $label = sanitize_title( (string) ( $details['name'] ?? '' ) ); + if ( $label ) { + $aliases[ $label ] = $type; + } + } + return $aliases; +} + /** * Normalize one shortcode attribute according to its schema definition. */ @@ -540,13 +784,30 @@ static function ( $item ) { array_filter( array_map( 'sanitize_key', explode( ',', (string) $value ) ), static function ( $item ) { - return in_array( $item, array( 'dashboard', 'orders', 'addresses', 'community' ), true ); + return in_array( $item, array( 'dashboard', 'orders', 'downloads', 'addresses', 'community' ), true ); } ) ) ); return $items ? implode( ',', $items ) : $definition['default']; } + if ( 'community-role-list' === $type ) { + $aliases = funkycommerce_community_role_filter_aliases(); + $items = array_values( + array_unique( + array_filter( + array_map( + static function ( $item ) use ( $aliases ) { + $key = sanitize_title( trim( (string) $item ) ); + return $aliases[ $key ] ?? ''; + }, + explode( ',', (string) $value ) + ) + ) + ) + ); + return in_array( 'all', $items, true ) ? 'all' : implode( ',', $items ); + } if ( 'url-path' === $type ) { $value = trim( (string) $value ); return 0 === strpos( $value, '/' ) ? sanitize_text_field( $value ) : esc_url_raw( $value ); @@ -557,12 +818,54 @@ static function ( $item ) { /** * Render a validated marker that the React storefront replaces with a live module. */ +function funkycommerce_apply_content_shortcode_aliases( $attributes, $tag ) { + $aliases = array(); + if ( 'hero' === $tag ) { + if ( ! empty( $attributes['h2'] ) && empty( $attributes['h1'] ) ) { + $attributes['heading_level'] = 'h2'; + $attributes['title'] = $attributes['h2']; + } + $aliases = array( + 'pill' => 'kicker', + 'h1' => 'title', + 'p' => 'description', + 'bgimg' => 'image', + ); + } elseif ( 'slider' === $tag ) { + $aliases = array( + 'h1' => 'titles', + 'p' => 'descriptions', + 'bgimgs' => 'images', + 'pill' => 'kickers', + ); + } + foreach ( $aliases as $alias => $canonical ) { + if ( isset( $attributes[ $alias ] ) && '' !== trim( (string) $attributes[ $alias ] ) ) { + $attributes[ $canonical ] = $attributes[ $alias ]; + } + } + + foreach ( array( 'cta1' => 'primary_cta', 'cta2' => 'secondary_cta' ) as $alias => $prefix ) { + if ( empty( $attributes[ $alias ] ) ) { + continue; + } + $parts = array_map( 'trim', explode( '|', (string) $attributes[ $alias ] ) ); + foreach ( array( 'label', 'href', 'target', 'rel' ) as $index => $field ) { + if ( ! empty( $parts[ $index ] ) ) { + $attributes[ $prefix . '_' . $field ] = $parts[ $index ]; + } + } + } + return $attributes; +} + function funkycommerce_render_content_shortcode_marker( $attributes, $content, $tag ) { $schemas = funkycommerce_content_shortcode_schemas(); if ( ! isset( $schemas[ $tag ] ) ) { return ''; } + $attributes = funkycommerce_apply_content_shortcode_aliases( $attributes, $tag ); $schema = $schemas[ $tag ]; $defaults = array_map( static function ( $definition ) { @@ -581,54 +884,87 @@ static function ( $definition ) { return $marker . '>'; } -foreach ( array_keys( funkycommerce_content_shortcode_schemas() ) as $funkycommerce_shortcode_tag ) { - add_shortcode( $funkycommerce_shortcode_tag, 'funkycommerce_render_content_shortcode_marker' ); -} - /** - * Legacy helper retained for upgrade safety now that shortcode-driven pages are regular Pages. + * Register content shortcodes after WordPress has initialized translations. */ -function funkycommerce_ensure_custom_special_pages() { - return; +function funkycommerce_register_content_shortcodes() { + foreach ( array_keys( funkycommerce_content_shortcode_schemas() ) as $shortcode_tag ) { + // The paid plugin owns native [chat_assistant] rendering. The theme only + // replaces it with a marker when the separate headless app is active. + if ( 'chat_assistant' === $shortcode_tag && ( ! function_exists( 'funkycommerce_is_headless_mode' ) || ! funkycommerce_is_headless_mode() ) ) { + continue; + } + add_shortcode( $shortcode_tag, 'funkycommerce_render_content_shortcode_marker' ); + } } -add_action( 'after_switch_theme', 'funkycommerce_ensure_custom_special_pages' ); -add_action( 'admin_init', 'funkycommerce_ensure_custom_special_pages' ); +add_action( 'init', 'funkycommerce_register_content_shortcodes' ); /** - * Resolve a special storefront page by its stable route slug. + * Register the editor-friendly dynamic counterpart to [video-hero]. */ -function funkycommerce_get_special_page_id( $key ) { - $page_slugs = array( - 'home' => 'home', - 'shop' => 'shop', - 'blog' => 'blog', - 'cart' => 'cart', - 'checkout' => 'checkout', - 'account' => 'account', +function funkycommerce_register_video_hero_block() { + $script_path = get_template_directory() . '/assets/video-hero-block.js'; + wp_register_script( + 'funkycommerce-video-hero-block', + get_template_directory_uri() . '/assets/video-hero-block.js', + array( 'wp-blocks', 'wp-block-editor', 'wp-components', 'wp-element', 'wp-i18n', 'wp-server-side-render' ), + file_exists( $script_path ) ? (string) filemtime( $script_path ) : null, + true ); - $slug = $page_slugs[ $key ] ?? ''; - $page = $slug ? get_page_by_path( $slug, OBJECT, 'page' ) : null; - - // Keep the conventional WooCommerce slug working for existing installations. - if ( ! $page && 'account' === $key ) { - $page = get_page_by_path( 'my-account', OBJECT, 'page' ); + register_block_type( + 'funkycommerce/video-hero', + array( + 'api_version' => 3, + 'editor_script' => 'funkycommerce-video-hero-block', + 'attributes' => array( + 'src' => array( 'type' => 'string', 'default' => '' ), 'variant' => array( 'type' => 'string', 'default' => 'fullbleed' ), 'poster' => array( 'type' => 'string', 'default' => '' ), + 'kicker' => array( 'type' => 'string', 'default' => '' ), 'title' => array( 'type' => 'string', 'default' => 'Video hero' ), + 'description' => array( 'type' => 'string', 'default' => '' ), 'primaryCtaLabel' => array( 'type' => 'string', 'default' => '' ), + 'primaryCtaHref' => array( 'type' => 'string', 'default' => '' ), 'secondaryCtaLabel' => array( 'type' => 'string', 'default' => '' ), + 'secondaryCtaHref' => array( 'type' => 'string', 'default' => '' ), 'align' => array( 'type' => 'string', 'default' => 'left' ), + 'height' => array( 'type' => 'string', 'default' => '70vh' ), 'overlayOpacity' => array( 'type' => 'number', 'default' => 55 ), + 'autoplay' => array( 'type' => 'boolean', 'default' => true ), 'loop' => array( 'type' => 'boolean', 'default' => true ), + 'muted' => array( 'type' => 'boolean', 'default' => true ), + ), + 'render_callback' => 'funkycommerce_render_video_hero_block', + ) + ); +} +add_action( 'init', 'funkycommerce_register_video_hero_block', 30 ); + +function funkycommerce_render_video_hero_block( $attributes ) { + $map = array( + 'src' => 'src', 'variant' => 'variant', 'poster' => 'poster', 'kicker' => 'kicker', 'title' => 'title', 'description' => 'description', + 'primaryCtaLabel' => 'primary_cta_label', 'primaryCtaHref' => 'primary_cta_href', + 'secondaryCtaLabel' => 'secondary_cta_label', 'secondaryCtaHref' => 'secondary_cta_href', + 'align' => 'align', 'height' => 'height', 'overlayOpacity' => 'overlay_opacity', + 'autoplay' => 'autoplay', 'loop' => 'loop', 'muted' => 'muted', + ); + $shortcode_attributes = array(); + foreach ( $map as $block_name => $shortcode_name ) { + if ( array_key_exists( $block_name, $attributes ) ) { + $value = is_bool( $attributes[ $block_name ] ) ? ( $attributes[ $block_name ] ? 'true' : 'false' ) : $attributes[ $block_name ]; + $shortcode_attributes[] = $shortcode_name . '="' . esc_attr( $value ) . '"'; + } } - - return $page ? (int) $page->ID : 0; + return do_shortcode( '[video-hero ' . implode( ' ', $shortcode_attributes ) . ']' ); } /** - * Extract a database ID from a WPGraphQL Page source. + * Extract a database ID from a WPGraphQL content-node source. */ -function funkycommerce_graphql_page_database_id( $page ) { - if ( $page instanceof WP_Post ) { - return (int) $page->ID; +function funkycommerce_graphql_content_database_id( $node ) { + if ( $node instanceof WP_Post ) { + return (int) $node->ID; } - if ( is_object( $page ) && isset( $page->databaseId ) ) { - return (int) $page->databaseId; + if ( is_object( $node ) && is_callable( array( $node, 'get_id' ) ) ) { + return (int) $node->get_id(); } - if ( is_object( $page ) && isset( $page->ID ) ) { - return (int) $page->ID; + if ( is_object( $node ) && isset( $node->databaseId ) ) { + return (int) $node->databaseId; + } + if ( is_object( $node ) && isset( $node->ID ) ) { + return (int) $node->ID; } return 0; } @@ -695,15 +1031,164 @@ function funkycommerce_extract_headless_references( $content ) { return array_values( array_unique( $references ) ); } +/** + * Return WordPress 7.0 block-level custom CSS generated for this content fragment. + */ +function funkycommerce_get_rendered_block_custom_css( $content ) { + if ( + ! function_exists( 'wp_styles' ) + || ! preg_match_all( '/\bwp-custom-css-[a-z0-9-]+\b/i', $content, $matches ) + ) { + return ''; + } + + $inline_styles = wp_styles()->get_data( 'wp-block-custom-css', 'after' ); + if ( ! is_array( $inline_styles ) ) { + return ''; + } + + $class_names = array_values( array_unique( $matches[0] ) ); + $matching = array_filter( + $inline_styles, + static function ( $css ) use ( $class_names ) { + if ( ! is_string( $css ) ) { + return false; + } + foreach ( $class_names as $class_name ) { + if ( false !== strpos( $css, '.' . $class_name ) ) { + return true; + } + } + return false; + } + ); + + return implode( "\n", $matching ); +} + +/** + * Run content filters with headless marker callbacks, then restore native callbacks. + */ +function funkycommerce_with_headless_shortcode_markers( $callback ) { + $content_shortcodes = funkycommerce_headless_content_shortcodes(); + $shortcodes = array_merge( funkycommerce_headless_component_shortcodes(), $content_shortcodes ); + $callbacks = array(); + + foreach ( $shortcodes as $shortcode ) { + $callbacks[ $shortcode ] = shortcode_exists( $shortcode ) ? $GLOBALS['shortcode_tags'][ $shortcode ] : null; + add_shortcode( + $shortcode, + in_array( $shortcode, $content_shortcodes, true ) + ? 'funkycommerce_render_content_shortcode_marker' + : 'funkycommerce_render_headless_component_marker' + ); + } + + try { + return call_user_func( $callback ); + } finally { + foreach ( $callbacks as $shortcode => $saved_callback ) { + if ( null === $saved_callback ) { + remove_shortcode( $shortcode ); + } else { + $GLOBALS['shortcode_tags'][ $shortcode ] = $saved_callback; + } + } + } +} + /** * Render supplemental page content while preserving application shortcode markers in place. */ function funkycommerce_render_headless_page_content( $page_id ) { $content = (string) get_post_field( 'post_content', $page_id ); $content = serialize_blocks( funkycommerce_filter_headless_blocks( parse_blocks( $content ) ) ); + $content = funkycommerce_with_headless_shortcode_markers( + static function () use ( $content ) { + return apply_filters( 'the_content', $content ); + } + ); + $content = funkycommerce_security_mark_content_scripts( $content, 'page' ); + + /* + * Per-block layout rules are stored in the style engine rather than the + * rendered markup. Ship them with the headless fragment so constrained, + * flex, grid, and child-sizing controls render exactly as WordPress saved. + */ + if ( function_exists( 'wp_style_engine_get_stylesheet_from_context' ) ) { + $block_support_styles = wp_style_engine_get_stylesheet_from_context( + 'block-supports', + array( + 'optimize' => true, + 'prettify' => false, + ) + ); + + if ( $block_support_styles ) { + $content .= ''; + } + } + + $block_custom_css = funkycommerce_get_rendered_block_custom_css( $content ); + if ( $block_custom_css ) { + $content .= ''; + } + + return $content; +} + +/** + * Render a post field with an explicit content type for editor-script scoping. + */ +function funkycommerce_render_headless_content_field( $post_id, $field, $filter ) { + $post_type = get_post_type( $post_id ); + if ( ! in_array( $post_type, array( 'post', 'product' ), true ) ) { + return ''; + } + + $content = (string) get_post_field( $field, $post_id ); + $content = funkycommerce_with_headless_shortcode_markers( + static function () use ( $content, $filter ) { + return apply_filters( $filter, $content ); + } + ); + return funkycommerce_security_mark_content_scripts( $content, $post_type ); +} + +/** + * Request the bundled docs enhancer without shipping executable editor content. + * + * Existing published docs are also detected by their known DOM shape in the + * storefront, so this remains backwards-compatible until WordPress is updated. + */ +function funkycommerce_mark_docs_navigation_behavior( $content ) { + if ( + false === strpos( $content, 'id="doc-sidebar"' ) + || false === strpos( $content, 'id="docs-content"' ) + || false === strpos( $content, 'id="scroll-spy"' ) + ) { + return $content; + } + + $content = preg_replace( '/ '
' +
+
- + -
+
$label ) : ?>
'Euro', 'USD' => 'US dollar', 'GBP' => 'Pound sterling', 'PLN' => 'Polish złoty' ); + $currencies = funkycommerce_currency_names(); $base = function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : 'EUR'; ?>
$label ) : ?>
+ + + - + - placeholder="" =""> + autocomplete="new-password" data-lpignore="true" data-1p-ignore="true" data-bwignore="true" placeholder="" ="">
@@ -539,6 +1063,38 @@ function funkycommerce_extension_is_active( $slugs ) { return false; } +/** + * Detect current and legacy Headless Login plugin releases. + */ +function funkycommerce_headless_login_is_active() { + return defined( 'WPGRAPHQL_LOGIN_VERSION' ) + || defined( 'WPGRAPHQL_HEADLESS_LOGIN_VERSION' ) + || class_exists( '\WPGraphQL\Login\Auth\ServerAuthentication' ) + || funkycommerce_extension_is_active( + array( + 'wp-graphql-headless-login', + 'wp-graphql-headless-login-main', + 'headless-login-for-wpgraphql', + ) + ); +} + +/** + * Detect WP GraphQL Polylang releases that do not expose a version constant. + */ +function funkycommerce_wpgraphql_polylang_is_active() { + return class_exists( '\WPGraphQL\Extensions\Polylang\Loader' ) + || function_exists( 'wp_graphql_polylang_init' ) + || defined( 'WP_GRAPHQL_POLYLANG_VERSION' ) + || funkycommerce_extension_is_active( + array( + 'wp-graphql-polylang', + 'wp-graphql-polylang-main', + 'wp-graphql-polylang-master', + ) + ); +} + /** * Return the current entitlement for one independently licensed companion. */ @@ -599,7 +1155,8 @@ function funkycommerce_render_extensions() {

- + +
@@ -632,6 +1189,40 @@ function funkycommerce_render_coverage() { set_param( 'full', 'admin_post_funkycommerce_artifact_regenerate_full' === $action ); + $request->set_param( 'route', sanitize_text_field( wp_unslash( $_POST['manual_artifact_route'] ?? '' ) ) ); + $request->set_param( 'locale', sanitize_text_field( wp_unslash( $_POST['manual_artifact_locale'] ?? '' ) ) ); + $result = FunkyCommerce_Artifact_REST::regenerate( $request ); + } + $error = is_wp_error( $result ) ? $result : null; + $url = add_query_arg( + array( + 'page' => 'funkycommerce-control-center', + 'artifact_notice' => $error ? 'error' : 'success', + 'artifact_code' => $error ? sanitize_key( $error->get_error_code() ) : '', + ), + admin_url( 'admin.php' ) + ); + wp_safe_redirect( $url ); + exit; +} +add_action( 'admin_post_funkycommerce_artifact_regenerate_path', 'funkycommerce_handle_artifact_operation' ); +add_action( 'admin_post_funkycommerce_artifact_regenerate_full', 'funkycommerce_handle_artifact_operation' ); +add_action( 'admin_post_funkycommerce_artifact_cleanup', 'funkycommerce_handle_artifact_operation' ); + /** * Render the complete tabbed Control Center. */ @@ -644,15 +1235,30 @@ function funkycommerce_render_control_center() { $settings = funkycommerce_control_center_settings(); $frontend = $settings['frontend_url'] ?: home_url( '/' ); $coverage = funkycommerce_control_coverage_counts(); + $artifact_status = class_exists( 'FunkyCommerce_Artifact_Store' ) ? FunkyCommerce_Artifact_Store::status() : null; $newsletter_count = function_exists( 'funkycommerce_submission_count' ) ? funkycommerce_submission_count( 'fc_newsletter', 'unread' ) : 0; $form_count = function_exists( 'funkycommerce_submission_count' ) ? funkycommerce_submission_count( 'fc_form_entry', 'unread' ) : 0; + $headless_login_active = funkycommerce_headless_login_is_active(); + $wpgraphql_polylang_active = funkycommerce_wpgraphql_polylang_is_active(); ?>
+ + +

+ +

+
-

+

- - + +
@@ -661,23 +1267,42 @@ function funkycommerce_render_control_center() {
+
+
+
+
+
+
+
-
-
+
+
-
+ $section ) : ?>
hidden>

+ +
+
+
+
+
+
+ +
+
+ $field ) : funkycommerce_render_control_field( $key, $field, $settings[ $key ] ?? ( $field['default'] ?? '' ) ); endforeach; ?>
@@ -688,7 +1313,7 @@ function funkycommerce_render_control_center() { __( 'Store tagline', 'funkycommerce-headless' ), 'type' => 'text' ), $settings[ 'store_tagline_' . $slug ] ?? '' ); funkycommerce_render_control_field( 'promo_text_' . $slug, array( 'label' => __( 'Promotional message', 'funkycommerce-headless' ), 'type' => 'text' ), $settings[ 'promo_text_' . $slug ] ?? '' ); - $ui_strings = $settings[ 'ui_strings_' . $slug ] ?? wp_json_encode( (array) get_option( 'funkycommerce_ui_strings_' . $slug, array() ), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); + $ui_strings = wp_json_encode( funkycommerce_storefront_ui_strings_for_language( $slug ), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); funkycommerce_render_control_field( 'ui_strings_' . $slug, array( 'label' => __( 'Storefront UI strings', 'funkycommerce-headless' ), 'type' => 'json', 'default' => '{}' ), $ui_strings ?: '{}' ); ?> @@ -697,10 +1322,43 @@ function funkycommerce_render_control_center() {
+
+
+ +Skip to content + - -

Headless control panel preview

- + +
+ +
+ +
+ + +
+ + +
+ +
+ + +
diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..e873f1a --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/readme.txt b/readme.txt index f19282e..347e6fc 100644 --- a/readme.txt +++ b/readme.txt @@ -1,8 +1,86 @@ -=== FunkyCommerce Headless === +=== Superfunky Headless === +Contributors: coded-letter +Tags: headless, woocommerce, wpgraphql, full-site-editing +Requires at least: 6.7 +Tested up to: 6.8 +Requires PHP: 7.4 +Stable tag: 1.2.6 +License: GPL-2.0-or-later +License URI: https://www.gnu.org/licenses/gpl-2.0.html This block theme is the WordPress control plane for the FunkyCommerce storefront. -Its frontend templates are intentionally minimal because customer-facing output is -rendered by the separate headless application. +By default it renders a minimal, headless-only shell because customer-facing output is +served by the separate headless application. When headless mode is disabled in the +Control Center, the theme also ships a complete native WordPress rendering path (see +"Native frontend theme" below) with accessible header/footer/navigation templates and +core front/home/singular/archive/search/404 routes styled to match the storefront. + +== 1.2.6 highlights == + +* Adds a Control Center option for horizontally scrollable empty-cart recommendations + containing either the first or all featured WooCommerce products. +* Keeps empty-cart recommendations consistent across drawer and dropdown cart layouts. +* Preserves canonical product links and requires a concrete priced variation before + adding variable products from empty-cart recommendations. + +== 1.2.5 highlights == + +* Restores backend-featured WooCommerce products in the enabled empty-cart promotion. +* Keeps hero background media full-frame and centered on mobile layouts. +* Expands the editable storefront UI-string contract across customer-facing controls, + filters, dialogs, downloads, account, community, wishlist, and reading-list surfaces. + +== 1.2.4 highlights == + +* Keeps backend UI-string overrides isolated per language, with English fallback and + explicit admin values taking precedence over bundled and Polylang translations. +* Improves dynamic storefront media loading by limiting eager requests and avoiding + hidden adjacent community-card image preloads. + +== 1.2.3 highlights == + +* Makes the community-members role attribute an exact comma-separated whitelist + of registered WordPress role slugs or labels and excludes roleless profiles. + +== 1.2.2 highlights == + +* Makes Stripe BLIK available when shoppers select PLN in the storefront, while keeping + presentation backend-controlled and persisting BLIK orders with PLN-denominated totals. +* Proxies externally hosted digital files through WooCommerce's signed download handler, + with per-order/file/IP rate limiting and headless storefront download support. + +== 1.2.1 highlights == + +* Adds editable 404 page content for native and headless not-found routes, with + Polylang translation resolution and compatibility for both 404 and 4o4 slugs. + +== 1.2.0 highlights == + +* Adds backend-controlled guest, optional-account, and required-account checkout modes, + including automatic login recovery and password-setting email for checkout-created users. +* Keeps completed digital order downloads available through the secure seven-day order-key + window even when WooCommerce attached a customer during checkout. +* Adds distraction-free checkout navigation, Home-only empty-menu fallbacks, printable PDF + receipts, and dismissible recent-order notifications with configurable link targets. + +== 1.1.20 highlights == + +* Adds the editor-ready [video-hero] module with direct MP4/WebM, YouTube, and Vimeo + sources; poster fallback; overlays; text and CTA pills; accessible playback and + mute controls; reduced-motion handling; and matching headless/native rendering. +* Adds a Video hero/banner block to the WordPress editor and a rendered example to + the storefront shortcode library. +* Restores [chat_assistant] in headless page content while preserving the paid + plugin's native WordPress renderer. +* Loads the inline headless assistant as a separate, lightweight frontend chunk only + when [chat_assistant] is present. Pages without it make no shortcode assistant + configuration or chat requests. +* Includes the latest atomic multilingual route and language-switch behavior from + the synchronized storefront release line. + +The WordPress theme ZIP and the headless storefront are separate deployment +artifacts. Theme updates install backend contracts, editor integrations, and native +renderers; headless React changes require a storefront rebuild and deployment. == Headless account URLs == @@ -48,6 +126,11 @@ Linked multilingual page translations are selected by their database identity, s translated front pages remain distinct even when a translation plugin reports the same public URI for more than one language. +Not-found routes use the published 404 page, or the WordPress-safe 4o4 alias, as their +editable content source. When Polylang is active, the frontend and native theme resolve +the translation linked to the language of the missing route. If no matching page or +translation is published, the built-in 404 content remains available. + Rendered page content also receives WordPress' block-library styles, merged block-theme global CSS, and Additional CSS. The `themeStyles` field on Page (and the `funkycommerceThemeStyles` root field) exposes typed color, gradient, font-family, @@ -61,6 +144,53 @@ WordPress previews. Multilingual post records retain their WPGraphQL language an translation identities; author archives use the selected language to filter their post query, including explicit `?lang=en` and `?lang=pl` preview links. +== Native frontend theme == + +The theme owns a dedicated build pipeline for its native, non-headless rendering path +(template HTML/PHP, template parts, and frontend assets) that is entirely independent of +the Control Center's PHP contracts: + +* `package.json` / `tailwind.config.js` / `postcss.config.js`: a Tailwind CSS v3 build + scoped to this theme (content-scanned over `templates/`, `parts/`, and `assets/js/`, + with Tailwind's Preflight reset disabled so it coexists with WordPress core and plugin + CSS). Run `npm install` once, then `npm run build` to compile + `assets/css/theme-source.css` and `assets/js/theme.js` into `assets/dist/theme.css` / + `assets/dist/theme.js`, and to inline both compiled files into + `parts/header.html` / `parts/footer.html` (see `build/sync-template-assets.mjs`). + `npm run watch:css` re-compiles CSS on change during development. `npm run lint:php` + checks the syntax of PHP files this theme package owns (currently + `inc/frontend-theme.php`) using the `php-parser` npm package, since a native `php -l` + binary is not guaranteed to be available in every environment this theme is built in. +* Because the compiled CSS/JS are inlined directly into the header/footer template + parts, the native shell (sticky/collapsible announcement header, accessible primary + navigation, dark-mode toggle, crystal-style loading overlay, footer newsletter panel, + and lazy-loaded Spotify slot) works immediately with zero additional PHP wiring. +* `inc/frontend-theme.php` is an optional, self-contained upgrade path that is **not** + currently loaded by `functions.php`. It idiomatically `wp_enqueue_style`/ + `wp_enqueue_script`s the same compiled files (so browsers can cache them separately + from inline page HTML), mirrors Control Center loader/Spotify settings to the frontend + script via `wp_localize_script()` when `funkycommerce_storefront_control_settings()` is + available, and reflects a configured Spotify playlist embed URL into the static footer + markup via a `render_block` filter. To activate it, add one line to `functions.php`: + `require_once get_template_directory() . '/inc/frontend-theme.php';`. The theme is + fully functional and styled without this line; it only upgrades asset delivery and + wires dynamic Control Center settings once added. +* `theme.json` exposes `settings.custom.fc.*` tokens (radius, loader size/duration/ + glow-color/glow-opacity) as `--wp--custom--fc--*` CSS custom properties, matching the + Control Center schema's `loading` section defaults, so the loader looks correct even + before `inc/frontend-theme.php` is wired in. `style.css` `@import`s the same compiled + stylesheet consumed by the existing `add_editor_style( 'style.css' )` call, giving the + block editor canvas and the public front end visual parity from one CSS source. +* The header's primary navigation uses a `core/navigation` block with no `ref`, relying + on WordPress's classic-menu-fallback (a "Header Menu" location menu is promoted to a + `wp_navigation` post on first render). The footer's link columns are static, editable + block content instead, to avoid ambiguity between the header/footer/mobile classic + menu locations resolving to the same fallback menu. +* The Spotify slot (`[data-funky-spotify-slot]` in `parts/footer.html`) stays hidden + with a placeholder until a playlist URL is configured; `assets/js/theme.js` lazily + mounts the embed iframe via `IntersectionObserver` once an embed URL is present + (either injected by `inc/frontend-theme.php`, or hand-edited into the template part). + == Control Center == Appearance > FunkyCommerce is the single configuration surface for the headless theme. @@ -90,6 +220,19 @@ Authentication settings are owned by the auth plugin. The deployment frontend UR remains under Build & Deploy because password-reset links and redirects need it. Header and footer menu assignment remains in WordPress' native menu locations. +== Admin theme sync == + +The free theme automatically applies the active Site Editor Global Styles to wp-admin +and the post editor. The selected background, text, link/accent, button, and heading +colours theme the admin shell and core controls. Selected body, heading, and button font +families are applied as well, including locally installed WordPress Font Library faces. +Changes made under Appearance > Editor > Styles are reflected on the next admin page +load and require no separate setting or paid companion. Theme styling loads after the +user profile colour scheme through WordPress's core admin colour handle. Screen Options +remain available, and hidden Quick/Bulk Edit templates stay closed until WordPress clones +them into the active list table. Boot-time security constants read their saved raw values +without loading the translated Control Center schema before WordPress theme setup. + The Runtime coverage tab distinguishes controls consumed by current runtime code from controls that are safely stored but still awaiting implementation. @@ -98,8 +241,9 @@ controls that are safely stored but still awaiting implementation. WordPress remains the canonical publication source for RSS 2.0, Atom, XML sitemaps, and robots.txt. The theme adds `/feed.xml`, `/rss.xml`, and `/atom.xml` aliases, maps public content URLs to the configured headless frontend, and publishes WooCommerce products as -Google Merchant-compatible RSS at `/product.feed.xml`, `/product-feed.xml`, and -`/feed/products/`. +Google Merchant-compatible RSS at `/product-feed.xml` and `/feed/products/`. +Atom metadata uses the public storefront URL for its site, self, author, and entry +identifiers so the mirrored `/atom.xml` document remains valid for feed readers. The merchant feed includes stable ID, title, description, frontend product URL, image, availability, condition, currency-aware price, brand, and SKU/MPN where available. It is @@ -109,6 +253,8 @@ Enabled `llms.txt`, `llms-full.txt`, brand voice, product JSON-LD, ranking signa conversational FAQ settings are published as real root documents. The storefront build mirrors these optional files plus robots.txt, all sitemap pages, RSS, Atom, and merchant feeds into static output whenever `VITE_GRAPHQL_ENDPOINT` is configured. +The Apple Pay field accepts the complete domain-association document, not only a +Merchant ID, and the storefront publishes it unchanged at the required well-known path. == Submission inboxes == diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..8c98ff93f082949710fede542e51e0db854f668a GIT binary patch literal 135434 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¢j~1& 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", + ); + } + return path.resolve(arguments_[outIndex + 1]); +} + +if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(import.meta.filename)) { + const result = await prepareWordPressOrgPackage({ + outRoot: parseArguments(process.argv.slice(2)), + repositoryRoot: process.cwd(), + }); + console.log(`Prepared WordPress.org ${result.kind} package at ${result.outRoot}`); +} diff --git a/style.css b/style.css index 177a059..eda4e61 100644 --- a/style.css +++ b/style.css @@ -1,22 +1,30 @@ /* -Theme Name: FunkyCommerce Headless +Theme Name: Superfunky Headless Theme URI: https://github.com/coded-letter/superfunky-theme Author: Coded Letter -Description: Minimal block theme providing the WordPress control plane for the FunkyCommerce headless storefront. +Author URI: https://codedletter.com/ +Description: A minimal, content-first block theme for polished WordPress previews and a smooth path to the FunkyCommerce headless storefront. Requires at least: 6.7 Tested up to: 6.8 Requires PHP: 7.4 -Version: 0.7.0 -License: GNU General Public License v2 or later +Version: 1.2.6 +Update URI: https://github.com/coded-letter/superfunky-theme +License: GPL-2.0-or-later License URI: https://www.gnu.org/licenses/gpl-2.0.html Text Domain: funkycommerce-headless Tags: block-patterns, block-styles, custom-colors, custom-logo, custom-menu, editor-style, featured-images, full-site-editing, translation-ready, wide-blocks */ /* - * Frontend presentation belongs to the headless storefront. These styles only keep - * WordPress previews readable while the control-panel experience is developed. + * Native frontend shell styles. When headless mode is enabled these mostly + * back the block-editor canvas and admin previews; when headless mode is + * disabled (see funkycommerce_is_headless_mode()) this same compiled sheet + * powers the public-facing native WordPress templates, giving editor/public + * style parity from a single source (assets/css/theme-source.css, compiled + * via the theme's own Tailwind build --- see package.json). */ +@import url("assets/dist/theme.css"); + body { margin: 0; } diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..95e1547 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,83 @@ +/** + * Dedicated Tailwind build for the FunkyCommerce Headless theme's native WordPress + * frontend (block templates, template parts, loader, and Spotify slot). This is + * intentionally separate from the headless storefront's own Tailwind config — the + * theme's compiled CSS is embedded/enqueued independently for the native, non-headless + * rendering path (see assets/css/theme-source.css and inc/frontend-theme.php). + * + * Brand tokens mirror workspace/frontend/apps/storefront/tailwind.config.ts and the + * matching CSS custom properties in theme.json so native and headless output stay in + * visual parity. + */ +module.exports = { + darkMode: ["class", '[data-funky-theme="dark"]'], + content: [ + "./templates/**/*.html", + "./parts/**/*.html", + "./assets/js/**/*.js", + ], + corePlugins: { + preflight: false, + }, + theme: { + extend: { + fontFamily: { + sans: ["ui-sans-serif", "system-ui", "-apple-system", "BlinkMacSystemFont", '"Segoe UI"', "sans-serif"], + display: ["ui-rounded", '"SF Pro Rounded"', "ui-sans-serif", "system-ui", "sans-serif"], + }, + colors: { + brand: { + 50: "rgb(var(--fc-brand-50) / )", + 100: "rgb(var(--fc-brand-100) / )", + 200: "rgb(var(--fc-brand-200) / )", + 300: "rgb(var(--fc-brand-300) / )", + 400: "rgb(var(--fc-brand-400) / )", + 500: "rgb(var(--fc-brand-500) / )", + 600: "rgb(var(--fc-brand-600) / )", + 700: "rgb(var(--fc-brand-700) / )", + 800: "rgb(var(--fc-brand-800) / )", + 900: "rgb(var(--fc-brand-900) / )", + 950: "rgb(var(--fc-brand-950) / )", + }, + }, + boxShadow: { + soft: "0 1px 2px rgba(15, 15, 40, 0.04), 0 12px 32px -12px rgba(15, 15, 40, 0.12)", + "soft-lg": "0 4px 12px rgba(15, 15, 40, 0.06), 0 24px 48px -16px rgba(15, 15, 40, 0.18)", + glow: "0 0 0 1px rgb(var(--fc-brand-500) / 0.15), 0 8px 24px -4px rgb(var(--fc-brand-500) / 0.35)", + }, + borderRadius: { + sm: "calc(var(--fc-radius, 16px) * 0.25)", + DEFAULT: "calc(var(--fc-radius, 16px) * 0.25)", + md: "calc(var(--fc-radius, 16px) * 0.375)", + lg: "calc(var(--fc-radius, 16px) * 0.5)", + xl: "calc(var(--fc-radius, 16px) * 0.75)", + "2xl": "var(--fc-radius, 16px)", + "3xl": "calc(var(--fc-radius, 16px) * 1.5)", + control: "calc(var(--fc-radius, 16px) * 3)", + }, + backgroundImage: { + "brand-gradient": "linear-gradient(135deg, rgb(var(--fc-brand-gradient-from)) 0%, rgb(var(--fc-brand-gradient-to)) 100%)", + "brand-gradient-soft": + "linear-gradient(135deg, rgb(var(--fc-brand-gradient-from) / 0.12) 0%, rgb(var(--fc-brand-gradient-to) / 0.12) 100%)", + }, + transitionTimingFunction: { + "out-back": "cubic-bezier(0.16, 1, 0.3, 1)", + }, + keyframes: { + "fc-fade-in": { + "0%": { opacity: "0" }, + "100%": { opacity: "1" }, + }, + "fc-rise-in": { + "0%": { opacity: "0", transform: "translateY(8px)" }, + "100%": { opacity: "1", transform: "translateY(0)" }, + }, + }, + animation: { + "fc-fade-in": "fc-fade-in 0.5s ease-out", + "fc-rise-in": "fc-rise-in 0.5s cubic-bezier(0.16, 1, 0.3, 1)", + }, + }, + }, + plugins: [], +}; diff --git a/templates/404.html b/templates/404.html new file mode 100644 index 0000000..d084c9c --- /dev/null +++ b/templates/404.html @@ -0,0 +1,16 @@ + + + +

+ +
+

Error 404

+

This page has moved on.

+

The address may have changed during migration. Search the site to find the content in its new home.

+ +
+ +
+ + + diff --git a/templates/archive.html b/templates/archive.html new file mode 100644 index 0000000..eed1d1b --- /dev/null +++ b/templates/archive.html @@ -0,0 +1,42 @@ + + + +
+ +
+ + +
+ + +
+ + +
+ + +
+ + + +
+ +
+ + + + + + + + + +

Nothing has been published here yet.

+ + +
+ +
+ + + diff --git a/templates/author.html b/templates/author.html index 6696e71..f93a66e 100644 --- a/templates/author.html +++ b/templates/author.html @@ -1,7 +1,7 @@ - -
+ +
@@ -13,8 +13,8 @@

Community posts

- -
+ +
@@ -31,8 +31,8 @@

Articles

- -
+ +
diff --git a/templates/front-page.html b/templates/front-page.html new file mode 100644 index 0000000..08a7843 --- /dev/null +++ b/templates/front-page.html @@ -0,0 +1,27 @@ + + + +
+ +
+ +
+ +

Independent by design

+ + + +
+ +
+ + + +
+ +
+ +
+ + + diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..45c3612 --- /dev/null +++ b/templates/home.html @@ -0,0 +1,61 @@ + + + +
+ +
+ +

Journal

+ + +

Ideas, stories and updates.

+ +
+ + + +
+ + +
+ + +
+ +
+ + +
+ + + +
+ +
+ + + + + + + + + + + +
+ +

Ready for your content.

+ + +

Publish or import a post to see it appear in this journal.

+ +
+ + +
+ +
+ + + diff --git a/templates/index.html b/templates/index.html index 8e31e0a..4c36d0d 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,18 +1,32 @@ - -
- -
- - - + +
+ +
+ + +
+ + +
+ + + +
+ +
+ - + + + + + - -

No content is available for preview.

- + +

No content is available yet.

+
diff --git a/templates/page.html b/templates/page.html new file mode 100644 index 0000000..68a821e --- /dev/null +++ b/templates/page.html @@ -0,0 +1,15 @@ + + + +
+ +
+ + + +
+ +
+ + + diff --git a/templates/search.html b/templates/search.html new file mode 100644 index 0000000..96b843f --- /dev/null +++ b/templates/search.html @@ -0,0 +1,39 @@ + + + +
+ +
+ + +
+ + +
+ + +
+ + +
+ + + + + + + + + +
+

No matches yet.

+

Try a broader phrase or browse through the navigation.

+
+ + +
+ +
+ + + diff --git a/templates/single-community_post.html b/templates/single-community_post.html index afaad66..e81a78b 100644 --- a/templates/single-community_post.html +++ b/templates/single-community_post.html @@ -1,7 +1,7 @@ - -
+ +
diff --git a/templates/single.html b/templates/single.html index a721d8c..78d4caa 100644 --- a/templates/single.html +++ b/templates/single.html @@ -1,38 +1,42 @@ - -
- - - - -
- - + +
+ +
+ + + +
+ + +
+ + + + + +
+ + +
+ + + +
+ + + + +
+ + + + +
+
- - - - - - -
- - - -
- - - - -
- - - - -
-
diff --git a/theme.json b/theme.json index d11bff2..d2545ba 100644 --- a/theme.json +++ b/theme.json @@ -9,27 +9,123 @@ "defaultPalette": false, "palette": [ { - "color": "#18181b", + "color": "#19181d", "name": "Foreground", "slug": "foreground" }, { - "color": "#f7f7f8", + "color": "#f7f5f2", "name": "Background", "slug": "background" }, { - "color": "#7c3aed", + "color": "#5c4ce6", + "name": "Accent", + "slug": "accent" + }, + { + "color": "#e8e4ff", + "name": "Accent soft", + "slug": "accent-soft" + }, + { + "color": "#d8ff72", + "name": "Highlight", + "slug": "highlight" + }, + { + "color": "#5c4ce6", "name": "Brand", "slug": "brand" + }, + { + "color": "#c4b5fd", + "name": "Brand light", + "slug": "brand-light" + }, + { + "color": "#4c1d95", + "name": "Brand dark", + "slug": "brand-dark" + }, + { + "color": "#ffffff", + "name": "Surface", + "slug": "surface" + }, + { + "color": "#09090b", + "name": "Surface dark", + "slug": "surface-dark" + }, + { + "color": "#e4e4e7", + "name": "Border", + "slug": "border" + }, + { + "color": "#71717a", + "name": "Muted", + "slug": "muted" + } + ], + "gradients": [ + { + "gradient": "linear-gradient(135deg, #7c3aed 0%, #ec4899 100%)", + "name": "Brand gradient", + "slug": "brand-gradient" } ] }, + "custom": { + "fc": { + "radius": "16px", + "loader": { + "size": "44px", + "duration": "1400ms", + "glow-color": "#c4b5fd", + "glow-opacity": "0.55" + }, + "shell": { + "radius": "18px" + } + } + }, "layout": { + "allowCustomContentAndWideSize": true, + "allowEditing": true, "contentSize": "720px", "wideSize": "1200px" }, "spacing": { + "defaultSpacingSizes": false, + "spacingSizes": [ + { + "name": "2X Small", + "size": "0.5rem", + "slug": "20" + }, + { + "name": "Small", + "size": "1rem", + "slug": "30" + }, + { + "name": "Medium", + "size": "clamp(1.5rem, 3vw, 2.25rem)", + "slug": "40" + }, + { + "name": "Large", + "size": "clamp(2.5rem, 6vw, 5rem)", + "slug": "50" + }, + { + "name": "2X Large", + "size": "clamp(4rem, 10vw, 8rem)", + "slug": "60" + } + ], "units": [ "px", "rem", @@ -38,12 +134,65 @@ ] }, "typography": { + "customFontSize": true, + "defaultFontSizes": false, + "dropCap": true, "fluid": true, + "fontStyle": true, + "fontWeight": true, + "letterSpacing": true, + "lineHeight": true, + "textAlign": true, + "textColumns": true, + "textDecoration": true, + "textIndent": "all", + "textTransform": true, + "writingMode": true, "fontFamilies": [ { - "fontFamily": "-apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif", + "fontFamily": "Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif", "name": "System", "slug": "system" + }, + { + "fontFamily": "\"Arial Rounded MT Bold\", \"SF Pro Rounded\", Inter, ui-sans-serif, sans-serif", + "name": "Display", + "slug": "display" + } + ], + "fontSizes": [ + { + "fluid": false, + "name": "Small", + "size": "13px", + "slug": "small" + }, + { + "fluid": { + "min": "14px", + "max": "20px" + }, + "name": "Medium", + "size": "20px", + "slug": "medium" + }, + { + "fluid": { + "min": "22px", + "max": "36px" + }, + "name": "Large", + "size": "36px", + "slug": "large" + }, + { + "fluid": { + "min": "25px", + "max": "42px" + }, + "name": "Extra Large", + "size": "42px", + "slug": "x-large" } ] }, @@ -63,12 +212,104 @@ }, "typography": { "fontFamily": "var:preset|font-family|system", - "lineHeight": "1.6" + "fontSize": "16px", + "lineHeight": "1.65" }, "elements": { "link": { "color": { "text": "var:preset|color|brand" + }, + ":hover": { + "color": { + "text": "var:preset|color|brand-dark" + } + } + }, + "button": { + "border": { + "radius": "var(--wp--custom--fc--radius, 16px)" + }, + "color": { + "background": "var:preset|color|brand", + "text": "var:preset|color|surface" + }, + ":hover": { + "color": { + "background": "var:preset|color|brand-dark", + "text": "var:preset|color|surface" + } + } + }, + "heading": { + "typography": { + "fontFamily": "var:preset|font-family|display", + "fontWeight": "680", + "letterSpacing": "-0.035em", + "lineHeight": "1.08" + } + } + }, + "blocks": { + "core/heading": { + "spacing": { + "margin": { + "top": "0.5em", + "bottom": "0.3em" + } + } + }, + "core/paragraph": { + "spacing": { + "margin": { + "top": "0.35em", + "bottom": "0.35em" + } + } + }, + "core/navigation": { + "typography": { + "fontSize": "0.9rem", + "fontWeight": "500" + } + }, + "core/button": { + "border": { + "radius": "var(--wp--custom--fc--radius, 16px)" + }, + "typography": { + "fontSize": "0.85rem", + "fontWeight": "650" + }, + "spacing": { + "padding": { + "bottom": "0.8rem", + "left": "1.2rem", + "right": "1.2rem", + "top": "0.8rem" + } + } + }, + "core/image": { + "border": { + "radius": "var(--wp--custom--shell--radius, 18px)" + } + }, + "core/post-featured-image": { + "border": { + "radius": "var(--wp--custom--shell--radius, 18px)" + } + }, + "core/quote": { + "border": { + "color": "var:preset|color|accent", + "style": "solid", + "width": "0 0 0 3px" + }, + "spacing": { + "padding": { + "left": "1.5rem" + } } } }