kasy-cli 1.32.0 → 1.34.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/bin/kasy.js +42 -0
- package/lib/commands/apple-web.js +222 -0
- package/lib/commands/configure.js +3 -91
- package/lib/commands/doctor.js +20 -0
- package/lib/commands/facebook.js +189 -0
- package/lib/commands/new.js +50 -2
- package/lib/scaffold/CHANGELOG.json +18 -0
- package/lib/scaffold/backends/firebase/setup-from-scratch.js +164 -0
- package/lib/scaffold/backends/supabase/deploy.js +92 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +10 -0
- package/lib/scaffold/shared/generator-utils.js +18 -6
- package/lib/utils/apple-web.js +147 -0
- package/lib/utils/facebook.js +162 -0
- package/lib/utils/i18n/messages-en.js +62 -0
- package/lib/utils/i18n/messages-es.js +62 -0
- package/lib/utils/i18n/messages-pt.js +62 -0
- package/package.json +2 -2
- package/templates/firebase/AGENTS.md +87 -0
- package/templates/firebase/CLAUDE.md +16 -0
- package/templates/firebase/DESIGN_SYSTEM.md +234 -0
- package/templates/firebase/docs/auth-setup.en.md +2 -2
- package/templates/firebase/docs/auth-setup.es.md +2 -2
- package/templates/firebase/docs/auth-setup.pt.md +2 -2
- package/templates/firebase/lib/components/components.dart +1 -0
- package/templates/firebase/lib/components/kasy_app_bar.dart +4 -1
- package/templates/firebase/lib/components/kasy_screen.dart +114 -0
- package/templates/firebase/lib/components/kasy_toast.dart +39 -70
- package/templates/firebase/lib/core/chrome/chrome_visibility.dart +22 -0
- package/templates/firebase/lib/core/config/features.dart +5 -0
- package/templates/firebase/lib/core/rating/widgets/review_popup.dart +46 -124
- package/templates/firebase/lib/core/widgets/update_bottom_sheet.dart +29 -126
- package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +11 -7
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_chat_composer.dart +21 -0
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +1 -1
- package/templates/firebase/lib/features/authentication/api/authentication_api.dart +61 -0
- package/templates/firebase/lib/features/authentication/ui/signin_page.dart +21 -15
- package/templates/firebase/lib/features/authentication/ui/signup_page.dart +20 -14
- package/templates/firebase/lib/features/feedbacks/ui/widgets/feature_card.dart +1 -2
- package/templates/firebase/lib/features/home/home_components_page.dart +7 -1
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +32 -0
- package/templates/firebase/lib/features/local_reminders/ui/reminder_page.dart +165 -209
- package/templates/firebase/lib/features/notifications/ui/components/notification_settings_sheet.dart +2 -2
- package/templates/firebase/lib/features/notifications/ui/components/notification_tile.dart +21 -8
- package/templates/firebase/lib/features/notifications/ui/components/push_notification_switcher.dart +2 -2
- package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +3 -6
- package/templates/firebase/lib/features/notifications/ui/widgets/notification_tile.dart +77 -126
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +3 -4
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +2 -4
- package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +2 -1
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +17 -8
- package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +2 -2
- package/templates/firebase/lib/i18n/en.i18n.json +5 -4
- package/templates/firebase/lib/i18n/es.i18n.json +5 -4
- package/templates/firebase/lib/i18n/pt.i18n.json +5 -4
- package/templates/firebase/lib/router.dart +2 -0
- package/templates/firebase/pubspec.yaml +1 -1
- package/templates/firebase/tool/design_check.dart +152 -0
- package/templates/firebase/assets/images/review.png +0 -0
- package/templates/firebase/assets/images/update.png +0 -0
- package/templates/firebase/lib/features/notifications/ui/components/notifications_header.dart +0 -32
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Facebook Login helpers, shared by `kasy facebook` and `kasy configure`.
|
|
3
|
+
*
|
|
4
|
+
* Facebook needs values in two places:
|
|
5
|
+
* - Client side (the app): App ID + Client Token in iOS Info.plist and Android
|
|
6
|
+
* strings.xml (build-time). Same for every backend.
|
|
7
|
+
* - Backend provider (server side): App ID + App Secret on Firebase (Identity
|
|
8
|
+
* Toolkit) or Supabase (Management API). Configured by the backend writers.
|
|
9
|
+
*
|
|
10
|
+
* The Meta-side work (create the app, get App ID / Client Token / App Secret, set
|
|
11
|
+
* OAuth redirect URIs and JS domains) has no automation API — it stays manual and
|
|
12
|
+
* is guided by `kasy facebook`. This module owns the native-file read/write and the
|
|
13
|
+
* ~/.kasy/facebook.json credential cache so future projects reuse the values.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const os = require('node:os');
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
const fs = require('fs-extra');
|
|
19
|
+
|
|
20
|
+
const CONFIG_DIR = path.join(os.homedir(), '.kasy');
|
|
21
|
+
const FACEBOOK_CONFIG_PATH = path.join(CONFIG_DIR, 'facebook.json');
|
|
22
|
+
|
|
23
|
+
// ── Native build-time files (Info.plist + strings.xml) ──────────────────────
|
|
24
|
+
|
|
25
|
+
function readFacebookCurrent(content, kind) {
|
|
26
|
+
if (kind === 'plist') {
|
|
27
|
+
const appId = content.match(/<key>FacebookAppID<\/key>\s*<string>([^<]*)<\/string>/);
|
|
28
|
+
const token = content.match(/<key>FacebookClientToken<\/key>\s*<string>([^<]*)<\/string>/);
|
|
29
|
+
return { appId: (appId?.[1] || '').trim(), token: (token?.[1] || '').trim() };
|
|
30
|
+
}
|
|
31
|
+
// strings.xml
|
|
32
|
+
const appId = content.match(/<string name="facebook_app_id"[^>]*>([^<]*)<\/string>/);
|
|
33
|
+
const token = content.match(/<string name="facebook_client_token"[^>]*>([^<]*)<\/string>/);
|
|
34
|
+
return { appId: (appId?.[1] || '').trim(), token: (token?.[1] || '').trim() };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isFacebookPlaceholder(value) {
|
|
38
|
+
return !value || /^0+$/.test(value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function readFacebookState(projectDir) {
|
|
42
|
+
const plistPath = path.join(projectDir, 'ios', 'Runner', 'Info.plist');
|
|
43
|
+
const stringsPath = path.join(projectDir, 'android', 'app', 'src', 'main', 'res', 'values', 'strings.xml');
|
|
44
|
+
const state = { appId: '', token: '', plistExists: false, stringsExists: false };
|
|
45
|
+
if (await fs.pathExists(plistPath)) {
|
|
46
|
+
state.plistExists = true;
|
|
47
|
+
const plistRead = readFacebookCurrent(await fs.readFile(plistPath, 'utf8'), 'plist');
|
|
48
|
+
if (!isFacebookPlaceholder(plistRead.appId)) state.appId = plistRead.appId;
|
|
49
|
+
if (!isFacebookPlaceholder(plistRead.token)) state.token = plistRead.token;
|
|
50
|
+
}
|
|
51
|
+
if (await fs.pathExists(stringsPath)) {
|
|
52
|
+
state.stringsExists = true;
|
|
53
|
+
const stringsRead = readFacebookCurrent(await fs.readFile(stringsPath, 'utf8'), 'strings');
|
|
54
|
+
// Prefer plist value when both exist; only fall back if plist was placeholder.
|
|
55
|
+
if (!state.appId && !isFacebookPlaceholder(stringsRead.appId)) state.appId = stringsRead.appId;
|
|
56
|
+
if (!state.token && !isFacebookPlaceholder(stringsRead.token)) state.token = stringsRead.token;
|
|
57
|
+
}
|
|
58
|
+
return state;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function writeFacebookCredentials(projectDir, appId, clientToken) {
|
|
62
|
+
const plistPath = path.join(projectDir, 'ios', 'Runner', 'Info.plist');
|
|
63
|
+
const stringsPath = path.join(projectDir, 'android', 'app', 'src', 'main', 'res', 'values', 'strings.xml');
|
|
64
|
+
const results = { plist: 'skipped', strings: 'skipped' };
|
|
65
|
+
|
|
66
|
+
if (appId && (await fs.pathExists(plistPath))) {
|
|
67
|
+
let plist = await fs.readFile(plistPath, 'utf8');
|
|
68
|
+
plist = plist.replace(
|
|
69
|
+
/(<key>FacebookAppID<\/key>\s*<string>)[^<]*(<\/string>)/,
|
|
70
|
+
`$1${appId}$2`,
|
|
71
|
+
);
|
|
72
|
+
if (clientToken) {
|
|
73
|
+
plist = plist.replace(
|
|
74
|
+
/(<key>FacebookClientToken<\/key>\s*<string>)[^<]*(<\/string>)/,
|
|
75
|
+
`$1${clientToken}$2`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
// CFBundleURLSchemes — replace `fb<zeros>` with `fb<appId>` so deep links work.
|
|
79
|
+
plist = plist.replace(/<string>fb0+<\/string>/, `<string>fb${appId}</string>`);
|
|
80
|
+
await fs.outputFile(plistPath, plist, 'utf8');
|
|
81
|
+
results.plist = 'ok';
|
|
82
|
+
} else if (appId) {
|
|
83
|
+
results.plist = 'missing_file';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (appId && (await fs.pathExists(stringsPath))) {
|
|
87
|
+
let xml = await fs.readFile(stringsPath, 'utf8');
|
|
88
|
+
xml = xml.replace(
|
|
89
|
+
/(<string name="facebook_app_id"[^>]*>)[^<]*(<\/string>)/,
|
|
90
|
+
`$1${appId}$2`,
|
|
91
|
+
);
|
|
92
|
+
if (clientToken) {
|
|
93
|
+
xml = xml.replace(
|
|
94
|
+
/(<string name="facebook_client_token"[^>]*>)[^<]*(<\/string>)/,
|
|
95
|
+
`$1${clientToken}$2`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
// fb_login_protocol_scheme — set if present.
|
|
99
|
+
xml = xml.replace(
|
|
100
|
+
/(<string name="fb_login_protocol_scheme"[^>]*>)[^<]*(<\/string>)/,
|
|
101
|
+
`$1fb${appId}$2`,
|
|
102
|
+
);
|
|
103
|
+
await fs.outputFile(stringsPath, xml, 'utf8');
|
|
104
|
+
results.strings = 'ok';
|
|
105
|
+
} else if (appId) {
|
|
106
|
+
results.strings = 'missing_file';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return results;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── Credential cache (~/.kasy/facebook.json) ────────────────────────────────
|
|
113
|
+
|
|
114
|
+
/** Load cached Facebook credentials. Returns null if absent or without an App ID. */
|
|
115
|
+
async function loadFacebookCreds() {
|
|
116
|
+
try {
|
|
117
|
+
if (!(await fs.pathExists(FACEBOOK_CONFIG_PATH))) return null;
|
|
118
|
+
const data = await fs.readJson(FACEBOOK_CONFIG_PATH);
|
|
119
|
+
if (!data || !data.appId) return null;
|
|
120
|
+
return data;
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Persist Facebook credentials (App ID + Client Token + App Secret) with 0600. */
|
|
127
|
+
async function saveFacebookCreds({ appId, clientToken, appSecret }) {
|
|
128
|
+
await fs.ensureDir(CONFIG_DIR);
|
|
129
|
+
await fs.writeJson(
|
|
130
|
+
FACEBOOK_CONFIG_PATH,
|
|
131
|
+
{ appId, clientToken: clientToken || '', appSecret: appSecret || '' },
|
|
132
|
+
{ spaces: 2 },
|
|
133
|
+
);
|
|
134
|
+
try {
|
|
135
|
+
await fs.chmod(FACEBOOK_CONFIG_PATH, 0o600);
|
|
136
|
+
} catch {
|
|
137
|
+
/* ignore */
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Set `withFacebookWebSignin` to the given boolean in the project's features.dart. */
|
|
142
|
+
async function setFacebookWebFlag(projectDir, value) {
|
|
143
|
+
const file = path.join(projectDir, 'lib', 'core', 'config', 'features.dart');
|
|
144
|
+
if (!(await fs.pathExists(file))) return { ok: false, missing: true };
|
|
145
|
+
let content = await fs.readFile(file, 'utf8');
|
|
146
|
+
const re = /const bool withFacebookWebSignin\s*=\s*(?:true|false)\s*;/;
|
|
147
|
+
if (!re.test(content)) return { ok: false, missing: true };
|
|
148
|
+
content = content.replace(re, `const bool withFacebookWebSignin = ${value};`);
|
|
149
|
+
await fs.writeFile(file, content, 'utf8');
|
|
150
|
+
return { ok: true };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = {
|
|
154
|
+
FACEBOOK_CONFIG_PATH,
|
|
155
|
+
readFacebookCurrent,
|
|
156
|
+
isFacebookPlaceholder,
|
|
157
|
+
readFacebookState,
|
|
158
|
+
writeFacebookCredentials,
|
|
159
|
+
loadFacebookCreds,
|
|
160
|
+
saveFacebookCreds,
|
|
161
|
+
setFacebookWebFlag,
|
|
162
|
+
};
|
|
@@ -372,6 +372,60 @@ module.exports = {
|
|
|
372
372
|
'configure.settingSecrets': 'Setting {count} Firebase Secret(s)…',
|
|
373
373
|
'configure.savedSecrets': '{count} Firebase Secret(s) set',
|
|
374
374
|
'configure.secretFailed': 'Could not set secret {key}. Run manually: firebase functions:secrets:set {key}',
|
|
375
|
+
'cli.command.appleWeb.description': 'Configure Sign in with Apple on the WEB (Firebase or Supabase) using the credentials you created on Apple',
|
|
376
|
+
'appleWeb.title': 'Apple Sign-In on Web',
|
|
377
|
+
'appleWeb.notKasyProject': 'This folder does not look like a Kasy project (no kit_setup.json). Enter the project folder and run again.',
|
|
378
|
+
'appleWeb.missingProjectId': 'Could not find the project ID in kit_setup.json. Generate the project with the CLI before configuring Apple web.',
|
|
379
|
+
'appleWeb.required': 'Required',
|
|
380
|
+
'appleWeb.useSaved': 'Use your saved Apple credentials ({id})?',
|
|
381
|
+
'appleWeb.serviceIdPrompt': 'Apple Service ID (the OAuth client_id):',
|
|
382
|
+
'appleWeb.teamIdPrompt': 'Apple Team ID:',
|
|
383
|
+
'appleWeb.keyIdPrompt': 'Key ID of the .p8 key:',
|
|
384
|
+
'appleWeb.p8Prompt': 'Path to the .p8 file (Sign In with Apple):',
|
|
385
|
+
'appleWeb.p8ReadError': 'Could not read the .p8 file at that path.',
|
|
386
|
+
'appleWeb.incomplete': 'Missing data to configure (Service ID, Team ID, Key ID and .p8). Try again.',
|
|
387
|
+
'appleWeb.configuringFirebase': 'Configuring Apple web on Firebase…',
|
|
388
|
+
'appleWeb.configuringSupabase': 'Signing the secret and configuring Apple web on Supabase…',
|
|
389
|
+
'appleWeb.firebaseOk': 'Apple web configured on Firebase (it re-signs the secret itself, never expires).',
|
|
390
|
+
'appleWeb.supabaseOk': 'Apple web configured on Supabase.',
|
|
391
|
+
'appleWeb.failed': 'Could not configure Apple web.',
|
|
392
|
+
'appleWeb.flagUpdated': 'Apple button on web enabled (withAppleWebSignin = true).',
|
|
393
|
+
'appleWeb.flagMissing': 'Backend configured, but withAppleWebSignin was not found in features.dart to enable the button.',
|
|
394
|
+
'appleWeb.returnUrlNote': 'Make sure your Service ID Return URL on Apple is exactly:',
|
|
395
|
+
'appleWeb.manualTitle': 'Manual step on Apple',
|
|
396
|
+
'appleWeb.expiresNote': 'The Supabase secret expires in ~6 months. Run `kasy apple-web` again to renew (it reuses the saved .p8).',
|
|
397
|
+
'appleWeb.backendApi': 'API REST backend: auth lives on your server. Set up Sign in with Apple JS on your backend (Service ID + secret). The CLI has nowhere to store it.',
|
|
398
|
+
'appleWeb.apiDone': 'API backend: nothing to automate here. See the steps above.',
|
|
399
|
+
'appleWeb.allDone': 'Done! Apple Sign-In on the web is configured.',
|
|
400
|
+
'appleWeb.notFirebase': 'Apple Sign-In on the web is currently supported only on the Firebase backend (signInWithPopup). On Supabase it is roadmap (native iOS is already set up by `kasy new`); on the API backend auth lives on your server.',
|
|
401
|
+
'appleWeb.notFirebaseDone': 'Nothing to configure here for Apple web on this backend.',
|
|
402
|
+
'cli.command.facebook.description': 'Configure Facebook Login (Firebase/Supabase + native files) and guide the manual Meta steps',
|
|
403
|
+
'facebook.title': 'Facebook Login',
|
|
404
|
+
'facebook.notKasyProject': 'This folder does not look like a Kasy project (no kit_setup.json). Enter the project folder and run again.',
|
|
405
|
+
'facebook.metaTitle': 'Manual step on Meta',
|
|
406
|
+
'facebook.metaIntro': 'Create an app on Meta for Developers (Consumer) and grab: App ID, Client Token (Settings → Advanced) and App Secret (Settings → Basic). Add the Facebook Login product.',
|
|
407
|
+
'facebook.metaOpenLabel': 'Open the Meta dashboard to create/open your app:',
|
|
408
|
+
'facebook.metaOpenConfirm': 'Open the Meta dashboard now?',
|
|
409
|
+
'facebook.useSaved': 'Use your saved Facebook credentials ({id})?',
|
|
410
|
+
'facebook.required': 'Required',
|
|
411
|
+
'facebook.appIdPrompt': 'Meta App ID:',
|
|
412
|
+
'facebook.clientTokenPrompt': 'Meta Client Token:',
|
|
413
|
+
'facebook.appSecretPrompt': 'Meta App Secret (for the backend provider):',
|
|
414
|
+
'facebook.incomplete': 'App ID is missing. Try again.',
|
|
415
|
+
'facebook.nativeOk': 'App ID and Client Token written to Info.plist and strings.xml.',
|
|
416
|
+
'facebook.nativeMissing': 'Could not find Info.plist/strings.xml to write (run after the native platforms are generated).',
|
|
417
|
+
'facebook.configuringFirebase': 'Enabling the Facebook provider on Firebase…',
|
|
418
|
+
'facebook.configuringSupabase': 'Enabling the Facebook provider on Supabase…',
|
|
419
|
+
'facebook.firebaseOk': 'Facebook provider enabled on Firebase.',
|
|
420
|
+
'facebook.supabaseOk': 'Facebook provider enabled on Supabase.',
|
|
421
|
+
'facebook.backendFailed': 'Could not enable the Facebook provider on the backend.',
|
|
422
|
+
'facebook.backendApi': 'API REST backend: auth lives on your server. Set up Facebook Login on your backend (App ID + App Secret).',
|
|
423
|
+
'facebook.missingProjectId': 'Could not find the project ID in kit_setup.json. Skipped the backend part (native files were written).',
|
|
424
|
+
'facebook.noSecret': 'Without an App Secret I skipped the backend part. Run `kasy facebook` with the App Secret to enable the provider.',
|
|
425
|
+
'facebook.redirectNote': 'On Meta (Facebook Login → Settings), add this Valid OAuth Redirect URI:',
|
|
426
|
+
'facebook.flagUpdated': 'Facebook button on web enabled (withFacebookWebSignin = true).',
|
|
427
|
+
'facebook.webRoadmap': 'Facebook Login on web for Supabase is not wired yet (roadmap). Native (iOS/Android) already works.',
|
|
428
|
+
'facebook.allDone': 'Done! Facebook Login is configured.',
|
|
375
429
|
'cli.command.check.description': 'Check push notifications setup (use --fix to fix it)',
|
|
376
430
|
'deploy.q.project': 'Firebase Project ID:',
|
|
377
431
|
'deploy.q.serviceAccount': 'Path to service account JSON:',
|
|
@@ -506,6 +560,8 @@ module.exports = {
|
|
|
506
560
|
'doctor.ios.googleUrlSchemeSkipped': 'Google Sign-In iOS URL scheme check skipped (GoogleService-Info.plist not found)',
|
|
507
561
|
'doctor.ios.appleSignInEntitlementOk': 'Apple Sign-In entitlement configured',
|
|
508
562
|
'doctor.ios.appleSignInEntitlementMissing': 'Apple Sign-In entitlement missing — add Sign In with Apple capability in Xcode',
|
|
563
|
+
'doctor.appleWeb.ok': 'Apple Sign-In on web configured',
|
|
564
|
+
'doctor.appleWeb.pending': 'Apple Sign-In on web not configured (run `kasy apple-web`). iOS already works.',
|
|
509
565
|
'doctor.ios.facebookOk': 'Facebook credentials configured (iOS)',
|
|
510
566
|
'doctor.ios.facebookPlaceholders': 'Facebook credentials are still placeholders — update FacebookAppID and FacebookClientToken in Info.plist',
|
|
511
567
|
'doctor.ios.facebookSkipped': 'Facebook credential check skipped (no FacebookAppID in Info.plist)',
|
|
@@ -741,6 +797,12 @@ module.exports = {
|
|
|
741
797
|
'new.success.bundleId': 'App identifier (bundle ID)',
|
|
742
798
|
'new.success.bundleId.hint': "Your app's unique identifier on Android, iOS and Firebase (push).",
|
|
743
799
|
'new.success.api.serverContracts': 'API backend: you must implement the server contracts (delete account, AI chat and push). See patch/README.md',
|
|
800
|
+
'new.appleWeb.autoConfigured': 'Apple Sign-In on web configured automatically (reused your saved credentials).',
|
|
801
|
+
'new.success.appleWeb.configured': 'Apple Sign-In on web: configured',
|
|
802
|
+
'new.success.appleWeb.pending': 'Apple Sign-In on web: not configured yet (run `kasy apple-web`). iOS already works.',
|
|
803
|
+
'new.facebook.autoConfigured': 'Facebook Login configured automatically (reused your saved credentials).',
|
|
804
|
+
'new.success.facebook.configured': 'Facebook Login: configured',
|
|
805
|
+
'new.success.facebook.pending': 'Facebook Login: not configured yet (run `kasy facebook`).',
|
|
744
806
|
'new.success.nextSteps': 'Next steps:',
|
|
745
807
|
'new.success.step.cd': 'Go to your project folder:',
|
|
746
808
|
'new.success.step.deploy': 'Push the server to Firebase (DB + functions):',
|
|
@@ -374,6 +374,60 @@ module.exports = {
|
|
|
374
374
|
'configure.settingSecrets': 'Guardando {count} Firebase Secret(s)…',
|
|
375
375
|
'configure.savedSecrets': '{count} Firebase Secret(s) guardado(s)',
|
|
376
376
|
'configure.secretFailed': 'No pude guardar el secret {key}. Ejecuta manualmente: firebase functions:secrets:set {key}',
|
|
377
|
+
'cli.command.appleWeb.description': 'Configura el inicio de sesión con Apple en la WEB (Firebase o Supabase) con las credenciales que creaste en Apple',
|
|
378
|
+
'appleWeb.title': 'Inicio de sesión con Apple en la Web',
|
|
379
|
+
'appleWeb.notKasyProject': 'Esta carpeta no parece un proyecto Kasy (no encontré kit_setup.json). Entra a la carpeta del proyecto y vuelve a ejecutar.',
|
|
380
|
+
'appleWeb.missingProjectId': 'No encontré el ID del proyecto en kit_setup.json. Genera el proyecto con la CLI antes de configurar Apple web.',
|
|
381
|
+
'appleWeb.required': 'Campo obligatorio',
|
|
382
|
+
'appleWeb.useSaved': '¿Usar las credenciales de Apple guardadas ({id})?',
|
|
383
|
+
'appleWeb.serviceIdPrompt': 'Apple Service ID (el client_id de OAuth):',
|
|
384
|
+
'appleWeb.teamIdPrompt': 'Apple Team ID:',
|
|
385
|
+
'appleWeb.keyIdPrompt': 'Key ID de la clave .p8:',
|
|
386
|
+
'appleWeb.p8Prompt': 'Ruta del archivo .p8 (Sign In with Apple):',
|
|
387
|
+
'appleWeb.p8ReadError': 'No pude leer el archivo .p8 en esa ruta.',
|
|
388
|
+
'appleWeb.incomplete': 'Faltan datos para configurar (Service ID, Team ID, Key ID y .p8). Inténtalo de nuevo.',
|
|
389
|
+
'appleWeb.configuringFirebase': 'Configurando Apple web en Firebase…',
|
|
390
|
+
'appleWeb.configuringSupabase': 'Firmando el secret y configurando Apple web en Supabase…',
|
|
391
|
+
'appleWeb.firebaseOk': 'Apple web configurado en Firebase (vuelve a firmar el secret solo, no expira).',
|
|
392
|
+
'appleWeb.supabaseOk': 'Apple web configurado en Supabase.',
|
|
393
|
+
'appleWeb.failed': 'No pude configurar Apple web.',
|
|
394
|
+
'appleWeb.flagUpdated': 'Botón de Apple en la web activado (withAppleWebSignin = true).',
|
|
395
|
+
'appleWeb.flagMissing': 'Configuré el backend, pero no encontré withAppleWebSignin en features.dart para activar el botón.',
|
|
396
|
+
'appleWeb.returnUrlNote': 'Verifica que el Return URL de tu Service ID en Apple sea exactamente:',
|
|
397
|
+
'appleWeb.manualTitle': 'Paso manual en Apple',
|
|
398
|
+
'appleWeb.expiresNote': 'El secret de Supabase expira en ~6 meses. Ejecuta `kasy apple-web` de nuevo para renovar (reutiliza la .p8 guardada).',
|
|
399
|
+
'appleWeb.backendApi': 'Backend API REST: la autenticación vive en tu servidor. Configura Sign in with Apple JS en tu backend (Service ID + secret). La CLI no tiene dónde guardarlo.',
|
|
400
|
+
'appleWeb.apiDone': 'Backend API: nada que automatizar aquí. Mira los pasos de arriba.',
|
|
401
|
+
'appleWeb.allDone': '¡Listo! El inicio de sesión con Apple en la web está configurado.',
|
|
402
|
+
'appleWeb.notFirebase': 'El inicio de sesión con Apple en la web hoy solo se admite en el backend Firebase (signInWithPopup). En Supabase es roadmap (iOS nativo ya está configurado por `kasy new`); en el backend API la autenticación vive en tu servidor.',
|
|
403
|
+
'appleWeb.notFirebaseDone': 'Nada que configurar aquí para Apple web en este backend.',
|
|
404
|
+
'cli.command.facebook.description': 'Configura el inicio de sesión con Facebook (Firebase/Supabase + archivos nativos) y guía los pasos manuales en Meta',
|
|
405
|
+
'facebook.title': 'Inicio de sesión con Facebook',
|
|
406
|
+
'facebook.notKasyProject': 'Esta carpeta no parece un proyecto Kasy (no encontré kit_setup.json). Entra a la carpeta del proyecto y vuelve a ejecutar.',
|
|
407
|
+
'facebook.metaTitle': 'Paso manual en Meta',
|
|
408
|
+
'facebook.metaIntro': 'Crea una app en Meta for Developers (Consumer) y obtén: App ID, Client Token (Settings → Advanced) y App Secret (Settings → Basic). Agrega el producto Facebook Login.',
|
|
409
|
+
'facebook.metaOpenLabel': 'Abre el panel de Meta para crear/abrir tu app:',
|
|
410
|
+
'facebook.metaOpenConfirm': '¿Abrir el panel de Meta ahora?',
|
|
411
|
+
'facebook.useSaved': '¿Usar las credenciales de Facebook guardadas ({id})?',
|
|
412
|
+
'facebook.required': 'Campo obligatorio',
|
|
413
|
+
'facebook.appIdPrompt': 'Meta App ID:',
|
|
414
|
+
'facebook.clientTokenPrompt': 'Meta Client Token:',
|
|
415
|
+
'facebook.appSecretPrompt': 'Meta App Secret (para el proveedor del backend):',
|
|
416
|
+
'facebook.incomplete': 'Falta el App ID. Inténtalo de nuevo.',
|
|
417
|
+
'facebook.nativeOk': 'App ID y Client Token escritos en Info.plist y strings.xml.',
|
|
418
|
+
'facebook.nativeMissing': 'No encontré Info.plist/strings.xml para escribir (ejecuta después de generar las plataformas nativas).',
|
|
419
|
+
'facebook.configuringFirebase': 'Habilitando el proveedor de Facebook en Firebase…',
|
|
420
|
+
'facebook.configuringSupabase': 'Habilitando el proveedor de Facebook en Supabase…',
|
|
421
|
+
'facebook.firebaseOk': 'Proveedor de Facebook habilitado en Firebase.',
|
|
422
|
+
'facebook.supabaseOk': 'Proveedor de Facebook habilitado en Supabase.',
|
|
423
|
+
'facebook.backendFailed': 'No pude habilitar el proveedor de Facebook en el backend.',
|
|
424
|
+
'facebook.backendApi': 'Backend API REST: la autenticación vive en tu servidor. Configura Facebook Login en tu backend (App ID + App Secret).',
|
|
425
|
+
'facebook.missingProjectId': 'No encontré el ID del proyecto en kit_setup.json. Omití la parte del backend (los archivos nativos se escribieron).',
|
|
426
|
+
'facebook.noSecret': 'Sin App Secret omití la parte del backend. Ejecuta `kasy facebook` con el App Secret para habilitar el proveedor.',
|
|
427
|
+
'facebook.redirectNote': 'En Meta (Facebook Login → Settings), agrega este Valid OAuth Redirect URI:',
|
|
428
|
+
'facebook.flagUpdated': 'Botón de Facebook en la web activado (withFacebookWebSignin = true).',
|
|
429
|
+
'facebook.webRoadmap': 'El inicio de sesión con Facebook en la web para Supabase aún no viene activado (roadmap). El nativo (iOS/Android) ya funciona.',
|
|
430
|
+
'facebook.allDone': '¡Listo! El inicio de sesión con Facebook está configurado.',
|
|
377
431
|
'cli.command.check.description': 'Verifica si las notificaciones push están configuradas (usa --fix para arreglar)',
|
|
378
432
|
'deploy.q.project': 'Firebase Project ID:',
|
|
379
433
|
'deploy.q.serviceAccount': 'Ruta al service account JSON:',
|
|
@@ -508,6 +562,8 @@ module.exports = {
|
|
|
508
562
|
'doctor.ios.googleUrlSchemeSkipped': 'Check del URL scheme iOS de Google Sign-In omitido (GoogleService-Info.plist ausente)',
|
|
509
563
|
'doctor.ios.appleSignInEntitlementOk': 'Entitlement de Apple Sign-In configurado',
|
|
510
564
|
'doctor.ios.appleSignInEntitlementMissing': 'Entitlement de Apple Sign-In ausente — agrega la capability Sign In with Apple en Xcode',
|
|
565
|
+
'doctor.appleWeb.ok': 'Inicio de sesión con Apple en la web configurado',
|
|
566
|
+
'doctor.appleWeb.pending': 'Inicio de sesión con Apple en la web no configurado (ejecuta `kasy apple-web`). iOS ya funciona.',
|
|
511
567
|
'doctor.ios.facebookOk': 'Credenciales de Facebook configuradas (iOS)',
|
|
512
568
|
'doctor.ios.facebookPlaceholders': 'Credenciales de Facebook son aún placeholders — actualiza FacebookAppID y FacebookClientToken en Info.plist',
|
|
513
569
|
'doctor.ios.facebookSkipped': 'Check de credenciales de Facebook omitido (FacebookAppID ausente en Info.plist)',
|
|
@@ -741,6 +797,12 @@ module.exports = {
|
|
|
741
797
|
'new.success.bundleId': 'Identificador de la app (bundle ID)',
|
|
742
798
|
'new.success.bundleId.hint': 'Identificador único de tu app en Android, iOS y Firebase (push).',
|
|
743
799
|
'new.success.api.serverContracts': 'Backend API: debes implementar los contratos del servidor (eliminar cuenta, IA chat y push). Consulta patch/README.md',
|
|
800
|
+
'new.appleWeb.autoConfigured': 'Inicio de sesión con Apple en la web configurado automáticamente (se reutilizaron tus credenciales guardadas).',
|
|
801
|
+
'new.success.appleWeb.configured': 'Inicio de sesión con Apple en la web: configurado',
|
|
802
|
+
'new.success.appleWeb.pending': 'Inicio de sesión con Apple en la web: falta configurar (ejecuta `kasy apple-web`). iOS ya funciona.',
|
|
803
|
+
'new.facebook.autoConfigured': 'Inicio de sesión con Facebook configurado automáticamente (se reutilizaron tus credenciales guardadas).',
|
|
804
|
+
'new.success.facebook.configured': 'Inicio de sesión con Facebook: configurado',
|
|
805
|
+
'new.success.facebook.pending': 'Inicio de sesión con Facebook: falta configurar (ejecuta `kasy facebook`).',
|
|
744
806
|
'new.success.nextSteps': 'Próximos pasos:',
|
|
745
807
|
'new.success.step.cd': 'Ve a la carpeta del proyecto:',
|
|
746
808
|
'new.success.step.deploy': 'Sube el servidor a Firebase (DB + funciones):',
|
|
@@ -372,6 +372,60 @@ module.exports = {
|
|
|
372
372
|
'configure.settingSecrets': 'Gravando {count} Firebase Secret(s)…',
|
|
373
373
|
'configure.savedSecrets': '{count} Firebase Secret(s) gravado(s)',
|
|
374
374
|
'configure.secretFailed': 'Não consegui gravar o secret {key}. Rode manualmente: firebase functions:secrets:set {key}',
|
|
375
|
+
'cli.command.appleWeb.description': 'Configura o login com Apple na WEB (Firebase ou Supabase) com as credenciais que você criou na Apple',
|
|
376
|
+
'appleWeb.title': 'Login com Apple na Web',
|
|
377
|
+
'appleWeb.notKasyProject': 'Esta pasta não parece um projeto Kasy (não achei kit_setup.json). Entre na pasta do projeto e rode de novo.',
|
|
378
|
+
'appleWeb.missingProjectId': 'Não encontrei o ID do projeto no kit_setup.json. Gere o projeto pela CLI antes de configurar o Apple web.',
|
|
379
|
+
'appleWeb.required': 'Campo obrigatório',
|
|
380
|
+
'appleWeb.useSaved': 'Usar as credenciais Apple salvas ({id})?',
|
|
381
|
+
'appleWeb.serviceIdPrompt': 'Apple Service ID (o client_id do OAuth):',
|
|
382
|
+
'appleWeb.teamIdPrompt': 'Apple Team ID:',
|
|
383
|
+
'appleWeb.keyIdPrompt': 'Key ID da chave .p8:',
|
|
384
|
+
'appleWeb.p8Prompt': 'Caminho do arquivo .p8 (Sign In with Apple):',
|
|
385
|
+
'appleWeb.p8ReadError': 'Não consegui ler o arquivo .p8 nesse caminho.',
|
|
386
|
+
'appleWeb.incomplete': 'Faltam dados pra configurar (Service ID, Team ID, Key ID e .p8). Tente de novo.',
|
|
387
|
+
'appleWeb.configuringFirebase': 'Configurando Apple web no Firebase…',
|
|
388
|
+
'appleWeb.configuringSupabase': 'Assinando o secret e configurando Apple web no Supabase…',
|
|
389
|
+
'appleWeb.firebaseOk': 'Apple web configurado no Firebase (ele re-assina o secret sozinho, não expira).',
|
|
390
|
+
'appleWeb.supabaseOk': 'Apple web configurado no Supabase.',
|
|
391
|
+
'appleWeb.failed': 'Não consegui configurar o Apple web.',
|
|
392
|
+
'appleWeb.flagUpdated': 'Botão Apple na web ligado (withAppleWebSignin = true).',
|
|
393
|
+
'appleWeb.flagMissing': 'Configurei o backend, mas não achei withAppleWebSignin em features.dart pra ligar o botão.',
|
|
394
|
+
'appleWeb.returnUrlNote': 'Confira que o Return URL do seu Service ID na Apple é exatamente:',
|
|
395
|
+
'appleWeb.manualTitle': 'Passo manual na Apple',
|
|
396
|
+
'appleWeb.expiresNote': 'O secret do Supabase expira em ~6 meses. Rode `kasy apple-web` de novo pra renovar (ele reaproveita a .p8 salva).',
|
|
397
|
+
'appleWeb.backendApi': 'Backend API REST: a autenticação é do seu servidor. Configure o Sign in with Apple JS no seu backend (Service ID + secret). A CLI não tem onde gravar.',
|
|
398
|
+
'appleWeb.apiDone': 'Backend API: nada pra automatizar aqui. Veja os passos acima.',
|
|
399
|
+
'appleWeb.allDone': 'Pronto! O login com Apple na web está configurado.',
|
|
400
|
+
'appleWeb.notFirebase': 'Login com Apple na web hoje é suportado só no backend Firebase (signInWithPopup). No Supabase é roadmap (o iOS nativo já está configurado pelo `kasy new`); no backend API a auth é do seu servidor.',
|
|
401
|
+
'appleWeb.notFirebaseDone': 'Nada a configurar aqui pra Apple web neste backend.',
|
|
402
|
+
'cli.command.facebook.description': 'Configura o login com Facebook (Firebase/Supabase + arquivos nativos) e guia os passos manuais na Meta',
|
|
403
|
+
'facebook.title': 'Login com Facebook',
|
|
404
|
+
'facebook.notKasyProject': 'Esta pasta não parece um projeto Kasy (não achei kit_setup.json). Entre na pasta do projeto e rode de novo.',
|
|
405
|
+
'facebook.metaTitle': 'Passo manual na Meta',
|
|
406
|
+
'facebook.metaIntro': 'Crie um app no Meta for Developers (Consumer) e pegue: App ID, Client Token (Settings → Advanced) e App Secret (Settings → Basic). Ative o produto Facebook Login.',
|
|
407
|
+
'facebook.metaOpenLabel': 'Abra o painel da Meta pra criar/abrir seu app:',
|
|
408
|
+
'facebook.metaOpenConfirm': 'Abrir o painel da Meta agora?',
|
|
409
|
+
'facebook.useSaved': 'Usar as credenciais do Facebook salvas ({id})?',
|
|
410
|
+
'facebook.required': 'Campo obrigatório',
|
|
411
|
+
'facebook.appIdPrompt': 'Meta App ID:',
|
|
412
|
+
'facebook.clientTokenPrompt': 'Meta Client Token:',
|
|
413
|
+
'facebook.appSecretPrompt': 'Meta App Secret (pro provedor do backend):',
|
|
414
|
+
'facebook.incomplete': 'Faltou o App ID. Tente de novo.',
|
|
415
|
+
'facebook.nativeOk': 'App ID e Client Token gravados no Info.plist e strings.xml.',
|
|
416
|
+
'facebook.nativeMissing': 'Não achei Info.plist/strings.xml pra gravar (rode depois de gerar as plataformas nativas).',
|
|
417
|
+
'facebook.configuringFirebase': 'Habilitando o provedor Facebook no Firebase…',
|
|
418
|
+
'facebook.configuringSupabase': 'Habilitando o provedor Facebook no Supabase…',
|
|
419
|
+
'facebook.firebaseOk': 'Provedor Facebook habilitado no Firebase.',
|
|
420
|
+
'facebook.supabaseOk': 'Provedor Facebook habilitado no Supabase.',
|
|
421
|
+
'facebook.backendFailed': 'Não consegui habilitar o provedor Facebook no backend.',
|
|
422
|
+
'facebook.backendApi': 'Backend API REST: a autenticação é do seu servidor. Configure o Facebook Login no seu backend (App ID + App Secret).',
|
|
423
|
+
'facebook.missingProjectId': 'Não encontrei o ID do projeto no kit_setup.json. Pulei a parte do backend (os arquivos nativos foram gravados).',
|
|
424
|
+
'facebook.noSecret': 'Sem App Secret, pulei a parte do backend. Rode `kasy facebook` com o App Secret pra habilitar o provedor.',
|
|
425
|
+
'facebook.redirectNote': 'Na Meta (Facebook Login → Settings), adicione este Valid OAuth Redirect URI:',
|
|
426
|
+
'facebook.flagUpdated': 'Botão Facebook na web ligado (withFacebookWebSignin = true).',
|
|
427
|
+
'facebook.webRoadmap': 'Login Facebook na web no Supabase ainda não vem ligado (roadmap). O nativo (iOS/Android) já funciona.',
|
|
428
|
+
'facebook.allDone': 'Pronto! O Facebook Login está configurado.',
|
|
375
429
|
'cli.command.check.description': 'Confere se as notificações push estão configuradas (use --fix para corrigir)',
|
|
376
430
|
'deploy.q.project': 'Firebase Project ID:',
|
|
377
431
|
'deploy.q.serviceAccount': 'Caminho para o service account JSON:',
|
|
@@ -506,6 +560,8 @@ module.exports = {
|
|
|
506
560
|
'doctor.ios.googleUrlSchemeSkipped': 'Check do URL scheme iOS do Google Sign-In ignorado (GoogleService-Info.plist ausente)',
|
|
507
561
|
'doctor.ios.appleSignInEntitlementOk': 'Entitlement do Apple Sign-In configurado',
|
|
508
562
|
'doctor.ios.appleSignInEntitlementMissing': 'Entitlement do Apple Sign-In ausente — adicione a capability Sign In with Apple no Xcode',
|
|
563
|
+
'doctor.appleWeb.ok': 'Login com Apple na web configurado',
|
|
564
|
+
'doctor.appleWeb.pending': 'Login com Apple na web não configurado (rode `kasy apple-web`). O iOS já funciona.',
|
|
509
565
|
'doctor.ios.facebookOk': 'Credenciais do Facebook configuradas (iOS)',
|
|
510
566
|
'doctor.ios.facebookPlaceholders': 'Credenciais do Facebook ainda são placeholders — atualize FacebookAppID e FacebookClientToken no Info.plist',
|
|
511
567
|
'doctor.ios.facebookSkipped': 'Check das credenciais do Facebook ignorado (FacebookAppID ausente no Info.plist)',
|
|
@@ -741,6 +797,12 @@ module.exports = {
|
|
|
741
797
|
'new.success.bundleId': 'Identificador do app (bundle ID)',
|
|
742
798
|
'new.success.bundleId.hint': 'Identificador único do seu app no Android, iOS e Firebase (push).',
|
|
743
799
|
'new.success.api.serverContracts': 'Backend API: você precisa implementar os contratos do servidor (excluir conta, IA chat e push). Veja patch/README.md',
|
|
800
|
+
'new.appleWeb.autoConfigured': 'Login com Apple na web configurado automaticamente (credenciais salvas reaproveitadas).',
|
|
801
|
+
'new.success.appleWeb.configured': 'Login com Apple na web: configurado',
|
|
802
|
+
'new.success.appleWeb.pending': 'Login com Apple na web: falta configurar (rode `kasy apple-web`). O iOS já funciona.',
|
|
803
|
+
'new.facebook.autoConfigured': 'Login com Facebook configurado automaticamente (credenciais salvas reaproveitadas).',
|
|
804
|
+
'new.success.facebook.configured': 'Login com Facebook: configurado',
|
|
805
|
+
'new.success.facebook.pending': 'Login com Facebook: falta configurar (rode `kasy facebook`).',
|
|
744
806
|
'new.success.nextSteps': 'Próximos passos:',
|
|
745
807
|
'new.success.step.cd': 'Entre na pasta do projeto:',
|
|
746
808
|
'new.success.step.deploy': 'Suba o servidor pro Firebase (banco + funções):',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kasy-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.34.0",
|
|
4
4
|
"description": "CLI for scaffolding production-ready Flutter SaaS apps with Firebase, Supabase, or API REST backends.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"kasy": "./bin/kasy.js"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
-
"prepack": "node scripts/check-feature-patches.js && node scripts/bundle-template.js && node test/generated-matches-kit.test.js && node test/supabase-verify-jwt.test.js && node test/supabase-cors.test.js && node test/supabase-google-web.test.js && node test/backend-pubspec-local-reminders.test.js && node test/stripe-webhook-orphan-guard.test.js && node test/facebook-strip.test.js && node test/path-provider-pin.test.js && node test/features-flags-parity.test.js",
|
|
34
|
+
"prepack": "node scripts/check-feature-patches.js && node scripts/bundle-template.js && node test/generated-matches-kit.test.js && node test/supabase-verify-jwt.test.js && node test/supabase-cors.test.js && node test/supabase-google-web.test.js && node test/backend-pubspec-local-reminders.test.js && node test/stripe-webhook-orphan-guard.test.js && node test/facebook-strip.test.js && node test/path-provider-pin.test.js && node test/features-flags-parity.test.js && node test/apple-web.test.js && node test/facebook.test.js",
|
|
35
35
|
"start": "node ./bin/kasy.js",
|
|
36
36
|
"setup": "node ./bin/kasy.js setup",
|
|
37
37
|
"doctor": "node ./bin/kasy.js doctor",
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# AGENTS.md — working in this codebase
|
|
2
|
+
|
|
3
|
+
This project was generated with the **Kasy** kit. This file orients any AI coding
|
|
4
|
+
assistant (Claude Code, Cursor, GitHub Copilot, …). Read it before generating
|
|
5
|
+
code. Claude Code users: `CLAUDE.md` points here.
|
|
6
|
+
|
|
7
|
+
## What this is
|
|
8
|
+
|
|
9
|
+
A production-ready Flutter app (web, iOS, Android) built on the **Kasy design
|
|
10
|
+
system** and a **feature-first** architecture. It was generated with **one**
|
|
11
|
+
backend — Firebase, Supabase, or a REST API. Only the **data layer** differs
|
|
12
|
+
between backends; the UI, state, i18n and design system are identical. So almost
|
|
13
|
+
everything below is backend-agnostic.
|
|
14
|
+
|
|
15
|
+
## Golden rules (every time)
|
|
16
|
+
|
|
17
|
+
1. **Use the design system, never raw values.** No hardcoded `fontSize`,
|
|
18
|
+
`Color(0x…)`, `Colors.*`, or literal padding / radius / icon sizes. Use
|
|
19
|
+
`context.textTheme.*` / `KasyTextTheme.*`, `context.colors.*`,
|
|
20
|
+
`KasySpacing.*`, `KasyRadius.*`, `KasyIconSize.*`. Full reference:
|
|
21
|
+
**`DESIGN_SYSTEM.md`**.
|
|
22
|
+
2. **Use kit components, not raw Material.** `KasyButton` (not
|
|
23
|
+
ElevatedButton / TextButton), `showKasyConfirmDialog` (not AlertDialog),
|
|
24
|
+
`showKasyToast` (not SnackBar), plus `KasyCard`, `KasyTextField`,
|
|
25
|
+
`KasyScreen`, etc.
|
|
26
|
+
3. **The Home screen is the visual reference** — clean, minimal, lots of
|
|
27
|
+
surface, one accent colour used sparingly. New screens should mirror it.
|
|
28
|
+
4. **English everywhere** in code, comments and identifiers. **No hardcoded
|
|
29
|
+
user-facing strings** — use slang i18n (`context.t.…`).
|
|
30
|
+
5. **Package imports only**: `import 'package:kasy_kit/…';`, never relative.
|
|
31
|
+
6. **Zero analyzer issues.** Run `flutter analyze` and fix everything before
|
|
32
|
+
considering a change done.
|
|
33
|
+
|
|
34
|
+
## Architecture (feature-first, three layers)
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
lib/
|
|
38
|
+
├── components/ # Kasy design-system widgets (KasyButton, KasyCard, …)
|
|
39
|
+
├── core/ # cross-cutting: theme/, data/, states/, security/, widgets/, i18n
|
|
40
|
+
└── features/
|
|
41
|
+
└── <feature>/
|
|
42
|
+
├── api/ # data source (Firebase / Supabase / REST) → returns entities
|
|
43
|
+
├── repositories/ # entities → domain models (business logic)
|
|
44
|
+
├── providers/ # Riverpod notifiers → immutable page state
|
|
45
|
+
└── ui/ # pages, components (use provider + domain), widgets (dumb)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- **Data flow:** `api → repository → provider (notifier) → ui`. Only repositories
|
|
49
|
+
use api classes. The api returns entities; repositories return domain models.
|
|
50
|
+
- **State:** a Riverpod notifier exposes an immutable (freezed) state; the view
|
|
51
|
+
`ref.watch`es it and triggers actions. Use `.when` / `.map` for
|
|
52
|
+
data / loading / error.
|
|
53
|
+
- **Backend note:** the `api` layer is the only backend-specific code. A new
|
|
54
|
+
data-backed feature follows the same `api → repo → provider` chain the
|
|
55
|
+
existing features use.
|
|
56
|
+
|
|
57
|
+
## Building a screen or component — checklist
|
|
58
|
+
|
|
59
|
+
1. Read `DESIGN_SYSTEM.md` (tokens, typography roles, components).
|
|
60
|
+
2. Reuse a kit component before building one. Wrap new pages in `KasyScreen`
|
|
61
|
+
(or mirror an existing page's scaffold).
|
|
62
|
+
3. Use semantic typography roles (`pageTitle`, `sectionTitle`, `rowTitle`, …)
|
|
63
|
+
and tokens for **every** size, colour and spacing.
|
|
64
|
+
4. Strings via `context.t.*` (add keys to the i18n JSON, never inline text).
|
|
65
|
+
5. `flutter analyze` clean. Optional: `dart run tool/design_check.dart` flags
|
|
66
|
+
any raw values that slipped into `lib/features`.
|
|
67
|
+
|
|
68
|
+
## Platform notes (native-only features)
|
|
69
|
+
|
|
70
|
+
Some features only work on a real iOS / Android build and are gated with
|
|
71
|
+
`kIsWeb`. They never run on web; where the user explicitly triggers them, a
|
|
72
|
+
"native app only" toast is shown instead of failing silently.
|
|
73
|
+
|
|
74
|
+
- **In-app review / store rating** — sends the user to the App Store / Play
|
|
75
|
+
Store. It is never shown on web. **iOS requires the numeric App Store ID**:
|
|
76
|
+
run `kasy configure` and fill `App Store ID (numeric)`. Android works
|
|
77
|
+
automatically from the package name. Until the ID is set, iOS store review
|
|
78
|
+
throws at runtime.
|
|
79
|
+
- **Push / FCM token, notification permission** — native APIs; on web the admin
|
|
80
|
+
actions surface the "native app only" toast.
|
|
81
|
+
|
|
82
|
+
## Optional design guard-rail
|
|
83
|
+
|
|
84
|
+
`dart run tool/design_check.dart` scans `lib/features` for raw values
|
|
85
|
+
(hardcoded fonts / colours / Material widgets). It is **opt-in and
|
|
86
|
+
non-blocking** — relax it, wire it into CI, or delete it freely. Per-line
|
|
87
|
+
escape hatch: `// design-check: ignore`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This project shares one AI-guidance file across tools. **Read `AGENTS.md`** in
|
|
4
|
+
this directory first — it defines the architecture, the Kasy design system, and
|
|
5
|
+
the conventions to follow before generating any code.
|
|
6
|
+
|
|
7
|
+
Quick reminders:
|
|
8
|
+
|
|
9
|
+
- Use design-system tokens, never raw values — see `DESIGN_SYSTEM.md`
|
|
10
|
+
(`context.textTheme.*`, `context.colors.*`, `KasySpacing.*`, `KasyRadius.*`,
|
|
11
|
+
`KasyIconSize.*`).
|
|
12
|
+
- Use Kasy components (`KasyButton`, `KasyCard`, `showKasyToast`,
|
|
13
|
+
`showKasyConfirmDialog`, …), not raw Material.
|
|
14
|
+
- The Home screen is the visual reference: clean, minimal, one accent colour.
|
|
15
|
+
- English in code; user strings via `context.t.*` (slang i18n).
|
|
16
|
+
- Run `flutter analyze` and keep it clean.
|