fimo 0.9.0-experimental.1 → 0.9.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.
Files changed (205) hide show
  1. package/assets/skills/fimo/SKILL.md +17 -54
  2. package/assets/skills/fimo-cli/SKILL.md +15 -15
  3. package/assets/skills/fimo-cli/references/agent-setup.md +36 -53
  4. package/assets/skills/fimo-cli/references/agents.md +4 -4
  5. package/assets/skills/fimo-cli/references/content.md +2 -2
  6. package/assets/skills/fimo-cli/references/deploy.md +1 -1
  7. package/assets/skills/fimo-cli/references/forms.md +1 -1
  8. package/assets/skills/fimo-cli/references/init.md +16 -24
  9. package/assets/skills/fimo-cli/references/project-vars-and-secrets.md +14 -11
  10. package/assets/skills/fimo-cli/references/projects.md +1 -1
  11. package/assets/skills/fimo-cli/references/support.md +11 -9
  12. package/assets/skills/fimo-cli/references/webhooks.md +3 -1
  13. package/assets/skills/fimo-code/SKILL.md +67 -0
  14. package/assets/skills/fimo-migration/SKILL.md +2 -22
  15. package/assets/skills/fimo-migration/references/content.md +0 -24
  16. package/assets/skills/fimo-migration/references/manifest.md +0 -35
  17. package/assets/skills/fimo-migration/references/qa.md +2 -24
  18. package/assets/skills/fimo-migration/references/workflow.md +1 -12
  19. package/assets/skills/fimo-studio/references/visual-editing.md +3 -5
  20. package/assets/skills/fimo-studio/references/webhooks.md +1 -1
  21. package/dist/build/vite/plugins/translations.d.ts +8 -2
  22. package/dist/build/vite/plugins/translations.js +121 -3
  23. package/dist/cli/bundle.json +2 -2
  24. package/dist/cli/index.js +311 -1209
  25. package/dist/runtime/app/FimoProviders.d.ts +11 -1
  26. package/dist/runtime/app/FimoProviders.js +23 -2
  27. package/dist/runtime/content/client/createContentClient.js +2 -1
  28. package/dist/runtime/i18n/FimoLocaleContext.js +0 -1
  29. package/dist/runtime/primitives/lib/useFimoLiveValue.d.ts +3 -5
  30. package/dist/runtime/primitives/lib/useFimoLiveValue.js +4 -11
  31. package/dist/runtime/primitives/translations.d.ts +27 -1
  32. package/dist/runtime/primitives/translations.js +85 -7
  33. package/dist/runtime/shared/fimo-config.server.js +0 -1
  34. package/dist/runtime/shared/fimo-config.types.d.ts +0 -2
  35. package/dist/scripts/extract-translations.js +11 -16
  36. package/dist/scripts/lib/validation-report.d.ts +0 -1
  37. package/dist/scripts/lib/validation-report.js +0 -3
  38. package/dist/scripts/validate-route-metadata.js +0 -11
  39. package/package.json +4 -84
  40. package/release.json +2 -2
  41. package/scripts/postinstall.mjs +11 -65
  42. package/templates/react-router/AGENTS.md +44 -0
  43. package/templates/react-router/CLAUDE.md +5 -0
  44. package/templates/react-router/package.json +1 -1
  45. package/assets/agents-starter/AGENTS.md +0 -66
  46. package/assets/skills/fimo-migration/references/wrap.md +0 -617
  47. package/dist/runtime/astro/index.d.ts +0 -2
  48. package/dist/runtime/astro/index.js +0 -3
  49. package/dist/runtime/astro/server.d.ts +0 -3
  50. package/dist/runtime/astro/server.js +0 -6
  51. package/dist/runtime/next/FimoNextProviders.d.ts +0 -24
  52. package/dist/runtime/next/FimoNextProviders.js +0 -20
  53. package/dist/runtime/next/FimoNextScripts.d.ts +0 -10
  54. package/dist/runtime/next/FimoNextScripts.js +0 -19
  55. package/dist/runtime/next/index.d.ts +0 -14
  56. package/dist/runtime/next/index.js +0 -14
  57. package/dist/runtime/next/server.d.ts +0 -2
  58. package/dist/runtime/next/server.js +0 -4
  59. package/dist/runtime/nuxt/composables.d.ts +0 -9
  60. package/dist/runtime/nuxt/composables.js +0 -57
  61. package/dist/runtime/nuxt/index.d.ts +0 -3
  62. package/dist/runtime/nuxt/index.js +0 -6
  63. package/dist/runtime/nuxt/server.d.ts +0 -3
  64. package/dist/runtime/nuxt/server.js +0 -6
  65. package/dist/runtime/primitives/labels-hook.d.ts +0 -33
  66. package/dist/runtime/primitives/labels-hook.js +0 -41
  67. package/dist/runtime/primitives/labels-store.d.ts +0 -12
  68. package/dist/runtime/primitives/labels-store.js +0 -60
  69. package/dist/runtime/react/FimoSpaProviders.d.ts +0 -36
  70. package/dist/runtime/react/FimoSpaProviders.js +0 -63
  71. package/dist/runtime/react/index.d.ts +0 -33
  72. package/dist/runtime/react/index.js +0 -39
  73. package/dist/runtime/remix/FimoRemixProviders.d.ts +0 -16
  74. package/dist/runtime/remix/FimoRemixProviders.js +0 -12
  75. package/dist/runtime/remix/FimoRemixScripts.d.ts +0 -10
  76. package/dist/runtime/remix/FimoRemixScripts.js +0 -19
  77. package/dist/runtime/remix/index.d.ts +0 -12
  78. package/dist/runtime/remix/index.js +0 -11
  79. package/dist/runtime/remix/server.d.ts +0 -2
  80. package/dist/runtime/remix/server.js +0 -2
  81. package/dist/runtime/shared/display-routes.d.ts +0 -14
  82. package/dist/runtime/shared/display-routes.js +0 -25
  83. package/dist/runtime/shared/i18n-messages.d.ts +0 -29
  84. package/dist/runtime/shared/i18n-messages.js +0 -79
  85. package/dist/runtime/shared/labels-fetch.pure.d.ts +0 -17
  86. package/dist/runtime/shared/labels-fetch.pure.js +0 -71
  87. package/dist/runtime/shared/labels-fetch.server.d.ts +0 -7
  88. package/dist/runtime/shared/labels-fetch.server.js +0 -97
  89. package/dist/runtime/shared/labels-t.pure.d.ts +0 -2
  90. package/dist/runtime/shared/labels-t.pure.js +0 -10
  91. package/dist/runtime/strapi/client.d.ts +0 -30
  92. package/dist/runtime/strapi/client.js +0 -89
  93. package/dist/runtime/strapi/index.d.ts +0 -5
  94. package/dist/runtime/strapi/index.js +0 -6
  95. package/dist/runtime/strapi/reshape.d.ts +0 -46
  96. package/dist/runtime/strapi/reshape.js +0 -114
  97. package/dist/runtime/svelte/index.d.ts +0 -3
  98. package/dist/runtime/svelte/index.js +0 -7
  99. package/dist/runtime/svelte/server.d.ts +0 -3
  100. package/dist/runtime/svelte/server.js +0 -6
  101. package/dist/runtime/vue/index.d.ts +0 -3
  102. package/dist/runtime/vue/index.js +0 -5
  103. package/dist/runtime/vue/server.d.ts +0 -3
  104. package/dist/runtime/vue/server.js +0 -6
  105. package/dist/runtime/wrapped/FimoFileLink.d.ts +0 -17
  106. package/dist/runtime/wrapped/FimoFileLink.js +0 -19
  107. package/dist/runtime/wrapped/FimoMediaImage.d.ts +0 -14
  108. package/dist/runtime/wrapped/FimoMediaImage.js +0 -20
  109. package/dist/runtime/wrapped/FimoMediaVideo.d.ts +0 -14
  110. package/dist/runtime/wrapped/FimoMediaVideo.js +0 -20
  111. package/dist/runtime/wrapped/FimoRichTextBlock.d.ts +0 -23
  112. package/dist/runtime/wrapped/FimoRichTextBlock.js +0 -22
  113. package/dist/runtime/wrapped/FimoText.d.ts +0 -22
  114. package/dist/runtime/wrapped/FimoText.js +0 -20
  115. package/dist/runtime/wrapped/WrappedContentContext.d.ts +0 -28
  116. package/dist/runtime/wrapped/WrappedContentContext.js +0 -30
  117. package/dist/runtime/wrapped/WrappedProviders.d.ts +0 -33
  118. package/dist/runtime/wrapped/WrappedProviders.js +0 -101
  119. package/dist/runtime/wrapped/dom.d.ts +0 -4
  120. package/dist/runtime/wrapped/dom.js +0 -23
  121. package/dist/runtime/wrapped/server.d.ts +0 -77
  122. package/dist/runtime/wrapped/server.js +0 -237
  123. package/dist/scripts/apply-wrap-templates.d.ts +0 -61
  124. package/dist/scripts/apply-wrap-templates.js +0 -132
  125. package/dist/scripts/apply-wrap.d.ts +0 -46
  126. package/dist/scripts/apply-wrap.js +0 -278
  127. package/dist/scripts/cms/discover.d.ts +0 -2
  128. package/dist/scripts/cms/discover.js +0 -17
  129. package/dist/scripts/cms/example-content.d.ts +0 -33
  130. package/dist/scripts/cms/example-content.js +0 -162
  131. package/dist/scripts/cms/strapi/detect.d.ts +0 -2
  132. package/dist/scripts/cms/strapi/detect.js +0 -186
  133. package/dist/scripts/cms/strapi/index.d.ts +0 -3
  134. package/dist/scripts/cms/strapi/index.js +0 -25
  135. package/dist/scripts/cms/strapi/list-source.d.ts +0 -17
  136. package/dist/scripts/cms/strapi/list-source.js +0 -90
  137. package/dist/scripts/cms/strapi/schema-map.d.ts +0 -9
  138. package/dist/scripts/cms/strapi/schema-map.js +0 -277
  139. package/dist/scripts/cms/strapi/shapes.d.ts +0 -23
  140. package/dist/scripts/cms/strapi/shapes.js +0 -38
  141. package/dist/scripts/cms/strapi/touchpoints.d.ts +0 -31
  142. package/dist/scripts/cms/strapi/touchpoints.js +0 -145
  143. package/dist/scripts/cms/strapi/wire-client.d.ts +0 -15
  144. package/dist/scripts/cms/strapi/wire-client.js +0 -107
  145. package/dist/scripts/cms/strapi/wiring.d.ts +0 -17
  146. package/dist/scripts/cms/strapi/wiring.js +0 -46
  147. package/dist/scripts/cms/types.d.ts +0 -145
  148. package/dist/scripts/cms/types.js +0 -11
  149. package/dist/scripts/detect-framework.d.ts +0 -39
  150. package/dist/scripts/detect-framework.js +0 -133
  151. package/dist/scripts/draft-rows.d.ts +0 -19
  152. package/dist/scripts/draft-rows.js +0 -135
  153. package/dist/scripts/draft-schemas.d.ts +0 -27
  154. package/dist/scripts/draft-schemas.js +0 -188
  155. package/dist/scripts/frameworks/astro/routes.d.ts +0 -3
  156. package/dist/scripts/frameworks/astro/routes.js +0 -55
  157. package/dist/scripts/frameworks/astro/template-style.d.ts +0 -7
  158. package/dist/scripts/frameworks/astro/template-style.js +0 -19
  159. package/dist/scripts/frameworks/discover.d.ts +0 -70
  160. package/dist/scripts/frameworks/discover.js +0 -178
  161. package/dist/scripts/frameworks/next/rewire.d.ts +0 -30
  162. package/dist/scripts/frameworks/next/rewire.js +0 -258
  163. package/dist/scripts/frameworks/next/routes.d.ts +0 -17
  164. package/dist/scripts/frameworks/next/routes.js +0 -134
  165. package/dist/scripts/frameworks/next/wiring.d.ts +0 -33
  166. package/dist/scripts/frameworks/next/wiring.js +0 -186
  167. package/dist/scripts/frameworks/nuxt/routes.d.ts +0 -3
  168. package/dist/scripts/frameworks/nuxt/routes.js +0 -54
  169. package/dist/scripts/frameworks/nuxt/template-style.d.ts +0 -9
  170. package/dist/scripts/frameworks/nuxt/template-style.js +0 -27
  171. package/dist/scripts/frameworks/remix/routes.d.ts +0 -3
  172. package/dist/scripts/frameworks/remix/routes.js +0 -50
  173. package/dist/scripts/frameworks/support.d.ts +0 -75
  174. package/dist/scripts/frameworks/support.js +0 -165
  175. package/dist/scripts/frameworks/sveltekit/routes.d.ts +0 -3
  176. package/dist/scripts/frameworks/sveltekit/routes.js +0 -50
  177. package/dist/scripts/frameworks/sveltekit/template-style.d.ts +0 -9
  178. package/dist/scripts/frameworks/sveltekit/template-style.js +0 -54
  179. package/dist/scripts/frameworks/vite/routes.d.ts +0 -15
  180. package/dist/scripts/frameworks/vite/routes.js +0 -93
  181. package/dist/scripts/frameworks/vite/wiring.d.ts +0 -10
  182. package/dist/scripts/frameworks/vite/wiring.js +0 -99
  183. package/dist/scripts/i18n-namespace.d.ts +0 -27
  184. package/dist/scripts/i18n-namespace.js +0 -155
  185. package/dist/scripts/render-parity/capture.d.ts +0 -56
  186. package/dist/scripts/render-parity/capture.js +0 -366
  187. package/dist/scripts/render-parity/compare.d.ts +0 -59
  188. package/dist/scripts/render-parity/compare.js +0 -0
  189. package/dist/scripts/render-parity/normalize-html.d.ts +0 -13
  190. package/dist/scripts/render-parity/normalize-html.js +0 -223
  191. package/dist/scripts/render-parity/types.d.ts +0 -41
  192. package/dist/scripts/render-parity/types.js +0 -1
  193. package/dist/scripts/scan-wrappable.d.ts +0 -196
  194. package/dist/scripts/scan-wrappable.js +0 -1232
  195. /package/assets/skills/{fimo → fimo-code}/assets/react-router-locales/navigation.tsx +0 -0
  196. /package/assets/skills/{fimo → fimo-code}/assets/react-router-locales/routes.ts +0 -0
  197. /package/assets/skills/{fimo → fimo-code}/references/assets.md +0 -0
  198. /package/assets/skills/{fimo → fimo-code}/references/content.md +0 -0
  199. /package/assets/skills/{fimo → fimo-code}/references/design.md +0 -0
  200. /package/assets/skills/{fimo → fimo-code}/references/forms.md +0 -0
  201. /package/assets/skills/{fimo → fimo-code}/references/labels.md +0 -0
  202. /package/assets/skills/{fimo → fimo-code}/references/locales.md +0 -0
  203. /package/assets/skills/{fimo → fimo-code}/references/setup-plain-vite.md +0 -0
  204. /package/assets/skills/{fimo → fimo-code}/references/setup-react-router.md +0 -0
  205. /package/assets/skills/{fimo → fimo-code}/references/ui.md +0 -0
@@ -1,5 +1,15 @@
1
1
  import React from 'react';
2
- export type { DisplayRoute, ParamInfo } from '../shared/display-routes.js';
2
+ export interface ParamInfo {
3
+ schema: string;
4
+ queryField: string;
5
+ displayField: string;
6
+ }
7
+ export interface DisplayRoute {
8
+ id: string;
9
+ name: string;
10
+ path: string;
11
+ params?: Record<string, ParamInfo>;
12
+ }
3
13
  export interface FimoProvidersProps {
4
14
  children?: React.ReactNode;
5
15
  locale?: string;
@@ -11,10 +11,31 @@ import { ViteLifecycles } from '../core/ViteLifecycles.js';
11
11
  import { FimoLocaleProvider } from '../i18n/FimoLocaleContext.js';
12
12
  import { getFimoTextDirection, resolveFimoLocale, toHtmlLang } from '../i18n/locale.js';
13
13
  import { ScreenshotProvider } from '../iframe/ScreenshotProvider.js';
14
- import { getDisplayRoutes as getDisplayRoutesFromConfig } from '../shared/display-routes.js';
15
14
  import { OptionalTracking } from '../t/OptionalTracking.js';
15
+ const titleCase = (seg) => seg
16
+ .replace(/[_\-]+/g, ' ')
17
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
18
+ .split(/\s+/)
19
+ .filter(Boolean)
20
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
21
+ .join(' ');
22
+ const generateRouteName = (path) => {
23
+ if (path === '/') {
24
+ return 'Home';
25
+ }
26
+ const firstSegment = path.replace(/^\//, '').split('/')[0];
27
+ return titleCase(firstSegment);
28
+ };
16
29
  function getDisplayRoutes() {
17
- return getDisplayRoutesFromConfig(fimoConfig);
30
+ const configRoutes = fimoConfig.routes ?? {};
31
+ return Object.entries(configRoutes)
32
+ .filter(([, route]) => route.path)
33
+ .map(([id, route]) => ({
34
+ id,
35
+ path: route.path,
36
+ name: route.name || generateRouteName(route.path),
37
+ params: route.params,
38
+ }));
18
39
  }
19
40
  function PreviewBridge() {
20
41
  const navigate = useNavigate();
@@ -100,10 +100,11 @@ export function createContentClient(config) {
100
100
  return (await list(params)).items;
101
101
  }
102
102
  async function create(payload) {
103
+ const localizedPayload = info.localized ? withMutationLocale(payload, readCollectionLocale({})) : payload;
103
104
  const res = await fetch(`${apiBase()}/entries/${info.uid}`, {
104
105
  method: 'POST',
105
106
  headers: fimoHeaders({ 'Content-Type': 'application/json' }),
106
- body: JSON.stringify(splitWritePayload(payload)),
107
+ body: JSON.stringify(splitWritePayload(localizedPayload)),
107
108
  });
108
109
  if (!res.ok) {
109
110
  throw new Error(await res.text());
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  import { jsx as _jsx } from "react/jsx-runtime";
3
2
  import { createContext, useContext } from 'react';
4
3
  const fallbackLocale = {
@@ -9,11 +9,9 @@ import { type RefObject } from 'react';
9
9
  * preview knows to dispatch events instead of falling back to raw DOM writes,
10
10
  * and updates internal state when a mutation arrives.
11
11
  *
12
- * In prod (published app): only `useFimoLiveValueProd` is linked. The dev
13
- * check must work under any bundler (Vite, webpack, Turbopack) and plain
14
- * Node SSR: `import.meta.env.DEV` only exists under Vite, and
15
- * `process.env.NODE_ENV` is statically replaced by Next/webpack — checking
16
- * both keeps the hook correct across adapters.
12
+ * In prod (published app): Vite statically inlines `import.meta.env.DEV`, so
13
+ * `useFimoLiveValueProd` is the only implementation linked and the listener
14
+ * machinery is dead-code-eliminated.
17
15
  */
18
16
  declare function useFimoLiveValueDev<T, E extends HTMLElement = HTMLElement>(external: T): {
19
17
  value: T;
@@ -10,11 +10,9 @@ import { useEffect, useRef, useState } from 'react';
10
10
  * preview knows to dispatch events instead of falling back to raw DOM writes,
11
11
  * and updates internal state when a mutation arrives.
12
12
  *
13
- * In prod (published app): only `useFimoLiveValueProd` is linked. The dev
14
- * check must work under any bundler (Vite, webpack, Turbopack) and plain
15
- * Node SSR: `import.meta.env.DEV` only exists under Vite, and
16
- * `process.env.NODE_ENV` is statically replaced by Next/webpack — checking
17
- * both keeps the hook correct across adapters.
13
+ * In prod (published app): Vite statically inlines `import.meta.env.DEV`, so
14
+ * `useFimoLiveValueProd` is the only implementation linked and the listener
15
+ * machinery is dead-code-eliminated.
18
16
  */
19
17
  function useFimoLiveValueDev(external) {
20
18
  const ref = useRef(null);
@@ -48,9 +46,4 @@ function useFimoLiveValueProd(external) {
48
46
  const ref = useRef(null);
49
47
  return { value: external, ref };
50
48
  }
51
- // globalThis lookup, not a bare `process` reference: this file is part of the
52
- // browser-facing runtime surface, which must typecheck without node types
53
- // (the generated-types gate enforces it).
54
- const isDevRuntime = import.meta.env?.DEV === true ||
55
- globalThis.process?.env?.NODE_ENV === 'development';
56
- export const useFimoLiveValue = (isDevRuntime ? useFimoLiveValueDev : useFimoLiveValueProd);
49
+ export const useFimoLiveValue = (import.meta.env.DEV ? useFimoLiveValueDev : useFimoLiveValueProd);
@@ -1,2 +1,28 @@
1
- export { useLabels, useTranslations } from './labels-hook.js';
1
+ import { FimoString } from './lib/index.js';
2
+ /**
3
+ * Hook to provide a label function (`t`).
4
+ *
5
+ * Returns FimoString with source tracking for inline editing.
6
+ * Use with <Text> component for rendering, or String() for escape hatch.
7
+ *
8
+ * @example
9
+ * const { t } = useLabels();
10
+ *
11
+ * // With Text component (recommended)
12
+ * <Text value={t('welcome')} />
13
+ *
14
+ * // Escape hatch for attributes
15
+ * <input placeholder={String(t('email.placeholder'))} />
16
+ */
17
+ export declare function useLabels(): {
18
+ t: (fullKey: string) => FimoString;
19
+ locale: string;
20
+ };
21
+ /**
22
+ * Legacy alias for older projects. Prefer useLabels() in new code.
23
+ */
24
+ export declare function useTranslations(): {
25
+ t: (fullKey: string) => FimoString;
26
+ locale: string;
27
+ };
2
28
  //# sourceMappingURL=translations.d.ts.map
@@ -1,14 +1,61 @@
1
- // Vite flavor of the label layer: hydrates the framework-neutral store from
2
- // the `virtual:translations` module (fimo/vite labels plugin) and wires HMR.
3
- // Framework-agnostic consumers use ./labels-store.ts + ./labels-hook.ts.
1
+ import { useSyncExternalStore } from 'react';
4
2
  import { defaultLocale as bundledDefaultLocale, dictionaries as bundledDictionaries, signature as translationSignature, updatedAt as bundledUpdatedAt, } from 'virtual:translations';
5
- import { applyLabelUpdate, hydrateLabels } from './labels-store.js';
6
- hydrateLabels({
3
+ import { useLocale } from '../i18n/FimoLocaleContext.js';
4
+ import { FimoString } from './lib/index.js';
5
+ const labelListeners = new Set();
6
+ let labelState = {
7
7
  dictionaries: bundledDictionaries ?? {},
8
8
  defaultLocale: bundledDefaultLocale || 'en',
9
9
  signature: translationSignature,
10
10
  updatedAt: bundledUpdatedAt ?? null,
11
- });
11
+ };
12
+ function subscribeLabels(listener) {
13
+ labelListeners.add(listener);
14
+ return () => {
15
+ labelListeners.delete(listener);
16
+ };
17
+ }
18
+ function getLabelSnapshot() {
19
+ return labelState;
20
+ }
21
+ function setLabelState(next) {
22
+ if (next.signature === labelState.signature) {
23
+ return;
24
+ }
25
+ labelState = next;
26
+ for (const listener of labelListeners) {
27
+ listener();
28
+ }
29
+ }
30
+ function isRecord(value) {
31
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
32
+ }
33
+ function normalizeDictionaries(value) {
34
+ if (!isRecord(value)) {
35
+ return null;
36
+ }
37
+ const dictionaries = {};
38
+ for (const [locale, labels] of Object.entries(value)) {
39
+ if (!isRecord(labels)) {
40
+ continue;
41
+ }
42
+ dictionaries[locale] = Object.fromEntries(Object.entries(labels).filter((entry) => typeof entry[1] === 'string'));
43
+ }
44
+ return dictionaries;
45
+ }
46
+ function applyLabelUpdate(payload) {
47
+ if (!isRecord(payload)) {
48
+ return;
49
+ }
50
+ const dictionaries = normalizeDictionaries(payload.dictionaries);
51
+ if (!dictionaries) {
52
+ return;
53
+ }
54
+ const defaultLocale = typeof payload.defaultLocale === 'string' ? payload.defaultLocale : labelState.defaultLocale;
55
+ const signature = typeof payload.signature === 'string' ? payload.signature : JSON.stringify({ dictionaries, defaultLocale });
56
+ const updatedAt = typeof payload.updatedAt === 'string' ? payload.updatedAt : null;
57
+ setLabelState({ dictionaries, defaultLocale, signature, updatedAt });
58
+ }
12
59
  // @ts-ignore - import.meta.hot is provided by Vite during development.
13
60
  if (import.meta.hot) {
14
61
  const onLabelsUpdated = (payload) => {
@@ -22,4 +69,35 @@ if (import.meta.hot) {
22
69
  import.meta.hot.off('fimo:labels-updated', onLabelsUpdated);
23
70
  });
24
71
  }
25
- export { useLabels, useTranslations } from './labels-hook.js';
72
+ /**
73
+ * Hook to provide a label function (`t`).
74
+ *
75
+ * Returns FimoString with source tracking for inline editing.
76
+ * Use with <Text> component for rendering, or String() for escape hatch.
77
+ *
78
+ * @example
79
+ * const { t } = useLabels();
80
+ *
81
+ * // With Text component (recommended)
82
+ * <Text value={t('welcome')} />
83
+ *
84
+ * // Escape hatch for attributes
85
+ * <input placeholder={String(t('email.placeholder'))} />
86
+ */
87
+ export function useLabels() {
88
+ const localeContext = useLocale();
89
+ const labels = useSyncExternalStore(subscribeLabels, getLabelSnapshot, getLabelSnapshot);
90
+ const t = (fullKey) => {
91
+ const locale = localeContext.locale || labels.defaultLocale || 'en';
92
+ const dictionary = labels.dictionaries[locale] ?? labels.dictionaries[labels.defaultLocale] ?? {};
93
+ const text = dictionary[fullKey] ?? '';
94
+ return new FimoString(text, `__labels.${locale}.${fullKey}.value`);
95
+ };
96
+ return { t, locale: localeContext.locale };
97
+ }
98
+ /**
99
+ * Legacy alias for older projects. Prefer useLabels() in new code.
100
+ */
101
+ export function useTranslations() {
102
+ return useLabels();
103
+ }
@@ -20,7 +20,6 @@ export function getConfigServer(rootDir) {
20
20
  i18n: raw.i18n,
21
21
  routes: raw.routes,
22
22
  version: raw.version,
23
- framework: raw.framework,
24
23
  };
25
24
  }
26
25
  catch {
@@ -57,7 +57,5 @@ export interface FimoConfig {
57
57
  i18n?: FimoI18nConfig;
58
58
  routes?: Record<string, FimoRouteConfig>;
59
59
  version?: string | number;
60
- /** The project's framework. Absent means the Fimo template (react-router). */
61
- framework?: 'react-router' | 'next' | string;
62
60
  }
63
61
  //# sourceMappingURL=fimo-config.types.d.ts.map
@@ -8,7 +8,6 @@
8
8
  import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
9
9
  import { join } from 'node:path';
10
10
  import { parseSync } from 'oxc-parser';
11
- import { collectScopedBindings, isFunctionLike, resolveScopedKey } from './i18n-namespace.js';
12
11
  function findReactFiles(dir, files = []) {
13
12
  if (!existsSync(dir)) {
14
13
  return files;
@@ -35,21 +34,17 @@ function extractFromFile(filePath) {
35
34
  const code = readFileSync(filePath, 'utf-8');
36
35
  const result = parseSync(filePath, code);
37
36
  const ast = result.program;
38
- function visit(node, scopes) {
37
+ function visit(node) {
39
38
  if (!node || typeof node !== 'object') {
40
39
  return;
41
40
  }
42
- // A function introduces a new scope layer with its `useTranslations`
43
- // bindings; nested calls resolve innermost-first.
44
- const childScopes = isFunctionLike(node) ? [...scopes, collectScopedBindings(node)] : scopes;
45
- if (node.type === 'CallExpression' && node.callee?.type === 'Identifier') {
46
- const name = node.callee.name;
47
- const firstArg = (node.arguments ?? [])[0];
48
- if (firstArg?.type === 'Literal' && typeof firstArg.value === 'string') {
49
- const { key, scoped, unresolvable } = resolveScopedKey(name, firstArg.value, childScopes);
50
- // A namespace-scoped var (any name) OR a bare Fimo `t(...)`. A passed-in
51
- // param `t` is unresolvable (the caller owns the namespace) — skip it.
52
- if (!unresolvable && (scoped || name === 't')) {
41
+ if (node.type === 'CallExpression') {
42
+ const callee = node.callee;
43
+ if (callee?.type === 'Identifier' && callee?.name === 't') {
44
+ const args = node.arguments || [];
45
+ const firstArg = args[0];
46
+ if (firstArg?.type === 'Literal' && typeof firstArg.value === 'string') {
47
+ const key = firstArg.value;
53
48
  declarations.push({ key, file: filePath });
54
49
  }
55
50
  }
@@ -58,15 +53,15 @@ function extractFromFile(filePath) {
58
53
  const child = node[key];
59
54
  if (Array.isArray(child)) {
60
55
  for (const item of child) {
61
- visit(item, childScopes);
56
+ visit(item);
62
57
  }
63
58
  }
64
59
  else if (typeof child === 'object' && child !== null) {
65
- visit(child, childScopes);
60
+ visit(child);
66
61
  }
67
62
  }
68
63
  }
69
- visit(ast, []);
64
+ visit(ast);
70
65
  }
71
66
  catch (error) {
72
67
  console.error(`Failed to parse ${filePath}:`, error);
@@ -23,7 +23,6 @@ export interface PhaseResult {
23
23
  skipped?: {
24
24
  reason: string;
25
25
  };
26
- warnings?: string[];
27
26
  }
28
27
  export interface ValidationReport {
29
28
  ok: boolean;
@@ -76,9 +76,6 @@ function formatPhaseHuman(phase) {
76
76
  out.push(` ${parts.join(', ')}`);
77
77
  }
78
78
  }
79
- for (const warning of phase.warnings ?? []) {
80
- out.push(` ⚠ ${warning}`);
81
- }
82
79
  for (const err of phase.errors) {
83
80
  const loc = err.file ? ` (${err.file}${err.line ? `:${err.line}` : ''}${err.column ? `:${err.column}` : ''})` : '';
84
81
  out.push(` [${err.kind}] ${err.rule}: ${err.message}${loc}`);
@@ -31,17 +31,6 @@ export function validateRouteMetadata(options = {}) {
31
31
  const srcDir = path.resolve(cwd, 'src');
32
32
  const routesFilePath = path.resolve(srcDir, 'routes.ts');
33
33
  const errors = [];
34
- // Everything below the i18n checks cross-validates React Router's
35
- // src/routes.ts. Adopted (wrapped) projects on another framework declare it
36
- // in fimo-config.json — their routes come from framework scans, so only the
37
- // framework-neutral config checks apply.
38
- const earlyConfig = readJson(configFilePath);
39
- const framework = typeof earlyConfig.framework === 'string' ? earlyConfig.framework : 'react-router';
40
- if (framework !== 'react-router') {
41
- const i18n = validateI18nConfig(earlyConfig);
42
- const configRoutes = loadConfigRoutes(earlyConfig);
43
- return { errors: i18n.errors, routesChecked: configRoutes.length };
44
- }
45
34
  // Parse routes from src/routes.ts (file → path mapping)
46
35
  let fileRoutes = [];
47
36
  if (!fs.existsSync(routesFilePath)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fimo",
3
- "version": "0.9.0-experimental.1",
3
+ "version": "0.9.0",
4
4
  "description": "Fimo CLI - create, deploy, and manage Fimo projects",
5
5
  "bin": {
6
6
  "fimo": "dist/cli/index.js"
@@ -22,18 +22,6 @@
22
22
  "types": "./dist/runtime/index.d.ts",
23
23
  "import": "./dist/runtime/index.js"
24
24
  },
25
- "./react": {
26
- "types": "./dist/runtime/react/index.d.ts",
27
- "import": "./dist/runtime/react/index.js"
28
- },
29
- "./next": {
30
- "types": "./dist/runtime/next/index.d.ts",
31
- "import": "./dist/runtime/next/index.js"
32
- },
33
- "./next/server": {
34
- "types": "./dist/runtime/next/server.d.ts",
35
- "import": "./dist/runtime/next/server.js"
36
- },
37
25
  "./react-router": {
38
26
  "types": "./dist/runtime/react-router/index.d.ts",
39
27
  "import": "./dist/runtime/react-router/index.js"
@@ -53,50 +41,6 @@
53
41
  "./templates": {
54
42
  "types": "./dist/runtime/templates.d.ts",
55
43
  "import": "./dist/runtime/templates.js"
56
- },
57
- "./strapi": {
58
- "types": "./dist/runtime/strapi/index.d.ts",
59
- "import": "./dist/runtime/strapi/index.js"
60
- },
61
- "./remix": {
62
- "types": "./dist/runtime/remix/index.d.ts",
63
- "import": "./dist/runtime/remix/index.js"
64
- },
65
- "./remix/server": {
66
- "types": "./dist/runtime/remix/server.d.ts",
67
- "import": "./dist/runtime/remix/server.js"
68
- },
69
- "./astro": {
70
- "types": "./dist/runtime/astro/index.d.ts",
71
- "import": "./dist/runtime/astro/index.js"
72
- },
73
- "./astro/server": {
74
- "types": "./dist/runtime/astro/server.d.ts",
75
- "import": "./dist/runtime/astro/server.js"
76
- },
77
- "./nuxt": {
78
- "types": "./dist/runtime/nuxt/index.d.ts",
79
- "import": "./dist/runtime/nuxt/index.js"
80
- },
81
- "./nuxt/server": {
82
- "types": "./dist/runtime/nuxt/server.d.ts",
83
- "import": "./dist/runtime/nuxt/server.js"
84
- },
85
- "./svelte": {
86
- "types": "./dist/runtime/svelte/index.d.ts",
87
- "import": "./dist/runtime/svelte/index.js"
88
- },
89
- "./svelte/server": {
90
- "types": "./dist/runtime/svelte/server.d.ts",
91
- "import": "./dist/runtime/svelte/server.js"
92
- },
93
- "./vue": {
94
- "types": "./dist/runtime/vue/index.d.ts",
95
- "import": "./dist/runtime/vue/index.js"
96
- },
97
- "./vue/server": {
98
- "types": "./dist/runtime/vue/server.d.ts",
99
- "import": "./dist/runtime/vue/server.js"
100
44
  }
101
45
  },
102
46
  "publishConfig": {
@@ -153,37 +97,16 @@
153
97
  "yaml": "2.8.2"
154
98
  },
155
99
  "peerDependencies": {
156
- "@remix-run/react": ">=2.0.0",
157
- "@sveltejs/kit": ">=2.0.0",
158
100
  "@tanstack/react-query": "^5.80.6",
159
- "astro": ">=4.0.0",
160
- "next": ">=14.0.0",
161
- "nuxt": ">=3.0.0",
162
- "react": "^18.2.0 || ^19.0.0",
163
- "react-dom": "^18.2.0 || ^19.0.0",
101
+ "react": "^19.0.0",
102
+ "react-dom": "^19.0.0",
164
103
  "react-router": "^7.0.0 || ^8.0.0",
165
- "vite": "^7.0.0 || ^8.0.0",
166
- "vue": ">=3.0.0"
104
+ "vite": "^7.0.0 || ^8.0.0"
167
105
  },
168
106
  "peerDependenciesMeta": {
169
- "@remix-run/react": {
170
- "optional": true
171
- },
172
- "@sveltejs/kit": {
173
- "optional": true
174
- },
175
107
  "@tanstack/react-query": {
176
108
  "optional": true
177
109
  },
178
- "astro": {
179
- "optional": true
180
- },
181
- "next": {
182
- "optional": true
183
- },
184
- "nuxt": {
185
- "optional": true
186
- },
187
110
  "react": {
188
111
  "optional": true
189
112
  },
@@ -195,9 +118,6 @@
195
118
  },
196
119
  "vite": {
197
120
  "optional": true
198
- },
199
- "vue": {
200
- "optional": true
201
121
  }
202
122
  },
203
123
  "engines": {
package/release.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "source": "npm",
3
- "apiUrl": "https://api.staging.fimo.team",
4
- "webUrl": "https://staging.fimo.team"
3
+ "apiUrl": "https://api.fimo.ai",
4
+ "webUrl": "https://fimo.ai"
5
5
  }
@@ -1,39 +1,17 @@
1
1
  #!/usr/bin/env node
2
- // Two paths:
2
+ // GLOBAL install (`npm i -g fimo`, `pnpm add -g fimo`, etc.)
3
+ // → print a short, non-blocking hint. We steer users toward `npx fimo@latest`
4
+ // (always current) and `fimo init`, and don't run any interactive prompt
5
+ // from a package lifecycle script.
3
6
  //
4
- // 1) GLOBAL install (`npm i -g fimo`, `pnpm add -g fimo`, etc.)
5
- // → print a short, non-blocking hint. We steer users toward
6
- // `npx fimo@latest` (always current) and `fimo init`, and don't run any
7
- // interactive prompt from a package lifecycle script.
8
- //
9
- // 2) LOCAL / project install (`pnpm install` in a project that has `fimo`
10
- // in its dependencies)
11
- // → if the install was inside a Fimo project (`.fimo.settings.json`
12
- // found by walking up from `INIT_CWD`), re-sync the project-scope
13
- // `fimo` skill into every per-agent dir the project is set up for.
14
- // The skill source is `node_modules/fimo/assets/skills/fimo/`, version-
15
- // locked to the installed package.
16
- //
17
- // Silent on no-op; non-fatal on any failure (a skill copy must
18
- // never break the user's install).
19
- //
20
- // pnpm caveat: by default, pnpm strips build scripts from non-approved deps.
21
- // Fimo-controlled installs (`fimo init --install`, sandbox `fimopm install`,
22
- // and E2E helper installs) pass a scoped allow-all-builds flag for pnpm so the
23
- // fimo postinstall hook can refresh project skills. The template intentionally
24
- // does not persist allowlists in package.json / pnpm-workspace.yaml because
25
- // those conflict with sandbox install flags and drift when deps change. npm /
26
- // yarn run lifecycle scripts by default and need no extra config.
27
-
28
- import fs from 'node:fs';
29
- import path from 'node:path';
30
- import { fileURLToPath } from 'node:url';
7
+ // LOCAL / project install (`pnpm install` in a project that depends on `fimo`)
8
+ // → nothing to do. Fimo no longer writes skills into projects: the only skill
9
+ // is a tiny global `fimo` bootstrap installed by `fimo skills install`, and
10
+ // the rich skills are fetched on demand via `fimo skills get`. Keeping this
11
+ // path a no-op means `pnpm install` never touches the working tree.
31
12
 
32
13
  import { isGlobalInstall } from './lib/postinstall-onboarding.mjs';
33
14
 
34
- const THIS_FILE = fileURLToPath(import.meta.url);
35
- const CLI_ROOT = path.resolve(path.dirname(THIS_FILE), '..');
36
-
37
15
  const ESC = String.fromCharCode(27);
38
16
  const green = (s) => `${ESC}[32m${s}${ESC}[0m`;
39
17
  const bold = (s) => `${ESC}[1m${s}${ESC}[0m`;
@@ -49,40 +27,8 @@ if (isGlobalInstall(process.env)) {
49
27
  console.log();
50
28
  console.log(` Get started:`);
51
29
  console.log(` ${cyan('fimo init')} ${dim('# sign in, create or link a project')}`);
52
- console.log(` ${cyan('fimo skills install')} ${dim('# set up skills for your AI coding tools')}`);
30
+ console.log(` ${cyan('fimo skills install')} ${dim('# set up Fimo skills for your AI coding tools')}`);
53
31
  console.log();
54
- process.exit(0);
55
32
  }
56
33
 
57
- // Project-install path: dynamic-import the built helper and re-sync the
58
- // project-scope `fimo` skill if we resolve to a Fimo project root.
59
- // Skipped silently when:
60
- // - The CLI hasn't been built yet (workspace dev install before `pnpm build`)
61
- // - `INIT_CWD` isn't inside a Fimo project (helper returns 'not-a-fimo-project')
62
- // - The project has no per-agent rule files yet (helper returns 'no-agents')
63
- const helperPath = path.join(CLI_ROOT, 'dist', 'cli', 'project-skill.js');
64
- if (!fs.existsSync(helperPath)) {
65
- process.exit(0);
66
- }
67
-
68
- try {
69
- const { syncProjectFimoSkill } = await import(helperPath);
70
- const initCwd = process.env.INIT_CWD ?? process.cwd();
71
- const result = await syncProjectFimoSkill({ cwd: initCwd });
72
-
73
- if (result.status === 'installed' && !result.skillSourceMissing) {
74
- const skillDest =
75
- result.agents.length === 1 ? result.agents[0] : `${result.agents.length} agents (${result.agents.join(', ')})`;
76
- console.log(`${green('✓')} fimo skill synced for ${skillDest}`);
77
- for (const failure of result.failures) {
78
- console.error(` ! fimo skill failed for ${failure.agent}: ${failure.error}`);
79
- }
80
- }
81
- // Other statuses (not-a-fimo-project, no-agents, skill-source-missing)
82
- // are silent — they're expected for non-Fimo deps and pre-create installs.
83
- } catch (err) {
84
- // Never break the user's install over a skill-sync failure.
85
- if (process.env.FIMO_DEBUG_POSTINSTALL === '1') {
86
- console.error('[fimo postinstall] sync failed:', err);
87
- }
88
- }
34
+ process.exit(0);
@@ -0,0 +1,44 @@
1
+ # Fimo project
2
+
3
+ This is a Fimo project — a content + media + forms + analytics platform for React websites with a CMS
4
+ dashboard. Fimo owns everything after the build: content, editing, environments, and hosting stay
5
+ editable by the whole team.
6
+
7
+ ## Load the Fimo skills before doing Fimo work
8
+
9
+ Fimo ships its skills through its CLI so they always match this project's installed version. Before
10
+ writing schema-driven code, running CLI/deploy workflows, or migrating content, load the relevant skill:
11
+
12
+ ```bash
13
+ npx fimo@latest skills list # what's available
14
+ npx fimo@latest skills get fimo-code # writing code: schemas, forms, labels, fimo/ui
15
+ npx fimo@latest skills get fimo-cli # CLI + workflows: push, deploy, sync, labels, domains
16
+ ```
17
+
18
+ Add `--ref <doc>` for a skill's reference docs. Always fetch fresh from the CLI rather than trusting a
19
+ cached copy.
20
+
21
+ ## Critical: never hardcode user-facing things
22
+
23
+ Fimo's whole point is that editors change content in the dashboard, not in code. Anything hardcoded
24
+ breaks that. Load `fimo-code` for the exact primitives, but the rule is:
25
+
26
+ - **Content** → declare a schema, render with the generated `useList()` / `useGet()` hooks + `fimo/ui`. Never `const posts = [...]` in JSX.
27
+ - **Strings** → `t('key')` + `fimo labels set`. Never `<h1>Welcome</h1>`.
28
+ - **Media** → `<Image>` / `<Video>` from `fimo/ui`. Never `<img src>`.
29
+ - **Forms** → the generated Zod schema + `submitX` helper. Never hand-roll fetch/validation.
30
+
31
+ ## Development loop
32
+
33
+ 1. Start the dev server with the project's package manager (`pnpm dev`, `npm run dev`, …) — live reload on localhost.
34
+ 2. Make edits; the running server shows them instantly. No deploy needed to see local changes.
35
+ 3. When a unit of work is done, **offer** to deploy: `fimo deploy` for a hosted preview / shareable link, or `fimo deploy --publish` to go live. Only after the developer confirms — never deploy on your own.
36
+
37
+ ## Project rules
38
+
39
+ - **Deploy is on-demand**, not a save step. Batch related edits into one deploy.
40
+ - Do not add a second deploy tool (Vercel, Netlify, etc.).
41
+ - **Never edit `.fimo.settings.json`** — it links this folder to the remote project.
42
+ - The scaffolded homepage is disposable starter content. Replace it on the first real generation.
43
+ - **Secrets live in the Fimo dashboard**, not in a committed `.env`.
44
+ - When scripting, pass flags to `fimo` commands (`fimo deploy -m "…"`, `fimo delete -y`) — bare interactive forms hang in non-TTY shells.
@@ -0,0 +1,5 @@
1
+ # Fimo project
2
+
3
+ Instructions for this project live in `AGENTS.md` (the cross-tool standard). Read it:
4
+
5
+ @AGENTS.md
@@ -21,7 +21,7 @@
21
21
  "cmdk": "^1.1.1",
22
22
  "date-fns": "^4.1.0",
23
23
  "embla-carousel-react": "^8.6.0",
24
- "fimo": "0.9.0-experimental.1",
24
+ "fimo": "0.9.0",
25
25
  "input-otp": "^1.4.2",
26
26
  "isbot": "^5",
27
27
  "lucide-react": "^0.577.0",