rei-kit 0.1.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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -0
  3. package/dist/app-error-DF9cijE0.js +63 -0
  4. package/dist/app-error-DF9cijE0.js.map +1 -0
  5. package/dist/components/BaseButton.vue.d.ts +20 -0
  6. package/dist/components/BaseInput.vue.d.ts +23 -0
  7. package/dist/components/BaseSheet.vue.d.ts +32 -0
  8. package/dist/components/EmptyState.vue.d.ts +19 -0
  9. package/dist/components/LocaleLinks.vue.d.ts +33 -0
  10. package/dist/components/PageHeader.vue.d.ts +20 -0
  11. package/dist/components/SectionHeading.vue.d.ts +26 -0
  12. package/dist/components/SegmentedControl.vue.d.ts +27 -0
  13. package/dist/components/SettingsGroup.vue.d.ts +16 -0
  14. package/dist/components/SettingsRow.vue.d.ts +35 -0
  15. package/dist/components/SkeletonList.vue.d.ts +8 -0
  16. package/dist/components/StatCard.vue.d.ts +8 -0
  17. package/dist/components/ToneDot.vue.d.ts +16 -0
  18. package/dist/composables/use-debounced-callback.d.ts +23 -0
  19. package/dist/composables/use-drag-scroll.d.ts +26 -0
  20. package/dist/composables/use-online.d.ts +18 -0
  21. package/dist/composables/use-theme.d.ts +23 -0
  22. package/dist/composables/use-today.d.ts +10 -0
  23. package/dist/composables/use-visual-viewport.d.ts +29 -0
  24. package/dist/i18n/runtime.d.ts +53 -0
  25. package/dist/index.d.ts +47 -0
  26. package/dist/index.js +1349 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/styles.css +27 -0
  29. package/dist/supabase/index.d.ts +28 -0
  30. package/dist/supabase.js +100 -0
  31. package/dist/supabase.js.map +1 -0
  32. package/dist/tokens.css +139 -0
  33. package/dist/utils/app-error.d.ts +58 -0
  34. package/dist/utils/date.d.ts +122 -0
  35. package/dist/utils/day-label.d.ts +27 -0
  36. package/dist/utils/download.d.ts +7 -0
  37. package/dist/utils/format.d.ts +24 -0
  38. package/dist/utils/haptics.d.ts +9 -0
  39. package/dist/utils/platform.d.ts +23 -0
  40. package/dist/utils/redirect.d.ts +41 -0
  41. package/package.json +98 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ramazan Doğan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # rei-kit
2
+
3
+ Vue 3 and Tailwind 4 design system and shared runtime. Extracted from
4
+ [Hibi](https://github.com/ramazandogna/hibi).
5
+
6
+ 零 — the layer everything else starts from.
7
+
8
+ ## Why it exists
9
+
10
+ Hibi's shared layer turned out to be genuinely portable: no file under
11
+ `shared/` imported from `features/`, so the components, the date and theme
12
+ helpers, and the i18n runtime could leave without being rewritten. This is that
13
+ layer, packaged so the next app installs it instead of copying it.
14
+
15
+ ## Status
16
+
17
+ **v0.0.0 — extraction complete, no consumer yet.** Thirteen components,
18
+ eight utilities, six composables, a generic i18n runtime and an optional
19
+ Supabase entry. Hibi moves onto it next.
20
+
21
+ ## Install
22
+
23
+ ```sh
24
+ pnpm add rei-kit
25
+ ```
26
+
27
+ `vue`, `tailwindcss` and `lucide-vue-next` are peer dependencies — the app
28
+ supplies them, so there is never a second copy of the Vue runtime.
29
+ `vue-i18n` is optional and only needed for the i18n runtime.
30
+
31
+ ## Use
32
+
33
+ ```vue
34
+ <script setup lang="ts">
35
+ import { BaseButton, BaseSheet, useTheme } from 'rei-kit'
36
+
37
+ const theme = useTheme()
38
+ </script>
39
+ ```
40
+
41
+ Supabase lives behind its own entry, so an app that does not use it never
42
+ downloads it:
43
+
44
+ ```ts
45
+ import { createSupabaseClient } from 'rei-kit/supabase'
46
+ ```
47
+
48
+ ```css
49
+ /* your app's main.css */
50
+ @import 'tailwindcss';
51
+ @import 'rei-kit/tokens.css'; /* Tailwind source: tokens and utilities */
52
+ @import 'rei-kit/styles.css'; /* compiled component styles */
53
+
54
+ /* the whole rebrand */
55
+ @theme {
56
+ --color-primary: #6b4de6;
57
+ --color-positive: #2fa36b;
58
+ --color-negative: #d1453b;
59
+ }
60
+ ```
61
+
62
+ Tokens are named for what they do, not for the app they came from, so a new
63
+ product changes values rather than renaming anything.
64
+
65
+ ## Commands
66
+
67
+ | Command | What it does |
68
+ | ---------------- | ----------------------------------------------------- |
69
+ | `pnpm dev` | Rebuild on change, for use with a linked app |
70
+ | `pnpm build` | Type-check, then build |
71
+ | `pnpm check` | Everything CI runs: format, lint, types, tests, build |
72
+ | `pnpm test:unit` | Vitest, watch mode |
73
+ | `pnpm lint` | oxlint + ESLint, with `--fix` |
74
+
75
+ ## Releasing
76
+
77
+ Pushing a `v*` tag runs the full check and publishes to npm. Nothing publishes
78
+ from a branch, so `main` can move without shipping.
79
+
80
+ ```sh
81
+ pnpm version minor
82
+ git push --follow-tags
83
+ ```
84
+
85
+ ## License
86
+
87
+ MIT
@@ -0,0 +1,63 @@
1
+ //#region src/utils/app-error.ts
2
+ /**
3
+ * A normalised error thrown by the data layer.
4
+ *
5
+ * Extends `Error` so it can be thrown, caught and logged like any other error,
6
+ * and keeps the original in `cause` for debugging.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * try {
11
+ * await createHabit(input)
12
+ * } catch (e) {
13
+ * const err = toAppError(e)
14
+ * if (err.kind === 'conflict') return // already exists, not a real failure
15
+ * showToast(err.message)
16
+ * }
17
+ * ```
18
+ */
19
+ var AppError = class extends Error {
20
+ kind;
21
+ constructor(kind, message, options) {
22
+ super(message, options);
23
+ this.name = "AppError";
24
+ this.kind = kind;
25
+ }
26
+ };
27
+ var mappers = [];
28
+ /**
29
+ * Teaches `toAppError` about a backend it does not import.
30
+ *
31
+ * The core has no database dependency; `rei-kit/supabase` registers the
32
+ * Postgrest mapping when it is imported, so an app that never touches Supabase
33
+ * never downloads the code that knows about it.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * registerErrorMapper((error) =>
38
+ * isPrismaConflict(error) ? new AppError('conflict', 'Already exists.') : null,
39
+ * )
40
+ * ```
41
+ */
42
+ function registerErrorMapper(mapper) {
43
+ mappers.push(mapper);
44
+ }
45
+ /**
46
+ * Normalises anything thrown by the data layer.
47
+ *
48
+ * @param error - Whatever was caught.
49
+ * @returns An `AppError`, never a rethrow.
50
+ */
51
+ function toAppError(error) {
52
+ if (error instanceof AppError) return error;
53
+ for (const map of mappers) {
54
+ const mapped = map(error);
55
+ if (mapped) return mapped;
56
+ }
57
+ if (error instanceof TypeError) return new AppError("network", "Could not reach the server.", { cause: error });
58
+ return new AppError("unknown", "Something went wrong.", { cause: error });
59
+ }
60
+ //#endregion
61
+ export { registerErrorMapper as n, toAppError as r, AppError as t };
62
+
63
+ //# sourceMappingURL=app-error-DF9cijE0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-error-DF9cijE0.js","names":[],"sources":["../src/utils/app-error.ts"],"sourcesContent":["/** Error categories the UI can branch on, independent of Postgres or Supabase. */\nexport type AppErrorKind = 'conflict' | 'not-found' | 'network' | 'unknown'\n\n/**\n * A normalised error thrown by the data layer.\n *\n * Extends `Error` so it can be thrown, caught and logged like any other error,\n * and keeps the original in `cause` for debugging.\n *\n * @example\n * ```ts\n * try {\n * await createHabit(input)\n * } catch (e) {\n * const err = toAppError(e)\n * if (err.kind === 'conflict') return // already exists, not a real failure\n * showToast(err.message)\n * }\n * ```\n */\nexport class AppError extends Error {\n readonly kind: AppErrorKind\n\n constructor(kind: AppErrorKind, message: string, options?: ErrorOptions) {\n super(message, options)\n this.name = 'AppError'\n this.kind = kind\n }\n}\n\n/**\n * Normalises anything thrown by Supabase into an {@link AppError}.\n *\n * Idempotent: an `AppError` is returned as-is, so wrapping twice is safe.\n *\n * @param error - Anything caught from the data layer.\n * @returns An `AppError` with a user-facing message and a `kind` to branch on.\n */\n/**\n * Turns a backend-specific error into an `AppError`, or returns `null` to let\n * the next mapper try.\n */\nexport type ErrorMapper = (error: unknown) => AppError | null\n\nconst mappers: ErrorMapper[] = []\n\n/**\n * Teaches `toAppError` about a backend it does not import.\n *\n * The core has no database dependency; `rei-kit/supabase` registers the\n * Postgrest mapping when it is imported, so an app that never touches Supabase\n * never downloads the code that knows about it.\n *\n * @example\n * ```ts\n * registerErrorMapper((error) =>\n * isPrismaConflict(error) ? new AppError('conflict', 'Already exists.') : null,\n * )\n * ```\n */\nexport function registerErrorMapper(mapper: ErrorMapper): void {\n mappers.push(mapper)\n}\n\n/**\n * Normalises anything thrown by the data layer.\n *\n * @param error - Whatever was caught.\n * @returns An `AppError`, never a rethrow.\n */\nexport function toAppError(error: unknown): AppError {\n if (error instanceof AppError) return error\n\n for (const map of mappers) {\n const mapped = map(error)\n if (mapped) return mapped\n }\n\n // A failed fetch surfaces as a TypeError, which is the only reliable signal\n // the browser gives that the request never left.\n if (error instanceof TypeError) {\n return new AppError('network', 'Could not reach the server.', { cause: error })\n }\n\n return new AppError('unknown', 'Something went wrong.', { cause: error })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoBA,IAAa,WAAb,cAA8B,MAAM;CAClC;CAEA,YAAY,MAAoB,SAAiB,SAAwB;EACvE,MAAM,SAAS,OAAO;EACtB,KAAK,OAAO;EACZ,KAAK,OAAO;CACd;AACF;AAgBA,IAAM,UAAyB,CAAC;;;;;;;;;;;;;;;AAgBhC,SAAgB,oBAAoB,QAA2B;CAC7D,QAAQ,KAAK,MAAM;AACrB;;;;;;;AAQA,SAAgB,WAAW,OAA0B;CACnD,IAAI,iBAAiB,UAAU,OAAO;CAEtC,KAAK,MAAM,OAAO,SAAS;EACzB,MAAM,SAAS,IAAI,KAAK;EACxB,IAAI,QAAQ,OAAO;CACrB;CAIA,IAAI,iBAAiB,WACnB,OAAO,IAAI,SAAS,WAAW,+BAA+B,EAAE,OAAO,MAAM,CAAC;CAGhF,OAAO,IAAI,SAAS,WAAW,yBAAyB,EAAE,OAAO,MAAM,CAAC;AAC1E"}
@@ -0,0 +1,20 @@
1
+ type __VLS_Props = {
2
+ variant?: 'primary' | 'ghost' | 'danger';
3
+ size?: 'sm' | 'md';
4
+ loading?: boolean;
5
+ disabled?: boolean;
6
+ type?: 'button' | 'submit';
7
+ };
8
+ declare var __VLS_1: {};
9
+ type __VLS_Slots = {} & {
10
+ default?: (props: typeof __VLS_1) => any;
11
+ };
12
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
13
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
16
+ type __VLS_WithSlots<T, S> = T & {
17
+ new (): {
18
+ $slots: S;
19
+ };
20
+ };
@@ -0,0 +1,23 @@
1
+ type __VLS_Props = {
2
+ label: string;
3
+ error?: string | undefined;
4
+ hint?: string | undefined;
5
+ /**
6
+ * Hides the label visually but keeps it for assistive tech. For fields whose
7
+ * surrounding row already names them — dropping the label entirely would
8
+ * leave the input with no accessible name at all.
9
+ */
10
+ labelHidden?: boolean;
11
+ type?: 'text' | 'email' | 'password' | 'number';
12
+ };
13
+ type __VLS_ModelProps = {
14
+ modelValue?: string | undefined;
15
+ };
16
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
17
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
18
+ "update:modelValue": (value: string | undefined) => any;
19
+ }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
20
+ "onUpdate:modelValue"?: (value: string | undefined) => any;
21
+ }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
22
+ declare const _default: typeof __VLS_export;
23
+ export default _default;
@@ -0,0 +1,32 @@
1
+ type __VLS_Props = {
2
+ title: string;
3
+ subtitle?: string;
4
+ /**
5
+ * Accessible name for the close button.
6
+ *
7
+ * A prop rather than a translation: a component that calls t() forces every
8
+ * consumer onto one i18n setup, and this is the package's only visible string.
9
+ */
10
+ closeLabel?: string;
11
+ };
12
+ type __VLS_ModelProps = {
13
+ modelValue: boolean;
14
+ };
15
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
16
+ declare var __VLS_18: {};
17
+ type __VLS_Slots = {} & {
18
+ default?: (props: typeof __VLS_18) => any;
19
+ };
20
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
21
+ "update:modelValue": (value: boolean) => any;
22
+ }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
23
+ "onUpdate:modelValue"?: (value: boolean) => any;
24
+ }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
25
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
26
+ declare const _default: typeof __VLS_export;
27
+ export default _default;
28
+ type __VLS_WithSlots<T, S> = T & {
29
+ new (): {
30
+ $slots: S;
31
+ };
32
+ };
@@ -0,0 +1,19 @@
1
+ type __VLS_Props = {
2
+ title: string;
3
+ description?: string | undefined;
4
+ };
5
+ declare var __VLS_1: {}, __VLS_3: {};
6
+ type __VLS_Slots = {} & {
7
+ icon?: (props: typeof __VLS_1) => any;
8
+ } & {
9
+ action?: (props: typeof __VLS_3) => any;
10
+ };
11
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
12
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
13
+ declare const _default: typeof __VLS_export;
14
+ export default _default;
15
+ type __VLS_WithSlots<T, S> = T & {
16
+ new (): {
17
+ $slots: S;
18
+ };
19
+ };
@@ -0,0 +1,33 @@
1
+ declare const __VLS_export: <L extends string>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
2
+ props: import('vue').PublicProps & __VLS_PrettifyLocal<({
3
+ locales: readonly L[];
4
+ /** Endonyms, e.g. `{ en: 'English', tr: 'Türkçe' }`. */
5
+ labels: Record<L, string>;
6
+ /** Accessible name for the group. */
7
+ label?: string;
8
+ } & {
9
+ /**
10
+ * Two-way bound rather than taking the runtime's ref as a prop: props are not
11
+ * unwrapped in a template and cannot be assigned to, so the ref would compare
12
+ * against itself and the click handler would not compile.
13
+ */
14
+ modelValue: "system" | L;
15
+ }) & {
16
+ "onUpdate:modelValue"?: (value: "system" | L) => any;
17
+ }> & (typeof globalThis extends {
18
+ __VLS_PROPS_FALLBACK: infer P;
19
+ } ? P : {});
20
+ expose: (exposed: {}) => void;
21
+ attrs: any;
22
+ slots: {};
23
+ emit: (event: "update:modelValue", value: "system" | L) => void;
24
+ }>) => import('vue').VNode & {
25
+ __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
26
+ };
27
+ declare const _default: typeof __VLS_export;
28
+ export default _default;
29
+ type __VLS_PrettifyLocal<T> = (T extends any ? {
30
+ [K in keyof T]: T[K];
31
+ } : {
32
+ [K in keyof T as K]: T[K];
33
+ }) & {};
@@ -0,0 +1,20 @@
1
+ type __VLS_Props = {
2
+ title: string;
3
+ };
4
+ declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {};
5
+ type __VLS_Slots = {} & {
6
+ left?: (props: typeof __VLS_1) => any;
7
+ } & {
8
+ title?: (props: typeof __VLS_3) => any;
9
+ } & {
10
+ right?: (props: typeof __VLS_5) => any;
11
+ };
12
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
13
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
16
+ type __VLS_WithSlots<T, S> = T & {
17
+ new (): {
18
+ $slots: S;
19
+ };
20
+ };
@@ -0,0 +1,26 @@
1
+ /** The three classes a category needs to colour a heading. */
2
+ export interface Tone {
3
+ /** Solid background for the dot, e.g. `bg-positive`. */
4
+ fill: string;
5
+ /** Tinted surface for the pill, e.g. `bg-positive/5 border-positive/25`. */
6
+ card: string;
7
+ /** Foreground that pairs with the surface, e.g. `text-positive`. */
8
+ text: string;
9
+ }
10
+ /**
11
+ * A pill heading for a group of things.
12
+ *
13
+ * The tone arrives as three class strings rather than a category name: Tailwind
14
+ * reads source files as plain text, so a class assembled at runtime never
15
+ * reaches the stylesheet — the app has to write them out, and it is the app
16
+ * that knows its own categories anyway.
17
+ */
18
+ type __VLS_Props = {
19
+ tone: Tone;
20
+ label: string;
21
+ /** Hidden when zero, so an empty group's heading stays quiet. */
22
+ count?: number;
23
+ };
24
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
25
+ declare const _default: typeof __VLS_export;
26
+ export default _default;
@@ -0,0 +1,27 @@
1
+ declare const __VLS_export: <T extends string | number>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
2
+ props: import('vue').PublicProps & __VLS_PrettifyLocal<({
3
+ options: readonly {
4
+ value: T;
5
+ label: string;
6
+ }[];
7
+ } & {
8
+ modelValue: T;
9
+ }) & {
10
+ "onUpdate:modelValue"?: (value: T) => any;
11
+ }> & (typeof globalThis extends {
12
+ __VLS_PROPS_FALLBACK: infer P;
13
+ } ? P : {});
14
+ expose: (exposed: {}) => void;
15
+ attrs: any;
16
+ slots: {};
17
+ emit: (event: "update:modelValue", value: T) => void;
18
+ }>) => import('vue').VNode & {
19
+ __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
20
+ };
21
+ declare const _default: typeof __VLS_export;
22
+ export default _default;
23
+ type __VLS_PrettifyLocal<T> = (T extends any ? {
24
+ [K in keyof T]: T[K];
25
+ } : {
26
+ [K in keyof T as K]: T[K];
27
+ }) & {};
@@ -0,0 +1,16 @@
1
+ type __VLS_Props = {
2
+ title: string;
3
+ };
4
+ declare var __VLS_1: {};
5
+ type __VLS_Slots = {} & {
6
+ default?: (props: typeof __VLS_1) => any;
7
+ };
8
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
9
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
10
+ declare const _default: typeof __VLS_export;
11
+ export default _default;
12
+ type __VLS_WithSlots<T, S> = T & {
13
+ new (): {
14
+ $slots: S;
15
+ };
16
+ };
@@ -0,0 +1,35 @@
1
+ import { Component } from 'vue';
2
+ /**
3
+ * One line in a settings card.
4
+ *
5
+ * `as` decides the element: a row that navigates has to be a button, and a row
6
+ * that merely holds a control must not be, or the control becomes unreachable.
7
+ */
8
+ type __VLS_Props = {
9
+ label: string;
10
+ description?: string;
11
+ icon?: Component | undefined;
12
+ /** Renders the row as a button with a chevron. */
13
+ interactive?: boolean;
14
+ /** Puts the control on its own line below the label, for wide controls. */
15
+ stacked?: boolean;
16
+ };
17
+ declare var __VLS_15: {}, __VLS_22: {};
18
+ type __VLS_Slots = {} & {
19
+ default?: (props: typeof __VLS_15) => any;
20
+ } & {
21
+ default?: (props: typeof __VLS_22) => any;
22
+ };
23
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
24
+ click: () => any;
25
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
26
+ onClick?: () => any;
27
+ }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
28
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
29
+ declare const _default: typeof __VLS_export;
30
+ export default _default;
31
+ type __VLS_WithSlots<T, S> = T & {
32
+ new (): {
33
+ $slots: S;
34
+ };
35
+ };
@@ -0,0 +1,8 @@
1
+ type __VLS_Props = {
2
+ rows?: number;
3
+ rowHeight?: string;
4
+ label?: string;
5
+ };
6
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,8 @@
1
+ type __VLS_Props = {
2
+ value: string;
3
+ label: string;
4
+ trend?: 'up' | 'down' | 'flat' | null;
5
+ };
6
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * A small coloured dot, optionally labelled.
3
+ *
4
+ * Takes the colour as a class rather than a category, so an app can key it off
5
+ * whatever its own domain calls a category — habit kinds, expense types,
6
+ * priorities — without this component knowing about any of them.
7
+ */
8
+ type __VLS_Props = {
9
+ /** Background utility for the dot, e.g. `bg-positive`. */
10
+ fill: string;
11
+ /** Optional text after the dot. Omit for a bare marker. */
12
+ label?: string;
13
+ };
14
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
15
+ declare const _default: typeof __VLS_export;
16
+ export default _default;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Delays a callback until the caller stops calling it.
3
+ *
4
+ * Used for note autosave: a request per keystroke would be wasteful, but losing
5
+ * the last keystrokes when the user navigates away would be worse — so the
6
+ * pending call is flushed on dispose, and `flush` is exposed for route guards.
7
+ *
8
+ * @param callback - Runs with the arguments of the most recent call.
9
+ * @param delay - Quiet period in milliseconds.
10
+ * @returns `run` to schedule, `flush` to run now, `cancel` to drop.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const save = useDebouncedCallback((body: string) => mutate(body), 800)
15
+ * watch(text, (value) => save.run(value))
16
+ * onBeforeRouteLeave(() => save.flush())
17
+ * ```
18
+ */
19
+ export declare function useDebouncedCallback<A extends unknown[]>(callback: (...args: A) => void, delay?: number): {
20
+ run: (...args: A) => void;
21
+ flush: () => void;
22
+ cancel: () => void;
23
+ };
@@ -0,0 +1,26 @@
1
+ import { Ref } from 'vue';
2
+ /**
3
+ * Drag-to-scroll for a horizontally scrolling element.
4
+ *
5
+ * The app puts `touch-action: pan-y` on the page content so the tab-swipe
6
+ * gesture keeps its pointer events — the browser never claims a horizontal
7
+ * drag, which also means it never pans this element natively. Rather than give
8
+ * that up, horizontal scrolling is driven here.
9
+ *
10
+ * @param target - The scroll container.
11
+ * @returns `didDrag`, so a click handler can ignore the press that ended a drag.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const scroller = ref<HTMLElement | null>(null)
16
+ * const { didDrag } = useDragScroll(scroller)
17
+ *
18
+ * function onClick() {
19
+ * if (didDrag()) return
20
+ * // …treat as a tap
21
+ * }
22
+ * ```
23
+ */
24
+ export declare function useDragScroll(target: Ref<HTMLElement | null>): {
25
+ didDrag: () => boolean;
26
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Tracks whether the browser thinks it has a network connection.
3
+ *
4
+ * Note the limit: `navigator.onLine` only reports whether a network interface
5
+ * is up, not whether requests actually succeed. Treat it as a hint for the UI,
6
+ * never as a reason to skip error handling.
7
+ *
8
+ * Listeners are removed on unmount, so the composable is safe to call per view.
9
+ *
10
+ * @returns A readonly ref that flips with the browser's online/offline events.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const isOnline = useOnline()
15
+ * // <p v-if="!isOnline">You're offline.</p>
16
+ * ```
17
+ */
18
+ export declare function useOnline(): Readonly<import('vue').Ref<boolean, boolean>>;
@@ -0,0 +1,23 @@
1
+ import { Ref } from 'vue';
2
+ /** What the user asked for; `system` follows the OS. */
3
+ export type ThemePreference = 'system' | 'light' | 'dark';
4
+ export declare function isThemePreference(value: unknown): value is ThemePreference;
5
+ /** Reads the stored preference, falling back to `system`. */
6
+ export declare function readStoredTheme(): ThemePreference;
7
+ /** Adds or removes `.dark` on `<html>`, resolving `system` against the OS. */
8
+ export declare function applyTheme(preference: ThemePreference): void;
9
+ /**
10
+ * Sets where the preference is stored.
11
+ *
12
+ * Safe in either order: called before the first `useTheme()` it simply changes
13
+ * the key, and called after it re-reads under the new one, so the controller
14
+ * never reads from one key while writing to another.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * setThemeStorageKey('hibi-theme') // once, at startup
19
+ * ```
20
+ */
21
+ export declare function setThemeStorageKey(key: string): void;
22
+ /** @returns The shared preference ref; assigning to it stores and applies it. */
23
+ export declare function useTheme(): Ref<ThemePreference>;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @returns Read-only ref holding today's `YYYY-MM-DD` key.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * const today = useToday()
7
+ * const isFuture = computed(() => day > today.value)
8
+ * ```
9
+ */
10
+ export declare function useToday(): Readonly<import('vue').Ref<string, string>>;
@@ -0,0 +1,29 @@
1
+ /** The visible area, once the on-screen keyboard has taken its share. */
2
+ export interface VisualViewportRect {
3
+ height: number;
4
+ offsetTop: number;
5
+ }
6
+ /**
7
+ * Tracks the visual viewport.
8
+ *
9
+ * Chrome and Android browsers honour `interactive-widget=resizes-content`, so
10
+ * the layout viewport already shrinks for the keyboard there. Safari on iOS
11
+ * does not implement it: it shrinks only the *visual* viewport, leaving a sheet
12
+ * sized in `dvh` sitting partly underneath the keyboard.
13
+ *
14
+ * `null` means the API is unavailable, which callers should read as "trust the
15
+ * layout viewport" rather than as zero.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const viewport = useVisualViewport()
20
+ * // :style="viewport ? { height: `${viewport.height}px` } : undefined"
21
+ * ```
22
+ */
23
+ export declare function useVisualViewport(): Readonly<import('vue').Ref<{
24
+ readonly height: number;
25
+ readonly offsetTop: number;
26
+ } | null, {
27
+ readonly height: number;
28
+ readonly offsetTop: number;
29
+ } | null>>;