diff --git a/latest/linux/mdcms b/latest/linux/mdcms index f8f11b2..f93aeae 100644 Binary files a/latest/linux/mdcms and b/latest/linux/mdcms differ diff --git a/latest/linux/mdcms.deb b/latest/linux/mdcms.deb index 3cfa335..cc568e8 100644 Binary files a/latest/linux/mdcms.deb and b/latest/linux/mdcms.deb differ diff --git a/latest/macos/mdcms b/latest/macos/mdcms index f4f5ecc..8ea55eb 100644 Binary files a/latest/macos/mdcms and b/latest/macos/mdcms differ diff --git a/latest/windows/mdcms.exe b/latest/windows/mdcms.exe index a8757ed..06706e4 100644 Binary files a/latest/windows/mdcms.exe and b/latest/windows/mdcms.exe differ diff --git a/sample-sites/README.md b/sample-sites/README.md new file mode 100644 index 0000000..12139f2 --- /dev/null +++ b/sample-sites/README.md @@ -0,0 +1,12 @@ +## Add folder info using this document + +* The contents of a **Readme.md** will show up embedded on the top of the folder it is in (in the web interface and the mobile apps) +* Formatting is supported with the bar on top (using Markdown) +* It uses Nextcloud Text so you can collaborate on it ๐ +* You can use and remix the templates as you like, they are in the public domain via the [CC0 license](https://creativecommons.org/publicdomain/zero/1.0/) + +## Action items + +* [ ] Try out the new templates +* [ ] Add your own templates in this folder +* [ ] โฆ diff --git a/sample-sites/kitchen-table/assets/images/bread.jpg b/sample-sites/kitchen-table/assets/images/bread.jpg new file mode 100644 index 0000000..52b268c Binary files /dev/null and b/sample-sites/kitchen-table/assets/images/bread.jpg differ diff --git a/sample-sites/kitchen-table/assets/images/hero.jpg b/sample-sites/kitchen-table/assets/images/hero.jpg new file mode 100644 index 0000000..fa020c4 Binary files /dev/null and b/sample-sites/kitchen-table/assets/images/hero.jpg differ diff --git a/sample-sites/kitchen-table/assets/images/market.jpg b/sample-sites/kitchen-table/assets/images/market.jpg new file mode 100644 index 0000000..39b1c3b Binary files /dev/null and b/sample-sites/kitchen-table/assets/images/market.jpg differ diff --git a/sample-sites/kitchen-table/assets/images/pasta.jpg b/sample-sites/kitchen-table/assets/images/pasta.jpg new file mode 100644 index 0000000..9c2fc9e Binary files /dev/null and b/sample-sites/kitchen-table/assets/images/pasta.jpg differ diff --git a/sample-sites/kitchen-table/config.yml b/sample-sites/kitchen-table/config.yml new file mode 100644 index 0000000..f914c73 --- /dev/null +++ b/sample-sites/kitchen-table/config.yml @@ -0,0 +1,6 @@ +# mdcms v0.3 | DO NOT REMOVE THIS COMMENT +sitename: The Kitchen Table +sitedescription: Recipes, techniques, and stories from Amelia Fontaine +navigation: topbar +search: true +footer: "ยฉ 2026 Amelia Fontaine ยท The Kitchen Table" diff --git a/samplesite/index.html b/sample-sites/kitchen-table/index.html similarity index 76% rename from samplesite/index.html rename to sample-sites/kitchen-table/index.html index c2b1e8a..1466873 100644 --- a/samplesite/index.html +++ b/sample-sites/kitchen-table/index.html @@ -1,5 +1,6 @@ + + + + +
+ + +{salutation}, {name}!
; +} +``` + +Use it in a route or another component: + +```tsx +--- +import Greeting from '../components/Greeting.tsx'; +--- + +This is the card body.
+Are you sure you want to delete this item?
+Current time: {time}
; +} +``` + +## Lifecycle Hooks + +Client-side components can use the following lifecycle hooks: + +| Hook | When it runs | +|------|-------------| +| `onMount(fn)` | After the component is first rendered and inserted into the DOM | +| `onUpdate(fn)` | After every re-render (reactive signal change) | +| `onCleanup(fn)` | Before the component unmounts or before the next `onUpdate` call | +| `onDestroy(fn)` | When the component is permanently unmounted | + +```tsx +import { signal, onMount, onUpdate, onCleanup } from 'velox/client'; + +export default function DataComponent() { + const data = signalLoading...
:Error: {error.message}
; + + return ( +{t('home.hero.subtitle')}
+ {t('home.cta')} +{t('post.publishedOn', { date: publishedDate })}
+``` + +### Pluralisation + +Velox uses the ICU message format for pluralisation: + +```typescript +t('post.comments', { count: 0 }); // "No comments" +t('post.comments', { count: 1 }); // "1 comment" +t('post.comments', { count: 42 }); // "42 comments" +``` + +### In Client Components + +```typescript +import { useTranslations } from 'velox/i18n/client'; + +export default function LikeButton({ postId }: { postId: string }) { + const t = useTranslations(); + const liked = signal(false); + + return ( + + ); +} +``` + +## Locale Routing + +With `routing: 'prefix-except-default'` and `defaultLocale: 'en'`: + +| URL | Locale | +|-----|--------| +| `/` | `en` | +| `/about` | `en` | +| `/fr` | `fr` | +| `/fr/about` | `fr` | +| `/de/blog/my-post` | `de` | + +Velox automatically generates alternate hreflang links for SEO. + +## Locale Switcher Component + +```tsx +import { useLocale, usePathname } from 'velox/i18n/client'; + +export default function LocaleSwitcher() { + const { locale, locales } = useLocale(); + const pathname = usePathname(); + + return ( +Edit routes/index.velox to get started.
Send us a message at hello@example.com
+Todo from API: {todo.title}
+Count: {count.value}
+ +{count.value}
+{count}
+``` + +The second form (`{count}` without `.value`) subscribes the DOM node directly to the signal. This is more efficient because Velox skips the component function entirely and updates only the text node when the signal changes. + +## Computed Signals + +A computed signal derives its value from one or more other signals. It re-evaluates automatically when its dependencies change: + +```typescript +import { signal, computed } from 'velox/client'; + +const firstName = signal('Jane'); +const lastName = signal('Doe'); +const fullName = computed(() => `${firstName.value} ${lastName.value}`); + +console.log(fullName.value); // "Jane Doe" +firstName.value = 'John'; +console.log(fullName.value); // "John Doe" +``` + +Computed signals are lazy and memoised โ the computation only runs when the value is read, and only re-runs if a dependency has changed since the last read. + +## Effects + +An effect runs a side effect whenever its reactive dependencies change: + +```typescript +import { signal, effect } from 'velox/client'; + +const query = signal(''); + +effect(() => { + console.log('Search query changed:', query.value); + // this re-runs every time query.value changes +}); +``` + +Effects clean themselves up automatically when the component unmounts. To clean up manually within an effect (e.g., cancel a previous request): + +```typescript +effect(() => { + const controller = new AbortController(); + fetch(`/api/search?q=${query.value}`, { signal: controller.signal }) + .then(r => r.json()) + .then(results => resultsSignal.value = results); + + return () => controller.abort(); // cleanup function +}); +``` + +## Stores + +For shared state across components, define a store. A store is a module that encapsulates signals and exposes a clean interface: + +```typescript +// lib/stores/cart.ts +import { signal, computed } from 'velox/client'; + +export interface CartItem { + id: string; + name: string; + price: number; + quantity: number; +} + +const items = signalHello, {userProfile.value.data.name}
+ )} + {userProfile.value.status === 'error' && ( +Error: {userProfile.value.error.message}
+ )} +{salutation}, {name}!
;\n}\n```\n\nUse it in a route or another component:\n\n```tsx\n---\nimport Greeting from '../components/Greeting.tsx';\n---\n\nThis is the card body.
\nAre you sure you want to delete this item?
\nCurrent time: {time}
;\n}\n```\n\n## Lifecycle Hooks\n\nClient-side components can use the following lifecycle hooks:\n\n| Hook | When it runs |\n|------|-------------|\n| `onMount(fn)` | After the component is first rendered and inserted into the DOM |\n| `onUpdate(fn)` | After every re-render (reactive signal change) |\n| `onCleanup(fn)` | Before the component unmounts or before the next `onUpdate` call |\n| `onDestroy(fn)` | When the component is permanently unmounted |\n\n```tsx\nimport { signal, onMount, onUpdate, onCleanup } from 'velox/client';\n\nexport default function DataComponent() {\n const data = signalLoading...
:{t('home.hero.subtitle')}
\n {t('home.cta')}\n{t('post.publishedOn', { date: publishedDate })}
\n```\n\n### Pluralisation\n\nVelox uses the ICU message format for pluralisation:\n\n```typescript\nt('post.comments', { count: 0 }); // \"No comments\"\nt('post.comments', { count: 1 }); // \"1 comment\"\nt('post.comments', { count: 42 }); // \"42 comments\"\n```\n\n### In Client Components\n\n```typescript\nimport { useTranslations } from 'velox/i18n/client';\n\nexport default function LikeButton({ postId }: { postId: string }) {\n const t = useTranslations();\n const liked = signal(false);\n\n return (\n \n );\n}\n```\n\n## Locale Routing\n\nWith `routing: 'prefix-except-default'` and `defaultLocale: 'en'`:\n\n| URL | Locale |\n|-----|--------|\n| `/` | `en` |\n| `/about` | `en` |\n| `/fr` | `fr` |\n| `/fr/about` | `fr` |\n| `/de/blog/my-post` | `de` |\n\nVelox automatically generates alternate hreflang links for SEO.\n\n## Locale Switcher Component\n\n```tsx\nimport { useLocale, usePathname } from 'velox/i18n/client';\n\nexport default function LocaleSwitcher() {\n const { locale, locales } = useLocale();\n const pathname = usePathname();\n\n return (\nEdit routes/index.velox to get started.
Send us a message at hello@example.com
\nTodo from API: {todo.title}
\nCount: {count.value}
\n \n{count.value}
\n{count}
\n```\n\nThe second form (`{count}` without `.value`) subscribes the DOM node directly to the signal. This is more efficient because Velox skips the component function entirely and updates only the text node when the signal changes.\n\n## Computed Signals\n\nA computed signal derives its value from one or more other signals. It re-evaluates automatically when its dependencies change:\n\n```typescript\nimport { signal, computed } from 'velox/client';\n\nconst firstName = signal('Jane');\nconst lastName = signal('Doe');\nconst fullName = computed(() => `${firstName.value} ${lastName.value}`);\n\nconsole.log(fullName.value); // \"Jane Doe\"\nfirstName.value = 'John';\nconsole.log(fullName.value); // \"John Doe\"\n```\n\nComputed signals are lazy and memoised \u2014 the computation only runs when the value is read, and only re-runs if a dependency has changed since the last read.\n\n## Effects\n\nAn effect runs a side effect whenever its reactive dependencies change:\n\n```typescript\nimport { signal, effect } from 'velox/client';\n\nconst query = signal('');\n\neffect(() => {\n console.log('Search query changed:', query.value);\n // this re-runs every time query.value changes\n});\n```\n\nEffects clean themselves up automatically when the component unmounts. To clean up manually within an effect (e.g., cancel a previous request):\n\n```typescript\neffect(() => {\n const controller = new AbortController();\n fetch(`/api/search?q=${query.value}`, { signal: controller.signal })\n .then(r => r.json())\n .then(results => resultsSignal.value = results);\n\n return () => controller.abort(); // cleanup function\n});\n```\n\n## Stores\n\nFor shared state across components, define a store. A store is a module that encapsulates signals and exposes a clean interface:\n\n```typescript\n// lib/stores/cart.ts\nimport { signal, computed } from 'velox/client';\n\nexport interface CartItem {\n id: string;\n name: string;\n price: number;\n quantity: number;\n}\n\nconst items = signalHello, {userProfile.value.data.name}
\n )}\n {userProfile.value.status === 'error' && (\nError: {userProfile.value.error.message}
\n )}\n