@rokkit/app 1.1.18 → 1.2.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.
@@ -0,0 +1,16 @@
1
+ type $$ComponentProps = {
2
+ skins?: Array<string | {
3
+ name: string;
4
+ label?: string;
5
+ icon?: string;
6
+ }>;
7
+ labels?: Record<string, string>;
8
+ showLabels?: boolean;
9
+ size?: 'sm' | 'md' | 'lg';
10
+ disabled?: boolean;
11
+ class?: string;
12
+ onchange?: (skin: string) => void;
13
+ };
14
+ declare const SkinSwitcherToggle: import("svelte").Component<$$ComponentProps, {}, "">;
15
+ type SkinSwitcherToggle = ReturnType<typeof SkinSwitcherToggle>;
16
+ export default SkinSwitcherToggle;
@@ -0,0 +1,8 @@
1
+ type $$ComponentProps = {
2
+ container?: string;
3
+ };
4
+ declare const TableOfContents: import("svelte").Component<$$ComponentProps, {
5
+ rescan: () => void;
6
+ }, "">;
7
+ type TableOfContents = ReturnType<typeof TableOfContents>;
8
+ export default TableOfContents;
@@ -0,0 +1,4 @@
1
+ import type { ThemeSwitcherToggleProps } from '../types/theme-switcher.js';
2
+ declare const ThemeSwitcherToggle: import("svelte").Component<ThemeSwitcherToggleProps, {}, "">;
3
+ type ThemeSwitcherToggle = ReturnType<typeof ThemeSwitcherToggle>;
4
+ export default ThemeSwitcherToggle;
@@ -0,0 +1,3 @@
1
+ export { default as ThemeSwitcherToggle } from './ThemeSwitcherToggle.svelte';
2
+ export { default as TableOfContents } from './TableOfContents.svelte';
3
+ export { default as SkinSwitcherToggle } from './SkinSwitcherToggle.svelte';
@@ -0,0 +1,3 @@
1
+ export { ThemeSwitcherToggle, SkinSwitcherToggle, TableOfContents } from './components/index.js';
2
+ export * from './types/index.js';
3
+ export { ColorModeManager, resolveMode, type ColorMode, type ResolvedMode } from './utils/color-mode.svelte.js';
@@ -0,0 +1,2 @@
1
+ export * from './theme-switcher.js';
2
+ export * from './skin-switcher.js';
@@ -0,0 +1,10 @@
1
+ export type SkinSwitcherOption = {
2
+ value: string;
3
+ label: string;
4
+ icon?: string;
5
+ };
6
+ export declare function buildSkinSwitcherOptions(skins: Array<string | {
7
+ name: string;
8
+ label?: string;
9
+ icon?: string;
10
+ }>, labels?: Record<string, string>): SkinSwitcherOption[];
@@ -0,0 +1,68 @@
1
+ /**
2
+ * ThemeSwitcherToggle Types
3
+ *
4
+ * Types and defaults for the theme mode switcher component.
5
+ */
6
+ import type { ToggleItemSnippet } from '@rokkit/ui';
7
+ import type { ColorMode } from '../utils/color-mode.svelte.js';
8
+ export interface ThemeSwitcherIcons {
9
+ system?: string;
10
+ light?: string;
11
+ dark?: string;
12
+ }
13
+ export declare const defaultThemeSwitcherIcons: Required<ThemeSwitcherIcons>;
14
+ export interface ThemeSwitcherOption {
15
+ value: ColorMode;
16
+ label: string;
17
+ icon: string;
18
+ [key: string]: unknown;
19
+ }
20
+ export interface ThemeSwitcherLabels {
21
+ system?: string;
22
+ light?: string;
23
+ dark?: string;
24
+ }
25
+ /**
26
+ * Build toggle options from icons, modes, and labels
27
+ */
28
+ export declare function buildThemeSwitcherOptions(icons: Required<ThemeSwitcherIcons>, modes: ColorMode[], labels?: ThemeSwitcherLabels): ThemeSwitcherOption[];
29
+ /**
30
+ * Visual variant.
31
+ * - `'single'` — one icon-only button that cycles between light and dark on click.
32
+ * System mode is not part of the cycle. The icon shows the mode you'd switch *to*.
33
+ * - `'pair'` — two buttons: light and dark. Equivalent to `includeSystem={false}`
34
+ * for the legacy triad UI.
35
+ * - `'triad'` — three buttons: system, light, dark. Default.
36
+ */
37
+ export type ThemeSwitcherVariant = 'single' | 'pair' | 'triad';
38
+ export interface ThemeSwitcherToggleProps {
39
+ /** Visual variant. Default: 'triad'. */
40
+ variant?: ThemeSwitcherVariant;
41
+ /**
42
+ * Which modes to show. Only honored for `variant: 'triad'`.
43
+ * Default: `['system', 'light', 'dark']`.
44
+ */
45
+ modes?: ColorMode[];
46
+ /**
47
+ * Whether to include system mode. Default: true. Shortcut for filtering 'system'
48
+ * from modes. Only honored for `variant: 'triad'`. For `'pair'` and `'single'`
49
+ * system is never included.
50
+ */
51
+ includeSystem?: boolean;
52
+ /** Override icons per mode */
53
+ icons?: ThemeSwitcherIcons;
54
+ /** Override labels per mode. Merged over messages.mode */
55
+ labels?: ThemeSwitcherLabels;
56
+ /** Show text labels alongside icons. Ignored for `variant: 'single'`. Default: false */
57
+ showLabels?: boolean;
58
+ /** Size variant. Default: 'sm' */
59
+ size?: 'sm' | 'md' | 'lg';
60
+ /** Disabled state */
61
+ disabled?: boolean;
62
+ /** Additional CSS classes */
63
+ class?: string;
64
+ /** Custom snippet for rendering toggle items (only honored for `'pair'` / `'triad'`) */
65
+ item?: ToggleItemSnippet;
66
+ /** Called when mode changes */
67
+ onchange?: (mode: ColorMode) => void;
68
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Color Mode Manager
3
+ *
4
+ * Bridges three-mode UI (system/light/dark) with vibe's two-mode store (light/dark).
5
+ * Resolves 'system' to actual OS preference via matchMedia.
6
+ */
7
+ export type ColorMode = 'system' | 'light' | 'dark';
8
+ export type ResolvedMode = 'light' | 'dark';
9
+ /**
10
+ * Resolve a ColorMode to an actual light/dark value.
11
+ * When 'system', queries the OS preference via matchMedia.
12
+ */
13
+ export declare function resolveMode(mode: ColorMode): ResolvedMode;
14
+ /**
15
+ * Theme target interface — anything with a settable mode property.
16
+ * Compatible with the vibe singleton from @rokkit/states.
17
+ */
18
+ interface ThemeTarget {
19
+ mode: string;
20
+ }
21
+ /**
22
+ * Reactive color mode manager that tracks system/light/dark,
23
+ * resolves to actual light/dark, and syncs to a theme target (e.g. vibe).
24
+ */
25
+ export declare class ColorModeManager {
26
+ #private;
27
+ constructor(target: ThemeTarget, initialMode?: ColorMode);
28
+ get mode(): ColorMode;
29
+ set mode(value: ColorMode);
30
+ get resolved(): ResolvedMode;
31
+ /**
32
+ * Start listening for OS preference changes.
33
+ * Returns a cleanup function to stop listening.
34
+ * Call in onMount or $effect.root.
35
+ */
36
+ listen(): () => void;
37
+ }
38
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rokkit/app",
3
- "version": "1.1.18",
3
+ "version": "1.2.0",
4
4
  "description": "App-level controls for Rokkit applications - theme management and UI chrome",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,41 +11,43 @@
11
11
  "access": "public"
12
12
  },
13
13
  "svelte": "./src/index.ts",
14
- "types": "./src/index.ts",
14
+ "types": "./dist/index.d.ts",
15
15
  "exports": {
16
16
  ".": {
17
- "types": "./src/index.ts",
17
+ "types": "./dist/index.d.ts",
18
18
  "svelte": "./src/index.ts",
19
19
  "default": "./src/index.ts"
20
20
  },
21
21
  "./types": {
22
- "types": "./src/types/index.ts",
22
+ "types": "./dist/types/index.d.ts",
23
23
  "default": "./src/types/index.ts"
24
24
  }
25
25
  },
26
26
  "files": [
27
27
  "src",
28
+ "dist/**/*.d.ts",
28
29
  "README.md",
29
30
  "LICENSE"
30
31
  ],
31
32
  "scripts": {
32
- "prepublishOnly": "cp ../../LICENSE .",
33
+ "prepublishOnly": "cp ../../LICENSE . && bun run build",
33
34
  "postpublish": "rm -f LICENSE",
34
35
  "check": "svelte-check --tsconfig ./tsconfig.json",
35
36
  "test": "vitest run",
36
37
  "test:watch": "vitest",
37
- "build": "echo 'No build step needed for source-only package'"
38
+ "build": "svelte-package -i src -o dist"
38
39
  },
39
40
  "dependencies": {
40
- "@rokkit/actions": "1.1.18",
41
- "@rokkit/core": "1.1.18",
42
- "@rokkit/states": "1.1.18",
43
- "@rokkit/ui": "1.1.18"
41
+ "@rokkit/actions": "1.2.0",
42
+ "@rokkit/core": "1.2.0",
43
+ "@rokkit/states": "1.2.0",
44
+ "@rokkit/ui": "1.2.0"
44
45
  },
45
46
  "peerDependencies": {
46
47
  "svelte": "^5.0.0"
47
48
  },
48
49
  "devDependencies": {
50
+ "@sveltejs/package": "^2.5.8",
49
51
  "@sveltejs/vite-plugin-svelte": "^6.2.4",
50
52
  "@testing-library/jest-dom": "^6.9.1",
51
53
  "@testing-library/svelte": "^5.3.1",
@@ -1,18 +1,19 @@
1
- <script>
2
- // @ts-nocheck
1
+ <script lang="ts">
3
2
  import { onMount } from 'svelte'
4
3
  import { keyboard } from '@rokkit/actions'
5
4
 
6
- let { container = 'main-content' } = $props()
5
+ type Heading = { id: string; text: string; level: string }
7
6
 
8
- let headings = $state([])
7
+ let { container = 'main-content' }: { container?: string } = $props()
8
+
9
+ let headings = $state<Heading[]>([])
9
10
  let activeId = $state('')
10
11
  let focusedIndex = $state(0)
11
- let navEl = $state(null)
12
- let observer = null
13
- let scrollLockTimer = null
12
+ let navEl = $state<HTMLElement | null>(null)
13
+ let observer: IntersectionObserver | null = null
14
+ let scrollLockTimer: ReturnType<typeof setTimeout> | null = null
14
15
 
15
- function slugify(text) {
16
+ function slugify(text: string | null) {
16
17
  return (text ?? '')
17
18
  .toLowerCase()
18
19
  .trim()
@@ -40,7 +41,7 @@
40
41
  observer?.disconnect()
41
42
  const main = getContainer()
42
43
  if (!main || headings.length === 0) return
43
- observer = new IntersectionObserver(
44
+ const obs = new IntersectionObserver(
44
45
  (entries) => {
45
46
  const visible = entries
46
47
  .filter((e) => e.isIntersecting)
@@ -49,17 +50,18 @@
49
50
  },
50
51
  { root: main, rootMargin: '-5% 0px -70% 0px' }
51
52
  )
53
+ observer = obs
52
54
  headings.forEach(({ id }) => {
53
55
  const el = document.getElementById(id)
54
- if (el) observer.observe(el)
56
+ if (el) obs.observe(el)
55
57
  })
56
58
  }
57
59
 
58
- function scrollToHeading(id) {
59
- const el = document.getElementById(id)
60
+ function scrollToHeading(id: string | undefined) {
61
+ const el = document.getElementById(id ?? '')
60
62
  const main = getContainer()
61
63
  if (!el || !main) return
62
- clearTimeout(scrollLockTimer)
64
+ clearTimeout(scrollLockTimer ?? undefined)
63
65
  scrollLockTimer = setTimeout(() => {
64
66
  scrollLockTimer = null
65
67
  }, 1000)
@@ -74,7 +76,7 @@
74
76
  }
75
77
 
76
78
  function getItems() {
77
- return navEl?.querySelectorAll('[data-toc-item]') ?? []
79
+ return navEl?.querySelectorAll<HTMLElement>('[data-toc-item]') ?? []
78
80
  }
79
81
 
80
82
  function handlePrevious() {
@@ -109,7 +111,7 @@
109
111
  observe()
110
112
  return () => {
111
113
  observer?.disconnect()
112
- clearTimeout(scrollLockTimer)
114
+ clearTimeout(scrollLockTimer ?? undefined)
113
115
  }
114
116
  })
115
117
  </script>
@@ -1,4 +1,7 @@
1
- export interface SkinSwitcherOption {
1
+ // A `type` alias (not an `interface`) so it satisfies the
2
+ // `Record<string, unknown>` index signature that Toggle's `ToggleItem`
3
+ // expects — TS interfaces don't implicitly carry an index signature.
4
+ export type SkinSwitcherOption = {
2
5
  value: string
3
6
  label: string
4
7
  icon?: string