kasy-cli 1.5.3 → 1.7.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 +7 -4
- package/docs/cli-reference.md +4 -6
- package/lib/commands/add.js +128 -102
- package/lib/commands/check.js +55 -38
- package/lib/commands/codemagic.js +61 -58
- package/lib/commands/deploy.js +49 -45
- package/lib/commands/docs.js +19 -18
- package/lib/commands/doctor.js +46 -44
- package/lib/commands/features.js +42 -20
- package/lib/commands/ios.js +69 -69
- package/lib/commands/new.js +529 -771
- package/lib/commands/notifications.js +59 -59
- package/lib/commands/remove.js +28 -27
- package/lib/commands/run.js +3 -1
- package/lib/commands/update.js +104 -96
- package/lib/commands/validate.js +24 -19
- package/lib/scaffold/catalog.js +45 -11
- package/lib/scaffold/features/README.md +1 -2
- package/lib/scaffold/shared/generator-utils.js +1 -1
- package/lib/utils/apple-release.js +23 -11
- package/lib/utils/brand.js +72 -0
- package/lib/utils/checks.js +20 -9
- package/lib/utils/i18n.js +102 -78
- package/lib/utils/prompts.js +29 -177
- package/lib/utils/ui.js +92 -0
- package/lib/utils/updates.js +9 -8
- package/package.json +2 -1
- package/templates/firebase/lib/features/home/home_page.dart +0 -19
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_routes.dart +0 -1
- package/templates/firebase/lib/router.dart +0 -9
- package/templates/firebase/lib/features/dev/keyboard_test_page.dart +0 -93
package/lib/utils/prompts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const ui = require('./ui');
|
|
2
2
|
const { isLicenseFormatValid } = require('./license');
|
|
3
3
|
const {
|
|
4
4
|
LANGUAGE_CHOICES,
|
|
@@ -6,20 +6,10 @@ const {
|
|
|
6
6
|
detectDefaultLanguage,
|
|
7
7
|
normalizeLanguage
|
|
8
8
|
} = require('./i18n');
|
|
9
|
-
const {
|
|
10
|
-
AVAILABLE_BACKENDS,
|
|
11
|
-
getVisibleFeatures,
|
|
12
|
-
normalizeBackend,
|
|
13
|
-
parseFeatureList
|
|
14
|
-
} = require('../scaffold/catalog');
|
|
15
|
-
|
|
16
|
-
const KASY_AUDIENCE = process.env.KASY_INTERNAL === '1' ? 'internal' : 'public';
|
|
17
9
|
|
|
18
|
-
function
|
|
19
|
-
return {
|
|
20
|
-
|
|
21
|
-
throw new Error(t('prompt.cancelled'));
|
|
22
|
-
}
|
|
10
|
+
function makeCancel(t) {
|
|
11
|
+
return () => {
|
|
12
|
+
throw new Error(t('prompt.cancelled'));
|
|
23
13
|
};
|
|
24
14
|
}
|
|
25
15
|
|
|
@@ -30,178 +20,41 @@ async function promptLanguage(language) {
|
|
|
30
20
|
}
|
|
31
21
|
|
|
32
22
|
const detectedLanguage = detectDefaultLanguage();
|
|
33
|
-
const initial = Math.max(
|
|
34
|
-
0,
|
|
35
|
-
LANGUAGE_CHOICES.findIndex((choice) => choice.value === detectedLanguage)
|
|
36
|
-
);
|
|
37
|
-
|
|
38
23
|
const t = createTranslator(detectedLanguage);
|
|
39
|
-
const response = await prompts(
|
|
40
|
-
{
|
|
41
|
-
type: 'select',
|
|
42
|
-
name: 'language',
|
|
43
|
-
message: t('prompt.language.select'),
|
|
44
|
-
choices: LANGUAGE_CHOICES,
|
|
45
|
-
initial
|
|
46
|
-
},
|
|
47
|
-
getPromptOptions(t)
|
|
48
|
-
);
|
|
49
24
|
|
|
50
|
-
|
|
25
|
+
const value = await ui.select({
|
|
26
|
+
message: t('prompt.language.select'),
|
|
27
|
+
initialValue: detectedLanguage,
|
|
28
|
+
options: LANGUAGE_CHOICES.map((choice) => ({
|
|
29
|
+
value: choice.value,
|
|
30
|
+
label: choice.title,
|
|
31
|
+
})),
|
|
32
|
+
onCancel: makeCancel(t),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return normalizeLanguage(value) || detectedLanguage;
|
|
51
36
|
}
|
|
52
37
|
|
|
53
38
|
async function promptLicenseKey(initial = '', options = {}) {
|
|
54
39
|
const t = options.t || createTranslator(options.language);
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (isLicenseFormatValid(value)) {
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return t('prompt.license.invalid');
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
getPromptOptions(t)
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
return response.licenseKey;
|
|
40
|
+
const value = await ui.text({
|
|
41
|
+
message: t('prompt.license.enter'),
|
|
42
|
+
initialValue: initial,
|
|
43
|
+
validate: (v) => (isLicenseFormatValid(v) ? undefined : t('prompt.license.invalid')),
|
|
44
|
+
onCancel: makeCancel(t),
|
|
45
|
+
});
|
|
46
|
+
return value;
|
|
73
47
|
}
|
|
74
48
|
|
|
75
49
|
async function promptProjectName(options = {}) {
|
|
76
50
|
const t = options.t || createTranslator(options.language);
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
getPromptOptions(t)
|
|
86
|
-
);
|
|
87
|
-
return response.projectName.trim();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async function runSetupWizard(options = {}) {
|
|
91
|
-
const t = options.t || createTranslator(options.language);
|
|
92
|
-
const defaultAppName = options.defaultAppName || 'MyApp';
|
|
93
|
-
const selectedFeaturesFromArgv = parseFeatureList(options.selectedFeatures);
|
|
94
|
-
|
|
95
|
-
const baseQuestions = [
|
|
96
|
-
{
|
|
97
|
-
type: 'text',
|
|
98
|
-
name: 'appName',
|
|
99
|
-
message: t('prompt.appName.enter'),
|
|
100
|
-
initial: defaultAppName,
|
|
101
|
-
validate: (value) => (value && value.trim() ? true : t('prompt.appName.required'))
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
type: 'text',
|
|
105
|
-
name: 'bundleId',
|
|
106
|
-
message: t('prompt.bundleId.enter'),
|
|
107
|
-
initial: 'com.example.app',
|
|
108
|
-
validate: (value) => {
|
|
109
|
-
if (!value || !value.trim()) {
|
|
110
|
-
return t('prompt.bundleId.required');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const valid = /^[a-zA-Z][\w]*(\.[a-zA-Z][\w]*)+$/.test(value.trim());
|
|
114
|
-
return valid ? true : t('prompt.bundleId.invalid');
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
];
|
|
118
|
-
|
|
119
|
-
const backendFromArgv = normalizeBackend(options.selectedBackend);
|
|
120
|
-
if (!backendFromArgv) {
|
|
121
|
-
baseQuestions.push({
|
|
122
|
-
type: 'select',
|
|
123
|
-
name: 'backend',
|
|
124
|
-
message: t('prompt.backend.select'),
|
|
125
|
-
choices: AVAILABLE_BACKENDS.map((backend) => ({
|
|
126
|
-
title: backend.id,
|
|
127
|
-
value: backend.id
|
|
128
|
-
})),
|
|
129
|
-
initial: 0
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (selectedFeaturesFromArgv.length === 0) {
|
|
134
|
-
baseQuestions.push({
|
|
135
|
-
type: 'multiselect',
|
|
136
|
-
name: 'features',
|
|
137
|
-
message: t('prompt.features.select'),
|
|
138
|
-
instructions: t('prompt.features.instructions'),
|
|
139
|
-
choices: getVisibleFeatures({ audience: KASY_AUDIENCE }).map((feature) => ({
|
|
140
|
-
title: feature.status === 'internal' ? `${feature.id} [beta]` : feature.id,
|
|
141
|
-
value: feature.id,
|
|
142
|
-
selected: false
|
|
143
|
-
})),
|
|
144
|
-
min: 0
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const core = await prompts(baseQuestions, getPromptOptions(t));
|
|
149
|
-
const backend = backendFromArgv || core.backend;
|
|
150
|
-
const features = selectedFeaturesFromArgv.length > 0
|
|
151
|
-
? selectedFeaturesFromArgv
|
|
152
|
-
: parseFeatureList(core.features);
|
|
153
|
-
|
|
154
|
-
if (backend === 'firebase') {
|
|
155
|
-
const firebaseAnswers = await prompts(
|
|
156
|
-
{
|
|
157
|
-
type: 'text',
|
|
158
|
-
name: 'firebaseProjectId',
|
|
159
|
-
message: t('prompt.firebase.projectId.enter'),
|
|
160
|
-
validate: (value) => (value && value.trim() ? true : t('prompt.firebase.projectId.required'))
|
|
161
|
-
},
|
|
162
|
-
getPromptOptions(t)
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
...core,
|
|
167
|
-
backend,
|
|
168
|
-
features,
|
|
169
|
-
...firebaseAnswers
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (backend === 'supabase') {
|
|
174
|
-
const supabaseAnswers = await prompts(
|
|
175
|
-
[
|
|
176
|
-
{
|
|
177
|
-
type: 'text',
|
|
178
|
-
name: 'supabaseUrl',
|
|
179
|
-
message: t('prompt.supabase.url.enter'),
|
|
180
|
-
validate: (value) => (value && value.trim() ? true : t('prompt.supabase.url.required'))
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
type: 'text',
|
|
184
|
-
name: 'supabaseAnonKey',
|
|
185
|
-
message: t('prompt.supabase.anonKey.enter'),
|
|
186
|
-
validate: (value) => (value && value.trim() ? true : t('prompt.supabase.anonKey.required'))
|
|
187
|
-
}
|
|
188
|
-
],
|
|
189
|
-
getPromptOptions(t)
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
...core,
|
|
194
|
-
backend,
|
|
195
|
-
features,
|
|
196
|
-
...supabaseAnswers
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
...core,
|
|
202
|
-
backend,
|
|
203
|
-
features
|
|
204
|
-
};
|
|
51
|
+
const value = await ui.text({
|
|
52
|
+
message: t('prompt.projectName.enter'),
|
|
53
|
+
initialValue: t('prompt.projectName.default'),
|
|
54
|
+
validate: (v) => (v && v.trim() ? undefined : t('prompt.projectName.required')),
|
|
55
|
+
onCancel: makeCancel(t),
|
|
56
|
+
});
|
|
57
|
+
return String(value).trim();
|
|
205
58
|
}
|
|
206
59
|
|
|
207
60
|
module.exports = {
|
|
@@ -209,5 +62,4 @@ module.exports = {
|
|
|
209
62
|
promptLicenseKey,
|
|
210
63
|
promptLanguage,
|
|
211
64
|
promptProjectName,
|
|
212
|
-
runSetupWizard
|
|
213
65
|
};
|
package/lib/utils/ui.js
CHANGED
|
@@ -48,6 +48,21 @@ async function text({ message, placeholder, initialValue, defaultValue, validate
|
|
|
48
48
|
return handleCancel(result, onCancel);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
async function password({ message, mask, validate, onCancel }) {
|
|
52
|
+
const result = await clack.password({
|
|
53
|
+
message,
|
|
54
|
+
mask,
|
|
55
|
+
validate: validate
|
|
56
|
+
? (value) => {
|
|
57
|
+
const out = validate(value);
|
|
58
|
+
if (out === true || out == null) return undefined;
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
: undefined,
|
|
62
|
+
});
|
|
63
|
+
return handleCancel(result, onCancel);
|
|
64
|
+
}
|
|
65
|
+
|
|
51
66
|
async function confirm({ message, initialValue = true, onCancel }) {
|
|
52
67
|
const result = await clack.confirm({ message, initialValue });
|
|
53
68
|
return handleCancel(result, onCancel);
|
|
@@ -63,13 +78,87 @@ function outro(message) { clack.outro(message); }
|
|
|
63
78
|
function note(message, title) { clack.note(message, title); }
|
|
64
79
|
function cancel(message) { clack.cancel(message); }
|
|
65
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Spinner integrated with Clack's vertical line (│).
|
|
83
|
+
* Returns: { start(msg), message(msg), stop(msg, code?) }
|
|
84
|
+
* code: 0 = success (✦), 1 = cancel (■), 2 = error (▲)
|
|
85
|
+
*/
|
|
66
86
|
function spinner() { return clack.spinner(); }
|
|
67
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Multi-step spinner: each .next(text) succeeds the previous step
|
|
90
|
+
* with the previous message, then starts a new step with `text`.
|
|
91
|
+
* Mimics the old ora-based makeProgressSpinner() but uses Clack's
|
|
92
|
+
* vertical-line aesthetic so the line stays connected.
|
|
93
|
+
*/
|
|
94
|
+
function makeStepper() {
|
|
95
|
+
let current = null;
|
|
96
|
+
let currentMsg = '';
|
|
97
|
+
return {
|
|
98
|
+
next(text) {
|
|
99
|
+
if (current) current.stop(currentMsg);
|
|
100
|
+
current = clack.spinner();
|
|
101
|
+
currentMsg = text;
|
|
102
|
+
current.start(text);
|
|
103
|
+
},
|
|
104
|
+
update(text) {
|
|
105
|
+
if (current) {
|
|
106
|
+
currentMsg = text;
|
|
107
|
+
current.message(text);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
succeed(text) {
|
|
111
|
+
if (current) {
|
|
112
|
+
current.stop(text || currentMsg);
|
|
113
|
+
current = null;
|
|
114
|
+
currentMsg = '';
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
fail(text) {
|
|
118
|
+
if (current) {
|
|
119
|
+
// .error() renders the red ▲ icon; .stop() defaults to green ✦ success.
|
|
120
|
+
current.error(text || currentMsg);
|
|
121
|
+
current = null;
|
|
122
|
+
currentMsg = '';
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
warn(text) {
|
|
126
|
+
// Clack has no "warn" terminal state — prefix with ⚠ and close as success.
|
|
127
|
+
if (current) {
|
|
128
|
+
current.stop(`⚠ ${text || currentMsg}`);
|
|
129
|
+
current = null;
|
|
130
|
+
currentMsg = '';
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
stop() {
|
|
134
|
+
if (current) {
|
|
135
|
+
current.stop(currentMsg);
|
|
136
|
+
current = null;
|
|
137
|
+
currentMsg = '';
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Long-running task with rolling log output (e.g. pub get, build_runner).
|
|
145
|
+
* Shows last N lines while running; collapses to a single ✦ on success.
|
|
146
|
+
* Options: { title, limit, retainLog }
|
|
147
|
+
*/
|
|
148
|
+
function taskLog(options) { return clack.taskLog(options); }
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Progress bar for operations with known total steps.
|
|
152
|
+
* Returns: { start(msg), advance(n, msg?), message(msg), stop(msg) }
|
|
153
|
+
*/
|
|
154
|
+
function progress(options) { return clack.progress(options); }
|
|
155
|
+
|
|
68
156
|
const log = clack.log;
|
|
69
157
|
|
|
70
158
|
module.exports = {
|
|
71
159
|
select,
|
|
72
160
|
text,
|
|
161
|
+
password,
|
|
73
162
|
confirm,
|
|
74
163
|
multiselect,
|
|
75
164
|
intro,
|
|
@@ -77,6 +166,9 @@ module.exports = {
|
|
|
77
166
|
note,
|
|
78
167
|
cancel,
|
|
79
168
|
spinner,
|
|
169
|
+
makeStepper,
|
|
170
|
+
taskLog,
|
|
171
|
+
progress,
|
|
80
172
|
log,
|
|
81
173
|
isCancel: clack.isCancel,
|
|
82
174
|
};
|
package/lib/utils/updates.js
CHANGED
|
@@ -4,8 +4,8 @@ const https = require('node:https');
|
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const { existsSync, readJsonSync, writeJsonSync, ensureDirSync } = require('fs-extra');
|
|
6
6
|
const kleur = require('kleur');
|
|
7
|
-
const prompts = require('prompts');
|
|
8
7
|
const pkg = require('../../package.json');
|
|
8
|
+
const ui = require('./ui');
|
|
9
9
|
const { CONFIG_PATH } = require('./license');
|
|
10
10
|
|
|
11
11
|
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 horas
|
|
@@ -106,19 +106,20 @@ async function warnIfOutdatedBeforeNew(t) {
|
|
|
106
106
|
if (!cache.latestVersion || !isNewer(cache.latestVersion, pkg.version)) return;
|
|
107
107
|
|
|
108
108
|
const hint = t ? t('new.outdated.hint') : 'projetos criados agora não terão as últimas melhorias.';
|
|
109
|
-
console.log('');
|
|
110
109
|
console.log(
|
|
110
|
+
'\n' +
|
|
111
111
|
kleur.bgYellow().black(' UPDATE ') + ' ' +
|
|
112
112
|
kleur.bold(`v${cache.latestVersion} disponível`) +
|
|
113
|
-
kleur.dim(` — ${hint}`)
|
|
113
|
+
kleur.dim(` — ${hint}`) +
|
|
114
|
+
'\n'
|
|
114
115
|
);
|
|
115
|
-
console.log('');
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
// Called BEFORE any ui.intro fires in kasy new, so this prompt runs as a
|
|
118
|
+
// standalone Clack confirm (no rail to break). On cancel we exit cleanly.
|
|
119
|
+
const upgrade = await ui.confirm({
|
|
120
120
|
message: t ? t('new.outdated.upgradeNow') : 'Atualizar antes de criar? (requer assinatura ativa)',
|
|
121
|
-
|
|
121
|
+
initialValue: false,
|
|
122
|
+
onCancel: () => process.exit(0),
|
|
122
123
|
});
|
|
123
124
|
|
|
124
125
|
if (upgrade) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kasy-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.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"
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@clack/prompts": "^1.4.0",
|
|
48
|
+
"boxen": "^8.0.1",
|
|
48
49
|
"commander": "^12.0.0",
|
|
49
50
|
"fs-extra": "^11.2.0",
|
|
50
51
|
"gradient-string": "^1.2.0",
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import 'package:flutter/foundation.dart' show kDebugMode;
|
|
2
1
|
import 'package:flutter/material.dart';
|
|
3
2
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
4
|
-
import 'package:go_router/go_router.dart';
|
|
5
3
|
import 'package:kasy_kit/components/kasy_app_bar.dart';
|
|
6
4
|
import 'package:kasy_kit/core/bottom_menu/bart_inner_paths.dart';
|
|
7
5
|
import 'package:kasy_kit/core/bottom_menu/kasy_bart_navigation.dart';
|
|
@@ -15,7 +13,6 @@ import 'package:kasy_kit/features/home/home_components_page.dart';
|
|
|
15
13
|
import 'package:kasy_kit/features/home/home_features_page.dart';
|
|
16
14
|
import 'package:kasy_kit/features/notifications/shared/att_permission.dart';
|
|
17
15
|
import 'package:kasy_kit/features/notifications/shared/notification_permission_bottom_sheet.dart';
|
|
18
|
-
import 'package:kasy_kit/features/settings/ui/components/admin/admin_routes.dart';
|
|
19
16
|
import 'package:kasy_kit/features/subscription/shared/maybeshow_premium.dart';
|
|
20
17
|
import 'package:kasy_kit/i18n/translations.g.dart';
|
|
21
18
|
|
|
@@ -118,22 +115,6 @@ class HomePage extends ConsumerWidget {
|
|
|
118
115
|
);
|
|
119
116
|
},
|
|
120
117
|
),
|
|
121
|
-
if (kDebugMode) ...[
|
|
122
|
-
const SizedBox(height: KasySpacing.md),
|
|
123
|
-
FilledButton.icon(
|
|
124
|
-
onPressed: () =>
|
|
125
|
-
context.push(adminRouteKeyboardTest),
|
|
126
|
-
icon: const Icon(Icons.keyboard, size: 18),
|
|
127
|
-
label: const Text('Keyboard Test'),
|
|
128
|
-
style: FilledButton.styleFrom(
|
|
129
|
-
backgroundColor: Colors.deepOrange,
|
|
130
|
-
minimumSize: const Size(double.infinity, 48),
|
|
131
|
-
shape: RoundedRectangleBorder(
|
|
132
|
-
borderRadius: BorderRadius.circular(12),
|
|
133
|
-
),
|
|
134
|
-
),
|
|
135
|
-
),
|
|
136
|
-
],
|
|
137
118
|
const SizedBox(height: KasySpacing.xl),
|
|
138
119
|
]),
|
|
139
120
|
),
|
|
@@ -7,7 +7,6 @@ import 'package:kasy_kit/features/subscription/ui/component/premium_page_factory
|
|
|
7
7
|
const String adminRoutePaywalls = '/admin/paywalls';
|
|
8
8
|
const String adminRouteHomeWidgets = '/admin/home-widgets';
|
|
9
9
|
const String adminRouteSendPush = '/admin/send-push';
|
|
10
|
-
const String adminRouteKeyboardTest = '/admin/keyboard-test';
|
|
11
10
|
|
|
12
11
|
String adminRoutePremiumPreview(String variant) => '/admin/premium/$variant';
|
|
13
12
|
|
|
@@ -14,7 +14,6 @@ import 'package:kasy_kit/features/authentication/ui/phone_auth_page.dart';
|
|
|
14
14
|
import 'package:kasy_kit/features/authentication/ui/recover_password_page.dart';
|
|
15
15
|
import 'package:kasy_kit/features/authentication/ui/signin_page.dart';
|
|
16
16
|
import 'package:kasy_kit/features/authentication/ui/signup_page.dart';
|
|
17
|
-
import 'package:kasy_kit/features/dev/keyboard_test_page.dart';
|
|
18
17
|
import 'package:kasy_kit/features/feedbacks/ui/feedback_page.dart';
|
|
19
18
|
import 'package:kasy_kit/features/llm_chat/llm_chat_page.dart';
|
|
20
19
|
import 'package:kasy_kit/features/local_reminder/ui/reminder_page.dart';
|
|
@@ -205,14 +204,6 @@ GoRouter generateRouter({
|
|
|
205
204
|
child: const SendPushNotificationPage(),
|
|
206
205
|
),
|
|
207
206
|
),
|
|
208
|
-
GoRoute(
|
|
209
|
-
name: 'keyboard_test',
|
|
210
|
-
path: adminRouteKeyboardTest,
|
|
211
|
-
pageBuilder: (context, state) => kasyTransitionPage(
|
|
212
|
-
key: state.pageKey,
|
|
213
|
-
child: const KeyboardTestPage(),
|
|
214
|
-
),
|
|
215
|
-
),
|
|
216
207
|
GoRoute(
|
|
217
208
|
name: '404',
|
|
218
209
|
path: '/404',
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import 'package:flutter/material.dart';
|
|
2
|
-
import 'package:kasy_kit/components/kasy_text_field.dart';
|
|
3
|
-
import 'package:kasy_kit/core/theme/theme.dart';
|
|
4
|
-
|
|
5
|
-
class KeyboardTestPage extends StatefulWidget {
|
|
6
|
-
const KeyboardTestPage({super.key});
|
|
7
|
-
|
|
8
|
-
@override
|
|
9
|
-
State<KeyboardTestPage> createState() => _KeyboardTestPageState();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
class _KeyboardTestPageState extends State<KeyboardTestPage> {
|
|
13
|
-
final _name = TextEditingController();
|
|
14
|
-
final _email = TextEditingController();
|
|
15
|
-
final _phone = TextEditingController();
|
|
16
|
-
final _password = TextEditingController();
|
|
17
|
-
final _bio = TextEditingController();
|
|
18
|
-
|
|
19
|
-
@override
|
|
20
|
-
void dispose() {
|
|
21
|
-
_name.dispose();
|
|
22
|
-
_email.dispose();
|
|
23
|
-
_phone.dispose();
|
|
24
|
-
_password.dispose();
|
|
25
|
-
_bio.dispose();
|
|
26
|
-
super.dispose();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@override
|
|
30
|
-
Widget build(BuildContext context) {
|
|
31
|
-
return Scaffold(
|
|
32
|
-
appBar: AppBar(title: const Text('Keyboard Test')),
|
|
33
|
-
body: ListView(
|
|
34
|
-
padding: EdgeInsets.fromLTRB(
|
|
35
|
-
KasySpacing.pageHorizontalGutter,
|
|
36
|
-
KasySpacing.md,
|
|
37
|
-
KasySpacing.pageHorizontalGutter,
|
|
38
|
-
MediaQuery.paddingOf(context).bottom + 40,
|
|
39
|
-
),
|
|
40
|
-
children: [
|
|
41
|
-
const Text(
|
|
42
|
-
'Tap between the fields — the keyboard should stay open without closing and reopening.',
|
|
43
|
-
style: TextStyle(fontSize: 13, color: Colors.grey),
|
|
44
|
-
),
|
|
45
|
-
const SizedBox(height: KasySpacing.lg),
|
|
46
|
-
KasyTextField(
|
|
47
|
-
controller: _name,
|
|
48
|
-
label: 'Name',
|
|
49
|
-
hint: 'Your full name',
|
|
50
|
-
textInputAction: TextInputAction.next,
|
|
51
|
-
textCapitalization: TextCapitalization.words,
|
|
52
|
-
),
|
|
53
|
-
const SizedBox(height: KasyTextField.adjacentFieldSpacing),
|
|
54
|
-
KasyTextField(
|
|
55
|
-
controller: _email,
|
|
56
|
-
label: 'Email',
|
|
57
|
-
hint: 'you@example.com',
|
|
58
|
-
contentType: KasyTextFieldContentType.email,
|
|
59
|
-
keyboardType: TextInputType.emailAddress,
|
|
60
|
-
textInputAction: TextInputAction.next,
|
|
61
|
-
),
|
|
62
|
-
const SizedBox(height: KasyTextField.adjacentFieldSpacing),
|
|
63
|
-
KasyTextField(
|
|
64
|
-
controller: _phone,
|
|
65
|
-
label: 'Phone',
|
|
66
|
-
hint: '+55 11 99999-9999',
|
|
67
|
-
contentType: KasyTextFieldContentType.phone,
|
|
68
|
-
keyboardType: TextInputType.phone,
|
|
69
|
-
textInputAction: TextInputAction.next,
|
|
70
|
-
),
|
|
71
|
-
const SizedBox(height: KasyTextField.adjacentFieldSpacing),
|
|
72
|
-
KasyTextField(
|
|
73
|
-
controller: _password,
|
|
74
|
-
label: 'Password',
|
|
75
|
-
hint: 'Min 8 characters',
|
|
76
|
-
contentType: KasyTextFieldContentType.password,
|
|
77
|
-
textInputAction: TextInputAction.next,
|
|
78
|
-
),
|
|
79
|
-
const SizedBox(height: KasyTextField.adjacentFieldSpacing),
|
|
80
|
-
KasyTextField(
|
|
81
|
-
controller: _bio,
|
|
82
|
-
label: 'Bio',
|
|
83
|
-
hint: 'Tell us about yourself',
|
|
84
|
-
textInputAction: TextInputAction.done,
|
|
85
|
-
minLines: 3,
|
|
86
|
-
maxLines: 5,
|
|
87
|
-
textCapitalization: TextCapitalization.sentences,
|
|
88
|
-
),
|
|
89
|
-
],
|
|
90
|
-
),
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
}
|