kasy-cli 1.36.1 → 1.37.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/lib/scaffold/CHANGELOG.json +14 -0
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +1 -1
- package/lib/scaffold/backends/patch-base-hashes.json +2 -2
- package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +3 -0
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +1 -1
- package/lib/scaffold/shared/generator-utils.js +8 -0
- package/package.json +1 -1
- package/templates/firebase/.firebase/hosting.YnVpbGQvd2Vi.cache +24 -22
- package/templates/firebase/firebase.json +1 -1
- package/templates/firebase/lib/components/kasy_sidebar.dart +29 -30
- package/templates/firebase/lib/components/kasy_tabs.dart +25 -3
- package/templates/firebase/lib/features/authentication/api/auth_web_support.dart +13 -0
- package/templates/firebase/lib/features/authentication/api/auth_web_support_web.dart +17 -0
- package/templates/firebase/lib/features/authentication/api/authentication_api.dart +3 -0
- package/templates/firebase/lib/features/authentication/ui/signin_page.dart +19 -5
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +58 -36
- package/templates/firebase/lib/features/settings/settings_page.dart +0 -1
- package/templates/firebase/lib/main.dart +7 -0
- package/templates/firebase/pubspec.yaml +2 -2
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
+
"1.37.0": {
|
|
3
|
+
"modules": {
|
|
4
|
+
"components": {
|
|
5
|
+
"pt": "Sidebar e abas mais responsivas: a sidebar ganhou um modo de colapso mais consistente (vira rail fino conforme a largura) e teve a busca não usada removida pra ficar mais enxuta; as abas (KasyTabs) agora se medem e se adaptam melhor em telas estreitas, sem cortar nem espremer.",
|
|
6
|
+
"en": "More responsive sidebar and tabs: the sidebar got a more consistent collapse mode (turns into a thin rail by width) and had its unused search removed for a leaner component; tabs (KasyTabs) now measure and adapt better on narrow screens, without clipping or squeezing.",
|
|
7
|
+
"es": "Sidebar y pestañas más responsivas: la sidebar tiene un modo de colapso más consistente (se vuelve un rail fino según el ancho) y se le quitó la búsqueda sin uso para un componente más limpio; las pestañas (KasyTabs) ahora se miden y adaptan mejor en pantallas estrechas, sin recortar ni apretar."
|
|
8
|
+
},
|
|
9
|
+
"web": {
|
|
10
|
+
"pt": "Web mais redonda: login social na web agora trata o fluxo por redirecionamento (redirect), útil quando o popup é bloqueado pelo navegador; e o Firebase Hosting foi ajustado pra publicar o build/web sem descartar arquivos ocultos necessários, deixando o deploy do site mais previsível.",
|
|
11
|
+
"en": "Smoother web: social login on web now handles the redirect flow, useful when the browser blocks the popup; and Firebase Hosting was tuned to publish build/web without dropping needed hidden files, making the site deploy more predictable.",
|
|
12
|
+
"es": "Web más pulida: el inicio de sesión social en web ahora maneja el flujo por redirección (redirect), útil cuando el navegador bloquea el popup; y Firebase Hosting se ajustó para publicar build/web sin descartar archivos ocultos necesarios, dejando el deploy del sitio más predecible."
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
2
16
|
"1.36.1": {
|
|
3
17
|
"modules": {
|
|
4
18
|
"onboarding": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"api/lib/features/ai_chat/api/ai_chat_message_entity.dart": "5aca4fdb5b1c38df04c9d377c6e6eeb6930ef85b2a32689fe6403e7cc7645373",
|
|
12
12
|
"api/lib/features/ai_chat/providers/ai_chat_notifier.dart": "f16ea3f3266e63a5591ceff9bc1900845f0b96e9bbbc5463ec6ab894f878eae3",
|
|
13
13
|
"api/lib/features/authentication/api/authentication_api_interface.dart": "ef9219237babd73e4b3ac148a24ebbbdf39ab98166d21f578c359074b528e5da",
|
|
14
|
-
"api/lib/features/authentication/api/authentication_api.dart": "
|
|
14
|
+
"api/lib/features/authentication/api/authentication_api.dart": "4ee563f9fd81b9738b71928edc146846e6226fb3b8a6974cf1dc3f3cd8813d4d",
|
|
15
15
|
"api/lib/features/authentication/repositories/authentication_repository.dart": "8e98ca0d39df3aac1dab159e6bdfe8d2bbb8e43931459984eb4ecfb676698ca2",
|
|
16
16
|
"api/lib/features/feedbacks/api/entities/feature_request_entity.dart": "e72c683c7321bf1f3c19fc1c6d54c6964bd99970e87c9ff31d8918efa93f0090",
|
|
17
17
|
"api/lib/features/feedbacks/api/entities/feature_vote_entity.dart": "a13ccfc493d8f5277beffc8e28f77ec322ab0c0a56358c3669353206b7c6756b",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"supabase/lib/features/ai_chat/api/ai_chat_conversation_entity.dart": "1c4e4223e802d7fe8e93d9e7a09fcc6be81a41792d722e8b7e697837cde4737f",
|
|
43
43
|
"supabase/lib/features/ai_chat/api/ai_chat_message_entity.dart": "5aca4fdb5b1c38df04c9d377c6e6eeb6930ef85b2a32689fe6403e7cc7645373",
|
|
44
44
|
"supabase/lib/features/ai_chat/providers/ai_chat_notifier.dart": "f16ea3f3266e63a5591ceff9bc1900845f0b96e9bbbc5463ec6ab894f878eae3",
|
|
45
|
-
"supabase/lib/features/authentication/api/authentication_api.dart": "
|
|
45
|
+
"supabase/lib/features/authentication/api/authentication_api.dart": "4ee563f9fd81b9738b71928edc146846e6226fb3b8a6974cf1dc3f3cd8813d4d",
|
|
46
46
|
"supabase/lib/features/authentication/repositories/authentication_repository.dart": "8e98ca0d39df3aac1dab159e6bdfe8d2bbb8e43931459984eb4ecfb676698ca2",
|
|
47
47
|
"supabase/lib/features/feedbacks/api/entities/feature_request_entity.dart": "e72c683c7321bf1f3c19fc1c6d54c6964bd99970e87c9ff31d8918efa93f0090",
|
|
48
48
|
"supabase/lib/features/feedbacks/api/entities/feature_vote_entity.dart": "a13ccfc493d8f5277beffc8e28f77ec322ab0c0a56358c3669353206b7c6756b",
|
package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart
CHANGED
|
@@ -67,6 +67,9 @@ class SupabaseAuthenticationApi implements AuthenticationApi {
|
|
|
67
67
|
/// — the caller stays in its loading state until the browser leaves.
|
|
68
68
|
/// See [isMobileWebBrowser].
|
|
69
69
|
Future<Credentials> _googleSignInWithRedirectWeb() async {
|
|
70
|
+
// Flag the login so startup lands on Home, not the stale tab URL the
|
|
71
|
+
// redirect returns to (linking is a no-op on Supabase, so only login here).
|
|
72
|
+
markWebRedirectLogin();
|
|
70
73
|
await fb_auth.FirebaseAuth.instance.signInWithRedirect(
|
|
71
74
|
fb_auth.GoogleAuthProvider(),
|
|
72
75
|
);
|
|
@@ -975,6 +975,8 @@ async function writeMainDart(projectDir, backend, modules, packageName, options
|
|
|
975
975
|
lines.push(`import 'package:${pkg}/firebase_options_dev.dart' as firebase_dev;`);
|
|
976
976
|
}
|
|
977
977
|
lines.push(`import 'package:${pkg}/i18n/translations.g.dart';`);
|
|
978
|
+
lines.push(`import 'package:${pkg}/features/authentication/api/auth_web_support.dart'`);
|
|
979
|
+
lines.push(` if (dart.library.js_interop) 'package:${pkg}/features/authentication/api/auth_web_support_web.dart';`);
|
|
978
980
|
lines.push(`import 'package:${pkg}/features/authentication/api/authentication_api.dart';`);
|
|
979
981
|
lines.push(`import 'package:${pkg}/features/notifications/api/local_notifier.dart';`);
|
|
980
982
|
if (withFirebase) {
|
|
@@ -1063,6 +1065,12 @@ async function writeMainDart(projectDir, backend, modules, packageName, options
|
|
|
1063
1065
|
lines.push('');
|
|
1064
1066
|
}
|
|
1065
1067
|
|
|
1068
|
+
lines.push(` // Web: if we just returned from a social-login redirect, reset the address`);
|
|
1069
|
+
lines.push(` // bar to '/' BEFORE the router reads the initial URL, so the app boots on Home`);
|
|
1070
|
+
lines.push(` // instead of the stale tab URL (e.g. /settings) the redirect returned to.`);
|
|
1071
|
+
lines.push(` resetUrlAfterRedirectLogin();`);
|
|
1072
|
+
lines.push('');
|
|
1073
|
+
|
|
1066
1074
|
if (withSentry) {
|
|
1067
1075
|
lines.push(` // initialize sentry for error reporting in production only`);
|
|
1068
1076
|
lines.push(` // run the app with Sentry for production environment`);
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
stripe_success.html,1781195415353,677b2fb430839866f32a33a751aae85b4c4819123be820f3f5b2f689b8eed00e
|
|
2
2
|
manifest.json,1779810782774,1b929ac7267ff738d05646106a0fac3591082b6f365051928a62518af30b3fd5
|
|
3
3
|
local_notifications.js,1772398909034,0b44b177c4b8a705ad1b0e3d7532537aad7b230e9472f93c820742b7548797b8
|
|
4
|
+
index.html,1781388256310,bb8142eb84e9e44049957c3edf86006e1ae5f194c397a8326ce505b68fdf58f4
|
|
5
|
+
flutter_bootstrap.js,1781388256056,25f49550b20f234b84194d9e067725abdc33590343251086f6c7d147325dfb41
|
|
4
6
|
flutter.js,1777393608000,b42e549a02f5c1428b0ad85dbdc663d400a6d2db10cb8aefcc0fddd592fd6ceb
|
|
5
7
|
firebase-messaging-sw.js,1772484605561,dba3c939f7c240e62f63d06408d5f70e45f1168d00df98726e6a7a20cbd3b0df
|
|
6
8
|
favicon.png,1779810782545,c967afd3c3d4dd165ef2e5cf3f203c3982dfff6a95881859ed163d9c38d75512
|
|
9
|
+
.last_build_id,1781388166126,fb2a0536078d7023bcc9d41eb14cc00d74532f9465fac7aa9b88dae42e4a249c
|
|
7
10
|
splash/img/light-4x.png,1780546027751,abf0e55345e532a4645fe14b01c2948b07c78df50cc6da23648837dfb6271c74
|
|
8
11
|
splash/img/light-3x.png,1780546027699,5f02c94032cb7053807ce89603b109f14ea2740eaa3145dc76c924cbe1d534a9
|
|
9
12
|
splash/img/light-2x.png,1780546027647,3c073b9da154ac8be530f8beefa3579246f223acd819e856a2db000fdabcb67e
|
|
@@ -31,16 +34,8 @@ canvaskit/canvaskit.js,1777393746000,df5aa63d90b0491908ad19d64fd18f29db1dc416548
|
|
|
31
34
|
canvaskit/chromium/canvaskit.wasm,1777393830000,44d29356b9e2c903751a8aa583716275e165d1fb56eea46963c95c7c43f7ca34
|
|
32
35
|
canvaskit/chromium/canvaskit.js.symbols,1777393830000,60722421e56edacbeb674cc3ef0f6152969067e74b51a960c80cd36e75b75a54
|
|
33
36
|
canvaskit/chromium/canvaskit.js,1777393830000,10ea3badcf26e29aba851699c03e09bc35fea65cdc440a035bac263d6d02d665
|
|
37
|
+
assets/.env,1781370223435,777e85eb4de226724b28db8664404d0b73ae37f63fd66c955ac90c89cd491e7e
|
|
34
38
|
assets/packages/mixpanel_flutter/assets/mixpanel.js,1779806094332,1915455df317dd1a9f42c6f3ea5bbe3ee357614f89d994f157a2e821f37956c3
|
|
35
|
-
assets/packages/lucide_icons_flutter/assets/lucide.ttf,1779806149220,bfca5b519d438d7194550cbc60a4e7c10f53a36ac96d48c7e17503a45b6b7b0b
|
|
36
|
-
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w600.ttf,1779806149210,cf2218879264e413687602c192e43b86000404ff85e2bca933d9a67020299ed4
|
|
37
|
-
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w500.ttf,1779806149217,587ce31612aa88e6c019210a18625531557eda111f5f8e838a298277efc9e308
|
|
38
|
-
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w400.ttf,1779806149212,644f7ccdf1b74bdb898210c4171327a25d7476690c7ade2d3082dd473bc24fe1
|
|
39
|
-
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w300.ttf,1779806149214,5dd834d2d84e5a1524e027fa21d87c6e8aaaf6421f201be2ec61e57c0b7bce72
|
|
40
|
-
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w200.ttf,1779806149208,d929819511975bf5030c0f359e8c16d21ad14b20ac24d5f2ff238462114b1d9b
|
|
41
|
-
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w100.ttf,1779806149216,1e9e6ea39a38b874c40242d02533adac9dd3d09c8119dae55458e65c6c3c16e6
|
|
42
|
-
assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1779806141382,010d647b6cab4e1a9b4398957353f43589903ce8dc760d05d894944b664e29bd
|
|
43
|
-
assets/fonts/MaterialIcons-Regular.otf,1662569016000,3a631b0761788d7cf2a71e8732a5b31288d6e7aa996c671c70d53a251bbd3848
|
|
44
39
|
assets/assets/images/splash_logo_light_android12.png,1779867497377,da2caee4f16230230dbb04d84913f0a9df2eae6bcb8f9d1ce83ed556cee6d094
|
|
45
40
|
assets/assets/images/splash_logo_light.png,1779867497286,643a1f45d6ef9458c4c0eed9f4889cc2adbdf1db616913215be66673a05a0066
|
|
46
41
|
assets/assets/images/splash_logo_dark_android12.png,1779867497464,b237d9f9035d419525a10b32889c30d44c706d62990b30bdc6bef911fe9352d6
|
|
@@ -58,16 +53,23 @@ assets/assets/icons/google_play_games.png,1772398653083,533c1d87d7be8690ab173ef4
|
|
|
58
53
|
assets/assets/icons/google.png,1772398653083,f423e7e7be1e06008d45617d07f095f04da7fdcab9a56523f9e0633828e464e0
|
|
59
54
|
assets/assets/icons/facebook.png,1772398653083,79ac67e449c1db63319d43329ca91f682b4e0bc6a0883b0dfb2a849e13ae6eb6
|
|
60
55
|
assets/assets/icons/apple.png,1772398653083,2a737d8801ca81452b2978bb2e1ac72136acf1a4c338f2de2f77b65e9f6de1fa
|
|
61
|
-
version.json,
|
|
62
|
-
flutter_service_worker.js,
|
|
63
|
-
assets/
|
|
64
|
-
assets/AssetManifest.bin,
|
|
65
|
-
assets/
|
|
66
|
-
assets/shaders/
|
|
67
|
-
assets/shaders/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
main.dart.
|
|
71
|
-
main.dart.
|
|
72
|
-
assets/
|
|
73
|
-
|
|
56
|
+
version.json,1781388383201,55f3aab1f3d71610bf791902e59e92cd77db8963f8290193f573c4172fa6fd32
|
|
57
|
+
flutter_service_worker.js,1781388386040,baeeaf9f4b8e6f40d3b0549429ceb70ca01f120db6a7ef2f60d5809233dc2205
|
|
58
|
+
assets/AssetManifest.bin,1781388383326,78bccb08a36307a400711a1d7e6868bd5f89a24e63439fb5b6747660323e4475
|
|
59
|
+
assets/AssetManifest.bin.json,1781388383326,50a11b51c3fc4cbed1b5ec3a4f466c6a6b75c481d08d4782c8191f99bdbdba9c
|
|
60
|
+
assets/FontManifest.json,1781388383326,4d84ab517c27984d36f9a3c8be6f2a72788c0c3985c1d5874297fef0a53407ca
|
|
61
|
+
assets/shaders/ink_sparkle.frag,1781388383423,1c8e222328206d1e06754f76fb53947aad38d62180aafad5298a3c6f510b173d
|
|
62
|
+
assets/shaders/stretch_effect.frag,1781388383423,1a7d4ac2be40cf0a459dfb390ef08bcd740f37913ffdee8de3c2ea836a18410e
|
|
63
|
+
assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1781388385468,12bc6bf55aad4657f62747b6c1c9b5c120a594ed3540db21729b6db3c847340d
|
|
64
|
+
assets/fonts/MaterialIcons-Regular.otf,1781388385471,89da9939a222284d9e14e9757b4dbdb75965434cb84b0f2969495153f3a018c9
|
|
65
|
+
main.dart.js_3.part.js,1781388360492,6f52d11c8e140736cd046ce6b541ad8d8bf20e6ef709f39d8ef4eb3dddbece8b
|
|
66
|
+
main.dart.js_1.part.js,1781388360481,42136799a40b5acb14b5acf0a8cef39bbef13b4dc99ce808a7bc8a1f8cdd8b09
|
|
67
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w300.ttf,1781388385457,aebf5e4f6b1e6de1e61a866f813aa5b08efb4cdd108cc2831d40aad71398c88f
|
|
68
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w400.ttf,1781388385463,edcd01ee13f6ec2285fd18863c0ce0efb0876b290fb427ad5c052f753b3210c8
|
|
69
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w600.ttf,1781388385468,008f1e974c9ff4e8d40d16221226e0d34d2298a7feb2bdea8c134a9333929c9c
|
|
70
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w100.ttf,1781388385446,c16d95412fe2600bda1f53c2b8dc4bd82496f60f7c5a6ecfa180b0f866128712
|
|
71
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w500.ttf,1781388385464,3f68f958d0fa13077968a6767339c38ede8705bcadd0382078db6ec4bac96fa0
|
|
72
|
+
assets/packages/lucide_icons_flutter/assets/lucide.ttf,1781388385448,9b000e47cddf9ada4a6b85fc9577fef7ed2ee261c9279e9a44e4753da28f83d3
|
|
73
|
+
assets/packages/lucide_icons_flutter/assets/build_font/LucideVariable-w200.ttf,1781388385451,1f798186cfc3e2fcd54b15aa7b8695bc7c29b781c5058029a8d80ecdfa615904
|
|
74
|
+
assets/NOTICES,1781388383326,a18efc42c5ed99b56481537d0f229ddd3add671c548a893aaf8766f30c854158
|
|
75
|
+
main.dart.js,1781388361400,b625f1b346dc2f1475ee99ebb1d698654e74183d737c91e1bbac020869096a84
|
|
@@ -192,6 +192,22 @@ const _NavItem _kHelpItem = _NavItem(
|
|
|
192
192
|
/// Controls which edge receives the content-facing hairline + shadow nudge.
|
|
193
193
|
enum KasySidebarSide { left, right }
|
|
194
194
|
|
|
195
|
+
/// How the rail picks its initial width — the standard SaaS sidebar presets.
|
|
196
|
+
enum KasySidebarCollapseMode {
|
|
197
|
+
/// Adapts to the viewport: wide on desktop, auto-collapses to a thin icon
|
|
198
|
+
/// rail on tablet and mobile. The toggle overrides it on any breakpoint. This
|
|
199
|
+
/// is the default and what a typical app navigation uses.
|
|
200
|
+
responsive,
|
|
201
|
+
|
|
202
|
+
/// Starts wide on every breakpoint (the "pinned" sidebar); the user collapses
|
|
203
|
+
/// it to the thin rail via the toggle. Stays wide on mobile too.
|
|
204
|
+
expanded,
|
|
205
|
+
|
|
206
|
+
/// Starts as the thin icon rail (with hover tooltips); the user expands it via
|
|
207
|
+
/// the toggle.
|
|
208
|
+
collapsed,
|
|
209
|
+
}
|
|
210
|
+
|
|
195
211
|
/// A SaaS-style sidebar modelled on the HeroUI Figma kit: brand logo + panel
|
|
196
212
|
/// toggle, a workspace selector, a segmented control, a navigable list with an
|
|
197
213
|
/// active pill, and a pinned ⌘K search row. Collapses to an icon rail (with
|
|
@@ -203,9 +219,8 @@ class KasySidebar extends StatefulWidget {
|
|
|
203
219
|
super.key,
|
|
204
220
|
this.onSettingsTap,
|
|
205
221
|
this.onLogout,
|
|
206
|
-
this.
|
|
222
|
+
this.collapseMode = KasySidebarCollapseMode.responsive,
|
|
207
223
|
this.isDrawer = false,
|
|
208
|
-
this.showSearch = false,
|
|
209
224
|
this.side = KasySidebarSide.left,
|
|
210
225
|
this.routes,
|
|
211
226
|
this.onTapItem,
|
|
@@ -250,19 +265,15 @@ class KasySidebar extends StatefulWidget {
|
|
|
250
265
|
/// (confirm dialog + sign-out). When null, the Logout row does nothing real.
|
|
251
266
|
final VoidCallback? onLogout;
|
|
252
267
|
|
|
253
|
-
///
|
|
254
|
-
|
|
268
|
+
/// How the rail picks its initial width (responsive / pinned-expanded /
|
|
269
|
+
/// collapsed). See [KasySidebarCollapseMode].
|
|
270
|
+
final KasySidebarCollapseMode collapseMode;
|
|
255
271
|
|
|
256
272
|
/// Present the rail as a slide-in drawer (the mobile pattern): it always opens
|
|
257
273
|
/// wide and hides the collapse toggle, regardless of viewport width. Use when
|
|
258
274
|
/// opening the sidebar from an app-bar menu button on a phone.
|
|
259
275
|
final bool isDrawer;
|
|
260
276
|
|
|
261
|
-
/// Whether to pin a ⌘K search row above the footer in connected mode. Opt-in
|
|
262
|
-
/// (off by default) so the live app navigation stays lean; flip it on when the
|
|
263
|
-
/// sidebar should double as a command/search entry point.
|
|
264
|
-
final bool showSearch;
|
|
265
|
-
|
|
266
277
|
/// The screen edge this sidebar is anchored to.
|
|
267
278
|
final KasySidebarSide side;
|
|
268
279
|
|
|
@@ -302,14 +313,6 @@ class _KasySidebarState extends State<KasySidebar> {
|
|
|
302
313
|
/// Viewport width below which the sidebar auto-collapses (tablet breakpoint).
|
|
303
314
|
static const double _kBreakpoint = 1024.0;
|
|
304
315
|
|
|
305
|
-
/// Below this (mobile), the rail hides its collapse toggle: on a phone the
|
|
306
|
-
/// thin-vs-wide choice is a fixed pre-configuration (set via [isDrawer] for a
|
|
307
|
-
/// wide drawer, or the collapsed default for a thin rail), not a live toggle.
|
|
308
|
-
/// Width does NOT force wide here — a collapsed config stays thin on mobile.
|
|
309
|
-
static const double _kMobileBreakpoint = 768.0;
|
|
310
|
-
|
|
311
|
-
bool _isMobile(BuildContext context) =>
|
|
312
|
-
MediaQuery.sizeOf(context).width < _kMobileBreakpoint;
|
|
313
316
|
|
|
314
317
|
/// True when wired to Bart's navigation (real, tappable screens).
|
|
315
318
|
bool get _connected =>
|
|
@@ -321,7 +324,11 @@ class _KasySidebarState extends State<KasySidebar> {
|
|
|
321
324
|
@override
|
|
322
325
|
void initState() {
|
|
323
326
|
super.initState();
|
|
324
|
-
_collapsePreference = widget.
|
|
327
|
+
_collapsePreference = switch (widget.collapseMode) {
|
|
328
|
+
KasySidebarCollapseMode.responsive => null,
|
|
329
|
+
KasySidebarCollapseMode.expanded => false,
|
|
330
|
+
KasySidebarCollapseMode.collapsed => true,
|
|
331
|
+
};
|
|
325
332
|
// Connected mode follows Bart's currentItem (empty highlight here); the
|
|
326
333
|
// showcase defaults to the active layer from the Figma reference.
|
|
327
334
|
_activeItemId = _connected ? '' : 'object2';
|
|
@@ -577,15 +584,6 @@ class _KasySidebarState extends State<KasySidebar> {
|
|
|
577
584
|
child: Column(
|
|
578
585
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
579
586
|
children: [
|
|
580
|
-
if (widget.showSearch)
|
|
581
|
-
_buildItemRow(
|
|
582
|
-
c,
|
|
583
|
-
icon: KasyIcons.search,
|
|
584
|
-
label: 'Search',
|
|
585
|
-
isActive: false,
|
|
586
|
-
onTap: () {},
|
|
587
|
-
trailing: [_buildKbd(c)],
|
|
588
|
-
),
|
|
589
587
|
_buildNavItem(context, _kHelpItem, c),
|
|
590
588
|
_buildItemRow(
|
|
591
589
|
c,
|
|
@@ -619,9 +617,10 @@ class _KasySidebarState extends State<KasySidebar> {
|
|
|
619
617
|
MediaQuery.sizeOf(context).width >= _kBreakpoint
|
|
620
618
|
? _kTopBandHeight
|
|
621
619
|
: kasyAppBarBodyTopOverlap(context);
|
|
622
|
-
//
|
|
623
|
-
//
|
|
624
|
-
|
|
620
|
+
// The collapse toggle is available on every breakpoint so any config can be
|
|
621
|
+
// switched thin↔wide — except a drawer, which is a dismissible overlay you
|
|
622
|
+
// close whole rather than collapse in place.
|
|
623
|
+
final bool showToggle = !widget.isDrawer;
|
|
625
624
|
final bool anchoredLeft = widget.side == KasySidebarSide.left;
|
|
626
625
|
// Brand wordmark — same artwork as the splash screen.
|
|
627
626
|
final Widget logo = Image.asset(
|
|
@@ -125,6 +125,14 @@ class _KasyTabsState extends State<KasyTabs> {
|
|
|
125
125
|
// Whether we have a valid measurement yet.
|
|
126
126
|
bool _measured = false;
|
|
127
127
|
|
|
128
|
+
// Last layout width seen. The pill/underline geometry is measured from the
|
|
129
|
+
// laid-out tab boxes, so it must be recomputed whenever the bar's own width
|
|
130
|
+
// changes — e.g. when a collapsing sidebar animates the tabs open, the first
|
|
131
|
+
// measure happens at the narrow start width and would otherwise stay stuck in
|
|
132
|
+
// the corner until the next selection. Re-measuring per resize lets the
|
|
133
|
+
// indicator track the animation smoothly.
|
|
134
|
+
double? _lastLayoutWidth;
|
|
135
|
+
|
|
128
136
|
@override
|
|
129
137
|
void initState() {
|
|
130
138
|
super.initState();
|
|
@@ -241,9 +249,23 @@ class _KasyTabsState extends State<KasyTabs> {
|
|
|
241
249
|
Widget build(BuildContext context) {
|
|
242
250
|
if (widget.items.isEmpty) return const SizedBox.shrink();
|
|
243
251
|
|
|
244
|
-
return
|
|
245
|
-
|
|
246
|
-
|
|
252
|
+
return LayoutBuilder(
|
|
253
|
+
builder: (context, constraints) {
|
|
254
|
+
final double width = constraints.maxWidth;
|
|
255
|
+
// Re-measure on every width change (e.g. a sidebar animating open) so
|
|
256
|
+
// the indicator follows instead of sticking at its first-frame spot.
|
|
257
|
+
if (_lastLayoutWidth == null ||
|
|
258
|
+
(_lastLayoutWidth! - width).abs() > 0.5) {
|
|
259
|
+
_lastLayoutWidth = width;
|
|
260
|
+
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
261
|
+
if (mounted) _measure();
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
return widget.variant == KasyTabsVariant.primary
|
|
265
|
+
? _buildPrimary(context)
|
|
266
|
+
: _buildSecondary(context);
|
|
267
|
+
},
|
|
268
|
+
);
|
|
247
269
|
}
|
|
248
270
|
|
|
249
271
|
// ── Primary (pill indicator) ───────────────────────────────────────────────
|
|
@@ -10,3 +10,16 @@
|
|
|
10
10
|
/// Non-web stub: always false. The real implementation lives in
|
|
11
11
|
/// `auth_web_support_web.dart` and is selected via conditional import on web.
|
|
12
12
|
bool isMobileWebBrowser() => false;
|
|
13
|
+
|
|
14
|
+
/// Marks that a full-page redirect *sign-in* (not provider linking) is starting.
|
|
15
|
+
/// Persisted across the redirect's page reload so [resetUrlAfterRedirectLogin]
|
|
16
|
+
/// can tell, at startup, that we returned from a login (and must land on Home)
|
|
17
|
+
/// rather than from a link (which stays on the originating screen, e.g. Settings).
|
|
18
|
+
/// Non-web stub: no-op.
|
|
19
|
+
void markWebRedirectLogin() {}
|
|
20
|
+
|
|
21
|
+
/// If we just returned from a redirect sign-in (see [markWebRedirectLogin]),
|
|
22
|
+
/// clear the marker and reset the address bar to `/` so the app boots on Home
|
|
23
|
+
/// instead of whatever stale tab URL the redirect returned to. MUST run before
|
|
24
|
+
/// the router reads the initial URL. Non-web stub: no-op.
|
|
25
|
+
void resetUrlAfterRedirectLogin() {}
|
|
@@ -23,3 +23,20 @@ bool isMobileWebBrowser() {
|
|
|
23
23
|
// iPadOS 13+ masquerades as desktop Safari but still exposes touch points.
|
|
24
24
|
return ua.contains('macintosh') && web.window.navigator.maxTouchPoints > 1;
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
/// localStorage key flagging that a redirect *sign-in* is in flight. localStorage
|
|
28
|
+
/// (not in-memory) because the redirect reloads the whole page.
|
|
29
|
+
const String _redirectLoginKey = 'kasy_redirect_login';
|
|
30
|
+
|
|
31
|
+
void markWebRedirectLogin() {
|
|
32
|
+
web.window.localStorage.setItem(_redirectLoginKey, '1');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
void resetUrlAfterRedirectLogin() {
|
|
36
|
+
if (web.window.localStorage.getItem(_redirectLoginKey) == null) return;
|
|
37
|
+
web.window.localStorage.removeItem(_redirectLoginKey);
|
|
38
|
+
// Absolute path: land on Home, dropping the stale tab path (e.g. /settings)
|
|
39
|
+
// the redirect returned to. replaceState (not pushState) so no bogus history
|
|
40
|
+
// entry is left behind.
|
|
41
|
+
web.window.history.replaceState(null, '', '/');
|
|
42
|
+
}
|
|
@@ -209,6 +209,9 @@ class FirebaseAuthenticationApi implements AuthenticationApi {
|
|
|
209
209
|
/// we sign in. The linked/signed-in user is materialised at startup by
|
|
210
210
|
/// [getRedirectResult].
|
|
211
211
|
Future<Credentials> _signInWithRedirectWeb(AuthProvider provider) async {
|
|
212
|
+
// Flag the login so startup lands on Home, not the stale tab URL the
|
|
213
|
+
// redirect returns to (linking does NOT flag — it stays put, e.g. Settings).
|
|
214
|
+
markWebRedirectLogin();
|
|
212
215
|
final current = _auth.currentUser;
|
|
213
216
|
if (current != null && current.isAnonymous) {
|
|
214
217
|
await current.linkWithRedirect(provider);
|
|
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|
|
5
5
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
6
6
|
import 'package:go_router/go_router.dart';
|
|
7
7
|
import 'package:kasy_kit/components/components.dart';
|
|
8
|
+
import 'package:kasy_kit/core/bottom_menu/web_url.dart';
|
|
8
9
|
import 'package:kasy_kit/core/config/features.dart';
|
|
9
10
|
import 'package:kasy_kit/core/data/models/user.dart';
|
|
10
11
|
import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
|
|
@@ -41,6 +42,14 @@ class SigninPage extends ConsumerWidget {
|
|
|
41
42
|
body: SizedBox.shrink(),
|
|
42
43
|
);
|
|
43
44
|
}
|
|
45
|
+
// Web: the sign-in screen is reached via the router redirect (e.g. after a
|
|
46
|
+
// logout), where Bart may have left a stale tab path in the address bar
|
|
47
|
+
// (it writes tab URLs directly via pushState). Pin the URL to '/signin' so
|
|
48
|
+
// it matches the screen actually shown. No-op on native.
|
|
49
|
+
if (kIsWeb) {
|
|
50
|
+
WidgetsBinding.instance
|
|
51
|
+
.addPostFrameCallback((_) => syncBrowserUrl('/signin'));
|
|
52
|
+
}
|
|
44
53
|
final state = ref.watch(signinStateProvider);
|
|
45
54
|
final isSending = state is SigninStateSending;
|
|
46
55
|
final showBack = shouldShowAuthBackButton(context);
|
|
@@ -238,9 +247,9 @@ class _SignupPrompt extends StatelessWidget {
|
|
|
238
247
|
}
|
|
239
248
|
}
|
|
240
249
|
|
|
241
|
-
/// "Continue as guest" — creates the anonymous account on demand and
|
|
242
|
-
///
|
|
243
|
-
///
|
|
250
|
+
/// "Continue as guest" — creates the anonymous account on demand and navigates
|
|
251
|
+
/// into the app. Shown only on native in anonymous mode (see the build
|
|
252
|
+
/// condition in [SigninPage]).
|
|
244
253
|
class _GuestContinueButton extends ConsumerStatefulWidget {
|
|
245
254
|
const _GuestContinueButton();
|
|
246
255
|
|
|
@@ -256,8 +265,13 @@ class _GuestContinueButtonState extends ConsumerState<_GuestContinueButton> {
|
|
|
256
265
|
setState(() => _loading = true);
|
|
257
266
|
try {
|
|
258
267
|
await ref.read(userStateNotifierProvider.notifier).continueAsGuest();
|
|
259
|
-
|
|
260
|
-
//
|
|
268
|
+
if (!mounted) return;
|
|
269
|
+
// Navigate explicitly: the user is now an anonymous guest (has an id but
|
|
270
|
+
// is NOT an AuthenticatedUserData), and the router redirect intentionally
|
|
271
|
+
// only bounces *authenticated* users off the auth routes — so a guest can
|
|
272
|
+
// still open sign-in to upgrade. That means it won't carry us home on its
|
|
273
|
+
// own here; we do it ourselves.
|
|
274
|
+
context.go('/');
|
|
261
275
|
} catch (_) {
|
|
262
276
|
if (!mounted) return;
|
|
263
277
|
setState(() => _loading = false);
|
|
@@ -559,16 +559,16 @@ ComponentPreviewDefinition? getComponentPreviewDefinition(
|
|
|
559
559
|
builder: _buildSidebarAppNav,
|
|
560
560
|
),
|
|
561
561
|
ComponentPreviewVariant(
|
|
562
|
-
label: '
|
|
563
|
-
builder:
|
|
562
|
+
label: 'Mobile drawer',
|
|
563
|
+
builder: _buildSidebarMobileDrawer,
|
|
564
564
|
),
|
|
565
565
|
ComponentPreviewVariant(
|
|
566
566
|
label: 'Collapsed rail',
|
|
567
567
|
builder: _buildSidebarCollapsed,
|
|
568
568
|
),
|
|
569
569
|
ComponentPreviewVariant(
|
|
570
|
-
label: '
|
|
571
|
-
builder:
|
|
570
|
+
label: 'Pinned (always wide)',
|
|
571
|
+
builder: _buildSidebarPinned,
|
|
572
572
|
),
|
|
573
573
|
ComponentPreviewVariant(
|
|
574
574
|
label: 'Workspace showcase',
|
|
@@ -629,56 +629,70 @@ ComponentPreviewDefinition? getComponentPreviewDefinition(
|
|
|
629
629
|
// Sidebar — interactive preview
|
|
630
630
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
631
631
|
|
|
632
|
-
// The live Home configuration (real pages, highlight-only nav). The default
|
|
633
|
-
//
|
|
634
|
-
Widget _buildSidebarAppNav(BuildContext context) => const _SidebarPreview(
|
|
632
|
+
// The live Home configuration (real pages, highlight-only nav). The default —
|
|
633
|
+
// adapts to the breakpoint, which is what actually ships in the app.
|
|
634
|
+
Widget _buildSidebarAppNav(BuildContext context) => const _SidebarPreview(
|
|
635
|
+
caption: 'The default app navigation, wired to the live Home pages. '
|
|
636
|
+
'Adapts to the breakpoint: wide on desktop, a thin icon rail on '
|
|
637
|
+
'tablet and mobile — the toggle expands it on any screen.',
|
|
638
|
+
);
|
|
635
639
|
|
|
636
|
-
//
|
|
637
|
-
|
|
638
|
-
|
|
640
|
+
// The mobile pattern: always opens wide as a slide-in drawer, no collapse
|
|
641
|
+
// toggle (the sheet you open from an app-bar menu button on a phone).
|
|
642
|
+
Widget _buildSidebarMobileDrawer(BuildContext context) => const _SidebarPreview(
|
|
643
|
+
isDrawer: true,
|
|
644
|
+
caption: 'The phone pattern: always opens wide as a slide-in drawer, with '
|
|
645
|
+
'the collapse toggle hidden. This is what an app-bar menu button opens.',
|
|
646
|
+
);
|
|
639
647
|
|
|
640
|
-
//
|
|
641
|
-
Widget _buildSidebarCollapsed(BuildContext context) =>
|
|
642
|
-
|
|
648
|
+
// Starts as the narrow icon rail (tooltips on hover), at any breakpoint.
|
|
649
|
+
Widget _buildSidebarCollapsed(BuildContext context) => const _SidebarPreview(
|
|
650
|
+
collapseMode: KasySidebarCollapseMode.collapsed,
|
|
651
|
+
caption: 'Starts collapsed to the thin icon rail (labels show as hover '
|
|
652
|
+
'tooltips). Expand it with the toggle on any breakpoint.',
|
|
653
|
+
);
|
|
643
654
|
|
|
644
|
-
//
|
|
645
|
-
Widget
|
|
646
|
-
|
|
655
|
+
// Pinned wide on every breakpoint; the user collapses it themselves.
|
|
656
|
+
Widget _buildSidebarPinned(BuildContext context) => const _SidebarPreview(
|
|
657
|
+
collapseMode: KasySidebarCollapseMode.expanded,
|
|
658
|
+
caption: 'Pinned wide on every breakpoint instead of auto-collapsing; the '
|
|
659
|
+
'user narrows it with the toggle when they want the room.',
|
|
660
|
+
);
|
|
647
661
|
|
|
648
|
-
// The rich SaaS showcase (workspace selector, KasyTabs segment,
|
|
649
|
-
Widget _buildSidebarShowcase(BuildContext context) =>
|
|
650
|
-
|
|
662
|
+
// The rich SaaS showcase (workspace selector, KasyTabs segment, ⌘K search).
|
|
663
|
+
Widget _buildSidebarShowcase(BuildContext context) => const _SidebarPreview(
|
|
664
|
+
showcase: true,
|
|
665
|
+
caption: 'The rich SaaS layout: workspace switcher, a KasyTabs segment '
|
|
666
|
+
'(Layers / Assets) and a pinned ⌘K search row.',
|
|
667
|
+
);
|
|
651
668
|
|
|
652
669
|
/// Launches a [KasySidebar] as a full-height drawer (left + right buttons) so
|
|
653
|
-
/// the full-bleed chrome can be inspected without crushing the preview card.
|
|
670
|
+
/// the full-bleed chrome can be inspected without crushing the preview card. A
|
|
671
|
+
/// short [caption] (Kasy typography) explains what each configuration is.
|
|
654
672
|
/// Connected variants mirror the live Home; [showcase] swaps to the HeroUI
|
|
655
673
|
/// workspace demo.
|
|
656
674
|
class _SidebarPreview extends StatelessWidget {
|
|
657
675
|
const _SidebarPreview({
|
|
676
|
+
required this.caption,
|
|
658
677
|
this.showcase = false,
|
|
659
|
-
this.
|
|
678
|
+
this.collapseMode = KasySidebarCollapseMode.responsive,
|
|
660
679
|
this.isDrawer = false,
|
|
661
|
-
this.showSearch = false,
|
|
662
680
|
});
|
|
663
681
|
|
|
682
|
+
final String caption;
|
|
664
683
|
final bool showcase;
|
|
665
|
-
final
|
|
684
|
+
final KasySidebarCollapseMode collapseMode;
|
|
666
685
|
final bool isDrawer;
|
|
667
|
-
final bool showSearch;
|
|
668
686
|
|
|
669
687
|
Widget _sidebar({required bool fromEnd}) {
|
|
670
688
|
final KasySidebarSide side =
|
|
671
689
|
fromEnd ? KasySidebarSide.right : KasySidebarSide.left;
|
|
672
690
|
if (showcase) {
|
|
673
|
-
return KasySidebar(
|
|
674
|
-
initiallyCollapsed: initiallyCollapsed,
|
|
675
|
-
side: side,
|
|
676
|
-
);
|
|
691
|
+
return KasySidebar(collapseMode: collapseMode, side: side);
|
|
677
692
|
}
|
|
678
693
|
return _DemoSidebar(
|
|
679
694
|
isDrawer: isDrawer,
|
|
680
|
-
|
|
681
|
-
showSearch: showSearch,
|
|
695
|
+
collapseMode: collapseMode,
|
|
682
696
|
side: side,
|
|
683
697
|
);
|
|
684
698
|
}
|
|
@@ -696,6 +710,17 @@ class _SidebarPreview extends StatelessWidget {
|
|
|
696
710
|
mainAxisSize: MainAxisSize.min,
|
|
697
711
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
698
712
|
children: [
|
|
713
|
+
Padding(
|
|
714
|
+
padding: const EdgeInsets.symmetric(horizontal: KasySpacing.md),
|
|
715
|
+
child: Text(
|
|
716
|
+
caption,
|
|
717
|
+
textAlign: TextAlign.center,
|
|
718
|
+
style: context.textTheme.bodySmall?.copyWith(
|
|
719
|
+
color: context.colors.muted,
|
|
720
|
+
),
|
|
721
|
+
),
|
|
722
|
+
),
|
|
723
|
+
const SizedBox(height: KasySpacing.md),
|
|
699
724
|
Padding(
|
|
700
725
|
padding: const EdgeInsets.symmetric(horizontal: KasySpacing.md),
|
|
701
726
|
child: _previewIntrinsicLaunch(
|
|
@@ -728,14 +753,12 @@ class _SidebarPreview extends StatelessWidget {
|
|
|
728
753
|
class _DemoSidebar extends StatefulWidget {
|
|
729
754
|
const _DemoSidebar({
|
|
730
755
|
this.isDrawer = false,
|
|
731
|
-
this.
|
|
732
|
-
this.showSearch = false,
|
|
756
|
+
this.collapseMode = KasySidebarCollapseMode.responsive,
|
|
733
757
|
this.side = KasySidebarSide.left,
|
|
734
758
|
});
|
|
735
759
|
|
|
736
760
|
final bool isDrawer;
|
|
737
|
-
final
|
|
738
|
-
final bool showSearch;
|
|
761
|
+
final KasySidebarCollapseMode collapseMode;
|
|
739
762
|
final KasySidebarSide side;
|
|
740
763
|
|
|
741
764
|
@override
|
|
@@ -783,8 +806,7 @@ class _DemoSidebarState extends State<_DemoSidebar> {
|
|
|
783
806
|
currentItem: _current,
|
|
784
807
|
onTapItem: (i) => _current.value = i, // highlight only
|
|
785
808
|
isDrawer: widget.isDrawer,
|
|
786
|
-
|
|
787
|
-
showSearch: widget.showSearch,
|
|
809
|
+
collapseMode: widget.collapseMode,
|
|
788
810
|
side: widget.side,
|
|
789
811
|
);
|
|
790
812
|
}
|
|
@@ -24,6 +24,8 @@ import 'package:kasy_kit/core/web_device_preview/web_device_preview.dart';
|
|
|
24
24
|
import 'package:kasy_kit/core/web_viewport_scale.dart';
|
|
25
25
|
import 'package:kasy_kit/core/widgets/focus_visibility.dart';
|
|
26
26
|
import 'package:kasy_kit/environments.dart';
|
|
27
|
+
import 'package:kasy_kit/features/authentication/api/auth_web_support.dart'
|
|
28
|
+
if (dart.library.js_interop) 'package:kasy_kit/features/authentication/api/auth_web_support_web.dart';
|
|
27
29
|
import 'package:kasy_kit/features/authentication/api/authentication_api.dart';
|
|
28
30
|
import 'package:kasy_kit/features/notifications/api/local_notifier.dart';
|
|
29
31
|
import 'package:kasy_kit/features/notifications/repositories/background_notification_handler.dart';
|
|
@@ -88,6 +90,11 @@ void main() async {
|
|
|
88
90
|
// MUST be registered at the top-level BEFORE runApp().
|
|
89
91
|
FirebaseMessaging.onBackgroundMessage(onBackgroundMessage);
|
|
90
92
|
|
|
93
|
+
// Web: if we just returned from a social-login redirect, reset the address bar
|
|
94
|
+
// to '/' BEFORE the router reads the initial URL, so the app boots on Home
|
|
95
|
+
// instead of the stale tab URL (e.g. /settings) the redirect returned to.
|
|
96
|
+
resetUrlAfterRedirectLogin();
|
|
97
|
+
|
|
91
98
|
// initialize sentry for error reporting in production only
|
|
92
99
|
if (env is DevEnvironment) {
|
|
93
100
|
run(sharedPrefs);
|
|
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|
|
16
16
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
|
17
17
|
# In Windows, build-name is used as the major, minor, and patch parts
|
|
18
18
|
# of the product and file versions while build-number is used as the build suffix.
|
|
19
|
-
version: 1.0.0+
|
|
19
|
+
version: 1.0.0+57
|
|
20
20
|
|
|
21
21
|
environment:
|
|
22
22
|
sdk: ^3.11.0
|
|
@@ -73,7 +73,7 @@ dependencies:
|
|
|
73
73
|
json_annotation: ^4.9.0
|
|
74
74
|
local_auth: ^3.0.1
|
|
75
75
|
logger: ^2.6.2
|
|
76
|
-
lucide_icons_flutter: ^3.1.
|
|
76
|
+
lucide_icons_flutter: ^3.1.14
|
|
77
77
|
mixpanel_flutter: ^2.5.0
|
|
78
78
|
open_filex: ^4.7.0
|
|
79
79
|
package_info_plus: ^8.3.0
|