@linguanahq/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +43 -0
- package/dist/index.js +1543 -0
- package/dist/templates/blog/react/.env.example +15 -0
- package/dist/templates/blog/react/ASSETS.md +1 -0
- package/dist/templates/blog/react/DEPLOY.md +23 -0
- package/dist/templates/blog/react/LICENSE +21 -0
- package/dist/templates/blog/react/README.md +36 -0
- package/dist/templates/blog/react/_gitignore +11 -0
- package/dist/templates/blog/react/index.html +2 -0
- package/dist/templates/blog/react/linguana.json +10 -0
- package/dist/templates/blog/react/package.json +34 -0
- package/dist/templates/blog/react/public/assets/scene-1.svg +1 -0
- package/dist/templates/blog/react/public/assets/scene-2.svg +1 -0
- package/dist/templates/blog/react/public/assets/scene-3.svg +1 -0
- package/dist/templates/blog/react/public/assets/scene-4.svg +1 -0
- package/dist/templates/blog/react/public/assets/scene-5.svg +1 -0
- package/dist/templates/blog/react/public/assets/scene-6.svg +1 -0
- package/dist/templates/blog/react/src/App.tsx +465 -0
- package/dist/templates/blog/react/src/Copy.tsx +322 -0
- package/dist/templates/blog/react/src/demo-messages.ts +221 -0
- package/dist/templates/blog/react/src/i18n.ts +62 -0
- package/dist/templates/blog/react/src/main.tsx +11 -0
- package/dist/templates/blog/react/src/messages.ts +71 -0
- package/dist/templates/blog/react/src/model.ts +128 -0
- package/dist/templates/blog/react/src/styles.css +1159 -0
- package/dist/templates/blog/react/src/vite-env.d.ts +1 -0
- package/dist/templates/blog/react/tsconfig.json +23 -0
- package/dist/templates/blog/react/vite.config.ts +36 -0
- package/dist/templates/blog/react/wrangler.jsonc +8 -0
- package/dist/templates/blog/solidjs/.env.example +15 -0
- package/dist/templates/blog/solidjs/ASSETS.md +1 -0
- package/dist/templates/blog/solidjs/DEPLOY.md +23 -0
- package/dist/templates/blog/solidjs/LICENSE +21 -0
- package/dist/templates/blog/solidjs/README.md +36 -0
- package/dist/templates/blog/solidjs/_gitignore +11 -0
- package/dist/templates/blog/solidjs/index.html +2 -0
- package/dist/templates/blog/solidjs/linguana.json +10 -0
- package/dist/templates/blog/solidjs/package.json +31 -0
- package/dist/templates/blog/solidjs/public/assets/scene-1.svg +1 -0
- package/dist/templates/blog/solidjs/public/assets/scene-2.svg +1 -0
- package/dist/templates/blog/solidjs/public/assets/scene-3.svg +1 -0
- package/dist/templates/blog/solidjs/public/assets/scene-4.svg +1 -0
- package/dist/templates/blog/solidjs/public/assets/scene-5.svg +1 -0
- package/dist/templates/blog/solidjs/public/assets/scene-6.svg +1 -0
- package/dist/templates/blog/solidjs/src/App.tsx +457 -0
- package/dist/templates/blog/solidjs/src/Copy.tsx +322 -0
- package/dist/templates/blog/solidjs/src/demo-messages.ts +221 -0
- package/dist/templates/blog/solidjs/src/i18n.ts +62 -0
- package/dist/templates/blog/solidjs/src/main.tsx +14 -0
- package/dist/templates/blog/solidjs/src/messages.ts +71 -0
- package/dist/templates/blog/solidjs/src/model.ts +128 -0
- package/dist/templates/blog/solidjs/src/styles.css +1159 -0
- package/dist/templates/blog/solidjs/src/vite-env.d.ts +1 -0
- package/dist/templates/blog/solidjs/tsconfig.json +24 -0
- package/dist/templates/blog/solidjs/vite.config.ts +36 -0
- package/dist/templates/blog/solidjs/wrangler.jsonc +8 -0
- package/dist/templates/blog/svelte/.env.example +15 -0
- package/dist/templates/blog/svelte/ASSETS.md +1 -0
- package/dist/templates/blog/svelte/DEPLOY.md +23 -0
- package/dist/templates/blog/svelte/LICENSE +21 -0
- package/dist/templates/blog/svelte/README.md +36 -0
- package/dist/templates/blog/svelte/_gitignore +11 -0
- package/dist/templates/blog/svelte/index.html +2 -0
- package/dist/templates/blog/svelte/linguana.json +10 -0
- package/dist/templates/blog/svelte/package.json +32 -0
- package/dist/templates/blog/svelte/public/assets/scene-1.svg +1 -0
- package/dist/templates/blog/svelte/public/assets/scene-2.svg +1 -0
- package/dist/templates/blog/svelte/public/assets/scene-3.svg +1 -0
- package/dist/templates/blog/svelte/public/assets/scene-4.svg +1 -0
- package/dist/templates/blog/svelte/public/assets/scene-5.svg +1 -0
- package/dist/templates/blog/svelte/public/assets/scene-6.svg +1 -0
- package/dist/templates/blog/svelte/src/App.svelte +196 -0
- package/dist/templates/blog/svelte/src/Copy.svelte +60 -0
- package/dist/templates/blog/svelte/src/Root.svelte +11 -0
- package/dist/templates/blog/svelte/src/demo-messages.ts +221 -0
- package/dist/templates/blog/svelte/src/i18n.ts +62 -0
- package/dist/templates/blog/svelte/src/main.ts +5 -0
- package/dist/templates/blog/svelte/src/messages.ts +71 -0
- package/dist/templates/blog/svelte/src/model.ts +128 -0
- package/dist/templates/blog/svelte/src/styles.css +1159 -0
- package/dist/templates/blog/svelte/src/vite-env.d.ts +1 -0
- package/dist/templates/blog/svelte/svelte.config.js +2 -0
- package/dist/templates/blog/svelte/tsconfig.json +23 -0
- package/dist/templates/blog/svelte/vite.config.ts +36 -0
- package/dist/templates/blog/svelte/wrangler.jsonc +8 -0
- package/dist/templates/blog/tanstack-start/.env.example +15 -0
- package/dist/templates/blog/tanstack-start/ASSETS.md +1 -0
- package/dist/templates/blog/tanstack-start/DEPLOY.md +23 -0
- package/dist/templates/blog/tanstack-start/LICENSE +21 -0
- package/dist/templates/blog/tanstack-start/README.md +36 -0
- package/dist/templates/blog/tanstack-start/_gitignore +11 -0
- package/dist/templates/blog/tanstack-start/linguana.json +10 -0
- package/dist/templates/blog/tanstack-start/nitro.config.ts +2 -0
- package/dist/templates/blog/tanstack-start/package.json +42 -0
- package/dist/templates/blog/tanstack-start/public/assets/scene-1.svg +1 -0
- package/dist/templates/blog/tanstack-start/public/assets/scene-2.svg +1 -0
- package/dist/templates/blog/tanstack-start/public/assets/scene-3.svg +1 -0
- package/dist/templates/blog/tanstack-start/public/assets/scene-4.svg +1 -0
- package/dist/templates/blog/tanstack-start/public/assets/scene-5.svg +1 -0
- package/dist/templates/blog/tanstack-start/public/assets/scene-6.svg +1 -0
- package/dist/templates/blog/tanstack-start/src/App.tsx +450 -0
- package/dist/templates/blog/tanstack-start/src/Copy.tsx +322 -0
- package/dist/templates/blog/tanstack-start/src/demo-messages.ts +221 -0
- package/dist/templates/blog/tanstack-start/src/i18n.ts +62 -0
- package/dist/templates/blog/tanstack-start/src/locale-server.ts +54 -0
- package/dist/templates/blog/tanstack-start/src/messages.ts +71 -0
- package/dist/templates/blog/tanstack-start/src/model.ts +128 -0
- package/dist/templates/blog/tanstack-start/src/router.tsx +10 -0
- package/dist/templates/blog/tanstack-start/src/routes/$.tsx +7 -0
- package/dist/templates/blog/tanstack-start/src/routes/__root.tsx +50 -0
- package/dist/templates/blog/tanstack-start/src/routes/index.tsx +5 -0
- package/dist/templates/blog/tanstack-start/src/styles.css +1159 -0
- package/dist/templates/blog/tanstack-start/src/vite-env.d.ts +1 -0
- package/dist/templates/blog/tanstack-start/tsconfig.json +23 -0
- package/dist/templates/blog/tanstack-start/vite.config.ts +50 -0
- package/dist/templates/blog/tanstack-start/wrangler.jsonc +12 -0
- package/dist/templates/blog/vue/.env.example +15 -0
- package/dist/templates/blog/vue/ASSETS.md +1 -0
- package/dist/templates/blog/vue/DEPLOY.md +23 -0
- package/dist/templates/blog/vue/LICENSE +21 -0
- package/dist/templates/blog/vue/README.md +36 -0
- package/dist/templates/blog/vue/_gitignore +11 -0
- package/dist/templates/blog/vue/index.html +2 -0
- package/dist/templates/blog/vue/linguana.json +10 -0
- package/dist/templates/blog/vue/package.json +34 -0
- package/dist/templates/blog/vue/public/assets/scene-1.svg +1 -0
- package/dist/templates/blog/vue/public/assets/scene-2.svg +1 -0
- package/dist/templates/blog/vue/public/assets/scene-3.svg +1 -0
- package/dist/templates/blog/vue/public/assets/scene-4.svg +1 -0
- package/dist/templates/blog/vue/public/assets/scene-5.svg +1 -0
- package/dist/templates/blog/vue/public/assets/scene-6.svg +1 -0
- package/dist/templates/blog/vue/src/App.vue +188 -0
- package/dist/templates/blog/vue/src/Copy.vue +59 -0
- package/dist/templates/blog/vue/src/demo-messages.ts +221 -0
- package/dist/templates/blog/vue/src/i18n.ts +62 -0
- package/dist/templates/blog/vue/src/main.ts +7 -0
- package/dist/templates/blog/vue/src/messages.ts +71 -0
- package/dist/templates/blog/vue/src/model.ts +128 -0
- package/dist/templates/blog/vue/src/styles.css +1159 -0
- package/dist/templates/blog/vue/src/vite-env.d.ts +1 -0
- package/dist/templates/blog/vue/tsconfig.json +23 -0
- package/dist/templates/blog/vue/vite.config.ts +36 -0
- package/dist/templates/blog/vue/wrangler.jsonc +8 -0
- package/dist/templates/saas/react/.env.example +15 -0
- package/dist/templates/saas/react/ASSETS.md +1 -0
- package/dist/templates/saas/react/DEPLOY.md +23 -0
- package/dist/templates/saas/react/LICENSE +21 -0
- package/dist/templates/saas/react/README.md +36 -0
- package/dist/templates/saas/react/_gitignore +11 -0
- package/dist/templates/saas/react/index.html +2 -0
- package/dist/templates/saas/react/linguana.json +10 -0
- package/dist/templates/saas/react/package.json +34 -0
- package/dist/templates/saas/react/src/App.tsx +631 -0
- package/dist/templates/saas/react/src/Copy.tsx +380 -0
- package/dist/templates/saas/react/src/demo-messages.ts +228 -0
- package/dist/templates/saas/react/src/i18n.ts +62 -0
- package/dist/templates/saas/react/src/main.tsx +11 -0
- package/dist/templates/saas/react/src/messages.ts +77 -0
- package/dist/templates/saas/react/src/model.ts +73 -0
- package/dist/templates/saas/react/src/styles.css +1159 -0
- package/dist/templates/saas/react/src/vite-env.d.ts +1 -0
- package/dist/templates/saas/react/tsconfig.json +23 -0
- package/dist/templates/saas/react/vite.config.ts +36 -0
- package/dist/templates/saas/react/wrangler.jsonc +8 -0
- package/dist/templates/saas/solidjs/.env.example +15 -0
- package/dist/templates/saas/solidjs/ASSETS.md +1 -0
- package/dist/templates/saas/solidjs/DEPLOY.md +23 -0
- package/dist/templates/saas/solidjs/LICENSE +21 -0
- package/dist/templates/saas/solidjs/README.md +36 -0
- package/dist/templates/saas/solidjs/_gitignore +11 -0
- package/dist/templates/saas/solidjs/index.html +2 -0
- package/dist/templates/saas/solidjs/linguana.json +10 -0
- package/dist/templates/saas/solidjs/package.json +31 -0
- package/dist/templates/saas/solidjs/src/App.tsx +629 -0
- package/dist/templates/saas/solidjs/src/Copy.tsx +380 -0
- package/dist/templates/saas/solidjs/src/demo-messages.ts +228 -0
- package/dist/templates/saas/solidjs/src/i18n.ts +62 -0
- package/dist/templates/saas/solidjs/src/main.tsx +14 -0
- package/dist/templates/saas/solidjs/src/messages.ts +77 -0
- package/dist/templates/saas/solidjs/src/model.ts +73 -0
- package/dist/templates/saas/solidjs/src/styles.css +1159 -0
- package/dist/templates/saas/solidjs/src/vite-env.d.ts +1 -0
- package/dist/templates/saas/solidjs/tsconfig.json +24 -0
- package/dist/templates/saas/solidjs/vite.config.ts +36 -0
- package/dist/templates/saas/solidjs/wrangler.jsonc +8 -0
- package/dist/templates/saas/svelte/.env.example +15 -0
- package/dist/templates/saas/svelte/ASSETS.md +1 -0
- package/dist/templates/saas/svelte/DEPLOY.md +23 -0
- package/dist/templates/saas/svelte/LICENSE +21 -0
- package/dist/templates/saas/svelte/README.md +36 -0
- package/dist/templates/saas/svelte/_gitignore +11 -0
- package/dist/templates/saas/svelte/index.html +2 -0
- package/dist/templates/saas/svelte/linguana.json +10 -0
- package/dist/templates/saas/svelte/package.json +32 -0
- package/dist/templates/saas/svelte/src/App.svelte +252 -0
- package/dist/templates/saas/svelte/src/Copy.svelte +74 -0
- package/dist/templates/saas/svelte/src/Root.svelte +11 -0
- package/dist/templates/saas/svelte/src/demo-messages.ts +228 -0
- package/dist/templates/saas/svelte/src/i18n.ts +62 -0
- package/dist/templates/saas/svelte/src/main.ts +5 -0
- package/dist/templates/saas/svelte/src/messages.ts +77 -0
- package/dist/templates/saas/svelte/src/model.ts +73 -0
- package/dist/templates/saas/svelte/src/styles.css +1159 -0
- package/dist/templates/saas/svelte/src/vite-env.d.ts +1 -0
- package/dist/templates/saas/svelte/svelte.config.js +2 -0
- package/dist/templates/saas/svelte/tsconfig.json +23 -0
- package/dist/templates/saas/svelte/vite.config.ts +36 -0
- package/dist/templates/saas/svelte/wrangler.jsonc +8 -0
- package/dist/templates/saas/tanstack-start/.env.example +15 -0
- package/dist/templates/saas/tanstack-start/ASSETS.md +1 -0
- package/dist/templates/saas/tanstack-start/DEPLOY.md +23 -0
- package/dist/templates/saas/tanstack-start/LICENSE +21 -0
- package/dist/templates/saas/tanstack-start/README.md +36 -0
- package/dist/templates/saas/tanstack-start/_gitignore +11 -0
- package/dist/templates/saas/tanstack-start/linguana.json +10 -0
- package/dist/templates/saas/tanstack-start/nitro.config.ts +2 -0
- package/dist/templates/saas/tanstack-start/package.json +42 -0
- package/dist/templates/saas/tanstack-start/src/App.tsx +616 -0
- package/dist/templates/saas/tanstack-start/src/Copy.tsx +380 -0
- package/dist/templates/saas/tanstack-start/src/demo-messages.ts +228 -0
- package/dist/templates/saas/tanstack-start/src/i18n.ts +62 -0
- package/dist/templates/saas/tanstack-start/src/locale-server.ts +54 -0
- package/dist/templates/saas/tanstack-start/src/messages.ts +77 -0
- package/dist/templates/saas/tanstack-start/src/model.ts +73 -0
- package/dist/templates/saas/tanstack-start/src/router.tsx +10 -0
- package/dist/templates/saas/tanstack-start/src/routes/$.tsx +7 -0
- package/dist/templates/saas/tanstack-start/src/routes/__root.tsx +50 -0
- package/dist/templates/saas/tanstack-start/src/routes/index.tsx +5 -0
- package/dist/templates/saas/tanstack-start/src/styles.css +1159 -0
- package/dist/templates/saas/tanstack-start/src/vite-env.d.ts +1 -0
- package/dist/templates/saas/tanstack-start/tsconfig.json +23 -0
- package/dist/templates/saas/tanstack-start/vite.config.ts +50 -0
- package/dist/templates/saas/tanstack-start/wrangler.jsonc +12 -0
- package/dist/templates/saas/vue/.env.example +15 -0
- package/dist/templates/saas/vue/ASSETS.md +1 -0
- package/dist/templates/saas/vue/DEPLOY.md +23 -0
- package/dist/templates/saas/vue/LICENSE +21 -0
- package/dist/templates/saas/vue/README.md +36 -0
- package/dist/templates/saas/vue/_gitignore +11 -0
- package/dist/templates/saas/vue/index.html +2 -0
- package/dist/templates/saas/vue/linguana.json +10 -0
- package/dist/templates/saas/vue/package.json +34 -0
- package/dist/templates/saas/vue/src/App.vue +244 -0
- package/dist/templates/saas/vue/src/Copy.vue +73 -0
- package/dist/templates/saas/vue/src/demo-messages.ts +228 -0
- package/dist/templates/saas/vue/src/i18n.ts +62 -0
- package/dist/templates/saas/vue/src/main.ts +7 -0
- package/dist/templates/saas/vue/src/messages.ts +77 -0
- package/dist/templates/saas/vue/src/model.ts +73 -0
- package/dist/templates/saas/vue/src/styles.css +1159 -0
- package/dist/templates/saas/vue/src/vite-env.d.ts +1 -0
- package/dist/templates/saas/vue/tsconfig.json +23 -0
- package/dist/templates/saas/vue/vite.config.ts +36 -0
- package/dist/templates/saas/vue/wrangler.jsonc +8 -0
- package/dist/templates/storefront/react/.env.example +15 -0
- package/dist/templates/storefront/react/ASSETS.md +1 -0
- package/dist/templates/storefront/react/DEPLOY.md +23 -0
- package/dist/templates/storefront/react/LICENSE +21 -0
- package/dist/templates/storefront/react/README.md +36 -0
- package/dist/templates/storefront/react/_gitignore +11 -0
- package/dist/templates/storefront/react/index.html +2 -0
- package/dist/templates/storefront/react/linguana.json +10 -0
- package/dist/templates/storefront/react/package.json +34 -0
- package/dist/templates/storefront/react/public/assets/collection.svg +1 -0
- package/dist/templates/storefront/react/public/assets/product-1.svg +1 -0
- package/dist/templates/storefront/react/public/assets/product-2.svg +1 -0
- package/dist/templates/storefront/react/public/assets/product-3.svg +1 -0
- package/dist/templates/storefront/react/public/assets/product-4.svg +1 -0
- package/dist/templates/storefront/react/public/assets/product-5.svg +1 -0
- package/dist/templates/storefront/react/public/assets/product-6.svg +1 -0
- package/dist/templates/storefront/react/public/assets/product-7.svg +1 -0
- package/dist/templates/storefront/react/public/assets/product-8.svg +1 -0
- package/dist/templates/storefront/react/src/App.tsx +616 -0
- package/dist/templates/storefront/react/src/Copy.tsx +304 -0
- package/dist/templates/storefront/react/src/demo-messages.ts +205 -0
- package/dist/templates/storefront/react/src/i18n.ts +64 -0
- package/dist/templates/storefront/react/src/main.tsx +11 -0
- package/dist/templates/storefront/react/src/messages.ts +71 -0
- package/dist/templates/storefront/react/src/model.ts +146 -0
- package/dist/templates/storefront/react/src/styles.css +1159 -0
- package/dist/templates/storefront/react/src/vite-env.d.ts +1 -0
- package/dist/templates/storefront/react/tsconfig.json +23 -0
- package/dist/templates/storefront/react/vite.config.ts +36 -0
- package/dist/templates/storefront/react/wrangler.jsonc +8 -0
- package/dist/templates/storefront/solidjs/.env.example +15 -0
- package/dist/templates/storefront/solidjs/ASSETS.md +1 -0
- package/dist/templates/storefront/solidjs/DEPLOY.md +23 -0
- package/dist/templates/storefront/solidjs/LICENSE +21 -0
- package/dist/templates/storefront/solidjs/README.md +36 -0
- package/dist/templates/storefront/solidjs/_gitignore +11 -0
- package/dist/templates/storefront/solidjs/index.html +2 -0
- package/dist/templates/storefront/solidjs/linguana.json +10 -0
- package/dist/templates/storefront/solidjs/package.json +31 -0
- package/dist/templates/storefront/solidjs/public/assets/collection.svg +1 -0
- package/dist/templates/storefront/solidjs/public/assets/product-1.svg +1 -0
- package/dist/templates/storefront/solidjs/public/assets/product-2.svg +1 -0
- package/dist/templates/storefront/solidjs/public/assets/product-3.svg +1 -0
- package/dist/templates/storefront/solidjs/public/assets/product-4.svg +1 -0
- package/dist/templates/storefront/solidjs/public/assets/product-5.svg +1 -0
- package/dist/templates/storefront/solidjs/public/assets/product-6.svg +1 -0
- package/dist/templates/storefront/solidjs/public/assets/product-7.svg +1 -0
- package/dist/templates/storefront/solidjs/public/assets/product-8.svg +1 -0
- package/dist/templates/storefront/solidjs/src/App.tsx +612 -0
- package/dist/templates/storefront/solidjs/src/Copy.tsx +304 -0
- package/dist/templates/storefront/solidjs/src/demo-messages.ts +205 -0
- package/dist/templates/storefront/solidjs/src/i18n.ts +64 -0
- package/dist/templates/storefront/solidjs/src/main.tsx +14 -0
- package/dist/templates/storefront/solidjs/src/messages.ts +71 -0
- package/dist/templates/storefront/solidjs/src/model.ts +146 -0
- package/dist/templates/storefront/solidjs/src/styles.css +1159 -0
- package/dist/templates/storefront/solidjs/src/vite-env.d.ts +1 -0
- package/dist/templates/storefront/solidjs/tsconfig.json +24 -0
- package/dist/templates/storefront/solidjs/vite.config.ts +36 -0
- package/dist/templates/storefront/solidjs/wrangler.jsonc +8 -0
- package/dist/templates/storefront/svelte/.env.example +15 -0
- package/dist/templates/storefront/svelte/ASSETS.md +1 -0
- package/dist/templates/storefront/svelte/DEPLOY.md +23 -0
- package/dist/templates/storefront/svelte/LICENSE +21 -0
- package/dist/templates/storefront/svelte/README.md +36 -0
- package/dist/templates/storefront/svelte/_gitignore +11 -0
- package/dist/templates/storefront/svelte/index.html +2 -0
- package/dist/templates/storefront/svelte/linguana.json +10 -0
- package/dist/templates/storefront/svelte/package.json +32 -0
- package/dist/templates/storefront/svelte/public/assets/collection.svg +1 -0
- package/dist/templates/storefront/svelte/public/assets/product-1.svg +1 -0
- package/dist/templates/storefront/svelte/public/assets/product-2.svg +1 -0
- package/dist/templates/storefront/svelte/public/assets/product-3.svg +1 -0
- package/dist/templates/storefront/svelte/public/assets/product-4.svg +1 -0
- package/dist/templates/storefront/svelte/public/assets/product-5.svg +1 -0
- package/dist/templates/storefront/svelte/public/assets/product-6.svg +1 -0
- package/dist/templates/storefront/svelte/public/assets/product-7.svg +1 -0
- package/dist/templates/storefront/svelte/public/assets/product-8.svg +1 -0
- package/dist/templates/storefront/svelte/src/App.svelte +218 -0
- package/dist/templates/storefront/svelte/src/Copy.svelte +66 -0
- package/dist/templates/storefront/svelte/src/Root.svelte +11 -0
- package/dist/templates/storefront/svelte/src/demo-messages.ts +205 -0
- package/dist/templates/storefront/svelte/src/i18n.ts +64 -0
- package/dist/templates/storefront/svelte/src/main.ts +5 -0
- package/dist/templates/storefront/svelte/src/messages.ts +71 -0
- package/dist/templates/storefront/svelte/src/model.ts +146 -0
- package/dist/templates/storefront/svelte/src/styles.css +1159 -0
- package/dist/templates/storefront/svelte/src/vite-env.d.ts +1 -0
- package/dist/templates/storefront/svelte/svelte.config.js +2 -0
- package/dist/templates/storefront/svelte/tsconfig.json +23 -0
- package/dist/templates/storefront/svelte/vite.config.ts +36 -0
- package/dist/templates/storefront/svelte/wrangler.jsonc +8 -0
- package/dist/templates/storefront/tanstack-start/.env.example +15 -0
- package/dist/templates/storefront/tanstack-start/ASSETS.md +1 -0
- package/dist/templates/storefront/tanstack-start/DEPLOY.md +23 -0
- package/dist/templates/storefront/tanstack-start/LICENSE +21 -0
- package/dist/templates/storefront/tanstack-start/README.md +36 -0
- package/dist/templates/storefront/tanstack-start/_gitignore +11 -0
- package/dist/templates/storefront/tanstack-start/linguana.json +10 -0
- package/dist/templates/storefront/tanstack-start/nitro.config.ts +2 -0
- package/dist/templates/storefront/tanstack-start/package.json +42 -0
- package/dist/templates/storefront/tanstack-start/public/assets/collection.svg +1 -0
- package/dist/templates/storefront/tanstack-start/public/assets/product-1.svg +1 -0
- package/dist/templates/storefront/tanstack-start/public/assets/product-2.svg +1 -0
- package/dist/templates/storefront/tanstack-start/public/assets/product-3.svg +1 -0
- package/dist/templates/storefront/tanstack-start/public/assets/product-4.svg +1 -0
- package/dist/templates/storefront/tanstack-start/public/assets/product-5.svg +1 -0
- package/dist/templates/storefront/tanstack-start/public/assets/product-6.svg +1 -0
- package/dist/templates/storefront/tanstack-start/public/assets/product-7.svg +1 -0
- package/dist/templates/storefront/tanstack-start/public/assets/product-8.svg +1 -0
- package/dist/templates/storefront/tanstack-start/src/App.tsx +601 -0
- package/dist/templates/storefront/tanstack-start/src/Copy.tsx +304 -0
- package/dist/templates/storefront/tanstack-start/src/demo-messages.ts +205 -0
- package/dist/templates/storefront/tanstack-start/src/i18n.ts +64 -0
- package/dist/templates/storefront/tanstack-start/src/locale-server.ts +54 -0
- package/dist/templates/storefront/tanstack-start/src/messages.ts +71 -0
- package/dist/templates/storefront/tanstack-start/src/model.ts +146 -0
- package/dist/templates/storefront/tanstack-start/src/router.tsx +10 -0
- package/dist/templates/storefront/tanstack-start/src/routes/$.tsx +7 -0
- package/dist/templates/storefront/tanstack-start/src/routes/__root.tsx +50 -0
- package/dist/templates/storefront/tanstack-start/src/routes/index.tsx +5 -0
- package/dist/templates/storefront/tanstack-start/src/styles.css +1159 -0
- package/dist/templates/storefront/tanstack-start/src/vite-env.d.ts +1 -0
- package/dist/templates/storefront/tanstack-start/tsconfig.json +23 -0
- package/dist/templates/storefront/tanstack-start/vite.config.ts +50 -0
- package/dist/templates/storefront/tanstack-start/wrangler.jsonc +12 -0
- package/dist/templates/storefront/vue/.env.example +15 -0
- package/dist/templates/storefront/vue/ASSETS.md +1 -0
- package/dist/templates/storefront/vue/DEPLOY.md +23 -0
- package/dist/templates/storefront/vue/LICENSE +21 -0
- package/dist/templates/storefront/vue/README.md +36 -0
- package/dist/templates/storefront/vue/_gitignore +11 -0
- package/dist/templates/storefront/vue/index.html +2 -0
- package/dist/templates/storefront/vue/linguana.json +10 -0
- package/dist/templates/storefront/vue/package.json +34 -0
- package/dist/templates/storefront/vue/public/assets/collection.svg +1 -0
- package/dist/templates/storefront/vue/public/assets/product-1.svg +1 -0
- package/dist/templates/storefront/vue/public/assets/product-2.svg +1 -0
- package/dist/templates/storefront/vue/public/assets/product-3.svg +1 -0
- package/dist/templates/storefront/vue/public/assets/product-4.svg +1 -0
- package/dist/templates/storefront/vue/public/assets/product-5.svg +1 -0
- package/dist/templates/storefront/vue/public/assets/product-6.svg +1 -0
- package/dist/templates/storefront/vue/public/assets/product-7.svg +1 -0
- package/dist/templates/storefront/vue/public/assets/product-8.svg +1 -0
- package/dist/templates/storefront/vue/src/App.vue +210 -0
- package/dist/templates/storefront/vue/src/Copy.vue +65 -0
- package/dist/templates/storefront/vue/src/demo-messages.ts +205 -0
- package/dist/templates/storefront/vue/src/i18n.ts +64 -0
- package/dist/templates/storefront/vue/src/main.ts +7 -0
- package/dist/templates/storefront/vue/src/messages.ts +71 -0
- package/dist/templates/storefront/vue/src/model.ts +146 -0
- package/dist/templates/storefront/vue/src/styles.css +1159 -0
- package/dist/templates/storefront/vue/src/vite-env.d.ts +1 -0
- package/dist/templates/storefront/vue/tsconfig.json +23 -0
- package/dist/templates/storefront/vue/vite.config.ts +36 -0
- package/dist/templates/storefront/vue/wrangler.jsonc +8 -0
- package/package.json +38 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Demo mode works without credentials. Connect with npx @linguanahq/cli link.
|
|
2
|
+
# Copying this file alone does not enable connected mode; use linguana.json.
|
|
3
|
+
# Build/server only. NEVER prefix the project token with VITE_.
|
|
4
|
+
LINGUANA_PROJECT_ID=
|
|
5
|
+
LINGUANA_ENVIRONMENT=development
|
|
6
|
+
LINGUANA_API_URL=https://linguana-api.mohammedibrahim.dev
|
|
7
|
+
LINGUANA_PROJECT_TOKEN=
|
|
8
|
+
LINGUANA_UPLOAD=false
|
|
9
|
+
# Paid translation is disabled; this template does not enable it automatically.
|
|
10
|
+
LINGUANA_TRANSLATE=false
|
|
11
|
+
# Browser-safe values. A publishable key reads published catalogs only.
|
|
12
|
+
VITE_LINGUANA_PROJECT_ID=
|
|
13
|
+
VITE_LINGUANA_ENVIRONMENT=development
|
|
14
|
+
VITE_LINGUANA_API_URL=https://linguana-api.mohammedibrahim.dev
|
|
15
|
+
VITE_LINGUANA_PUBLISHABLE_KEY=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
All illustrations in public/assets were drawn for these templates and are covered by LICENSE (MIT). Sample writing and translations are also MIT. No remote images, tracking pixels, or font services are used. Product images and prices describe fictional demonstration goods.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Deploy Elsewhere
|
|
2
|
+
|
|
3
|
+
Demo mode needs no Linguana account. Hosting authentication is separate from the Linguana CLI.
|
|
4
|
+
|
|
5
|
+
## Cloudflare Workers
|
|
6
|
+
|
|
7
|
+
Install dependencies, choose a unique Worker name in wrangler.jsonc, then:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npx wrangler login
|
|
11
|
+
npm run build
|
|
12
|
+
npx wrangler deploy
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Wrangler serves dist as static assets with single-page navigation fallback. Deep article/product links continue to work.
|
|
16
|
+
|
|
17
|
+
## Any static host
|
|
18
|
+
|
|
19
|
+
Run `npm run build` and publish dist/. Configure unknown paths to serve /index.html with status 200 (SPA fallback). A static preview can be started with `npm run preview`. Do not publish the project root.
|
|
20
|
+
|
|
21
|
+
## Connected deployments
|
|
22
|
+
|
|
23
|
+
Pull the intended environment with `linguana env pull --environment production`. Configure the variables from .env.example in the build host, including the matching public project/environment values. Build tokens are secrets used by the compiler only. Never upload .env files or the CLI account session. LINGUANA_TRANSLATE remains false. Catalog publication is an explicit dashboard action.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Linguana contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Elsewhere
|
|
2
|
+
|
|
3
|
+
A journal for the places worth taking your time in. Articles, search, and thoughtful reading layouts.
|
|
4
|
+
|
|
5
|
+
A standalone react app, generated from Linguana templates v0.1.0. Requires Node 22.12+ (or Bun).
|
|
6
|
+
|
|
7
|
+
## Run
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install
|
|
11
|
+
npm run dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
You can also use pnpm, Yarn, or Bun. English, French, Spanish, and Arabic catalogs are bundled in demo mode. No Linguana API requests are made. The language cookie persists between visits. Arabic switches the document to RTL.
|
|
15
|
+
|
|
16
|
+
## Connect your own project
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npx @linguanahq/cli login
|
|
20
|
+
npx @linguanahq/cli link
|
|
21
|
+
npx @linguanahq/cli doctor
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Linking writes nonsecret `linguana.json` and ignored `.env.local`. The account session stays outside this project. Only the public project ID, environment, API URL, and publishable key reach the browser. Build tokens never belong in `VITE_*` variables.
|
|
25
|
+
|
|
26
|
+
Connected mode uses your published project catalogs and English source fallback. It does not load demo translations. Builds can upload the source manifest but do not start paid translations or publish catalogs. Review and publish translations in your dashboard. Existing project languages remain unchanged unless you pass `--locales`.
|
|
27
|
+
|
|
28
|
+
To target another environment, use `linguana env pull --environment staging`, then `npm run build -- --mode staging`. Production uses `.env.production.local` and Vite's default production build mode. Configure matching private and public values on your build host; see .env.example.
|
|
29
|
+
|
|
30
|
+
## Customize
|
|
31
|
+
|
|
32
|
+
Edit native components in src/App.tsx, literal messages in src/Copy.tsx, source fallbacks in src/messages.ts, and bundled demo catalogs in src/demo-messages.ts. Keep explicit message IDs stable. Each Copy branch is an extractable literal message; structured items in src/model.ts hold identifiers, prices, dates, and media only. Build output includes .linguana/manifest.json for review.
|
|
33
|
+
|
|
34
|
+
Six articles include detail routes, category and search filters, localized dates, and a not-found view.
|
|
35
|
+
|
|
36
|
+
Run `npm run check-types` and `npm run build` before deploying. Deployment instructions are in DEPLOY.md. Source and illustrations are MIT; see LICENSE and ASSETS.md.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="description" content="A journal for the places worth taking your time in. Articles, search, and thoughtful reading layouts."><meta name="theme-color" content="#244d64"><title>Elsewhere</title></head><body><div id="app"></div><script type="module" src="/src/main.tsx"></script></body></html>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "blog-react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.12.0"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "vite --host 127.0.0.1",
|
|
12
|
+
"build": "vite build",
|
|
13
|
+
"preview": "vite preview",
|
|
14
|
+
"check-types": "tsc --noEmit",
|
|
15
|
+
"deploy": "npm run build && wrangler deploy"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@linguanahq/catalog": "0.1.1",
|
|
19
|
+
"@linguanahq/core": "0.1.1",
|
|
20
|
+
"@linguanahq/react": "0.1.1",
|
|
21
|
+
"react": "^19.2.7",
|
|
22
|
+
"react-dom": "^19.2.7"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@linguanahq/vite": "0.1.1",
|
|
26
|
+
"typescript": "^6.0.0",
|
|
27
|
+
"vite": "^8.1.3",
|
|
28
|
+
"@types/node": "^24.0.0",
|
|
29
|
+
"wrangler": "^4.127.0",
|
|
30
|
+
"@types/react": "^19.2.17",
|
|
31
|
+
"@types/react-dom": "^19.2.3",
|
|
32
|
+
"@vitejs/plugin-react": "^6.0.3"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" viewBox="0 0 800 600"><title>Original Linguana template illustration</title><rect width="800" height="600" fill="#dbe6e9"/><circle cx="640" cy="115" r="53" fill="#f3e6c9"/><path d="M0 330L170 160 310 290 500 210 800 350V600H0Z" fill="#748e9a"/><g stroke="#f4ead8" stroke-width="5"><path d="M55 260H255V540H55Z" fill="#d18d73"/><path d="M300 190H500V540H300Z" fill="#e9d3ad"/><path d="M545 290H765V540H545Z" fill="#92a4a1"/></g><g fill="#456071"><path d="M80 250L155 205 255 250ZM285 183L397 135 515 183ZM531 283L657 239 779 283Z"/><path d="M91 296h36v58H91zm85 0h36v58h-36zm-85 100h36v58H91zm85 0h36v58h-36zm165-170h38v63h-38zm85 0h38v63h-38zm-85 100h38v63h-38zm85 0h38v63h-38zm162 0h38v64h-38zm92 0h38v64h-38z"/></g><path d="M356 540V448q42-50 84 0v92M118 540v-53q40-50 80 0v53M610 540v-75h70v75" fill="#bc705d"/><path d="M0 545h800v55H0Z" fill="#cecbbb"/><path d="M0 580h800" stroke="#adb9b7" stroke-width="3"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" viewBox="0 0 800 600"><title>Original Linguana template illustration</title><rect width="800" height="600" fill="#dce5db"/><circle cx="640" cy="115" r="53" fill="#f3e6c9"/><path d="M0 300q130-80 300 0t500-10v150H0Z" fill="#9aac9f"/><path d="M0 370 Q200 330 400 380 T800 360V600H0Z" fill="#738d7a"/><path d="M60 430h160m80 35h220m40-40h170M30 530h240m200 15h180" stroke="#dbe7e4" stroke-width="3" opacity=".7"/><g fill="#3f685e"><path d="M75 50l60 280H15ZM195 5l80 340H115ZM700 30l75 330H625ZM770 70l60 300H710Z"/></g><path d="M265 340h270v170H265Z" fill="#b99b7e"/><path d="M240 342l160-125 160 125Z" fill="#526c68"/><path d="M308 380h65v70h-65Zm120 0h65v70h-65Z" fill="#e9d3a2"/><path d="M390 430h30v80h-30Z" fill="#4e6866"/><path d="M0 510q240-80 400 20t400-10v80H0Z" fill="#899e80"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" viewBox="0 0 800 600"><title>Original Linguana template illustration</title><rect width="800" height="600" fill="#d4e5e7"/><circle cx="640" cy="115" r="53" fill="#f3e6c9"/><path d="M0 370 Q200 330 400 380 T800 360V600H0Z" fill="#628697"/><path d="M60 430h160m80 35h220m40-40h170M30 530h240m200 15h180" stroke="#dbe7e4" stroke-width="3" opacity=".7"/><path d="M0 160q130 120 300 155l170 190-280-35L0 600Z" fill="#d9c39e"/><path d="M0 300l275 43 120 72-285 19L0 530" fill="#dec6a0"/><path d="M630 310q120-60 170-55v90Z" fill="#8da39a"/><path d="M30 510q180-80 330-56" fill="none" stroke="#efe0bf" stroke-width="14"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" viewBox="0 0 800 600"><title>Original Linguana template illustration</title><rect width="800" height="600" fill="#dfe6ed"/><circle cx="640" cy="115" r="53" fill="#f3e6c9"/><path d="M0 460L195 110 350 300 470 100 800 470V600H0Z" fill="#7b8fa1"/><path d="M195 110l-45 85 42-20 28 22 13-25zM470 100l-58 110 57-20 20 32 28-40z" fill="#f4f4ec"/><path d="M0 480q160-120 370 10t430-20v130H0Z" fill="#a8b39a"/><path d="M350 600q130-100 65-160" fill="none" stroke="#d9ceb6" stroke-width="18"/><path d="M70 450v80h110v-80Z" fill="#977666"/><path d="M50 450l76-50 78 50Z" fill="#405f65"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" viewBox="0 0 800 600"><title>Original Linguana template illustration</title><rect width="800" height="600" fill="#e0e9e6"/><circle cx="640" cy="115" r="53" fill="#f3e6c9"/><path d="M0 330L170 160 310 290 500 210 800 350V600H0Z" fill="#769496"/><path d="M0 370 Q200 330 400 380 T800 360V600H0Z" fill="#769496"/><path d="M60 430h160m80 35h220m40-40h170M30 530h240m200 15h180" stroke="#dbe7e4" stroke-width="3" opacity=".7"/><g stroke="#f4ead8" stroke-width="5"><path d="M55 260H255V540H55Z" fill="#bc8871"/><path d="M300 190H500V540H300Z" fill="#e9d3ad"/><path d="M545 290H765V540H545Z" fill="#92a4a1"/></g><g fill="#456071"><path d="M80 250L155 205 255 250ZM285 183L397 135 515 183ZM531 283L657 239 779 283Z"/><path d="M91 296h36v58H91zm85 0h36v58h-36zm-85 100h36v58H91zm85 0h36v58h-36zm165-170h38v63h-38zm85 0h38v63h-38zm-85 100h38v63h-38zm85 0h38v63h-38zm162 0h38v64h-38zm92 0h38v64h-38z"/></g><path d="M356 540V448q42-50 84 0v92M118 540v-53q40-50 80 0v53M610 540v-75h70v75" fill="#bc705d"/><path d="M0 545h800v55H0Z" fill="#cecbbb"/><path d="M0 580h800" stroke="#adb9b7" stroke-width="3"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" viewBox="0 0 800 600"><title>Original Linguana template illustration</title><rect width="800" height="600" fill="#e0e7e6"/><circle cx="640" cy="115" r="53" fill="#f3e6c9"/><path d="M0 300q130-80 300 0t500-10v150H0Z" fill="#9aac9f"/><path d="M0 370 Q200 330 400 380 T800 360V600H0Z" fill="#76948a"/><path d="M60 430h160m80 35h220m40-40h170M30 530h240m200 15h180" stroke="#dbe7e4" stroke-width="3" opacity=".7"/><g fill="#3f685e"><path d="M75 50l60 280H15ZM195 5l80 340H115ZM700 30l75 330H625ZM770 70l60 300H710Z"/></g><path d="M265 340h270v170H265Z" fill="#a78f77"/><path d="M240 342l160-125 160 125Z" fill="#526c68"/><path d="M308 380h65v70h-65Zm120 0h65v70h-65Z" fill="#e9d3a2"/><path d="M390 430h30v80h-30Z" fill="#4e6866"/><path d="M0 510q240-80 400 20t400-10v80H0Z" fill="#899e80"/></svg>
|
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { useTranslation } from "@linguanahq/react";
|
|
2
|
+
import { Fragment, useCallback, useEffect, useState } from "react";
|
|
3
|
+
import Copy from "./Copy.tsx";
|
|
4
|
+
import { demoLocales } from "./i18n";
|
|
5
|
+
import { messages } from "./messages";
|
|
6
|
+
import {
|
|
7
|
+
initialState,
|
|
8
|
+
items,
|
|
9
|
+
quantity,
|
|
10
|
+
readCart,
|
|
11
|
+
type State,
|
|
12
|
+
saveCart,
|
|
13
|
+
} from "./model";
|
|
14
|
+
|
|
15
|
+
export default function App({ path }: { path?: string }) {
|
|
16
|
+
const runtime = useTranslation();
|
|
17
|
+
const locale = () => runtime.locale;
|
|
18
|
+
const locales = () =>
|
|
19
|
+
runtime.availableLocales.length
|
|
20
|
+
? runtime.availableLocales
|
|
21
|
+
: demoLocales.filter((item) => item.code === runtime.sourceLocale);
|
|
22
|
+
const translate = runtime.translate;
|
|
23
|
+
const changeLocale = (value: string) => void runtime.setLocale(value);
|
|
24
|
+
const [state, setState] = useState(() => initialState(path));
|
|
25
|
+
const set = useCallback(
|
|
26
|
+
<K extends keyof State>(key: K, value: State[K]) =>
|
|
27
|
+
setState((previous) => ({ ...previous, [key]: value })),
|
|
28
|
+
[],
|
|
29
|
+
);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
set("cart", readCart());
|
|
32
|
+
const pop = () => set("path", window.location.pathname);
|
|
33
|
+
window.addEventListener("popstate", pop);
|
|
34
|
+
return () => window.removeEventListener("popstate", pop);
|
|
35
|
+
}, [set]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
document.documentElement.lang = runtime.locale;
|
|
38
|
+
document.documentElement.dir = runtime.locale.startsWith("ar")
|
|
39
|
+
? "rtl"
|
|
40
|
+
: "ltr";
|
|
41
|
+
}, [runtime.locale]);
|
|
42
|
+
const msg = (id: string, values?: Record<string, string | number>) =>
|
|
43
|
+
String(translate(id, messages[id] ?? id, values));
|
|
44
|
+
const value = (event: Event | { currentTarget: EventTarget | null }) =>
|
|
45
|
+
(event.currentTarget as HTMLInputElement).value;
|
|
46
|
+
const _money = (amount: number) =>
|
|
47
|
+
new Intl.NumberFormat(locale(), {
|
|
48
|
+
style: "currency",
|
|
49
|
+
currency: "USD",
|
|
50
|
+
maximumFractionDigits: 2,
|
|
51
|
+
}).format(amount);
|
|
52
|
+
const date = (input: string) =>
|
|
53
|
+
new Intl.DateTimeFormat(locale(), {
|
|
54
|
+
dateStyle: "medium",
|
|
55
|
+
timeZone: "UTC",
|
|
56
|
+
}).format(new Date(input));
|
|
57
|
+
const visible = () =>
|
|
58
|
+
items
|
|
59
|
+
.filter(
|
|
60
|
+
(item) =>
|
|
61
|
+
(state.category === "all" || item.category === state.category) &&
|
|
62
|
+
`${msg(`${item.id}Title`)} ${msg(`${item.id}Summary`)}`
|
|
63
|
+
.toLocaleLowerCase(locale())
|
|
64
|
+
.includes(state.query.toLocaleLowerCase(locale()).trim()),
|
|
65
|
+
)
|
|
66
|
+
.sort((a, b) =>
|
|
67
|
+
state.sort === "low"
|
|
68
|
+
? a.price - b.price
|
|
69
|
+
: state.sort === "high"
|
|
70
|
+
? b.price - a.price
|
|
71
|
+
: 0,
|
|
72
|
+
);
|
|
73
|
+
const detailHref = (id: string) =>
|
|
74
|
+
`/articles/${items.find((item) => item.id === id)!.slug}`;
|
|
75
|
+
const detail = () => items.find((item) => detailHref(item.id) === state.path);
|
|
76
|
+
const notFound = () => state.path !== "/" && detail() === undefined;
|
|
77
|
+
const clearFilters = () => {
|
|
78
|
+
set("query", "");
|
|
79
|
+
set("category", "all");
|
|
80
|
+
};
|
|
81
|
+
const _count = () =>
|
|
82
|
+
Object.values(state.cart).reduce((total, count) => total + count, 0);
|
|
83
|
+
const cartItems = () =>
|
|
84
|
+
items.filter((item) => (state.cart[item.id] ?? 0) > 0);
|
|
85
|
+
const _total = () =>
|
|
86
|
+
cartItems().reduce(
|
|
87
|
+
(total, item) => total + item.price * state.cart[item.id]!,
|
|
88
|
+
0,
|
|
89
|
+
);
|
|
90
|
+
const updateCart = (id: string, count: number) => {
|
|
91
|
+
const cart = { ...state.cart };
|
|
92
|
+
if (count <= 0) delete cart[id];
|
|
93
|
+
else cart[id] = quantity(count);
|
|
94
|
+
set("cart", cart);
|
|
95
|
+
saveCart(cart);
|
|
96
|
+
};
|
|
97
|
+
const _add = (id: string, count: number) => {
|
|
98
|
+
updateCart(id, (state.cart[id] ?? 0) + count);
|
|
99
|
+
set("added", true);
|
|
100
|
+
set("checkout", false);
|
|
101
|
+
};
|
|
102
|
+
const _checkout = () => {
|
|
103
|
+
set("cart", {});
|
|
104
|
+
saveCart({});
|
|
105
|
+
set("checkout", true);
|
|
106
|
+
set("added", false);
|
|
107
|
+
};
|
|
108
|
+
const _submit = (event: { preventDefault(): void }) => {
|
|
109
|
+
event.preventDefault();
|
|
110
|
+
const valid = Boolean(
|
|
111
|
+
state.name.trim() && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(state.email),
|
|
112
|
+
);
|
|
113
|
+
set("error", !valid);
|
|
114
|
+
set("sent", valid);
|
|
115
|
+
};
|
|
116
|
+
const navigate = (event: {
|
|
117
|
+
currentTarget: EventTarget | null;
|
|
118
|
+
preventDefault(): void;
|
|
119
|
+
metaKey: boolean;
|
|
120
|
+
ctrlKey: boolean;
|
|
121
|
+
shiftKey: boolean;
|
|
122
|
+
altKey: boolean;
|
|
123
|
+
button: number;
|
|
124
|
+
}) => {
|
|
125
|
+
if (
|
|
126
|
+
event.metaKey ||
|
|
127
|
+
event.ctrlKey ||
|
|
128
|
+
event.shiftKey ||
|
|
129
|
+
event.altKey ||
|
|
130
|
+
event.button !== 0
|
|
131
|
+
)
|
|
132
|
+
return;
|
|
133
|
+
const target = new URL((event.currentTarget as HTMLAnchorElement).href);
|
|
134
|
+
if (target.origin !== window.location.origin) return;
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
window.history.pushState({}, "", target.pathname);
|
|
137
|
+
set("path", target.pathname);
|
|
138
|
+
set("quantity", 1);
|
|
139
|
+
set("added", false);
|
|
140
|
+
window.scrollTo(0, 0);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<div className="site blog">
|
|
145
|
+
<a href="#main" className="skip-link">
|
|
146
|
+
<Copy name="skip" />
|
|
147
|
+
</a>
|
|
148
|
+
<header className="site-header">
|
|
149
|
+
<a
|
|
150
|
+
href="/"
|
|
151
|
+
className="brand"
|
|
152
|
+
onClick={(event) => {
|
|
153
|
+
navigate(event);
|
|
154
|
+
}}
|
|
155
|
+
aria-label={`Elsewhere — ${msg("home")}`}
|
|
156
|
+
>
|
|
157
|
+
<span className="brand-mark" aria-hidden="true">
|
|
158
|
+
{"✳"}
|
|
159
|
+
</span>
|
|
160
|
+
{"Elsewhere"}
|
|
161
|
+
</a>
|
|
162
|
+
<nav aria-label={msg("home")}>
|
|
163
|
+
<a
|
|
164
|
+
href="/"
|
|
165
|
+
onClick={(event) => {
|
|
166
|
+
navigate(event);
|
|
167
|
+
}}
|
|
168
|
+
>
|
|
169
|
+
<Copy name="latest" />
|
|
170
|
+
</a>
|
|
171
|
+
<label className="locale-picker">
|
|
172
|
+
<span className="sr-only">
|
|
173
|
+
<Copy name="language" />
|
|
174
|
+
</span>
|
|
175
|
+
<select
|
|
176
|
+
value={locale()}
|
|
177
|
+
aria-label={msg("language")}
|
|
178
|
+
onChange={(event) => {
|
|
179
|
+
changeLocale(value(event));
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
{locales().map((language) => (
|
|
183
|
+
<Fragment key={language.code}>
|
|
184
|
+
<option value={language.code}>{language.nativeName}</option>
|
|
185
|
+
</Fragment>
|
|
186
|
+
))}
|
|
187
|
+
</select>
|
|
188
|
+
</label>
|
|
189
|
+
</nav>
|
|
190
|
+
</header>
|
|
191
|
+
<main id="main">
|
|
192
|
+
{state.path === "/" && (
|
|
193
|
+
<>
|
|
194
|
+
<section className="hero blog-hero">
|
|
195
|
+
<div className="hero-copy">
|
|
196
|
+
<p className="eyebrow">
|
|
197
|
+
<Copy name="eyebrow" />
|
|
198
|
+
</p>
|
|
199
|
+
<h1>
|
|
200
|
+
<Copy name="headline" />
|
|
201
|
+
</h1>
|
|
202
|
+
<p className="intro">
|
|
203
|
+
<Copy name="intro" />
|
|
204
|
+
</p>
|
|
205
|
+
<a href="#journal" className="text-link">
|
|
206
|
+
<Copy name="latest" />
|
|
207
|
+
{" ↘"}
|
|
208
|
+
</a>
|
|
209
|
+
</div>
|
|
210
|
+
<a
|
|
211
|
+
href="/articles/lisbon"
|
|
212
|
+
onClick={(event) => {
|
|
213
|
+
navigate(event);
|
|
214
|
+
}}
|
|
215
|
+
>
|
|
216
|
+
<img
|
|
217
|
+
src="/assets/scene-1.svg"
|
|
218
|
+
alt={msg("p1Title")}
|
|
219
|
+
className="hero-image"
|
|
220
|
+
width={800}
|
|
221
|
+
height={600}
|
|
222
|
+
loading="lazy"
|
|
223
|
+
/>
|
|
224
|
+
<div className="image-caption">
|
|
225
|
+
<span>{"01 / LISBOA"}</span>
|
|
226
|
+
<Copy name="read" />
|
|
227
|
+
</div>
|
|
228
|
+
</a>
|
|
229
|
+
</section>
|
|
230
|
+
<section id="journal" className="section">
|
|
231
|
+
<div className="section-heading">
|
|
232
|
+
<h2>
|
|
233
|
+
<Copy name="latest" />
|
|
234
|
+
</h2>
|
|
235
|
+
<span className="muted">
|
|
236
|
+
<Copy name="notes" />
|
|
237
|
+
</span>
|
|
238
|
+
</div>
|
|
239
|
+
<div className="filters">
|
|
240
|
+
<fieldset
|
|
241
|
+
className="filter-buttons"
|
|
242
|
+
aria-label={msg(
|
|
243
|
+
"collection" in messages ? "collection" : "latest",
|
|
244
|
+
)}
|
|
245
|
+
>
|
|
246
|
+
<button
|
|
247
|
+
type="button"
|
|
248
|
+
className={
|
|
249
|
+
state.category === "all" ? "chip active" : "chip"
|
|
250
|
+
}
|
|
251
|
+
aria-pressed={state.category === "all"}
|
|
252
|
+
onClick={(_event) => {
|
|
253
|
+
set("category", "all");
|
|
254
|
+
}}
|
|
255
|
+
>
|
|
256
|
+
<Copy name="all" />
|
|
257
|
+
</button>
|
|
258
|
+
<button
|
|
259
|
+
type="button"
|
|
260
|
+
className={
|
|
261
|
+
state.category === "cities" ? "chip active" : "chip"
|
|
262
|
+
}
|
|
263
|
+
aria-pressed={state.category === "cities"}
|
|
264
|
+
onClick={(_event) => {
|
|
265
|
+
set("category", "cities");
|
|
266
|
+
}}
|
|
267
|
+
>
|
|
268
|
+
<Copy name="cities" />
|
|
269
|
+
</button>
|
|
270
|
+
<button
|
|
271
|
+
type="button"
|
|
272
|
+
className={
|
|
273
|
+
state.category === "nature" ? "chip active" : "chip"
|
|
274
|
+
}
|
|
275
|
+
aria-pressed={state.category === "nature"}
|
|
276
|
+
onClick={(_event) => {
|
|
277
|
+
set("category", "nature");
|
|
278
|
+
}}
|
|
279
|
+
>
|
|
280
|
+
<Copy name="nature" />
|
|
281
|
+
</button>
|
|
282
|
+
<button
|
|
283
|
+
type="button"
|
|
284
|
+
className={
|
|
285
|
+
state.category === "coast" ? "chip active" : "chip"
|
|
286
|
+
}
|
|
287
|
+
aria-pressed={state.category === "coast"}
|
|
288
|
+
onClick={(_event) => {
|
|
289
|
+
set("category", "coast");
|
|
290
|
+
}}
|
|
291
|
+
>
|
|
292
|
+
<Copy name="coast" />
|
|
293
|
+
</button>
|
|
294
|
+
</fieldset>
|
|
295
|
+
<label className="search">
|
|
296
|
+
<span className="sr-only">
|
|
297
|
+
<Copy name="search" />
|
|
298
|
+
</span>
|
|
299
|
+
<input
|
|
300
|
+
type="search"
|
|
301
|
+
placeholder={msg("search")}
|
|
302
|
+
value={state.query}
|
|
303
|
+
onInput={(event) => {
|
|
304
|
+
set("query", value(event));
|
|
305
|
+
}}
|
|
306
|
+
/>
|
|
307
|
+
</label>
|
|
308
|
+
</div>
|
|
309
|
+
<div className="article-grid">
|
|
310
|
+
{visible().map((item) => (
|
|
311
|
+
<Fragment key={item.id}>
|
|
312
|
+
<article className="article-card">
|
|
313
|
+
<a
|
|
314
|
+
href={detailHref(item.id)}
|
|
315
|
+
onClick={(event) => {
|
|
316
|
+
navigate(event);
|
|
317
|
+
}}
|
|
318
|
+
>
|
|
319
|
+
<img
|
|
320
|
+
src={item.image}
|
|
321
|
+
alt={msg(`${item.id}Title`)}
|
|
322
|
+
className=""
|
|
323
|
+
width={800}
|
|
324
|
+
height={600}
|
|
325
|
+
loading="lazy"
|
|
326
|
+
/>
|
|
327
|
+
</a>
|
|
328
|
+
<div className="meta">
|
|
329
|
+
<Copy name={item.category} />
|
|
330
|
+
<span>{date(item.date)}</span>
|
|
331
|
+
</div>
|
|
332
|
+
<h3>
|
|
333
|
+
<a
|
|
334
|
+
href={detailHref(item.id)}
|
|
335
|
+
onClick={(event) => {
|
|
336
|
+
navigate(event);
|
|
337
|
+
}}
|
|
338
|
+
>
|
|
339
|
+
<Copy name={`${item.id}Title`} />
|
|
340
|
+
</a>
|
|
341
|
+
</h3>
|
|
342
|
+
<p>
|
|
343
|
+
<Copy name={`${item.id}Summary`} />
|
|
344
|
+
</p>
|
|
345
|
+
<a
|
|
346
|
+
href={detailHref(item.id)}
|
|
347
|
+
onClick={(event) => {
|
|
348
|
+
navigate(event);
|
|
349
|
+
}}
|
|
350
|
+
>
|
|
351
|
+
<Copy name="read" />
|
|
352
|
+
{" ↗"}
|
|
353
|
+
</a>
|
|
354
|
+
</article>
|
|
355
|
+
</Fragment>
|
|
356
|
+
))}
|
|
357
|
+
</div>
|
|
358
|
+
{visible().length === 0 && (
|
|
359
|
+
<div className="empty" role="status">
|
|
360
|
+
<h3>
|
|
361
|
+
<Copy name="empty" />
|
|
362
|
+
</h3>
|
|
363
|
+
<p>
|
|
364
|
+
<Copy name="emptyHint" />
|
|
365
|
+
</p>
|
|
366
|
+
<button
|
|
367
|
+
type="button"
|
|
368
|
+
className="button"
|
|
369
|
+
onClick={(_event) => {
|
|
370
|
+
clearFilters();
|
|
371
|
+
}}
|
|
372
|
+
>
|
|
373
|
+
<Copy name="clear" />
|
|
374
|
+
</button>
|
|
375
|
+
</div>
|
|
376
|
+
)}
|
|
377
|
+
</section>
|
|
378
|
+
</>
|
|
379
|
+
)}
|
|
380
|
+
{detail() !== undefined && (
|
|
381
|
+
<article className="article-detail section">
|
|
382
|
+
<a
|
|
383
|
+
href="/"
|
|
384
|
+
onClick={(event) => {
|
|
385
|
+
navigate(event);
|
|
386
|
+
}}
|
|
387
|
+
>
|
|
388
|
+
<Copy name="back" />
|
|
389
|
+
</a>
|
|
390
|
+
<p className="eyebrow">
|
|
391
|
+
<Copy name={detail()!.category} />
|
|
392
|
+
</p>
|
|
393
|
+
<h1>
|
|
394
|
+
<Copy name={`${detail()!.id}Title`} />
|
|
395
|
+
</h1>
|
|
396
|
+
<div className="meta">
|
|
397
|
+
{date(detail()!.date)}
|
|
398
|
+
<Copy name="minutes" values={{ count: detail()!.minutes }} />
|
|
399
|
+
</div>
|
|
400
|
+
<img
|
|
401
|
+
src={detail()!.image}
|
|
402
|
+
alt={msg(`${detail()!.id}Title`)}
|
|
403
|
+
className="detail-image"
|
|
404
|
+
width={800}
|
|
405
|
+
height={600}
|
|
406
|
+
loading="lazy"
|
|
407
|
+
/>
|
|
408
|
+
<div className="prose">
|
|
409
|
+
<p className="lead">
|
|
410
|
+
<Copy name={`${detail()!.id}Summary`} />
|
|
411
|
+
</p>
|
|
412
|
+
<p>
|
|
413
|
+
<Copy name={`${detail()!.id}Body`} />
|
|
414
|
+
</p>
|
|
415
|
+
<p>
|
|
416
|
+
<Copy name={`${detail()!.id}End`} />
|
|
417
|
+
</p>
|
|
418
|
+
</div>
|
|
419
|
+
</article>
|
|
420
|
+
)}
|
|
421
|
+
{notFound() && (
|
|
422
|
+
<section className="section empty">
|
|
423
|
+
<p className="eyebrow">{"404"}</p>
|
|
424
|
+
<h1>
|
|
425
|
+
<Copy name="missing" />
|
|
426
|
+
</h1>
|
|
427
|
+
<p>
|
|
428
|
+
<Copy name="missingHint" />
|
|
429
|
+
</p>
|
|
430
|
+
<a
|
|
431
|
+
href="/"
|
|
432
|
+
onClick={(event) => {
|
|
433
|
+
navigate(event);
|
|
434
|
+
}}
|
|
435
|
+
>
|
|
436
|
+
<Copy name="back" />
|
|
437
|
+
</a>
|
|
438
|
+
</section>
|
|
439
|
+
)}
|
|
440
|
+
</main>
|
|
441
|
+
<footer className="site-footer">
|
|
442
|
+
<div>
|
|
443
|
+
<a
|
|
444
|
+
href="/"
|
|
445
|
+
className="brand"
|
|
446
|
+
onClick={(event) => {
|
|
447
|
+
navigate(event);
|
|
448
|
+
}}
|
|
449
|
+
>
|
|
450
|
+
{"Elsewhere"}
|
|
451
|
+
</a>
|
|
452
|
+
<p>
|
|
453
|
+
<Copy name="about" />
|
|
454
|
+
</p>
|
|
455
|
+
</div>
|
|
456
|
+
<p className="fine">
|
|
457
|
+
<Copy name="demo" />
|
|
458
|
+
<a href="https://linguana.mohammedibrahim.dev" rel="noreferrer">
|
|
459
|
+
{"Linguana ↗"}
|
|
460
|
+
</a>
|
|
461
|
+
</p>
|
|
462
|
+
</footer>
|
|
463
|
+
</div>
|
|
464
|
+
);
|
|
465
|
+
}
|