@multiplatform.one/cli 6.0.4 → 6.3.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/README.md +72 -0
- package/bin/createMultiplatformApp.mjs +77 -0
- package/lib/bin/multiplatformOne.mjs +23 -9
- package/lib/commands/e2e.mjs +3 -3
- package/lib/commands/init.mjs +16 -12
- package/lib/commands/initApp.mjs +145 -0
- package/lib/generateVscode.mjs +27 -10
- package/package.json +23 -12
- package/src/bin/multiplatformOne.ts +54 -16
- package/src/commands/init.ts +7 -18
- package/src/commands/initApp.spec.ts +210 -0
- package/src/commands/initApp.ts +228 -0
- package/src/generateVscode.ts +31 -9
- package/templates/app/AGENTS.md +142 -0
- package/templates/app/README.md +62 -0
- package/templates/app/apps/__NAME__/index.html +36 -0
- package/templates/app/apps/__NAME__/package.json +30 -0
- package/templates/app/apps/__NAME__/public/fonts/inter-400.woff2 +0 -0
- package/templates/app/apps/__NAME__/public/fonts/inter-700.woff2 +0 -0
- package/templates/app/apps/__NAME__/src/App.tsx +9 -0
- package/templates/app/apps/__NAME__/src/main.tsx +14 -0
- package/templates/app/apps/__NAME__/tsconfig.json +8 -0
- package/templates/app/apps/__NAME__/vite.config.ts +17 -0
- package/templates/app/features/__NAME__/package.json +34 -0
- package/templates/app/features/__NAME__/src/HomeScreen.tsx +70 -0
- package/templates/app/features/__NAME__/src/index.ts +1 -0
- package/templates/app/features/__NAME__/tsconfig.json +5 -0
- package/templates/app/gitignore +8 -0
- package/templates/app/npmrc +2 -0
- package/templates/app/oxlintrc.json +39 -0
- package/templates/app/package.json +20 -0
- package/templates/app/packages/config/config.json +4 -0
- package/templates/app/packages/config/index.ts +5 -0
- package/templates/app/packages/config/package.json +17 -0
- package/templates/app/packages/config/tsconfig.json +5 -0
- package/templates/app/pnpm-workspace.yaml +4 -0
- package/templates/app/tsconfig.base.json +16 -0
- package/templates/universal/AGENTS.md +142 -0
- package/templates/universal/README.md +97 -0
- package/templates/universal/apps/__NAME__/app.config.js +31 -0
- package/templates/universal/apps/__NAME__/config/tamagui.config.ts +4 -0
- package/templates/universal/apps/__NAME__/package.json +61 -0
- package/templates/universal/apps/__NAME__/polyfills.native.ts +23 -0
- package/templates/universal/apps/__NAME__/polyfills.ts +3 -0
- package/templates/universal/apps/__NAME__/public/fonts/inter-400.woff2 +0 -0
- package/templates/universal/apps/__NAME__/public/fonts/inter-700.woff2 +0 -0
- package/templates/universal/apps/__NAME__/routes/_layout.tsx +22 -0
- package/templates/universal/apps/__NAME__/routes/_layout.web.tsx +21 -0
- package/templates/universal/apps/__NAME__/routes/about.tsx +3 -0
- package/templates/universal/apps/__NAME__/routes/index.tsx +3 -0
- package/templates/universal/apps/__NAME__/src/app.ts +10 -0
- package/templates/universal/apps/__NAME__/tamagui.css +3 -0
- package/templates/universal/apps/__NAME__/tsconfig.json +8 -0
- package/templates/universal/apps/__NAME__/types.d.ts +9 -0
- package/templates/universal/apps/__NAME__/vite.config.ts +66 -0
- package/templates/universal/features/__NAME__/package.json +35 -0
- package/templates/universal/features/__NAME__/src/AboutScreen.tsx +27 -0
- package/templates/universal/features/__NAME__/src/AppLayout.tsx +50 -0
- package/templates/universal/features/__NAME__/src/ContactForm.spec.tsx +22 -0
- package/templates/universal/features/__NAME__/src/ContactForm.tsx +55 -0
- package/templates/universal/features/__NAME__/src/HomeScreen.tsx +40 -0
- package/templates/universal/features/__NAME__/src/index.ts +4 -0
- package/templates/universal/features/__NAME__/src/screens.spec.ts +12 -0
- package/templates/universal/features/__NAME__/tsconfig.json +5 -0
- package/templates/universal/features/__NAME__/vitest.config.mjs +7 -0
- package/templates/universal/gitignore +12 -0
- package/templates/universal/npmrc +3 -0
- package/templates/universal/oxlintrc.json +39 -0
- package/templates/universal/package.json +27 -0
- package/templates/universal/packages/config/config.json +4 -0
- package/templates/universal/packages/config/index.ts +5 -0
- package/templates/universal/packages/config/package.json +17 -0
- package/templates/universal/packages/config/tsconfig.json +5 -0
- package/templates/universal/packages/i18n/en/common.json +30 -0
- package/templates/universal/packages/i18n/es/common.json +30 -0
- package/templates/universal/packages/i18n/i18n.d.ts +16 -0
- package/templates/universal/packages/i18n/index.ts +26 -0
- package/templates/universal/packages/i18n/package.json +20 -0
- package/templates/universal/packages/i18n/resources.ts +13 -0
- package/templates/universal/packages/i18n/tsconfig.json +5 -0
- package/templates/universal/packages/themes/accent.ts +32 -0
- package/templates/universal/packages/themes/base.ts +50 -0
- package/templates/universal/packages/themes/fonts.ts +8 -0
- package/templates/universal/packages/themes/index.ts +33 -0
- package/templates/universal/packages/themes/package.json +23 -0
- package/templates/universal/packages/themes/tamagui.config.ts +14 -0
- package/templates/universal/packages/themes/tsconfig.json +5 -0
- package/templates/universal/pnpm-workspace.yaml +4 -0
- package/templates/universal/tsconfig.base.json +21 -0
- package/templates/universal/types/env.d.ts +9 -0
- package/types/bin/multiplatformOne.d.ts +11 -0
- package/types/bin/multiplatformOne.d.ts.map +1 -0
- package/types/commands/e2e.d.ts +14 -0
- package/types/commands/e2e.d.ts.map +1 -0
- package/types/commands/init.d.ts +16 -0
- package/types/commands/init.d.ts.map +1 -0
- package/types/commands/initApp.d.ts +25 -0
- package/types/commands/initApp.d.ts.map +1 -0
- package/types/generateVscode.d.ts +7 -0
- package/types/generateVscode.d.ts.map +1 -0
- package/types/index.d.ts +2 -0
- package/types/index.d.ts.map +1 -0
- package/types/types.d.ts +6 -0
- package/types/types.d.ts.map +1 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
2
|
+
import { fireEvent } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { ContactForm } from "./ContactForm";
|
|
5
|
+
|
|
6
|
+
describe("ContactForm", () => {
|
|
7
|
+
it("renders the starter form fields", () => {
|
|
8
|
+
const result = renderWithProviders(<ContactForm />);
|
|
9
|
+
expect(result.findTextElement("Contact")).toBeDefined();
|
|
10
|
+
expect(result.findTextElement("Name")).toBeDefined();
|
|
11
|
+
expect(result.findTextElement("Message")).toBeDefined();
|
|
12
|
+
expect(result.getButton("Send")).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("shows required errors when submitting empty", () => {
|
|
16
|
+
const result = renderWithProviders(<ContactForm />);
|
|
17
|
+
const send = result.getButton("Send");
|
|
18
|
+
expect(send).toBeDefined();
|
|
19
|
+
fireEvent.click(send as Element);
|
|
20
|
+
expect(result.findTextElement("This field is required")).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { H2, Paragraph, YStack } from "@multiplatform.one/components";
|
|
2
|
+
import { Button, Input, TextArea } from "@multiplatform.one/forms";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Starter form demonstrating @multiplatform.one/forms fields (Input, TextArea,
|
|
8
|
+
* Button) with the canonical onChangeText/value contract, helper/error slots,
|
|
9
|
+
* and a submit handler. Replace the console.info with a real mutation.
|
|
10
|
+
*/
|
|
11
|
+
export function ContactForm() {
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
const [name, setName] = useState("");
|
|
14
|
+
const [message, setMessage] = useState("");
|
|
15
|
+
const [submitted, setSubmitted] = useState(false);
|
|
16
|
+
const [sent, setSent] = useState(false);
|
|
17
|
+
|
|
18
|
+
const requiredError = t("screens.contact.required", "This field is required");
|
|
19
|
+
|
|
20
|
+
function handleSend() {
|
|
21
|
+
setSubmitted(true);
|
|
22
|
+
if (!name.trim() || !message.trim()) return;
|
|
23
|
+
// eslint-disable-next-line no-console
|
|
24
|
+
console.info("[__NAME__] contact form:", { name, message });
|
|
25
|
+
setSent(true);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<YStack gap="$4" maxWidth={480}>
|
|
30
|
+
<H2>{t("screens.contact.title", "Contact")}</H2>
|
|
31
|
+
<Input
|
|
32
|
+
label={t("screens.contact.name", "Name")}
|
|
33
|
+
placeholder={t("screens.contact.namePlaceholder", "Your name")}
|
|
34
|
+
value={name}
|
|
35
|
+
onChangeText={setName}
|
|
36
|
+
required
|
|
37
|
+
error={submitted && !name.trim() ? requiredError : undefined}
|
|
38
|
+
/>
|
|
39
|
+
<TextArea
|
|
40
|
+
label={t("screens.contact.message", "Message")}
|
|
41
|
+
placeholder={t("screens.contact.messagePlaceholder", "What do you want to say?")}
|
|
42
|
+
value={message}
|
|
43
|
+
onChangeText={setMessage}
|
|
44
|
+
required
|
|
45
|
+
error={submitted && !message.trim() ? requiredError : undefined}
|
|
46
|
+
/>
|
|
47
|
+
<Button onPress={handleSend}>{t("screens.contact.send", "Send")}</Button>
|
|
48
|
+
{sent ? (
|
|
49
|
+
<Paragraph color="$color11">
|
|
50
|
+
{t("screens.contact.sent", "Message sent (demo) — check the console.")}
|
|
51
|
+
</Paragraph>
|
|
52
|
+
) : null}
|
|
53
|
+
</YStack>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { H1, Paragraph, Screen, Separator, YStack } from "@multiplatform.one/components";
|
|
2
|
+
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
3
|
+
import { Link } from "one";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { ContactForm } from "./ContactForm";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Home screen (route: /). Keep product UI in features/<name>; the app shell
|
|
9
|
+
* (apps/<name>) only wires routes and providers.
|
|
10
|
+
*/
|
|
11
|
+
export function HomeScreen() {
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
// Knob wiring: spread complete knobProps fragments — never cherry-pick
|
|
14
|
+
// resolved tokens (see AGENTS.md, knobs system).
|
|
15
|
+
const { knobProps } = useResolvedKnobs({ component: "Card" });
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Screen>
|
|
19
|
+
<YStack
|
|
20
|
+
{...knobProps.surface}
|
|
21
|
+
{...knobProps.borderRadius}
|
|
22
|
+
{...knobProps.panelPadding}
|
|
23
|
+
{...knobProps.gap}
|
|
24
|
+
backgroundColor="$color2"
|
|
25
|
+
borderColor="$borderColor"
|
|
26
|
+
>
|
|
27
|
+
<H1 {...knobProps.heading}>{t("screens.home.welcome", "Welcome to __NAME_PASCAL__")}</H1>
|
|
28
|
+
<Paragraph {...knobProps.body} color="$color11">
|
|
29
|
+
{t(
|
|
30
|
+
"screens.home.message",
|
|
31
|
+
"One codebase — web, iOS, and Android. Edit features/__NAME__/src/HomeScreen.tsx to get started.",
|
|
32
|
+
)}
|
|
33
|
+
</Paragraph>
|
|
34
|
+
<Link href="/about">{t("nav.about", "About")}</Link>
|
|
35
|
+
</YStack>
|
|
36
|
+
<Separator />
|
|
37
|
+
<ContactForm />
|
|
38
|
+
</Screen>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { AboutScreen, AppLayout, ContactForm, HomeScreen } from "./index";
|
|
3
|
+
|
|
4
|
+
// Source-shape smoke: every starter screen is exported and renderable
|
|
5
|
+
// (component functions). Full render coverage lives next to each component.
|
|
6
|
+
describe("feature exports", () => {
|
|
7
|
+
it("exports the starter screens", () => {
|
|
8
|
+
for (const Component of [AboutScreen, AppLayout, ContactForm, HomeScreen]) {
|
|
9
|
+
expect(typeof Component).toBe("function");
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createVitestConfig } from "@multiplatform.one/config/vitest";
|
|
2
|
+
|
|
3
|
+
export default createVitestConfig({
|
|
4
|
+
tamaguiConfig: "../../packages/themes/tamagui.config.ts",
|
|
5
|
+
setupFiles: ["@multiplatform.one/test-utils/setup"],
|
|
6
|
+
include: ["src/**/*.spec.{ts,tsx}"],
|
|
7
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"rules": {
|
|
4
|
+
"no-unused-vars": [
|
|
5
|
+
"warn",
|
|
6
|
+
{
|
|
7
|
+
"argsIgnorePattern": "^_",
|
|
8
|
+
"varsIgnorePattern": "^_"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"no-console": "warn",
|
|
12
|
+
"eqeqeq": "error",
|
|
13
|
+
"no-var": "error",
|
|
14
|
+
"prefer-const": "error"
|
|
15
|
+
},
|
|
16
|
+
"overrides": [
|
|
17
|
+
{
|
|
18
|
+
"files": [
|
|
19
|
+
"apps/**/*.{ts,tsx,js,jsx}",
|
|
20
|
+
"features/**/*.{ts,tsx,js,jsx}",
|
|
21
|
+
"packages/**/*.{ts,tsx,js,jsx}"
|
|
22
|
+
],
|
|
23
|
+
"rules": {
|
|
24
|
+
"no-restricted-imports": [
|
|
25
|
+
"error",
|
|
26
|
+
{
|
|
27
|
+
"paths": [
|
|
28
|
+
{
|
|
29
|
+
"name": "tamagui",
|
|
30
|
+
"message": "Raw tamagui is only allowed inside framework packages. Import from @multiplatform.one/components, @multiplatform.one/forms, or @multiplatform.one/theme. See AGENTS.md."
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"ignorePatterns": ["**/node_modules/**", "**/dist/**", "**/build/**"]
|
|
39
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__NAME__",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "__NAME__ — multiplatform.one universal app (web + iOS + Android)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "pnpm --filter @app/__NAME__ dev",
|
|
9
|
+
"build": "pnpm --filter @app/__NAME__ build",
|
|
10
|
+
"serve": "pnpm --filter @app/__NAME__ serve",
|
|
11
|
+
"native": "pnpm --filter @app/__NAME__ prebuild:native",
|
|
12
|
+
"ios": "pnpm --filter @app/__NAME__ ios",
|
|
13
|
+
"android": "pnpm --filter @app/__NAME__ android",
|
|
14
|
+
"test": "pnpm -r test",
|
|
15
|
+
"lint": "oxlint -c oxlintrc.json .",
|
|
16
|
+
"typecheck": "pnpm -r typecheck"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/chrome": "^0.2.5",
|
|
20
|
+
"@types/firefox-webext-browser": "^143.0.0",
|
|
21
|
+
"oxlint": "^1.62.0"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20"
|
|
25
|
+
},
|
|
26
|
+
"packageManager": "pnpm@10.32.1"
|
|
27
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@package/config",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.ts",
|
|
9
|
+
"./config.json": "./config.json"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"typecheck": "tsc --noEmit"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"typescript": "~5.9.3"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"app": {
|
|
3
|
+
"name": "__NAME_PASCAL__"
|
|
4
|
+
},
|
|
5
|
+
"nav": {
|
|
6
|
+
"home": "Home",
|
|
7
|
+
"about": "About"
|
|
8
|
+
},
|
|
9
|
+
"screens": {
|
|
10
|
+
"home": {
|
|
11
|
+
"welcome": "Welcome to __NAME_PASCAL__",
|
|
12
|
+
"message": "One codebase — web, iOS, and Android. Edit features/__NAME__/src/HomeScreen.tsx to get started."
|
|
13
|
+
},
|
|
14
|
+
"about": {
|
|
15
|
+
"title": "About",
|
|
16
|
+
"message": "This screen lives in features/__NAME__ and is wired to the /about route by apps/__NAME__/routes/about.tsx.",
|
|
17
|
+
"back": "Back home"
|
|
18
|
+
},
|
|
19
|
+
"contact": {
|
|
20
|
+
"title": "Contact",
|
|
21
|
+
"name": "Name",
|
|
22
|
+
"namePlaceholder": "Your name",
|
|
23
|
+
"message": "Message",
|
|
24
|
+
"messagePlaceholder": "What do you want to say?",
|
|
25
|
+
"send": "Send",
|
|
26
|
+
"required": "This field is required",
|
|
27
|
+
"sent": "Message sent (demo) — check the console."
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"app": {
|
|
3
|
+
"name": "__NAME_PASCAL__"
|
|
4
|
+
},
|
|
5
|
+
"nav": {
|
|
6
|
+
"home": "Inicio",
|
|
7
|
+
"about": "Acerca de"
|
|
8
|
+
},
|
|
9
|
+
"screens": {
|
|
10
|
+
"home": {
|
|
11
|
+
"welcome": "Bienvenido a __NAME_PASCAL__",
|
|
12
|
+
"message": "Un solo código — web, iOS y Android. Edita features/__NAME__/src/HomeScreen.tsx para empezar."
|
|
13
|
+
},
|
|
14
|
+
"about": {
|
|
15
|
+
"title": "Acerca de",
|
|
16
|
+
"message": "Esta pantalla vive en features/__NAME__ y se conecta a la ruta /about desde apps/__NAME__/routes/about.tsx.",
|
|
17
|
+
"back": "Volver al inicio"
|
|
18
|
+
},
|
|
19
|
+
"contact": {
|
|
20
|
+
"title": "Contacto",
|
|
21
|
+
"name": "Nombre",
|
|
22
|
+
"namePlaceholder": "Tu nombre",
|
|
23
|
+
"message": "Mensaje",
|
|
24
|
+
"messagePlaceholder": "¿Qué quieres decir?",
|
|
25
|
+
"send": "Enviar",
|
|
26
|
+
"required": "Este campo es obligatorio",
|
|
27
|
+
"sent": "Mensaje enviado (demo) — mira la consola."
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18next TypeScript type augmentations.
|
|
3
|
+
*
|
|
4
|
+
* Key typing is intentionally loose (t() accepts any string; missing keys fall
|
|
5
|
+
* back at runtime). To restore strict typed keys, add
|
|
6
|
+
* `resources: { common: typeof import("./en/common.json") }` to
|
|
7
|
+
* CustomTypeOptions.
|
|
8
|
+
*/
|
|
9
|
+
import "i18next";
|
|
10
|
+
|
|
11
|
+
declare module "i18next" {
|
|
12
|
+
interface CustomTypeOptions {
|
|
13
|
+
defaultNS: "common";
|
|
14
|
+
returnNull: false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported languages. Add a language by creating `<lng>/common.json` and
|
|
3
|
+
* listing the code here (and in resources.ts for native).
|
|
4
|
+
*/
|
|
5
|
+
export const languages = ["en", "es"] as const;
|
|
6
|
+
|
|
7
|
+
/** Translation namespaces — one JSON file per namespace per language. */
|
|
8
|
+
export const namespaces = ["common"] as const;
|
|
9
|
+
|
|
10
|
+
/** Fallback language when a translation is missing. */
|
|
11
|
+
export const defaultLanguage = "en" as const;
|
|
12
|
+
|
|
13
|
+
/** Namespace used when t() calls do not specify one. */
|
|
14
|
+
export const defaultNamespace = "common" as const;
|
|
15
|
+
|
|
16
|
+
export type Language = (typeof languages)[number];
|
|
17
|
+
export type Namespace = (typeof namespaces)[number];
|
|
18
|
+
|
|
19
|
+
// Re-export framework i18n utilities
|
|
20
|
+
export {
|
|
21
|
+
createI18nConfig,
|
|
22
|
+
detectLanguage,
|
|
23
|
+
getPersistedLanguage,
|
|
24
|
+
persistLanguage,
|
|
25
|
+
} from "@multiplatform.one/i18n";
|
|
26
|
+
export type { I18nConfigOptions } from "@multiplatform.one/i18n";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@package/i18n",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.ts",
|
|
9
|
+
"./resources": "./resources.ts"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"typecheck": "tsc --noEmit"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@multiplatform.one/i18n": "__MPO_VERSION__"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "~5.9.3"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Static resource map for native (web loads translations through the vite
|
|
2
|
+
// i18next loader instead — see apps/__NAME__/routes/_layout.web.tsx).
|
|
3
|
+
import en from "./en/common.json";
|
|
4
|
+
import es from "./es/common.json";
|
|
5
|
+
|
|
6
|
+
export const resources = {
|
|
7
|
+
en: {
|
|
8
|
+
common: en,
|
|
9
|
+
},
|
|
10
|
+
es: {
|
|
11
|
+
common: es,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Framework default brand accent: solid-surface hsla(250) ramps. Replace with
|
|
2
|
+
// your brand color ramps to rebrand the whole app in one place.
|
|
3
|
+
export const accentTheme = {
|
|
4
|
+
darkPalette: [
|
|
5
|
+
"hsla(250, 50%, 35%, 1)",
|
|
6
|
+
"hsla(250, 50%, 38%, 1)",
|
|
7
|
+
"hsla(250, 50%, 41%, 1)",
|
|
8
|
+
"hsla(250, 50%, 43%, 1)",
|
|
9
|
+
"hsla(250, 50%, 46%, 1)",
|
|
10
|
+
"hsla(250, 50%, 49%, 1)",
|
|
11
|
+
"hsla(250, 50%, 52%, 1)",
|
|
12
|
+
"hsla(250, 50%, 54%, 1)",
|
|
13
|
+
"hsla(250, 50%, 57%, 1)",
|
|
14
|
+
"hsla(250, 50%, 60%, 1)",
|
|
15
|
+
"hsla(250, 50%, 90%, 1)",
|
|
16
|
+
"hsla(250, 50%, 95%, 1)",
|
|
17
|
+
],
|
|
18
|
+
lightPalette: [
|
|
19
|
+
"hsla(250, 50%, 95%, 1)",
|
|
20
|
+
"hsla(250, 50%, 95%, 1)",
|
|
21
|
+
"hsla(250, 50%, 65%, 1)",
|
|
22
|
+
"hsla(250, 50%, 62%, 1)",
|
|
23
|
+
"hsla(250, 50%, 59%, 1)",
|
|
24
|
+
"hsla(250, 50%, 57%, 1)",
|
|
25
|
+
"hsla(250, 50%, 54%, 1)",
|
|
26
|
+
"hsla(250, 50%, 51%, 1)",
|
|
27
|
+
"hsla(250, 50%, 48%, 1)",
|
|
28
|
+
"hsla(250, 50%, 46%, 1)",
|
|
29
|
+
"hsla(250, 50%, 43%, 1)",
|
|
30
|
+
"hsla(250, 50%, 40%, 1)",
|
|
31
|
+
],
|
|
32
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Framework default neutral ramps: Radix-mauve-shaped with a slight purple
|
|
2
|
+
// tint (hue 270) harmonizing with the hsla(250) accent. Steps: near-white
|
|
3
|
+
// surfaces (1-2), component/hover (3-5), borders (6-8), solid/muted (9-10),
|
|
4
|
+
// readable text (11-12). Swap these palettes to rebrand.
|
|
5
|
+
export const baseTheme = {
|
|
6
|
+
darkPalette: [
|
|
7
|
+
"hsla(270, 6%, 8.7%, 1)",
|
|
8
|
+
"hsla(270, 6%, 10.5%, 1)",
|
|
9
|
+
"hsla(270, 6%, 13.5%, 1)",
|
|
10
|
+
"hsla(270, 6%, 16%, 1)",
|
|
11
|
+
"hsla(270, 6%, 18.5%, 1)",
|
|
12
|
+
"hsla(270, 6%, 21.5%, 1)",
|
|
13
|
+
"hsla(270, 6%, 25.5%, 1)",
|
|
14
|
+
"hsla(270, 6%, 32%, 1)",
|
|
15
|
+
"hsla(270, 6%, 43%, 1)",
|
|
16
|
+
"hsla(270, 6%, 48%, 1)",
|
|
17
|
+
"hsla(270, 6%, 62.5%, 1)",
|
|
18
|
+
"hsla(270, 6%, 94%, 1)",
|
|
19
|
+
],
|
|
20
|
+
lightPalette: [
|
|
21
|
+
"hsla(270, 6%, 99.5%, 1)",
|
|
22
|
+
"hsla(270, 6%, 98.2%, 1)",
|
|
23
|
+
"hsla(270, 6%, 95.2%, 1)",
|
|
24
|
+
"hsla(270, 6%, 92.5%, 1)",
|
|
25
|
+
"hsla(270, 6%, 90%, 1)",
|
|
26
|
+
"hsla(270, 6%, 87%, 1)",
|
|
27
|
+
"hsla(270, 6%, 82.5%, 1)",
|
|
28
|
+
"hsla(270, 6%, 75%, 1)",
|
|
29
|
+
"hsla(270, 6%, 56.5%, 1)",
|
|
30
|
+
"hsla(270, 6%, 52%, 1)",
|
|
31
|
+
"hsla(270, 6%, 40%, 1)",
|
|
32
|
+
"hsla(270, 6%, 14%, 1)",
|
|
33
|
+
],
|
|
34
|
+
lightShadows: {
|
|
35
|
+
shadow1: "rgba(0,0,0,0.04)",
|
|
36
|
+
shadow2: "rgba(0,0,0,0.08)",
|
|
37
|
+
shadow3: "rgba(0,0,0,0.16)",
|
|
38
|
+
shadow4: "rgba(0,0,0,0.24)",
|
|
39
|
+
shadow5: "rgba(0,0,0,0.32)",
|
|
40
|
+
shadow6: "rgba(0,0,0,0.4)",
|
|
41
|
+
},
|
|
42
|
+
darkShadows: {
|
|
43
|
+
shadow1: "rgba(0,0,0,0.2)",
|
|
44
|
+
shadow2: "rgba(0,0,0,0.3)",
|
|
45
|
+
shadow3: "rgba(0,0,0,0.4)",
|
|
46
|
+
shadow4: "rgba(0,0,0,0.5)",
|
|
47
|
+
shadow5: "rgba(0,0,0,0.6)",
|
|
48
|
+
shadow6: "rgba(0,0,0,0.7)",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createFontLoader } from "@multiplatform.one/theme";
|
|
2
|
+
|
|
3
|
+
// Loads the default Inter faces on native (web gets them via CSS). Add custom
|
|
4
|
+
// fonts by listing { family, nativeModule } entries.
|
|
5
|
+
export const { importFonts } = createFontLoader({
|
|
6
|
+
fonts: [],
|
|
7
|
+
inter: true,
|
|
8
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createDefaultThemeConfig } from "@multiplatform.one/theme";
|
|
2
|
+
import { accentTheme } from "./accent";
|
|
3
|
+
import { baseTheme } from "./base";
|
|
4
|
+
|
|
5
|
+
// House defaults: neutral base ramps + brand accent ramps with a normalized
|
|
6
|
+
// interaction ramp. Fonts fall back to the framework's Inter defaults —
|
|
7
|
+
// pass `fonts` here to customize.
|
|
8
|
+
export const themeConfig = createDefaultThemeConfig({
|
|
9
|
+
base: baseTheme,
|
|
10
|
+
accent: accentTheme,
|
|
11
|
+
builderOptions: {
|
|
12
|
+
getTheme: ({ theme }) => ({
|
|
13
|
+
// Interaction ramp: background 5 → 6 → 4, border 5 → 6 → 7.
|
|
14
|
+
backgroundPress: theme.color5,
|
|
15
|
+
backgroundFocus: theme.color6,
|
|
16
|
+
backgroundActive: theme.color4,
|
|
17
|
+
placeholderColor: theme.color10,
|
|
18
|
+
borderColor: theme.color5,
|
|
19
|
+
borderColorHover: theme.color6,
|
|
20
|
+
borderColorFocus: theme.color7,
|
|
21
|
+
borderColorPress: theme.color7,
|
|
22
|
+
// Semantic aliases for palette slots not covered by Tamagui defaults.
|
|
23
|
+
textMuted: theme.color10,
|
|
24
|
+
textSubtle: theme.color6,
|
|
25
|
+
borderStrong: theme.color7,
|
|
26
|
+
borderStrongHover: theme.color8,
|
|
27
|
+
borderStrongPress: theme.color6,
|
|
28
|
+
surfaceStrong: theme.color7,
|
|
29
|
+
surfaceMuted: theme.color10,
|
|
30
|
+
surfacePressStrong: theme.color6,
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@package/themes",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.ts",
|
|
9
|
+
"./fonts": "./fonts.ts",
|
|
10
|
+
"./tamagui.config": "./tamagui.config.ts"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"typecheck": "tsc --noEmit"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@multiplatform.one/theme": "__MPO_VERSION__",
|
|
17
|
+
"@tamagui/font-inter": "2.0.0-rc.41",
|
|
18
|
+
"tamagui": "2.0.0-rc.41"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "~5.9.3"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { themeConfig } from "./index";
|
|
2
|
+
|
|
3
|
+
export const config = themeConfig.tamagui;
|
|
4
|
+
export type AppConfig = typeof config;
|
|
5
|
+
|
|
6
|
+
// Module augmentation must target @tamagui/web where TamaguiCustomConfig is
|
|
7
|
+
// defined; keep the @tamagui/core augmentation for consumers importing types
|
|
8
|
+
// from there.
|
|
9
|
+
declare module "@tamagui/web" {
|
|
10
|
+
interface TamaguiCustomConfig extends AppConfig {}
|
|
11
|
+
}
|
|
12
|
+
declare module "@tamagui/core" {
|
|
13
|
+
interface TamaguiCustomConfig extends AppConfig {}
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "Bundler",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"strict": true,
|
|
10
|
+
// Published @multiplatform.one/* packages currently resolve types to
|
|
11
|
+
// their TypeScript sources (not compiled .d.ts), so typecheck re-checks
|
|
12
|
+
// framework code. Implicit-any strictness is relaxed until the packages
|
|
13
|
+
// publish compiled declarations; the rest of strict mode stays on.
|
|
14
|
+
"noImplicitAny": false,
|
|
15
|
+
"skipLibCheck": true,
|
|
16
|
+
"esModuleInterop": true,
|
|
17
|
+
"resolveJsonModule": true,
|
|
18
|
+
"isolatedModules": true,
|
|
19
|
+
"noEmit": true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Published @multiplatform.one/* packages ship TypeScript source, so package
|
|
2
|
+
// typechecks follow them into code that reads import.meta.env (vite-injected).
|
|
3
|
+
// Packages that don't load vite/client types include this minimal shape
|
|
4
|
+
// instead (apps/* use vite/client directly). Values are `any` to match
|
|
5
|
+
// vite/client's ImportMetaEnv index signature.
|
|
6
|
+
interface ImportMeta {
|
|
7
|
+
// biome-ignore lint/suspicious/noExplicitAny: mirrors vite/client typing
|
|
8
|
+
readonly env?: Record<string, any>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Workspaces that have Storybook and can run visual regression (discovered from filesystem + package.json). */
|
|
2
|
+
export type StorybookVisualWorkspace = {
|
|
3
|
+
dirName: string;
|
|
4
|
+
absolutePath: string;
|
|
5
|
+
/** How to run visual test: run `make test/visual` in absolutePath, or run pnpm script by name. */
|
|
6
|
+
runVisual: "make" | "pnpm";
|
|
7
|
+
pnpmScript?: string;
|
|
8
|
+
/** If true, run `pnpm run build` then `pnpm exec lost-pixel` in workspace (no named script). */
|
|
9
|
+
runLostPixelAfterBuild?: boolean;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=multiplatformOne.d.ts.map
|