kasy-cli 1.10.0 → 1.13.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 +25 -4
- package/lib/commands/check.js +40 -50
- package/lib/commands/deploy.js +25 -25
- package/lib/commands/splash.js +220 -0
- package/lib/scaffold/CHANGELOG.json +9 -0
- package/lib/scaffold/backends/api/patch/lib/main.dart +29 -10
- package/lib/scaffold/backends/supabase/patch/lib/main.dart +29 -10
- package/lib/scaffold/features/README.md +15 -139
- package/lib/scaffold/shared/generator-utils.js +16 -15
- package/lib/utils/i18n.js +292 -43
- package/package.json +2 -2
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night/launch_background.xml +9 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-v21/launch_background.xml +9 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +2 -1
- package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -0
- package/templates/firebase/assets/images/splash_logo_dark.png +0 -0
- package/templates/firebase/assets/images/splash_logo_light.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json +9 -8
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +33 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png +0 -0
- package/templates/firebase/ios/Runner/Base.lproj/LaunchScreen.storyboard +1 -1
- package/templates/firebase/lib/core/initializer/onstart_widget.dart +7 -1
- package/templates/firebase/lib/core/theme/providers/theme_provider.dart +48 -24
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +4 -0
- package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +1 -0
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +13 -0
- package/templates/firebase/lib/features/settings/settings_page.dart +158 -18
- package/templates/firebase/lib/i18n/en.i18n.json +6 -2
- package/templates/firebase/lib/i18n/es.i18n.json +6 -2
- package/templates/firebase/lib/i18n/pt.i18n.json +6 -2
- package/templates/firebase/lib/main.dart +29 -10
- package/templates/firebase/pubspec.yaml +4 -5
- package/templates/firebase/test/core/data/repositories/user_repository_test.dart +1 -1
- package/templates/firebase/web/index.html +47 -39
- package/templates/firebase/web/splash/img/dark-1x.png +0 -0
- package/templates/firebase/web/splash/img/dark-2x.png +0 -0
- package/templates/firebase/web/splash/img/dark-3x.png +0 -0
- package/templates/firebase/web/splash/img/dark-4x.png +0 -0
- package/templates/firebase/web/splash/img/light-1x.png +0 -0
- package/templates/firebase/web/splash/img/light-2x.png +0 -0
- package/templates/firebase/web/splash/img/light-3x.png +0 -0
- package/templates/firebase/web/splash/img/light-4x.png +0 -0
- package/lib/scaffold/features/analytics/lib/core/data/api/analytics_api.dart +0 -124
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.es.md +0 -35
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.md +0 -35
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.pt.md +0 -35
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.es.md +0 -12
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.md +0 -12
- package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.pt.md +0 -12
- package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.es.md +0 -17
- package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.md +0 -17
- package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.pt.md +0 -17
- package/lib/scaffold/features/ci/.github/dependabot.yml +0 -16
- package/lib/scaffold/features/ci/.github/workflows/app.yml +0 -20
- package/lib/scaffold/features/ci/.gitlab/templates/deploy.yaml +0 -14
- package/lib/scaffold/features/ci/.gitlab/templates/dropbox.yaml +0 -19
- package/lib/scaffold/features/ci/.gitlab/templates/flutter.yaml +0 -163
- package/lib/scaffold/features/ci/.gitlab/templates/mailgun.yaml +0 -28
- package/lib/scaffold/features/ci/.gitlab-ci.yml +0 -37
- package/lib/scaffold/features/ci/codemagic.yaml +0 -157
- package/lib/scaffold/features/facebook/lib/core/data/api/tracking_api.dart +0 -111
- package/lib/scaffold/features/feedback/lib/features/feedbacks/api/entities/feature_request_entity.dart +0 -27
- package/lib/scaffold/features/feedback/lib/features/feedbacks/api/entities/feature_vote_entity.dart +0 -27
- package/lib/scaffold/features/feedback/lib/features/feedbacks/api/feature_request_api.dart +0 -50
- package/lib/scaffold/features/feedback/lib/features/feedbacks/api/feature_vote_api.dart +0 -79
- package/lib/scaffold/features/feedback/lib/features/feedbacks/models/feature_requests.dart +0 -48
- package/lib/scaffold/features/feedback/lib/features/feedbacks/models/feedback_state.dart +0 -42
- package/lib/scaffold/features/feedback/lib/features/feedbacks/providers/feedback_page_notifier.dart +0 -147
- package/lib/scaffold/features/feedback/lib/features/feedbacks/repositories/feature_request_repository.dart +0 -95
- package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/component/add_feature_form.dart +0 -199
- package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/feedback_page.dart +0 -175
- package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/widgets/add_feature_button.dart +0 -76
- package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/widgets/feature_card.dart +0 -279
- package/lib/scaffold/features/ios-release/.kasy/apple.env.example +0 -8
- package/lib/scaffold/features/ios-release/.kasy/codemagic.env.example +0 -7
- package/lib/scaffold/features/ios-release/docs/codemagic-release.en.md +0 -50
- package/lib/scaffold/features/ios-release/docs/codemagic-release.es.md +0 -50
- package/lib/scaffold/features/ios-release/docs/codemagic-release.pt.md +0 -50
- package/lib/scaffold/features/ios-release/docs/ios-release.en.md +0 -41
- package/lib/scaffold/features/ios-release/docs/ios-release.es.md +0 -41
- package/lib/scaffold/features/ios-release/docs/ios-release.pt.md +0 -41
- package/lib/scaffold/features/ios-release/scripts/bump-ios-version.js +0 -38
- package/lib/scaffold/features/ios-release/scripts/release-ios.sh +0 -137
- package/lib/scaffold/features/llm_chat/lib/features/llm_chat/llm_chat_page.dart +0 -301
- package/lib/scaffold/features/local_notifications/lib/features/local_reminder/providers/reminder_notifier.dart +0 -81
- package/lib/scaffold/features/local_notifications/lib/features/local_reminder/repositories/reminder_preferences.dart +0 -76
- package/lib/scaffold/features/local_notifications/lib/features/local_reminder/ui/reminder_page.dart +0 -282
- package/lib/scaffold/features/onboarding/lib/features/onboarding/api/entities/user_info_entity.dart +0 -24
- package/lib/scaffold/features/onboarding/lib/features/onboarding/api/user_infos_api.dart +0 -71
- package/lib/scaffold/features/onboarding/lib/features/onboarding/models/user_info.dart +0 -92
- package/lib/scaffold/features/onboarding/lib/features/onboarding/providers/onboarding_model.dart +0 -15
- package/lib/scaffold/features/onboarding/lib/features/onboarding/providers/onboarding_provider.dart +0 -78
- package/lib/scaffold/features/onboarding/lib/features/onboarding/repositories/user_infos_repository.dart +0 -29
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/animations/page_transitions.dart +0 -30
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_att_setup.dart +0 -66
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_features.dart +0 -72
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_loader.dart +0 -92
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +0 -73
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_questions.dart +0 -89
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/onboarding_page.dart +0 -94
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_background.dart +0 -80
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_feature.dart +0 -139
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +0 -110
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_progress.dart +0 -84
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +0 -173
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_reassurance.dart +0 -45
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +0 -77
- package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +0 -392
- package/lib/scaffold/features/revenuecat/lib/core/data/api/tracking_api.dart +0 -116
- package/lib/scaffold/features/revenuecat/lib/core/data/models/subscription.dart +0 -322
- package/lib/scaffold/features/revenuecat/lib/core/home_widgets/home_widget_background_task.dart +0 -41
- package/lib/scaffold/features/revenuecat/lib/core/states/user_state_notifier.dart +0 -305
- package/templates/firebase/assets/images/splashscreen.png +0 -0
package/bin/kasy.js
CHANGED
|
@@ -15,6 +15,7 @@ const { runRemove } = require('../lib/commands/remove');
|
|
|
15
15
|
const { runUpdate } = require('../lib/commands/update');
|
|
16
16
|
const { runDocs } = require('../lib/commands/docs');
|
|
17
17
|
const { runNotificationsText } = require('../lib/commands/notifications');
|
|
18
|
+
const { runSplash } = require('../lib/commands/splash');
|
|
18
19
|
const {
|
|
19
20
|
runConfigure: runIosConfigure,
|
|
20
21
|
runBuild: runIosBuild,
|
|
@@ -242,14 +243,15 @@ function buildProgram(language) {
|
|
|
242
243
|
const langName = t('cli.command.setup.langName');
|
|
243
244
|
applyLocalizedHelp(
|
|
244
245
|
program
|
|
245
|
-
.command('setup')
|
|
246
|
+
.command('setup', { hidden: true })
|
|
246
247
|
.argument(`[${directoryName}]`, t('cli.command.setup.directoryArg'), '.')
|
|
247
248
|
.option(`-l, --lang <${langName}>`, t('cli.command.setup.langOption'))
|
|
248
249
|
.option('-b, --backend <backend>', t('cli.command.setup.backendOption'))
|
|
249
250
|
.option('--with <features>', t('cli.command.setup.featuresOption'))
|
|
250
251
|
.description(t('cli.command.setup.description'))
|
|
251
252
|
.action(async (directory, options) => {
|
|
252
|
-
// `setup` is an alias for `new` — unified flow
|
|
253
|
+
// `setup` is an alias for `new` — unified flow.
|
|
254
|
+
// Hidden from root help to reduce noise; still callable.
|
|
253
255
|
await runNew(directory, {
|
|
254
256
|
language: options.lang,
|
|
255
257
|
backend: options.backend,
|
|
@@ -509,6 +511,26 @@ function buildProgram(language) {
|
|
|
509
511
|
t
|
|
510
512
|
);
|
|
511
513
|
|
|
514
|
+
applyLocalizedHelp(
|
|
515
|
+
program
|
|
516
|
+
.command('splash')
|
|
517
|
+
.argument('[directory]', 'Project folder (default: current directory)', '.')
|
|
518
|
+
.requiredOption('--light <path>', 'PNG with dark logo (shown in light mode)')
|
|
519
|
+
.requiredOption('--dark <path>', 'PNG with light logo (shown in dark mode)')
|
|
520
|
+
.option('--skip-generate', 'Only copy files, do not run flutter_native_splash:create', false)
|
|
521
|
+
.description(t('cli.command.splash.description'))
|
|
522
|
+
.action(async (directory, options) => {
|
|
523
|
+
const dir = directory || '.';
|
|
524
|
+
await runSplash(dir, {
|
|
525
|
+
language,
|
|
526
|
+
light: options.light,
|
|
527
|
+
dark: options.dark,
|
|
528
|
+
skipGenerate: options.skipGenerate,
|
|
529
|
+
});
|
|
530
|
+
}),
|
|
531
|
+
t
|
|
532
|
+
);
|
|
533
|
+
|
|
512
534
|
const notificationsCmd = program
|
|
513
535
|
.command('notifications')
|
|
514
536
|
.description(t('cli.command.notifications.description'));
|
|
@@ -516,7 +538,6 @@ function buildProgram(language) {
|
|
|
516
538
|
notificationsCmd
|
|
517
539
|
.command('text')
|
|
518
540
|
.argument('[directory]', 'Project folder (default: current directory)', '.')
|
|
519
|
-
.option('-d, --directory <path>', 'Project folder (default: current directory)')
|
|
520
541
|
.option('--locale <code>', 'i18n files to update: pt, en, es, or all (default: all)', 'all')
|
|
521
542
|
.option('--demo-title <text>', 'Home → Features demo notification title')
|
|
522
543
|
.option('--demo-body <text>', 'Home → Features demo card description / instant notification body')
|
|
@@ -524,7 +545,7 @@ function buildProgram(language) {
|
|
|
524
545
|
.option('--reminder-body <text>', 'Scheduled reminder notification body')
|
|
525
546
|
.description(t('cli.command.notifications.text.description'))
|
|
526
547
|
.action(async (directory, options) => {
|
|
527
|
-
const dir =
|
|
548
|
+
const dir = directory || '.';
|
|
528
549
|
await runNotificationsText(dir, {
|
|
529
550
|
language,
|
|
530
551
|
directory: dir,
|
package/lib/commands/check.js
CHANGED
|
@@ -110,67 +110,67 @@ async function runCheck(options = {}) {
|
|
|
110
110
|
const t = createTranslator(lang);
|
|
111
111
|
|
|
112
112
|
printCompactHeader(t);
|
|
113
|
-
ui.intro('
|
|
113
|
+
ui.intro(t('check.intro'));
|
|
114
114
|
|
|
115
115
|
// ── Detect backend ────────────────────────────────────────────────────────
|
|
116
116
|
const isFirebase = await fs.pathExists(path.join(projectDir, 'firebase.json'));
|
|
117
117
|
const isSupabase = await fs.pathExists(path.join(projectDir, 'supabase'));
|
|
118
118
|
|
|
119
119
|
if (isFirebase && !isSupabase) {
|
|
120
|
-
ok('
|
|
121
|
-
info('
|
|
120
|
+
ok(t('check.firebase.detected'));
|
|
121
|
+
info(t('check.firebase.adcInfo'));
|
|
122
122
|
const firebaseProjectId = await readFirebaseProjectId(projectDir);
|
|
123
123
|
const apnsLines = [
|
|
124
|
-
kleur.yellow(
|
|
125
|
-
kleur.dim('
|
|
124
|
+
kleur.yellow(t('check.apns.warn')),
|
|
125
|
+
kleur.dim(t('check.apns.where')),
|
|
126
126
|
];
|
|
127
127
|
if (firebaseProjectId) {
|
|
128
128
|
apnsLines.push(kleur.cyan(`https://console.firebase.google.com/project/${firebaseProjectId}/settings/cloudmessaging`));
|
|
129
129
|
}
|
|
130
130
|
ui.note(apnsLines.join('\n'));
|
|
131
|
-
ui.outro('
|
|
131
|
+
ui.outro(t('check.done'));
|
|
132
132
|
return;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
if (!isSupabase) {
|
|
136
|
-
ui.log.error('
|
|
137
|
-
ui.cancel('
|
|
136
|
+
ui.log.error(t('check.notKasy'));
|
|
137
|
+
ui.cancel(t('check.aborted'));
|
|
138
138
|
process.exit(1);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
// ── 1. Project linked? ────────────────────────────────────────────────────
|
|
142
142
|
const projectRef = await readProjectRef(projectDir);
|
|
143
143
|
if (!projectRef) {
|
|
144
|
-
fail('
|
|
145
|
-
ui.cancel('
|
|
144
|
+
fail(t('check.supabase.notLinked'), t('check.supabase.notLinkedHint'));
|
|
145
|
+
ui.cancel(t('check.aborted'));
|
|
146
146
|
process.exit(1);
|
|
147
147
|
}
|
|
148
|
-
ok('
|
|
148
|
+
ok(t('check.supabase.linked'), projectRef);
|
|
149
149
|
|
|
150
150
|
// ── Read secrets list ─────────────────────────────────────────────────────
|
|
151
151
|
const spinner = ui.spinner();
|
|
152
|
-
spinner.start('
|
|
152
|
+
spinner.start(t('check.spin.secrets'));
|
|
153
153
|
const secrets = await listSecretNames(projectDir);
|
|
154
|
-
spinner.stop('
|
|
154
|
+
spinner.stop(t('check.spin.secretsDone'));
|
|
155
155
|
|
|
156
156
|
if (!secrets) {
|
|
157
|
-
warn('
|
|
157
|
+
warn(t('check.secrets.listFailed'), t('check.secrets.checkLogin'));
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
// ── 2. FIREBASE_PROJECT_ID ────────────────────────────────────────────────
|
|
161
161
|
const firebaseProjectId = await readFirebaseProjectId(projectDir);
|
|
162
162
|
if (secrets) {
|
|
163
163
|
if (secrets.has('FIREBASE_PROJECT_ID')) {
|
|
164
|
-
ok('
|
|
164
|
+
ok(t('check.fbProjId.ok'));
|
|
165
165
|
} else {
|
|
166
|
-
fail('
|
|
166
|
+
fail(t('check.fbProjId.missing'));
|
|
167
167
|
if (firebaseProjectId) {
|
|
168
|
-
ui.log.message(kleur.gray(
|
|
168
|
+
ui.log.message(kleur.gray(t('check.fbProjId.fixHint', { id: firebaseProjectId })));
|
|
169
169
|
if (options.fix) {
|
|
170
170
|
const r = await runCmd(`supabase secrets set FIREBASE_PROJECT_ID="${firebaseProjectId}"`, projectDir);
|
|
171
171
|
r.ok
|
|
172
|
-
? ok('
|
|
173
|
-
: fail('
|
|
172
|
+
? ok(t('check.fbProjId.fixed'))
|
|
173
|
+
: fail(t('check.fbProjId.fixFailed'), r.error);
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
}
|
|
@@ -179,15 +179,15 @@ async function runCheck(options = {}) {
|
|
|
179
179
|
// ── 3. FIREBASE_SERVICE_ACCOUNT_JSON ─────────────────────────────────────
|
|
180
180
|
if (secrets) {
|
|
181
181
|
if (secrets.has('FIREBASE_SERVICE_ACCOUNT_JSON')) {
|
|
182
|
-
ok('
|
|
182
|
+
ok(t('check.fbSak.ok'));
|
|
183
183
|
} else {
|
|
184
|
-
fail('
|
|
184
|
+
fail(t('check.fbSak.missing'));
|
|
185
185
|
|
|
186
186
|
if (options.fix && firebaseProjectId) {
|
|
187
187
|
const fixSpinner = ui.spinner();
|
|
188
|
-
fixSpinner.start('
|
|
188
|
+
fixSpinner.start(t('check.fbSak.spin'));
|
|
189
189
|
const fcmResult = await createFcmServiceAccountKey(firebaseProjectId);
|
|
190
|
-
fixSpinner.stop('
|
|
190
|
+
fixSpinner.stop(t('check.fbSak.spinDone'));
|
|
191
191
|
|
|
192
192
|
if (fcmResult.ok) {
|
|
193
193
|
const secretSteps = await setSupabaseSecrets(projectDir, {
|
|
@@ -196,62 +196,52 @@ async function runCheck(options = {}) {
|
|
|
196
196
|
});
|
|
197
197
|
const setStep = secretSteps.find((s) => s.name === 'secret FIREBASE_SERVICE_ACCOUNT_JSON');
|
|
198
198
|
setStep?.ok
|
|
199
|
-
? ok('
|
|
200
|
-
: fail('
|
|
199
|
+
? ok(t('check.fbSak.fixed'))
|
|
200
|
+
: fail(t('check.fbSak.fixFailed'), setStep?.error);
|
|
201
201
|
} else {
|
|
202
|
-
fail('
|
|
203
|
-
ui.log.message(kleur.gray(
|
|
204
|
-
'Manual: Firebase Console → Configurações → Contas de serviço → Gerar chave\n' +
|
|
205
|
-
"Depois: supabase secrets set FIREBASE_SERVICE_ACCOUNT_JSON='$(cat chave.json)'"
|
|
206
|
-
));
|
|
202
|
+
fail(t('check.fbSak.genFailed'), fcmResult.error);
|
|
203
|
+
ui.log.message(kleur.gray(t('check.fbSak.manual')));
|
|
207
204
|
}
|
|
208
205
|
} else {
|
|
209
|
-
ui.log.message(kleur.gray(
|
|
210
|
-
'Corrija automaticamente: kasy check --fix\n' +
|
|
211
|
-
'Ou manualmente: Firebase Console → Configurações → Contas de serviço → Gerar chave\n' +
|
|
212
|
-
"Depois: supabase secrets set FIREBASE_SERVICE_ACCOUNT_JSON='$(cat chave.json)'"
|
|
213
|
-
));
|
|
206
|
+
ui.log.message(kleur.gray(t('check.fbSak.hint')));
|
|
214
207
|
}
|
|
215
208
|
}
|
|
216
209
|
}
|
|
217
210
|
|
|
218
211
|
// ── 4. Edge function send-push-notification ────────────────────────────────
|
|
219
212
|
const fnSpinner = ui.spinner();
|
|
220
|
-
fnSpinner.start('
|
|
213
|
+
fnSpinner.start(t('check.fn.spin'));
|
|
221
214
|
const functions = await listDeployedFunctions(projectDir);
|
|
222
|
-
fnSpinner.stop('
|
|
215
|
+
fnSpinner.stop(t('check.fn.spinDone'));
|
|
223
216
|
|
|
224
217
|
if (!functions) {
|
|
225
|
-
warn('
|
|
218
|
+
warn(t('check.fn.listFailed'), t('check.secrets.checkLogin'));
|
|
226
219
|
} else if (functions.has('send-push-notification')) {
|
|
227
|
-
ok('
|
|
220
|
+
ok(t('check.fn.deployed'));
|
|
228
221
|
} else {
|
|
229
|
-
fail('
|
|
222
|
+
fail(t('check.fn.missing'));
|
|
230
223
|
if (options.fix) {
|
|
231
224
|
const deploySpinner = ui.spinner();
|
|
232
|
-
deploySpinner.start('
|
|
225
|
+
deploySpinner.start(t('check.fn.deploySpin'));
|
|
233
226
|
const r = await runCmd('supabase functions deploy send-push-notification', projectDir);
|
|
234
227
|
r.ok
|
|
235
|
-
? deploySpinner.stop('
|
|
236
|
-
: deploySpinner.error(
|
|
228
|
+
? deploySpinner.stop(t('check.fn.deployDone'))
|
|
229
|
+
: deploySpinner.error(`${t('check.fn.deployFailed')}${r.error ? ` — ${r.error}` : ''}`);
|
|
237
230
|
} else {
|
|
238
|
-
ui.log.message(kleur.gray(
|
|
239
|
-
'Corrija automaticamente: kasy check --fix\n' +
|
|
240
|
-
'Ou manualmente: supabase functions deploy send-push-notification'
|
|
241
|
-
));
|
|
231
|
+
ui.log.message(kleur.gray(t('check.fn.hint')));
|
|
242
232
|
}
|
|
243
233
|
}
|
|
244
234
|
|
|
245
235
|
// ── 5. APNs reminder ─────────────────────────────────────────────────────
|
|
246
236
|
const apnsLines = [
|
|
247
|
-
kleur.yellow(
|
|
248
|
-
kleur.dim('
|
|
237
|
+
kleur.yellow(t('check.apns.warn')),
|
|
238
|
+
kleur.dim(t('check.apns.where')),
|
|
249
239
|
];
|
|
250
240
|
if (firebaseProjectId) {
|
|
251
241
|
apnsLines.push(kleur.cyan(`https://console.firebase.google.com/project/${firebaseProjectId}/settings/cloudmessaging`));
|
|
252
242
|
}
|
|
253
243
|
ui.note(apnsLines.join('\n'));
|
|
254
|
-
ui.outro('
|
|
244
|
+
ui.outro(t('check.done'));
|
|
255
245
|
}
|
|
256
246
|
|
|
257
247
|
module.exports = { runCheck };
|
package/lib/commands/deploy.js
CHANGED
|
@@ -123,14 +123,14 @@ async function deployFirebase(projectDir, options, tr) {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
const spinner = ui.spinner();
|
|
126
|
-
spinner.start('
|
|
126
|
+
spinner.start(tr('deploy.firebase.spin'));
|
|
127
127
|
let steps;
|
|
128
128
|
try {
|
|
129
129
|
steps = await runFirebaseDeploy(projectDir, null, firebaseProjectId, {
|
|
130
130
|
functionsRegion,
|
|
131
131
|
onProgress: (key) => { spinner.message(String(key)); },
|
|
132
132
|
});
|
|
133
|
-
spinner.stop('
|
|
133
|
+
spinner.stop(tr('deploy.firebase.spinDone'));
|
|
134
134
|
} catch (err) {
|
|
135
135
|
spinner.error(err.message);
|
|
136
136
|
throw err;
|
|
@@ -145,9 +145,9 @@ async function deployFirebase(projectDir, options, tr) {
|
|
|
145
145
|
// ── APNs reminder — required for iOS push notifications ──────────────────
|
|
146
146
|
// Cannot be automated: the .p8 key only exists in Apple Developer Portal.
|
|
147
147
|
const apnsBody = [
|
|
148
|
-
kleur.yellow('
|
|
149
|
-
kleur.dim('
|
|
150
|
-
kleur.dim('
|
|
148
|
+
kleur.yellow(tr('deploy.apns.title')),
|
|
149
|
+
kleur.dim(tr('deploy.apns.step1')),
|
|
150
|
+
kleur.dim(tr('deploy.apns.step2')),
|
|
151
151
|
kleur.cyan(`https://console.firebase.google.com/project/${firebaseProjectId}/settings/cloudmessaging`),
|
|
152
152
|
].join('\n');
|
|
153
153
|
ui.note(apnsBody);
|
|
@@ -155,28 +155,28 @@ async function deployFirebase(projectDir, options, tr) {
|
|
|
155
155
|
|
|
156
156
|
// ── Supabase deploy ───────────────────────────────────────────────────────────
|
|
157
157
|
|
|
158
|
-
async function deploySupabase(projectDir) {
|
|
158
|
+
async function deploySupabase(projectDir, tr) {
|
|
159
159
|
// ── 1. Get project ref ──────────────────────────────────────────────────
|
|
160
160
|
const projectRef = await readSupabaseProjectRef(projectDir);
|
|
161
161
|
if (!projectRef) {
|
|
162
|
-
ui.log.error('
|
|
163
|
-
ui.log.message(kleur.gray('
|
|
162
|
+
ui.log.error(tr('deploy.supabase.notLinked'));
|
|
163
|
+
ui.log.message(kleur.gray(tr('deploy.supabase.linkHint')));
|
|
164
164
|
process.exit(1);
|
|
165
165
|
}
|
|
166
|
-
ui.log.message(kleur.gray(
|
|
166
|
+
ui.log.message(kleur.gray(tr('deploy.supabase.projectRef', { ref: kleur.cyan(projectRef) })));
|
|
167
167
|
|
|
168
168
|
// ── 2. FCM Service Account JSON ─────────────────────────────────────────
|
|
169
169
|
const alreadySet = await isServiceAccountJsonSet(projectDir);
|
|
170
170
|
if (alreadySet) {
|
|
171
|
-
printStep(true, false, '
|
|
171
|
+
printStep(true, false, tr('deploy.supabase.sakAlready'));
|
|
172
172
|
} else {
|
|
173
173
|
const firebaseProjectId = await readFirebaseProjectId(projectDir);
|
|
174
174
|
|
|
175
175
|
if (firebaseProjectId) {
|
|
176
176
|
const fcmSpinner = ui.spinner();
|
|
177
|
-
fcmSpinner.start(
|
|
177
|
+
fcmSpinner.start(tr('deploy.supabase.sakSpin'));
|
|
178
178
|
const fcmResult = await createFcmServiceAccountKey(firebaseProjectId);
|
|
179
|
-
fcmSpinner.stop('
|
|
179
|
+
fcmSpinner.stop(tr('deploy.supabase.sakSpinDone'));
|
|
180
180
|
|
|
181
181
|
if (fcmResult.ok) {
|
|
182
182
|
const secretSteps = await setSupabaseSecrets(projectDir, {
|
|
@@ -186,23 +186,23 @@ async function deploySupabase(projectDir) {
|
|
|
186
186
|
for (const s of secretSteps) printStep(s.ok, false, s.name, s.error);
|
|
187
187
|
} else {
|
|
188
188
|
printStep(false, false, 'FIREBASE_SERVICE_ACCOUNT_JSON', fcmResult.error);
|
|
189
|
-
ui.log.warn(
|
|
189
|
+
ui.log.warn(tr('deploy.supabase.sakManual'));
|
|
190
190
|
}
|
|
191
191
|
} else {
|
|
192
|
-
printStep(false, false, 'FIREBASE_SERVICE_ACCOUNT_JSON', '
|
|
192
|
+
printStep(false, false, 'FIREBASE_SERVICE_ACCOUNT_JSON', tr('deploy.supabase.sakNoGS'));
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
// ── 3. Deploy edge functions ────────────────────────────────────────────
|
|
197
197
|
const fnSpinner = ui.spinner();
|
|
198
|
-
fnSpinner.start('
|
|
198
|
+
fnSpinner.start(tr('deploy.supabase.fnSpin'));
|
|
199
199
|
const fnResult = await deployFunctions(projectDir);
|
|
200
|
-
fnSpinner.stop('
|
|
200
|
+
fnSpinner.stop(tr('deploy.supabase.fnSpinDone'));
|
|
201
201
|
|
|
202
202
|
if (Array.isArray(fnResult)) {
|
|
203
203
|
fnResult.forEach((s) => printStep(s.ok, s.skipped, s.name, s.error));
|
|
204
204
|
} else if (fnResult.skipped) {
|
|
205
|
-
printStep(true, true,
|
|
205
|
+
printStep(true, true, tr('deploy.supabase.fnNone'));
|
|
206
206
|
} else {
|
|
207
207
|
printStep(fnResult.ok, false, 'supabase functions deploy', fnResult.error);
|
|
208
208
|
}
|
|
@@ -210,9 +210,9 @@ async function deploySupabase(projectDir) {
|
|
|
210
210
|
// ── 4. APNs reminder ────────────────────────────────────────────────────
|
|
211
211
|
const firebaseProjectId = await readFirebaseProjectId(projectDir);
|
|
212
212
|
const apnsLines = [
|
|
213
|
-
kleur.yellow('
|
|
214
|
-
kleur.dim('
|
|
215
|
-
kleur.dim('
|
|
213
|
+
kleur.yellow(tr('deploy.apns.title')),
|
|
214
|
+
kleur.dim(tr('deploy.apns.step1')),
|
|
215
|
+
kleur.dim(tr('deploy.apns.step2')),
|
|
216
216
|
];
|
|
217
217
|
if (firebaseProjectId) {
|
|
218
218
|
apnsLines.push(kleur.cyan(`https://console.firebase.google.com/project/${firebaseProjectId}/settings/cloudmessaging`));
|
|
@@ -240,17 +240,17 @@ async function runDeployCommand(directory, options = {}, { language: langHint }
|
|
|
240
240
|
|
|
241
241
|
if (isFirebase) {
|
|
242
242
|
printCompactHeader(tr);
|
|
243
|
-
ui.intro('
|
|
243
|
+
ui.intro(tr('deploy.firebase.intro'));
|
|
244
244
|
await deployFirebase(projectDir, options, tr);
|
|
245
|
-
ui.outro('
|
|
245
|
+
ui.outro(tr('deploy.outro'));
|
|
246
246
|
return;
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
if (isSupabase) {
|
|
250
250
|
printCompactHeader(tr);
|
|
251
|
-
ui.intro('
|
|
252
|
-
await deploySupabase(projectDir);
|
|
253
|
-
ui.outro('
|
|
251
|
+
ui.intro(tr('deploy.supabase.intro'));
|
|
252
|
+
await deploySupabase(projectDir, tr);
|
|
253
|
+
ui.outro(tr('deploy.outro'));
|
|
254
254
|
return;
|
|
255
255
|
}
|
|
256
256
|
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const fsp = require('node:fs/promises');
|
|
4
|
+
const { exec } = require('node:child_process');
|
|
5
|
+
const { promisify } = require('node:util');
|
|
6
|
+
const kleur = require('kleur');
|
|
7
|
+
const ui = require('../utils/ui');
|
|
8
|
+
const { printCompactHeader } = require('../utils/brand');
|
|
9
|
+
const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
10
|
+
|
|
11
|
+
const execAsync = promisify(exec);
|
|
12
|
+
|
|
13
|
+
const ASSETS_DIR = path.join('assets', 'images');
|
|
14
|
+
const LIGHT_NAME = 'splash_logo_light.png';
|
|
15
|
+
const DARK_NAME = 'splash_logo_dark.png';
|
|
16
|
+
|
|
17
|
+
const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parse the PNG IHDR chunk to read color type and detect transparency.
|
|
21
|
+
*
|
|
22
|
+
* PNG layout: 8-byte signature + chunks. The IHDR chunk is always first,
|
|
23
|
+
* placed at byte offset 8. It holds: length(4) + "IHDR"(4) + data(13) + crc(4).
|
|
24
|
+
* Color type is the 10th byte of data → absolute offset 25.
|
|
25
|
+
*
|
|
26
|
+
* Color types with alpha or transparency support:
|
|
27
|
+
* 4 = grayscale + alpha
|
|
28
|
+
* 6 = truecolor + alpha
|
|
29
|
+
* 3 = indexed (transparency only if a tRNS chunk is present)
|
|
30
|
+
*
|
|
31
|
+
* @param {string} filePath
|
|
32
|
+
* @returns {Promise<{ valid: boolean, hasAlpha: boolean, colorType: number, width: number, height: number }>}
|
|
33
|
+
*/
|
|
34
|
+
async function inspectPng(filePath) {
|
|
35
|
+
const handle = await fsp.open(filePath, 'r');
|
|
36
|
+
try {
|
|
37
|
+
const header = Buffer.alloc(33);
|
|
38
|
+
await handle.read(header, 0, 33, 0);
|
|
39
|
+
const sig = header.subarray(0, 8);
|
|
40
|
+
if (!sig.equals(PNG_SIGNATURE)) {
|
|
41
|
+
return { valid: false, hasAlpha: false, colorType: -1, width: 0, height: 0 };
|
|
42
|
+
}
|
|
43
|
+
const width = header.readUInt32BE(16);
|
|
44
|
+
const height = header.readUInt32BE(20);
|
|
45
|
+
const colorType = header.readUInt8(25);
|
|
46
|
+
|
|
47
|
+
let hasAlpha = colorType === 4 || colorType === 6;
|
|
48
|
+
|
|
49
|
+
if (!hasAlpha && colorType === 3) {
|
|
50
|
+
const { size } = await handle.stat();
|
|
51
|
+
const remaining = Math.min(size - 33, 256 * 1024);
|
|
52
|
+
if (remaining > 0) {
|
|
53
|
+
const tail = Buffer.alloc(remaining);
|
|
54
|
+
await handle.read(tail, 0, remaining, 33);
|
|
55
|
+
if (tail.includes(Buffer.from('tRNS'))) {
|
|
56
|
+
hasAlpha = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { valid: true, hasAlpha, colorType, width, height };
|
|
62
|
+
} finally {
|
|
63
|
+
await handle.close();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function assertKasyProject(projectDir, t) {
|
|
68
|
+
const kitSetupPath = path.join(projectDir, 'kit_setup.json');
|
|
69
|
+
const pubspecPath = path.join(projectDir, 'pubspec.yaml');
|
|
70
|
+
if (!(await fs.pathExists(kitSetupPath)) && !(await fs.pathExists(pubspecPath))) {
|
|
71
|
+
throw new Error(t('splash.error.notKasyProject'));
|
|
72
|
+
}
|
|
73
|
+
const assetsDir = path.join(projectDir, ASSETS_DIR);
|
|
74
|
+
await fs.ensureDir(assetsDir);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @param {string} flagValue
|
|
79
|
+
* @param {string} role 'light' | 'dark'
|
|
80
|
+
*/
|
|
81
|
+
function resolveInputPath(flagValue, role) {
|
|
82
|
+
if (!flagValue) return null;
|
|
83
|
+
const expanded = flagValue.startsWith('~')
|
|
84
|
+
? path.join(require('node:os').homedir(), flagValue.slice(1))
|
|
85
|
+
: flagValue;
|
|
86
|
+
return path.resolve(expanded);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {string} projectDir
|
|
91
|
+
* @param {{ light?: string, dark?: string, skipGenerate?: boolean, language?: string }} options
|
|
92
|
+
*/
|
|
93
|
+
async function runSplash(projectDir, options = {}) {
|
|
94
|
+
const language = options.language || detectDefaultLanguage();
|
|
95
|
+
const t = createTranslator(language);
|
|
96
|
+
|
|
97
|
+
printCompactHeader();
|
|
98
|
+
ui.intro(kleur.bold().cyan(t('splash.intro')));
|
|
99
|
+
|
|
100
|
+
await assertKasyProject(projectDir, t);
|
|
101
|
+
|
|
102
|
+
const lightPath = resolveInputPath(options.light, 'light');
|
|
103
|
+
const darkPath = resolveInputPath(options.dark, 'dark');
|
|
104
|
+
|
|
105
|
+
if (!lightPath || !darkPath) {
|
|
106
|
+
ui.log.error(t('splash.error.bothRequired'));
|
|
107
|
+
ui.log.message(kleur.dim('kasy splash --light <light.png> --dark <dark.png>'));
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const [role, p] of [['light', lightPath], ['dark', darkPath]]) {
|
|
112
|
+
if (!(await fs.pathExists(p))) {
|
|
113
|
+
ui.log.error(t('splash.error.fileNotFound', { path: p }));
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const inspectSpinner = ui.spinner();
|
|
119
|
+
inspectSpinner.start(t('splash.validating'));
|
|
120
|
+
|
|
121
|
+
let lightInfo;
|
|
122
|
+
let darkInfo;
|
|
123
|
+
try {
|
|
124
|
+
lightInfo = await inspectPng(lightPath);
|
|
125
|
+
darkInfo = await inspectPng(darkPath);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
inspectSpinner.stop(`✖ ${err.message || t('splash.error.notPng')}`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!lightInfo.valid || !darkInfo.valid) {
|
|
132
|
+
inspectSpinner.stop(`✖ ${t('splash.error.notPng')}`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
inspectSpinner.stop(t('splash.validated'));
|
|
137
|
+
|
|
138
|
+
const warnings = [];
|
|
139
|
+
if (!lightInfo.hasAlpha) {
|
|
140
|
+
warnings.push(t('splash.warn.noAlphaLight', { path: path.basename(lightPath) }));
|
|
141
|
+
}
|
|
142
|
+
if (!darkInfo.hasAlpha) {
|
|
143
|
+
warnings.push(t('splash.warn.noAlphaDark', { path: path.basename(darkPath) }));
|
|
144
|
+
}
|
|
145
|
+
if (lightInfo.width < 768 || lightInfo.height < 768) {
|
|
146
|
+
warnings.push(t('splash.warn.smallLight', {
|
|
147
|
+
w: lightInfo.width,
|
|
148
|
+
h: lightInfo.height,
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
if (darkInfo.width < 768 || darkInfo.height < 768) {
|
|
152
|
+
warnings.push(t('splash.warn.smallDark', {
|
|
153
|
+
w: darkInfo.width,
|
|
154
|
+
h: darkInfo.height,
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
if (warnings.length > 0) {
|
|
158
|
+
ui.note(warnings.map((w) => `${kleur.yellow('⚠')} ${w}`).join('\n'), t('splash.warn.title'));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const destLight = path.join(projectDir, ASSETS_DIR, LIGHT_NAME);
|
|
162
|
+
const destDark = path.join(projectDir, ASSETS_DIR, DARK_NAME);
|
|
163
|
+
|
|
164
|
+
const copySpinner = ui.spinner();
|
|
165
|
+
copySpinner.start(t('splash.copying'));
|
|
166
|
+
await fs.copy(lightPath, destLight, { overwrite: true });
|
|
167
|
+
await fs.copy(darkPath, destDark, { overwrite: true });
|
|
168
|
+
copySpinner.stop(t('splash.copied'));
|
|
169
|
+
|
|
170
|
+
if (options.skipGenerate) {
|
|
171
|
+
ui.note(t('splash.skipGenerate.hint'), t('splash.skipGenerate.title'));
|
|
172
|
+
ui.outro(t('splash.done'));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const genSpinner = ui.spinner();
|
|
177
|
+
genSpinner.start(t('splash.generating'));
|
|
178
|
+
const result = await runFlutterNativeSplash(projectDir);
|
|
179
|
+
if (result.ok) {
|
|
180
|
+
genSpinner.stop(t('splash.generated'));
|
|
181
|
+
} else {
|
|
182
|
+
genSpinner.stop(`⚠ ${t('splash.error.generateFailed')}`);
|
|
183
|
+
if (result.stderr) {
|
|
184
|
+
ui.log.message(kleur.dim(result.stderr.split('\n').slice(0, 8).join('\n')));
|
|
185
|
+
}
|
|
186
|
+
ui.log.message(kleur.dim('dart run flutter_native_splash:create'));
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const summary = [
|
|
191
|
+
`${kleur.bold(t('splash.summary.light'))}: ${kleur.white(LIGHT_NAME)} (${lightInfo.width}x${lightInfo.height})`,
|
|
192
|
+
`${kleur.bold(t('splash.summary.dark'))}: ${kleur.white(DARK_NAME)} (${darkInfo.width}x${darkInfo.height})`,
|
|
193
|
+
].join('\n');
|
|
194
|
+
ui.note(summary, t('splash.summary.title'));
|
|
195
|
+
|
|
196
|
+
ui.outro(t('splash.done'));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function runFlutterNativeSplash(projectDir) {
|
|
200
|
+
try {
|
|
201
|
+
const { stdout, stderr } = await execAsync(
|
|
202
|
+
'dart run flutter_native_splash:create',
|
|
203
|
+
{
|
|
204
|
+
cwd: projectDir,
|
|
205
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
206
|
+
timeout: 240_000,
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
return { ok: true, stdout, stderr };
|
|
210
|
+
} catch (err) {
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
error: err.message,
|
|
214
|
+
stdout: err.stdout || '',
|
|
215
|
+
stderr: err.stderr || '',
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module.exports = { runSplash, inspectPng };
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
+
"1.13.0": {
|
|
3
|
+
"modules": {
|
|
4
|
+
"onboarding": {
|
|
5
|
+
"pt": "Botão \"Já tem conta? Entrar\" na primeira tela do onboarding — quem já tem conta entra direto sem passar pelo fluxo todo, sem precisar criar usuário anônimo antes",
|
|
6
|
+
"en": "\"Already have an account? Log in\" button on the first onboarding screen — returning users go straight to sign-in instead of going through the full flow as an anonymous user first",
|
|
7
|
+
"es": "Botón \"¿Ya tienes cuenta? Iniciar sesión\" en la primera pantalla del onboarding — quien ya tiene cuenta entra directo sin pasar por todo el flujo como usuario anónimo"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
},
|
|
2
11
|
"1.10.0": {
|
|
3
12
|
"modules": {
|
|
4
13
|
"widget": {
|