rei-kit 0.1.0 → 0.2.1

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/README.md CHANGED
@@ -72,6 +72,27 @@ product changes values rather than renaming anything.
72
72
  | `pnpm test:unit` | Vitest, watch mode |
73
73
  | `pnpm lint` | oxlint + ESLint, with `--fix` |
74
74
 
75
+ ## Not breaking the apps that use it
76
+
77
+ Three layers, cheapest first.
78
+
79
+ **Pinned ranges.** A consumer depends on `^0.1.0`, which at 0.x means
80
+ `>=0.1.0 <0.2.0` — publishing 0.2.0 upgrades nobody. Apps move on their own
81
+ schedule, and a release can never reach an app that has not asked for it.
82
+
83
+ **The public API test.** `src/__tests__/public-api.spec.ts` lists every export
84
+ by name. The kit compiles perfectly well without an export nothing here calls,
85
+ so removing one is invisible to every other test; this one fails loudly and
86
+ asks whether the version should be a major.
87
+
88
+ **The consumer check.** `.github/workflows/consumer.yml` packs the tarball npm
89
+ would serve, installs it into Hibi, and runs Hibi's full gate — format, lint,
90
+ types, 37 tests, production build. A renamed prop shows up as a red pull
91
+ request here rather than as a broken app after release.
92
+
93
+ That last one is the important one: the kit's own tests never import it the way
94
+ an app does.
95
+
75
96
  ## Releasing
76
97
 
77
98
  Pushing a `v*` tag runs the full check and publishes to npm. Nothing publishes
@@ -0,0 +1,10 @@
1
+ type __VLS_Props = {
2
+ label: string;
3
+ };
4
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
5
+ click: () => any;
6
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
7
+ onClick?: () => any;
8
+ }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,34 @@
1
+ import { Component } from 'vue';
2
+ export interface TabItem<K extends string> {
3
+ /** Identity, compared against `active`. */
4
+ key: K;
5
+ /** Router destination. */
6
+ to: string;
7
+ /** Text under the icon. Already translated. */
8
+ label: string;
9
+ icon: Component;
10
+ }
11
+ declare const __VLS_export: <K 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<{
12
+ props: import('vue').PublicProps & __VLS_PrettifyLocal<{
13
+ items: readonly TabItem<K>[];
14
+ /** Which item is current. Usually from `route.meta`. */
15
+ active?: K | undefined;
16
+ /** Accessible name for the navigation landmark. */
17
+ label?: string;
18
+ }> & (typeof globalThis extends {
19
+ __VLS_PROPS_FALLBACK: infer P;
20
+ } ? P : {});
21
+ expose: (exposed: {}) => void;
22
+ attrs: any;
23
+ slots: {};
24
+ emit: {};
25
+ }>) => import('vue').VNode & {
26
+ __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
27
+ };
28
+ declare const _default: typeof __VLS_export;
29
+ export default _default;
30
+ type __VLS_PrettifyLocal<T> = (T extends any ? {
31
+ [K in keyof T]: T[K];
32
+ } : {
33
+ [K in keyof T as K]: T[K];
34
+ }) & {};
package/dist/index.d.ts CHANGED
@@ -43,5 +43,8 @@ export { default as StatCard } from './components/StatCard.vue';
43
43
  export { default as ToneDot } from './components/ToneDot.vue';
44
44
  export type { Tone } from './components/SectionHeading.vue';
45
45
  export { default as LocaleLinks } from './components/LocaleLinks.vue';
46
+ export { default as GoogleButton } from './components/GoogleButton.vue';
47
+ export { default as TabBar } from './components/TabBar.vue';
48
+ export type { TabItem } from './components/TabBar.vue';
46
49
  export { createI18nRuntime } from './i18n/runtime';
47
50
  export type { I18nRuntimeOptions, LocalePreference } from './i18n/runtime';
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { n as registerErrorMapper, r as toAppError, t as AppError } from "./app-error-DF9cijE0.js";
2
- import { Fragment, Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, mergeModels, mergeProps, nextTick, normalizeClass, normalizeStyle, onMounted, onScopeDispose, onUnmounted, openBlock, readonly, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useId, useModel, vModelDynamic, vModelRadio, watch, watchEffect, withCtx, withDirectives } from "vue";
2
+ import { Fragment, Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createStaticVNode, createTextVNode, createVNode, defineComponent, mergeModels, mergeProps, nextTick, normalizeClass, normalizeStyle, onMounted, onScopeDispose, onUnmounted, openBlock, readonly, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useId, useModel, vModelDynamic, vModelRadio, watch, watchEffect, withCtx, withDirectives } from "vue";
3
3
  import { ArrowDown, ArrowRight, ArrowUp, ChevronRight, X } from "lucide-vue-next";
4
+ import { RouterLink } from "vue-router";
4
5
  import { createI18n } from "vue-i18n";
5
6
  //#region src/utils/date.ts
6
7
  /**
@@ -651,12 +652,12 @@ function useVisualViewport() {
651
652
  }
652
653
  //#endregion
653
654
  //#region src/components/BaseButton.vue?vue&type=script&setup=true&lang.ts
654
- var _hoisted_1$12 = [
655
+ var _hoisted_1$13 = [
655
656
  "type",
656
657
  "disabled",
657
658
  "aria-busy"
658
659
  ];
659
- var _hoisted_2$11 = {
660
+ var _hoisted_2$12 = {
660
661
  key: 0,
661
662
  class: "size-4 animate-spin rounded-full border-2 border-current border-t-transparent",
662
663
  "aria-hidden": "true"
@@ -694,15 +695,15 @@ var BaseButton_default = /* @__PURE__ */ defineComponent({
694
695
  disabled: __props.disabled || __props.loading,
695
696
  "aria-busy": __props.loading,
696
697
  class: normalizeClass(["rounded-card focus-visible:outline-primary inline-flex items-center justify-center gap-2 font-medium transition-transform duration-100 select-none focus-visible:outline-2 focus-visible:outline-offset-2 active:scale-95 disabled:pointer-events-none disabled:opacity-50", [VARIANT_CLASS[__props.variant], SIZE_CLASS[__props.size]]])
697
- }, [__props.loading ? (openBlock(), createElementBlock("span", _hoisted_2$11)) : createCommentVNode("", true), renderSlot(_ctx.$slots, "default")], 10, _hoisted_1$12);
698
+ }, [__props.loading ? (openBlock(), createElementBlock("span", _hoisted_2$12)) : createCommentVNode("", true), renderSlot(_ctx.$slots, "default")], 10, _hoisted_1$13);
698
699
  };
699
700
  }
700
701
  });
701
702
  //#endregion
702
703
  //#region src/components/BaseInput.vue?vue&type=script&setup=true&lang.ts
703
- var _hoisted_1$11 = { class: "flex flex-col gap-1.5" };
704
- var _hoisted_2$10 = ["for"];
705
- var _hoisted_3$6 = [
704
+ var _hoisted_1$12 = { class: "flex flex-col gap-1.5" };
705
+ var _hoisted_2$11 = ["for"];
706
+ var _hoisted_3$7 = [
706
707
  "id",
707
708
  "type",
708
709
  "aria-invalid",
@@ -737,18 +738,18 @@ var BaseInput_default = /* @__PURE__ */ defineComponent({
737
738
  if (__props.hint) return hintId;
738
739
  });
739
740
  return (_ctx, _cache) => {
740
- return openBlock(), createElementBlock("div", _hoisted_1$11, [
741
+ return openBlock(), createElementBlock("div", _hoisted_1$12, [
741
742
  createElementVNode("label", {
742
743
  for: unref(id),
743
744
  class: normalizeClass(["text-ink text-sm font-medium", __props.labelHidden ? "sr-only" : ""])
744
- }, toDisplayString(__props.label), 11, _hoisted_2$10),
745
+ }, toDisplayString(__props.label), 11, _hoisted_2$11),
745
746
  withDirectives(createElementVNode("input", mergeProps({
746
747
  id: unref(id),
747
748
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => model.value = $event),
748
749
  type: __props.type,
749
750
  "aria-invalid": Boolean(__props.error),
750
751
  "aria-describedby": describedBy.value
751
- }, _ctx.$attrs, { class: ["border-hair bg-surface text-ink rounded-card focus-visible:outline-primary h-11 border px-3 focus-visible:outline-2 focus-visible:outline-offset-1", __props.error ? "border-negative" : ""] }), null, 16, _hoisted_3$6), [[vModelDynamic, model.value]]),
752
+ }, _ctx.$attrs, { class: ["border-hair bg-surface text-ink rounded-card focus-visible:outline-primary h-11 border px-3 focus-visible:outline-2 focus-visible:outline-offset-1", __props.error ? "border-negative" : ""] }), null, 16, _hoisted_3$7), [[vModelDynamic, model.value]]),
752
753
  __props.error ? (openBlock(), createElementBlock("p", {
753
754
  key: 0,
754
755
  id: errorId,
@@ -764,10 +765,10 @@ var BaseInput_default = /* @__PURE__ */ defineComponent({
764
765
  });
765
766
  //#endregion
766
767
  //#region src/components/BaseSheet.vue?vue&type=script&setup=true&lang.ts
767
- var _hoisted_1$10 = { class: "shell-frame md:rounded-shell relative flex max-h-full flex-col justify-end overflow-hidden" };
768
- var _hoisted_2$9 = ["aria-label"];
769
- var _hoisted_3$5 = { class: "flex shrink-0 items-start gap-3 px-6 pt-4 pb-5" };
770
- var _hoisted_4$4 = { class: "min-w-0 flex-1" };
768
+ var _hoisted_1$11 = { class: "shell-frame md:rounded-shell relative flex max-h-full flex-col justify-end overflow-hidden" };
769
+ var _hoisted_2$10 = ["aria-label"];
770
+ var _hoisted_3$6 = { class: "flex shrink-0 items-start gap-3 px-6 pt-4 pb-5" };
771
+ var _hoisted_4$5 = { class: "min-w-0 flex-1" };
771
772
  var _hoisted_5$3 = { class: "text-ink text-xl leading-tight font-semibold" };
772
773
  var _hoisted_6$1 = {
773
774
  key: 0,
@@ -845,7 +846,7 @@ var BaseSheet_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
845
846
  key: 0,
846
847
  class: "fixed inset-x-0 top-0 bottom-0 z-50 flex items-center justify-center",
847
848
  style: normalizeStyle(viewportStyle.value)
848
- }, [createElementVNode("div", _hoisted_1$10, [createElementVNode("div", {
849
+ }, [createElementVNode("div", _hoisted_1$11, [createElementVNode("div", {
849
850
  class: "bg-ink/45 absolute inset-0 backdrop-blur-[2px]",
850
851
  onClick: close
851
852
  }), createElementVNode("section", {
@@ -861,14 +862,14 @@ var BaseSheet_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
861
862
  class: "flex shrink-0 justify-center pt-3",
862
863
  "aria-hidden": "true"
863
864
  }, [createElementVNode("span", { class: "bg-hair h-1.5 w-10 rounded-full" })], -1)),
864
- createElementVNode("header", _hoisted_3$5, [createElementVNode("div", _hoisted_4$4, [createElementVNode("h2", _hoisted_5$3, toDisplayString(__props.title), 1), __props.subtitle ? (openBlock(), createElementBlock("p", _hoisted_6$1, toDisplayString(__props.subtitle), 1)) : createCommentVNode("", true)]), createElementVNode("button", {
865
+ createElementVNode("header", _hoisted_3$6, [createElementVNode("div", _hoisted_4$5, [createElementVNode("h2", _hoisted_5$3, toDisplayString(__props.title), 1), __props.subtitle ? (openBlock(), createElementBlock("p", _hoisted_6$1, toDisplayString(__props.subtitle), 1)) : createCommentVNode("", true)]), createElementVNode("button", {
865
866
  type: "button",
866
867
  class: "text-ink-soft hover:bg-muted hover:text-ink -mt-1 flex size-10 shrink-0 items-center justify-center rounded-full transition-colors active:scale-90",
867
868
  "aria-label": __props.closeLabel,
868
869
  onClick: close
869
870
  }, [createVNode(unref(X), { class: "size-5" })], 8, _hoisted_7$1)]),
870
871
  createElementVNode("div", _hoisted_8, [renderSlot(_ctx.$slots, "default", {}, void 0, true)])
871
- ], 8, _hoisted_2$9)])], 4)) : createCommentVNode("", true)]),
872
+ ], 8, _hoisted_2$10)])], 4)) : createCommentVNode("", true)]),
872
873
  _: 3
873
874
  })]);
874
875
  };
@@ -886,13 +887,13 @@ var _plugin_vue_export_helper_default = (sfc, props) => {
886
887
  var BaseSheet_default = /*#__PURE__*/ _plugin_vue_export_helper_default(BaseSheet_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-3f2e9ce4"]]);
887
888
  //#endregion
888
889
  //#region src/components/EmptyState.vue?vue&type=script&setup=true&lang.ts
889
- var _hoisted_1$9 = { class: "flex flex-col items-center gap-3 px-6 py-10 text-center" };
890
- var _hoisted_2$8 = {
890
+ var _hoisted_1$10 = { class: "flex flex-col items-center gap-3 px-6 py-10 text-center" };
891
+ var _hoisted_2$9 = {
891
892
  key: 0,
892
893
  class: "bg-muted text-primary rounded-card flex size-12 items-center"
893
894
  };
894
- var _hoisted_3$4 = { class: "text-ink text-base font-semibold" };
895
- var _hoisted_4$3 = {
895
+ var _hoisted_3$5 = { class: "text-ink text-base font-semibold" };
896
+ var _hoisted_4$4 = {
896
897
  key: 1,
897
898
  class: "text-ink-soft max-w-[36ch] text-sm"
898
899
  };
@@ -910,10 +911,10 @@ var EmptyState_default = /* @__PURE__ */ defineComponent({
910
911
  },
911
912
  setup(__props) {
912
913
  return (_ctx, _cache) => {
913
- return openBlock(), createElementBlock("div", _hoisted_1$9, [
914
- _ctx.$slots.icon ? (openBlock(), createElementBlock("div", _hoisted_2$8, [renderSlot(_ctx.$slots, "icon")])) : createCommentVNode("", true),
915
- createElementVNode("h3", _hoisted_3$4, toDisplayString(__props.title), 1),
916
- __props.description ? (openBlock(), createElementBlock("p", _hoisted_4$3, toDisplayString(__props.description), 1)) : createCommentVNode("", true),
914
+ return openBlock(), createElementBlock("div", _hoisted_1$10, [
915
+ _ctx.$slots.icon ? (openBlock(), createElementBlock("div", _hoisted_2$9, [renderSlot(_ctx.$slots, "icon")])) : createCommentVNode("", true),
916
+ createElementVNode("h3", _hoisted_3$5, toDisplayString(__props.title), 1),
917
+ __props.description ? (openBlock(), createElementBlock("p", _hoisted_4$4, toDisplayString(__props.description), 1)) : createCommentVNode("", true),
917
918
  _ctx.$slots.action ? (openBlock(), createElementBlock("div", _hoisted_5$2, [renderSlot(_ctx.$slots, "action")])) : createCommentVNode("", true)
918
919
  ]);
919
920
  };
@@ -921,10 +922,10 @@ var EmptyState_default = /* @__PURE__ */ defineComponent({
921
922
  });
922
923
  //#endregion
923
924
  //#region src/components/PageHeader.vue?vue&type=script&setup=true&lang.ts
924
- var _hoisted_1$8 = { class: "grid h-12 shrink-0 grid-cols-[2.5rem_1fr_2.5rem] items-center" };
925
- var _hoisted_2$7 = { class: "justify-self-start" };
926
- var _hoisted_3$3 = { class: "text-ink flex min-w-0 justify-center text-base font-semibold tabular-nums" };
927
- var _hoisted_4$2 = { class: "truncate" };
925
+ var _hoisted_1$9 = { class: "grid h-12 shrink-0 grid-cols-[2.5rem_1fr_2.5rem] items-center" };
926
+ var _hoisted_2$8 = { class: "justify-self-start" };
927
+ var _hoisted_3$4 = { class: "text-ink flex min-w-0 justify-center text-base font-semibold tabular-nums" };
928
+ var _hoisted_4$3 = { class: "truncate" };
928
929
  var _hoisted_5$1 = { class: "justify-self-end" };
929
930
  //#endregion
930
931
  //#region src/components/PageHeader.vue
@@ -933,9 +934,9 @@ var PageHeader_default = /* @__PURE__ */ defineComponent({
933
934
  props: { title: {} },
934
935
  setup(__props) {
935
936
  return (_ctx, _cache) => {
936
- return openBlock(), createElementBlock("header", _hoisted_1$8, [
937
- createElementVNode("div", _hoisted_2$7, [renderSlot(_ctx.$slots, "left")]),
938
- createElementVNode("h1", _hoisted_3$3, [renderSlot(_ctx.$slots, "title", {}, () => [createElementVNode("span", _hoisted_4$2, toDisplayString(__props.title), 1)])]),
937
+ return openBlock(), createElementBlock("header", _hoisted_1$9, [
938
+ createElementVNode("div", _hoisted_2$8, [renderSlot(_ctx.$slots, "left")]),
939
+ createElementVNode("h1", _hoisted_3$4, [renderSlot(_ctx.$slots, "title", {}, () => [createElementVNode("span", _hoisted_4$3, toDisplayString(__props.title), 1)])]),
939
940
  createElementVNode("div", _hoisted_5$1, [renderSlot(_ctx.$slots, "right")])
940
941
  ]);
941
942
  };
@@ -943,8 +944,8 @@ var PageHeader_default = /* @__PURE__ */ defineComponent({
943
944
  });
944
945
  //#endregion
945
946
  //#region src/components/ToneDot.vue?vue&type=script&setup=true&lang.ts
946
- var _hoisted_1$7 = { class: "inline-flex items-center gap-1.5" };
947
- var _hoisted_2$6 = {
947
+ var _hoisted_1$8 = { class: "inline-flex items-center gap-1.5" };
948
+ var _hoisted_2$7 = {
948
949
  key: 0,
949
950
  class: "text-ink-soft text-xs font-medium"
950
951
  };
@@ -965,13 +966,13 @@ var ToneDot_default = /* @__PURE__ */ defineComponent({
965
966
  * priorities — without this component knowing about any of them.
966
967
  */
967
968
  return (_ctx, _cache) => {
968
- return openBlock(), createElementBlock("span", _hoisted_1$7, [createElementVNode("span", { class: normalizeClass(["size-2 rounded-full", __props.fill]) }, null, 2), __props.label ? (openBlock(), createElementBlock("span", _hoisted_2$6, toDisplayString(__props.label), 1)) : createCommentVNode("", true)]);
969
+ return openBlock(), createElementBlock("span", _hoisted_1$8, [createElementVNode("span", { class: normalizeClass(["size-2 rounded-full", __props.fill]) }, null, 2), __props.label ? (openBlock(), createElementBlock("span", _hoisted_2$7, toDisplayString(__props.label), 1)) : createCommentVNode("", true)]);
969
970
  };
970
971
  }
971
972
  });
972
973
  //#endregion
973
974
  //#region src/components/SectionHeading.vue?vue&type=script&setup=true&lang.ts
974
- var _hoisted_1$6 = {
975
+ var _hoisted_1$7 = {
975
976
  key: 0,
976
977
  class: "text-ink-soft text-xs tabular-nums"
977
978
  };
@@ -989,15 +990,15 @@ var SectionHeading_default = /* @__PURE__ */ defineComponent({
989
990
  return openBlock(), createElementBlock("h2", { class: normalizeClass(["flex items-center gap-2 self-start rounded-full border px-3 py-1", __props.tone.card]) }, [
990
991
  createVNode(ToneDot_default, { fill: __props.tone.fill }, null, 8, ["fill"]),
991
992
  createElementVNode("span", { class: normalizeClass(["text-xs font-semibold tracking-wide uppercase", __props.tone.text]) }, toDisplayString(__props.label), 3),
992
- __props.count > 0 ? (openBlock(), createElementBlock("span", _hoisted_1$6, toDisplayString(__props.count), 1)) : createCommentVNode("", true)
993
+ __props.count > 0 ? (openBlock(), createElementBlock("span", _hoisted_1$7, toDisplayString(__props.count), 1)) : createCommentVNode("", true)
993
994
  ], 2);
994
995
  };
995
996
  }
996
997
  });
997
998
  //#endregion
998
999
  //#region src/components/SegmentedControl.vue?vue&type=script&setup=true&lang.ts
999
- var _hoisted_1$5 = { class: "bg-muted rounded-card flex w-full gap-1 p-1" };
1000
- var _hoisted_2$5 = ["value", "name"];
1000
+ var _hoisted_1$6 = { class: "bg-muted rounded-card flex w-full gap-1 p-1" };
1001
+ var _hoisted_2$6 = ["value", "name"];
1001
1002
  //#endregion
1002
1003
  //#region src/components/SegmentedControl.vue
1003
1004
  var SegmentedControl_default = /* @__PURE__ */ defineComponent({
@@ -1011,7 +1012,7 @@ var SegmentedControl_default = /* @__PURE__ */ defineComponent({
1011
1012
  const model = useModel(__props, "modelValue");
1012
1013
  const name = useId();
1013
1014
  return (_ctx, _cache) => {
1014
- return openBlock(), createElementBlock("div", _hoisted_1$5, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.options, (option) => {
1015
+ return openBlock(), createElementBlock("div", _hoisted_1$6, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.options, (option) => {
1015
1016
  return openBlock(), createElementBlock("label", {
1016
1017
  key: String(option.value),
1017
1018
  class: "flex-1 cursor-pointer"
@@ -1021,16 +1022,16 @@ var SegmentedControl_default = /* @__PURE__ */ defineComponent({
1021
1022
  value: option.value,
1022
1023
  name: unref(name),
1023
1024
  class: "sr-only"
1024
- }, null, 8, _hoisted_2$5), [[vModelRadio, model.value]]), createElementVNode("span", { class: normalizeClass(["flex h-10 items-center justify-center rounded-xl px-2 text-sm font-medium transition-colors select-none", model.value === option.value ? "bg-surface text-ink shadow-sm" : "text-ink-soft"]) }, toDisplayString(option.label), 3)]);
1025
+ }, null, 8, _hoisted_2$6), [[vModelRadio, model.value]]), createElementVNode("span", { class: normalizeClass(["flex h-10 items-center justify-center rounded-xl px-2 text-sm font-medium transition-colors select-none", model.value === option.value ? "bg-surface text-ink shadow-sm" : "text-ink-soft"]) }, toDisplayString(option.label), 3)]);
1025
1026
  }), 128))]);
1026
1027
  };
1027
1028
  }
1028
1029
  });
1029
1030
  //#endregion
1030
1031
  //#region src/components/SettingsGroup.vue?vue&type=script&setup=true&lang.ts
1031
- var _hoisted_1$4 = { class: "flex flex-col gap-2" };
1032
- var _hoisted_2$4 = { class: "text-ink-soft px-1 text-xs font-semibold tracking-wide uppercase" };
1033
- var _hoisted_3$2 = { class: "border-hair bg-surface rounded-card divide-hair divide-y overflow-hidden border" };
1032
+ var _hoisted_1$5 = { class: "flex flex-col gap-2" };
1033
+ var _hoisted_2$5 = { class: "text-ink-soft px-1 text-xs font-semibold tracking-wide uppercase" };
1034
+ var _hoisted_3$3 = { class: "border-hair bg-surface rounded-card divide-hair divide-y overflow-hidden border" };
1034
1035
  //#endregion
1035
1036
  //#region src/components/SettingsGroup.vue
1036
1037
  var SettingsGroup_default = /* @__PURE__ */ defineComponent({
@@ -1038,20 +1039,20 @@ var SettingsGroup_default = /* @__PURE__ */ defineComponent({
1038
1039
  props: { title: {} },
1039
1040
  setup(__props) {
1040
1041
  return (_ctx, _cache) => {
1041
- return openBlock(), createElementBlock("section", _hoisted_1$4, [createElementVNode("h2", _hoisted_2$4, toDisplayString(__props.title), 1), createElementVNode("div", _hoisted_3$2, [renderSlot(_ctx.$slots, "default")])]);
1042
+ return openBlock(), createElementBlock("section", _hoisted_1$5, [createElementVNode("h2", _hoisted_2$5, toDisplayString(__props.title), 1), createElementVNode("div", _hoisted_3$3, [renderSlot(_ctx.$slots, "default")])]);
1042
1043
  };
1043
1044
  }
1044
1045
  });
1045
1046
  //#endregion
1046
1047
  //#region src/components/SettingsRow.vue?vue&type=script&setup=true&lang.ts
1047
- var _hoisted_1$3 = { class: "flex items-center gap-3" };
1048
- var _hoisted_2$3 = {
1048
+ var _hoisted_1$4 = { class: "flex items-center gap-3" };
1049
+ var _hoisted_2$4 = {
1049
1050
  key: 0,
1050
1051
  class: "bg-muted text-ink-soft flex size-9 shrink-0 items-center justify-center rounded-xl",
1051
1052
  "aria-hidden": "true"
1052
1053
  };
1053
- var _hoisted_3$1 = { class: "min-w-0 flex-1" };
1054
- var _hoisted_4$1 = { class: "text-ink text-sm font-medium" };
1054
+ var _hoisted_3$2 = { class: "min-w-0 flex-1" };
1055
+ var _hoisted_4$2 = { class: "text-ink text-sm font-medium" };
1055
1056
  var _hoisted_5 = {
1056
1057
  key: 0,
1057
1058
  class: "text-ink-soft mt-0.5 text-xs leading-snug"
@@ -1087,9 +1088,9 @@ var SettingsRow_default = /* @__PURE__ */ defineComponent({
1087
1088
  class: normalizeClass(["flex w-full items-center gap-3 px-4 py-3 text-left", [__props.interactive ? "hover:bg-muted/60 transition-colors active:scale-[0.99]" : "", __props.stacked ? "flex-col items-stretch gap-3" : ""]]),
1088
1089
  onClick: _cache[0] || (_cache[0] = ($event) => __props.interactive && emit("click"))
1089
1090
  }, {
1090
- default: withCtx(() => [createElementVNode("div", _hoisted_1$3, [
1091
- __props.icon ? (openBlock(), createElementBlock("span", _hoisted_2$3, [(openBlock(), createBlock(resolveDynamicComponent(__props.icon), { class: "size-[18px]" }))])) : createCommentVNode("", true),
1092
- createElementVNode("div", _hoisted_3$1, [createElementVNode("p", _hoisted_4$1, toDisplayString(__props.label), 1), __props.description ? (openBlock(), createElementBlock("p", _hoisted_5, toDisplayString(__props.description), 1)) : createCommentVNode("", true)]),
1091
+ default: withCtx(() => [createElementVNode("div", _hoisted_1$4, [
1092
+ __props.icon ? (openBlock(), createElementBlock("span", _hoisted_2$4, [(openBlock(), createBlock(resolveDynamicComponent(__props.icon), { class: "size-[18px]" }))])) : createCommentVNode("", true),
1093
+ createElementVNode("div", _hoisted_3$2, [createElementVNode("p", _hoisted_4$2, toDisplayString(__props.label), 1), __props.description ? (openBlock(), createElementBlock("p", _hoisted_5, toDisplayString(__props.description), 1)) : createCommentVNode("", true)]),
1093
1094
  !__props.stacked ? (openBlock(), createElementBlock("div", _hoisted_6, [renderSlot(_ctx.$slots, "default")])) : createCommentVNode("", true),
1094
1095
  __props.interactive ? (openBlock(), createBlock(unref(ChevronRight), {
1095
1096
  key: 2,
@@ -1104,11 +1105,11 @@ var SettingsRow_default = /* @__PURE__ */ defineComponent({
1104
1105
  });
1105
1106
  //#endregion
1106
1107
  //#region src/components/SkeletonList.vue?vue&type=script&setup=true&lang.ts
1107
- var _hoisted_1$2 = {
1108
+ var _hoisted_1$3 = {
1108
1109
  role: "status",
1109
1110
  class: "flex flex-col gap-1"
1110
1111
  };
1111
- var _hoisted_2$2 = { class: "sr-only" };
1112
+ var _hoisted_2$3 = { class: "sr-only" };
1112
1113
  //#endregion
1113
1114
  //#region src/components/SkeletonList.vue
1114
1115
  var SkeletonList_default = /* @__PURE__ */ defineComponent({
@@ -1120,7 +1121,7 @@ var SkeletonList_default = /* @__PURE__ */ defineComponent({
1120
1121
  },
1121
1122
  setup(__props) {
1122
1123
  return (_ctx, _cache) => {
1123
- return openBlock(), createElementBlock("div", _hoisted_1$2, [createElementVNode("span", _hoisted_2$2, toDisplayString(__props.label), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(__props.rows, (row) => {
1124
+ return openBlock(), createElementBlock("div", _hoisted_1$3, [createElementVNode("span", _hoisted_2$3, toDisplayString(__props.label), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(__props.rows, (row) => {
1124
1125
  return openBlock(), createElementBlock("div", {
1125
1126
  key: row,
1126
1127
  class: normalizeClass(["bg-muted rounded-card animate-pulse", __props.rowHeight]),
@@ -1132,10 +1133,10 @@ var SkeletonList_default = /* @__PURE__ */ defineComponent({
1132
1133
  });
1133
1134
  //#endregion
1134
1135
  //#region src/components/StatCard.vue?vue&type=script&setup=true&lang.ts
1135
- var _hoisted_1$1 = { class: "border-hair rounded-card flex flex-1 flex-col gap-0.5 border p-3" };
1136
- var _hoisted_2$1 = { class: "flex items-baseline gap-1" };
1137
- var _hoisted_3 = { class: "text-ink text-xl font-semibold tabular-nums" };
1138
- var _hoisted_4 = { class: "text-ink-soft text-xs" };
1136
+ var _hoisted_1$2 = { class: "border-hair rounded-card flex flex-1 flex-col gap-0.5 border p-3" };
1137
+ var _hoisted_2$2 = { class: "flex items-baseline gap-1" };
1138
+ var _hoisted_3$1 = { class: "text-ink text-xl font-semibold tabular-nums" };
1139
+ var _hoisted_4$1 = { class: "text-ink-soft text-xs" };
1139
1140
  //#endregion
1140
1141
  //#region src/components/StatCard.vue
1141
1142
  var StatCard_default = /* @__PURE__ */ defineComponent({
@@ -1152,17 +1153,17 @@ var StatCard_default = /* @__PURE__ */ defineComponent({
1152
1153
  flat: ArrowRight
1153
1154
  };
1154
1155
  return (_ctx, _cache) => {
1155
- return openBlock(), createElementBlock("div", _hoisted_1$1, [createElementVNode("div", _hoisted_2$1, [createElementVNode("span", _hoisted_3, toDisplayString(__props.value), 1), __props.trend ? (openBlock(), createBlock(resolveDynamicComponent(TREND_ICON[__props.trend]), {
1156
+ return openBlock(), createElementBlock("div", _hoisted_1$2, [createElementVNode("div", _hoisted_2$2, [createElementVNode("span", _hoisted_3$1, toDisplayString(__props.value), 1), __props.trend ? (openBlock(), createBlock(resolveDynamicComponent(TREND_ICON[__props.trend]), {
1156
1157
  key: 0,
1157
1158
  class: "text-ink-soft size-3"
1158
- })) : createCommentVNode("", true)]), createElementVNode("span", _hoisted_4, toDisplayString(__props.label), 1)]);
1159
+ })) : createCommentVNode("", true)]), createElementVNode("span", _hoisted_4$1, toDisplayString(__props.label), 1)]);
1159
1160
  };
1160
1161
  }
1161
1162
  });
1162
1163
  //#endregion
1163
1164
  //#region src/components/LocaleLinks.vue?vue&type=script&setup=true&lang.ts
1164
- var _hoisted_1 = ["aria-label"];
1165
- var _hoisted_2 = [
1165
+ var _hoisted_1$1 = ["aria-label"];
1166
+ var _hoisted_2$1 = [
1166
1167
  "lang",
1167
1168
  "aria-pressed",
1168
1169
  "onClick"
@@ -1206,12 +1207,68 @@ var LocaleLinks_default = /* @__PURE__ */ defineComponent({
1206
1207
  class: normalizeClass(["rounded-full px-2.5 py-1.5 text-xs transition-colors", preference.value === locale ? "bg-muted text-ink font-semibold" : "text-ink-soft hover:text-ink"]),
1207
1208
  "aria-pressed": preference.value === locale,
1208
1209
  onClick: ($event) => preference.value = locale
1209
- }, toDisplayString(__props.labels[locale]), 11, _hoisted_2);
1210
- }), 128))], 8, _hoisted_1);
1210
+ }, toDisplayString(__props.labels[locale]), 11, _hoisted_2$1);
1211
+ }), 128))], 8, _hoisted_1$1);
1211
1212
  };
1212
1213
  }
1213
1214
  });
1214
1215
  //#endregion
1216
+ //#region src/components/GoogleButton.vue
1217
+ var GoogleButton_default = /* @__PURE__ */ defineComponent({
1218
+ __name: "GoogleButton",
1219
+ props: { label: {} },
1220
+ emits: ["click"],
1221
+ setup(__props, { emit: __emit }) {
1222
+ const emit = __emit;
1223
+ return (_ctx, _cache) => {
1224
+ return openBlock(), createElementBlock("button", {
1225
+ type: "button",
1226
+ class: "border-hair bg-surface text-ink rounded-card hover:bg-muted flex h-11 w-full items-center justify-center gap-2 border text-sm font-medium transition-colors active:scale-95",
1227
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("click"))
1228
+ }, [_cache[1] || (_cache[1] = createStaticVNode("<svg class=\"size-4\" viewBox=\"0 0 48 48\" aria-hidden=\"true\"><path fill=\"#EA4335\" d=\"M24 9.5c3.5 0 6.6 1.2 9 3.6l6.7-6.7C35.6 2.7 30.2.5 24 .5 14.6.5 6.5 5.9 2.6 13.7l7.8 6.1C12.3 13.7 17.7 9.5 24 9.5z\"></path><path fill=\"#4285F4\" d=\"M46.5 24.5c0-1.6-.1-3.1-.4-4.5H24v9h12.7c-.6 3-2.3 5.6-4.9 7.3l7.6 5.9c4.4-4.1 7.1-10.2 7.1-17.7z\"></path><path fill=\"#FBBC05\" d=\"M10.4 28.2a14.6 14.6 0 0 1 0-8.4l-7.8-6.1a24 24 0 0 0 0 20.6l7.8-6.1z\"></path><path fill=\"#34A853\" d=\"M24 47.5c6.2 0 11.5-2 15.4-5.6l-7.6-5.9c-2.1 1.4-4.8 2.3-7.8 2.3-6.3 0-11.7-4.2-13.6-10l-7.8 6.1C6.5 42.1 14.6 47.5 24 47.5z\"></path></svg>", 1)), createTextVNode(" " + toDisplayString(__props.label), 1)]);
1229
+ };
1230
+ }
1231
+ });
1232
+ //#endregion
1233
+ //#region src/components/TabBar.vue?vue&type=script&setup=true&lang.ts
1234
+ var _hoisted_1 = { class: "tab-bar" };
1235
+ var _hoisted_2 = ["aria-label"];
1236
+ var _hoisted_3 = { class: "tab-icon-slot" };
1237
+ var _hoisted_4 = { class: "tab-label" };
1238
+ //#endregion
1239
+ //#region src/components/TabBar.vue
1240
+ var TabBar_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
1241
+ __name: "TabBar",
1242
+ props: {
1243
+ items: {},
1244
+ active: {},
1245
+ label: { default: "" }
1246
+ },
1247
+ setup(__props) {
1248
+ return (_ctx, _cache) => {
1249
+ return openBlock(), createElementBlock("header", _hoisted_1, [createElementVNode("nav", {
1250
+ class: "tab-bar-inner",
1251
+ "aria-label": __props.label || void 0
1252
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item) => {
1253
+ return openBlock(), createBlock(unref(RouterLink), {
1254
+ key: item.key,
1255
+ to: item.to,
1256
+ class: normalizeClass(["tab-link", { "is-active": item.key === __props.active }]),
1257
+ "aria-current": item.key === __props.active ? "page" : void 0,
1258
+ onClick: _cache[0] || (_cache[0] = ($event) => unref(tapFeedback)())
1259
+ }, {
1260
+ default: withCtx(() => [createElementVNode("span", _hoisted_3, [(openBlock(), createBlock(resolveDynamicComponent(item.icon), { class: "tab-icon" }))]), createElementVNode("span", _hoisted_4, toDisplayString(item.label), 1)]),
1261
+ _: 2
1262
+ }, 1032, [
1263
+ "to",
1264
+ "class",
1265
+ "aria-current"
1266
+ ]);
1267
+ }), 128))], 8, _hoisted_2)]);
1268
+ };
1269
+ }
1270
+ }), [["__scopeId", "data-v-2c5e190d"]]);
1271
+ //#endregion
1215
1272
  //#region src/i18n/runtime.ts
1216
1273
  /**
1217
1274
  * Builds an i18n runtime around an app's own catalogue.
@@ -1344,6 +1401,6 @@ function createI18nRuntime(options) {
1344
1401
  */
1345
1402
  var VERSION = "0.0.0";
1346
1403
  //#endregion
1347
- export { AppError, BaseButton_default as BaseButton, BaseInput_default as BaseInput, BaseSheet_default as BaseSheet, EmptyState_default as EmptyState, LocaleLinks_default as LocaleLinks, PageHeader_default as PageHeader, SectionHeading_default as SectionHeading, SegmentedControl_default as SegmentedControl, SettingsGroup_default as SettingsGroup, SettingsRow_default as SettingsRow, SkeletonList_default as SkeletonList, StatCard_default as StatCard, ToneDot_default as ToneDot, VERSION, addDays, applyTheme, createI18nRuntime, downloadJson, eachDayOfYear, formatDate, fromDateKey, isApplePortable, isInstalled, isThemePreference, lastNDays, leadingBlanks, needsIosInstall, readStoredTheme, registerErrorMapper, relativeDayLabel, safeRedirect, setFormatLocale, setThemeStorageKey, startOfWeek, tapFeedback, toAppError, toDateKey, todayKey, useDebouncedCallback, useDragScroll, useOnline, useTheme, useToday, useVisualViewport };
1404
+ export { AppError, BaseButton_default as BaseButton, BaseInput_default as BaseInput, BaseSheet_default as BaseSheet, EmptyState_default as EmptyState, GoogleButton_default as GoogleButton, LocaleLinks_default as LocaleLinks, PageHeader_default as PageHeader, SectionHeading_default as SectionHeading, SegmentedControl_default as SegmentedControl, SettingsGroup_default as SettingsGroup, SettingsRow_default as SettingsRow, SkeletonList_default as SkeletonList, StatCard_default as StatCard, TabBar_default as TabBar, ToneDot_default as ToneDot, VERSION, addDays, applyTheme, createI18nRuntime, downloadJson, eachDayOfYear, formatDate, fromDateKey, isApplePortable, isInstalled, isThemePreference, lastNDays, leadingBlanks, needsIosInstall, readStoredTheme, registerErrorMapper, relativeDayLabel, safeRedirect, setFormatLocale, setThemeStorageKey, startOfWeek, tapFeedback, toAppError, toDateKey, todayKey, useDebouncedCallback, useDragScroll, useOnline, useTheme, useToday, useVisualViewport };
1348
1405
 
1349
1406
  //# sourceMappingURL=index.js.map