timepicker-ui-mcp 1.0.2 → 2.0.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/README.md CHANGED
@@ -1,45 +1,52 @@
1
1
  # timepicker-ui MCP server
2
2
 
3
- An MCP server for `timepicker-ui`. Connect it to Claude Code, Claude Desktop or another MCP client and the model can look up options, events and themes, generate setup snippets and custom themes, and migrate v3 code. Everything it returns comes from the library source, not from the model's memory.
3
+ An MCP server for `timepicker-ui`. Connect it to Claude Code, Claude Desktop or another MCP client and the model can look up options, events, CSS variables and themes, search the reference, generate setup snippets and custom themes, and get migration steps. Options, events, payloads, CSS variables, classes, troubleshooting and the 5.0 rename tables are generated from the library source and the docs site. Themes, modes, plugins, the method list, framework recipes and migration notes are written by hand in `src/data.ts` and `src/content.ts`.
4
4
 
5
- It runs over stdio and needs Node 18 or newer.
5
+ Every tool only reads this reference data and is marked `readOnlyHint`. Deprecated options and events are flagged, and left out of lists unless a tool is asked for them. Results link to the docs at https://timepicker-ui.vercel.app.
6
+
7
+ It runs over stdio and needs Node 20 or newer. It is built on the MCP TypeScript SDK v2 and serves clients on the 2025 protocol (`initialize`) and on the 2026-07-28 revision.
6
8
 
7
9
  ## Tools
8
10
 
9
- | Tool | Description |
10
- | ------------------------ | --------------------------------------------------------------------------------------- |
11
- | `list_themes` | Built-in themes and their CSS import paths. |
12
- | `list_modes` | UI modes (`clock`, `wheel`, `compact-wheel`) and the plugin each needs. |
13
- | `list_plugins` | Optional plugins (range, timezone, wheel) with import paths. |
14
- | `list_events` | Every EventEmitter event with its payload fields. |
15
- | `list_api` | Public instance and static API, plus the `getValue`/`setValue` contract. |
16
- | `list_css_classes` | The `tp-ui-` class names rendered by the templates. |
17
- | `get_options` | Grouped options with type, default and description. Optional `group` filter. |
18
- | `explain_option` | Full detail for one option (`group` + `name`). |
19
- | `get_theme` | The `--tp-` variables a built-in theme overrides. |
20
- | `framework_recipe` | Integration recipe and SSR notes for vanilla, react, next, vue, angular, svelte. |
21
- | `migration_guide` | `v3->v4` field mapping, or the `v4.3->v4.4` / `v4.4->v4.5` changes. |
22
- | `generate_snippet` | Vanilla JS or React setup (CSS imports and plugin registration). |
23
- | `generate_custom_theme` | A `[data-theme]` CSS block from `overrides`, or a full scaffold. |
24
- | `validate_time` | Checks `HH:MM`, `HH:MM:SS`, `h:MM AM/PM` or `h:MM:SS AM/PM` against a 12h or 24h clock. |
25
- | `validate_disabled_time` | Checks a `clock.disabledTime` config (hours, minutes, seconds, range strings). |
11
+ | Tool | Description |
12
+ | ------------------------ | --------------------------------------------------------------------------------------------------- |
13
+ | `search_reference` | Keyword search across options, nested fields, events, CSS variables, classes, methods and fixes. |
14
+ | `get_options` | Grouped options with type, default, description and nested fields. `group`, `includeDeprecated`. |
15
+ | `explain_option` | One option (`group` + `name`, dotted for nested fields such as `disabledTime.ranges`). |
16
+ | `list_events` | Events with payload fields. `includeInternal`, `includeDeprecated`. |
17
+ | `list_api` | Instance and static methods with deprecations, plus the `getValue`/`setValue` contract. |
18
+ | `list_css_variables` | `--tp-` variables with group and default. `group`, `includeUnread`. |
19
+ | `list_css_classes` | `tp-ui-` class names and state modifiers with example selectors. |
20
+ | `list_themes` | Built-in themes and their CSS import paths. |
21
+ | `get_theme` | The `--tp-` variables a built-in theme sets. |
22
+ | `list_modes` | UI modes (`clock`, `wheel`, `compact-wheel`) and the plugin each needs. |
23
+ | `list_plugins` | Optional plugins (range, timezone, wheel) with import paths and registration. |
24
+ | `framework_recipe` | Recipe and SSR notes for vanilla, react, react-wrapper (`timepicker-ui-react`), next, vue, angular, svelte. |
25
+ | `migration_guide` | `v3->v4`, `v4.3->v4.4`, `v4.4->v4.5` (with every renamed option) or `v4.5->v5` (draft). |
26
+ | `generate_snippet` | Vanilla JS or React setup with theme, mode, clock type, seconds and plugins. |
27
+ | `generate_custom_theme` | CSS for a theme applied with `ui.cssClass`, from `overrides` or as a scaffold. |
28
+ | `validate_time` | Checks `HH:MM`, `HH:MM:SS`, `h:MM AM/PM` or `h:MM:SS AM/PM` against a 12h or 24h clock. |
29
+ | `validate_disabled_time` | Checks a `clock.disabledTime` config (hours, minutes, seconds, range strings). |
30
+
31
+ Wrong arguments, such as an unknown option name, come back with `isError: true` and a hint, so the model can correct the call.
26
32
 
27
33
  ## Resources
28
34
 
29
- | URI | Description |
30
- | ------------------------------------ | --------------------------------------- |
31
- | `timepicker-ui://reference` | Grouped-options summary. |
32
- | `timepicker-ui://css-variables` | Every `--tp-` variable and its default. |
33
- | `timepicker-ui://custom-theme-guide` | How to author a custom theme. |
34
- | `timepicker-ui://troubleshooting` | Common symptoms, causes and fixes. |
35
+ | URI | Description |
36
+ | ------------------------------------ | -------------------------------------------------------- |
37
+ | `timepicker-ui://reference` | Grouped-options summary. |
38
+ | `timepicker-ui://css-variables` | Every `--tp-` variable with group, default and notes. |
39
+ | `timepicker-ui://custom-theme-guide` | How to style the picker with `ui.cssClass`. |
40
+ | `timepicker-ui://troubleshooting` | The docs troubleshooting entries with fixes and code. |
35
41
 
36
42
  ## Prompts
37
43
 
38
- | Prompt | Argument | Purpose |
39
- | -------------------- | ------------- | -------------------------------------- |
40
- | `integrate` | `framework` | Walk through a framework integration. |
41
- | `build-custom-theme` | `description` | Turn a palette into a theme. |
42
- | `migrate-v3-to-v4` | `code` | Convert v3 code to v4 grouped options. |
44
+ | Prompt | Argument | Purpose |
45
+ | -------------------- | ------------- | -------------------------------------------------------- |
46
+ | `integrate` | `framework` | Walk through a framework integration (with completion). |
47
+ | `build-custom-theme` | `description` | Turn a palette into a theme. |
48
+ | `migrate-v3-to-v4` | `code` | Convert v3 code to v4 grouped options. |
49
+ | `prepare-for-v5` | `code` | Replace deprecated 4.x API with the names 5.0 keeps. |
43
50
 
44
51
  ## Build and test
45
52
 
@@ -53,9 +60,12 @@ npm run inspect # open the MCP Inspector against the built server
53
60
 
54
61
  ## Where the data comes from
55
62
 
56
- `npm run build` first runs `scripts/generate.mjs`. It reads the library source in `../app` and writes `src/generated/*.json`: options and event payloads from `options.d.ts` and `types.d.ts`, the EventEmitter map, the theme stylesheets and `variables.scss`, and the `tp-ui-` classes from the templates.
63
+ `npm run build` first runs `scripts/generate.mjs`, which writes `src/generated/*.json`:
64
+
65
+ - From the library source in `../app` and the root `package.json`: options with defaults, examples, nested fields and `@deprecated` notes (`options.d.ts`), events and payloads (`EventEmitter.ts`, `types.d.ts`), the variables each built-in theme sets, and the library version.
66
+ - From the docs site in `../docs-app`: CSS variables and classes (`lib/generated/css-reference.json`, built from the published package CSS), the troubleshooting entries, and the renamed and removed tables of the 5.0 migration guide.
57
67
 
58
- The generated JSON is committed, so the server works without the `app/` source next to it. After changing the library, run `npm run build` again to refresh it.
68
+ The generated JSON is committed, so the server works without `app/` and `docs-app/` next to it. A source that is missing keeps its existing JSON. After changing the library or those docs pages, run `npm run build` again. When the CSS changes, regenerate the docs CSS reference first.
59
69
 
60
70
  ## Adding it to Claude Code
61
71
 
package/build/content.js CHANGED
@@ -1,4 +1,5 @@
1
- export const FRAMEWORK_NAMES = ['vanilla', 'react', 'next', 'vue', 'angular', 'svelte'];
1
+ export const FRAMEWORK_NAMES = ['vanilla', 'react', 'react-wrapper', 'next', 'vue', 'angular', 'svelte'];
2
+ const WRAPPER_NOTE = 'Render the input inside <div class="tp-ui">. Given a bare input, the picker wraps it in a new div.tp-ui, and destroy() leaves that div in place, which can break the framework when it removes the input.';
2
3
  export const FRAMEWORK_RECIPES = {
3
4
  vanilla: {
4
5
  title: 'Vanilla JS / TS',
@@ -11,65 +12,110 @@ export const FRAMEWORK_RECIPES = {
11
12
  "import 'timepicker-ui/main.css';",
12
13
  '',
13
14
  "const input = document.querySelector('#time');",
14
- 'const picker = new TimepickerUI(input, { clock: { type: \'24h\' } });',
15
+ "const picker = new TimepickerUI(input, { clock: { type: '24h' } });",
15
16
  'picker.create();',
16
17
  ].join('\n'),
18
+ docs: '/docs/quick-start',
17
19
  },
18
20
  react: {
19
21
  title: 'React',
20
22
  notes: [
21
- 'Construct in an effect after mount; destroy in the cleanup. Never construct during render.',
22
- 'Empty dependency array so it initializes once. Read values via picker.getValue() or the events.',
23
- 'There is also an official wrapper: the `timepicker-ui-react` package.',
23
+ 'Construct in an effect after mount and destroy in the cleanup. Never construct during render.',
24
+ 'Empty dependency array so it initializes once. Read values with picker.getValue() or the events.',
25
+ WRAPPER_NOTE,
26
+ 'The picker sets input.value directly and does not dispatch input or change events, so React onChange never runs. Update state in callbacks.onConfirm or picker.on("confirm", ...).',
27
+ 'The official wrapper `timepicker-ui-react` does this for you: see the react-wrapper recipe.',
24
28
  ],
25
29
  code: [
26
30
  "import { useEffect, useRef } from 'react';",
27
31
  "import { TimepickerUI } from 'timepicker-ui';",
28
32
  "import 'timepicker-ui/main.css';",
29
33
  '',
30
- 'export function TimePicker() {',
31
- ' const ref = useRef<HTMLInputElement>(null);',
34
+ 'export function TimeField() {',
35
+ ' const inputRef = useRef<HTMLInputElement>(null);',
36
+ '',
32
37
  ' useEffect(() => {',
33
- ' if (!ref.current) return;',
34
- ' const picker = new TimepickerUI(ref.current, { clock: { type: \'24h\' } });',
38
+ ' if (!inputRef.current) return;',
39
+ " const picker = new TimepickerUI(inputRef.current, { clock: { type: '24h' } });",
35
40
  ' picker.create();',
36
41
  " picker.on('confirm', (d) => console.log(d.hour, d.minutes));",
37
42
  ' return () => picker.destroy();',
38
43
  ' }, []);',
39
- ' return <input ref={ref} />;',
44
+ '',
45
+ ' return (',
46
+ ' <div className="tp-ui">',
47
+ ' <input ref={inputRef} type="text" placeholder="Select time" />',
48
+ ' </div>',
49
+ ' );',
50
+ '}',
51
+ ].join('\n'),
52
+ docs: '/docs/guides/server-rendering',
53
+ },
54
+ 'react-wrapper': {
55
+ title: 'React with timepicker-ui-react',
56
+ notes: [
57
+ 'Install with `npm install timepicker-ui-react`. It depends on timepicker-ui, so the CSS imports below resolve.',
58
+ 'Props: value / defaultValue, onConfirm, onUpdate, options (the grouped timepicker-ui options) and the usual input attributes such as name, required and placeholder.',
59
+ 'Keep a controlled value in state and update it in onConfirm (on OK) or onUpdate (on every change).',
60
+ ],
61
+ code: [
62
+ "import { useState } from 'react';",
63
+ "import { Timepicker } from 'timepicker-ui-react';",
64
+ "import 'timepicker-ui/main.css';",
65
+ '',
66
+ 'export function TimeField() {',
67
+ " const [time, setTime] = useState('10:00 AM');",
68
+ '',
69
+ ' return (',
70
+ ' <Timepicker',
71
+ ' value={time}',
72
+ " options={{ clock: { type: '12h' } }}",
73
+ ' onConfirm={({ hour, minutes, type }) => setTime(`${hour}:${minutes} ${type}`)}',
74
+ ' />',
75
+ ' );',
40
76
  '}',
41
77
  ].join('\n'),
78
+ docs: '/react/quick-start',
42
79
  },
43
80
  next: {
44
81
  title: 'Next.js (App Router)',
45
82
  notes: [
46
- 'The component must be a Client Component — add "use client" at the top.',
47
- 'timepicker-ui touches the DOM, so keep all usage inside useEffect (runs only in the browser). The library is SSR-safe (no DOM access at import time), but you still must not call create() on the server.',
48
- 'If you ever see hydration/DOM errors, dynamic-import the picker component with { ssr: false }.',
83
+ "Pages and layouts are Server Components by default. Put the picker in a file that starts with 'use client'.",
84
+ 'Importing timepicker-ui on the server is safe: loading the package does not touch window or document. new TimepickerUI() on the server throws "TimepickerUI requires browser environment", so create it in useEffect, which runs only in the browser.',
85
+ WRAPPER_NOTE,
49
86
  ],
50
87
  code: [
51
88
  "'use client';",
89
+ '',
52
90
  "import { useEffect, useRef } from 'react';",
53
91
  "import { TimepickerUI } from 'timepicker-ui';",
54
92
  "import 'timepicker-ui/main.css';",
55
93
  '',
56
- 'export function TimePicker() {',
57
- ' const ref = useRef<HTMLInputElement>(null);',
94
+ 'export function TimeField() {',
95
+ ' const inputRef = useRef<HTMLInputElement>(null);',
96
+ '',
58
97
  ' useEffect(() => {',
59
- ' if (!ref.current) return;',
60
- ' const picker = new TimepickerUI(ref.current);',
98
+ ' if (!inputRef.current) return;',
99
+ ' const picker = new TimepickerUI(inputRef.current);',
61
100
  ' picker.create();',
62
101
  ' return () => picker.destroy();',
63
102
  ' }, []);',
64
- ' return <input ref={ref} />;',
103
+ '',
104
+ ' return (',
105
+ ' <div className="tp-ui">',
106
+ ' <input ref={inputRef} type="text" placeholder="Select time" />',
107
+ ' </div>',
108
+ ' );',
65
109
  '}',
66
110
  ].join('\n'),
111
+ docs: '/docs/guides/server-rendering',
67
112
  },
68
113
  vue: {
69
114
  title: 'Vue 3',
70
115
  notes: [
71
- 'Create in onMounted, destroy in onBeforeUnmount.',
116
+ 'Create in onMounted and destroy in onBeforeUnmount. Keep the instance outside reactive state.',
72
117
  'Use a template ref to the input element.',
118
+ WRAPPER_NOTE,
73
119
  ],
74
120
  code: [
75
121
  '<script setup lang="ts">',
@@ -79,25 +125,31 @@ export const FRAMEWORK_RECIPES = {
79
125
  '',
80
126
  'const el = ref<HTMLInputElement | null>(null);',
81
127
  'let picker: TimepickerUI | null = null;',
128
+ '',
82
129
  'onMounted(() => {',
83
130
  ' if (!el.value) return;',
84
- ' picker = new TimepickerUI(el.value, { clock: { type: \'24h\' } });',
131
+ " picker = new TimepickerUI(el.value, { clock: { type: '24h' } });",
85
132
  ' picker.create();',
86
133
  '});',
134
+ '',
87
135
  'onBeforeUnmount(() => picker?.destroy());',
88
136
  '</script>',
89
137
  '',
90
138
  '<template>',
91
- ' <input ref="el" />',
139
+ ' <div class="tp-ui">',
140
+ ' <input ref="el" type="text" />',
141
+ ' </div>',
92
142
  '</template>',
93
143
  ].join('\n'),
144
+ docs: '/docs/quick-start',
94
145
  },
95
146
  angular: {
96
147
  title: 'Angular',
97
148
  notes: [
98
- 'Create in ngAfterViewInit (the view/input exists), destroy in ngOnDestroy.',
99
- 'Add timepicker-ui/main.css to angular.json styles or import it in styles.css.',
149
+ 'Create in ngAfterViewInit (the input exists) and destroy in ngOnDestroy.',
150
+ 'Add timepicker-ui/main.css to the styles array in angular.json or import it in styles.css.',
100
151
  'Use ViewChild + ElementRef to reach the input.',
152
+ WRAPPER_NOTE,
101
153
  ],
102
154
  code: [
103
155
  "import { AfterViewInit, Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';",
@@ -105,25 +157,30 @@ export const FRAMEWORK_RECIPES = {
105
157
  '',
106
158
  '@Component({',
107
159
  " selector: 'app-time',",
108
- " template: '<input #time />',",
160
+ ' template: \'<div class="tp-ui"><input #time type="text" /></div>\',',
109
161
  '})',
110
162
  'export class TimeComponent implements AfterViewInit, OnDestroy {',
111
163
  " @ViewChild('time') input!: ElementRef<HTMLInputElement>;",
112
164
  ' private picker?: TimepickerUI;',
165
+ '',
113
166
  ' ngAfterViewInit() {',
114
- ' this.picker = new TimepickerUI(this.input.nativeElement, { clock: { type: \'24h\' } });',
167
+ " this.picker = new TimepickerUI(this.input.nativeElement, { clock: { type: '24h' } });",
115
168
  ' this.picker.create();',
116
169
  ' }',
170
+ '',
117
171
  ' ngOnDestroy() {',
118
172
  ' this.picker?.destroy();',
119
173
  ' }',
120
174
  '}',
121
175
  ].join('\n'),
176
+ docs: '/docs/quick-start',
122
177
  },
123
178
  svelte: {
124
179
  title: 'Svelte',
125
180
  notes: [
126
- 'Bind the input, create in onMount, destroy in the returned cleanup.',
181
+ 'Bind the input, create in onMount and destroy in the returned cleanup.',
182
+ 'For SvelteKit the same code runs only in the browser, because onMount does not run on the server.',
183
+ WRAPPER_NOTE,
127
184
  ],
128
185
  code: [
129
186
  '<script lang="ts">',
@@ -132,21 +189,26 @@ export const FRAMEWORK_RECIPES = {
132
189
  " import 'timepicker-ui/main.css';",
133
190
  '',
134
191
  ' let input: HTMLInputElement;',
192
+ '',
135
193
  ' onMount(() => {',
136
- ' const picker = new TimepickerUI(input, { clock: { type: \'24h\' } });',
194
+ " const picker = new TimepickerUI(input, { clock: { type: '24h' } });",
137
195
  ' picker.create();',
138
196
  ' return () => picker.destroy();',
139
197
  ' });',
140
198
  '</script>',
141
199
  '',
142
- '<input bind:this={input} />',
200
+ '<div class="tp-ui">',
201
+ ' <input bind:this={input} type="text" />',
202
+ '</div>',
143
203
  ].join('\n'),
204
+ docs: '/docs/guides/server-rendering',
144
205
  },
145
206
  };
146
207
  export const MIGRATION_V3_TO_V4 = {
147
208
  summary: [
148
- 'v4 replaced the flat options object (40+ fields) with grouped options. There is NO backward-compatibility layer — all v3 code must be updated.',
209
+ 'v4 replaced the flat options object (40+ fields) with grouped options. There is no backward-compatibility layer, so all v3 code must be updated.',
149
210
  'Event handling also moved to the EventEmitter API (picker.on(event, handler)) and the grouped callbacks.* options.',
211
+ 'The "to" column uses the 4.5.0 names. 4.0-4.4 used older names for some of them; the v4.4->v4.5 path lists every rename.',
150
212
  ],
151
213
  mappings: [
152
214
  {
@@ -208,8 +270,7 @@ export const MIGRATION_V3_TO_V4 = {
208
270
  { from: 'onUpdate', to: 'callbacks.onUpdate' },
209
271
  { from: 'onSelectHour', to: 'callbacks.onSelectHour' },
210
272
  { from: 'onSelectMinute', to: 'callbacks.onSelectMinute' },
211
- { from: 'onSelectAM', to: 'callbacks.onSelectAM' },
212
- { from: 'onSelectPM', to: 'callbacks.onSelectPM' },
273
+ { from: 'onSelectAM / onSelectPM', to: 'callbacks.onSelectPeriod' },
213
274
  { from: 'onError', to: 'callbacks.onError' },
214
275
  ],
215
276
  },
@@ -233,49 +294,39 @@ export const MIGRATION_V44_TO_V45 = [
233
294
  'Range edges in clock.disabledTime.ranges may carry seconds. An edge without seconds covers its whole minute, so 4.4 ranges disable the same minutes.',
234
295
  'setOptions() (and update()) with an invalid disabledTime reports it through the error event and keeps the previous options instead of throwing.',
235
296
  'behavior.returnFocus (formerly focusInputAfterClose) still defaults to false; 5.0 makes true the default, so set it to true now.',
236
- 'Renamed options; the old names still work and are removed in 5.0: ui.mobile -> ui.initialView, ui.enableSwitchIcon -> ui.viewSwitch, ui.enableScrollbar -> ui.allowPageScroll, ui.appendModalSelector -> ui.appendTo, ui.iconTemplate / iconTemplateMobile -> ui.keyboardIcon / clockIcon, ui.inline.containerId -> ui.inline.container, clock.currentTime -> clock.defaultTime, clock.smoothHourSnap -> clock.discreteHourJump (inverted), clock.disabledTime.interval -> ranges, behavior.focusInputAfterClose -> returnFocus, timezone.whitelist -> list, clearBehavior.clearInput -> ui.clearButton: { clearInput }. Labels: one key per unit (labels.hour, labels.minute, labels.second), labels.clockTitle / keyboardTitle, labels.clock / period / timeFields / range / switchToKeyboard / switchToClock, and range.fromLabel / toLabel / timezone.label move to labels.rangeFrom / rangeTo / timezone.',
297
+ 'Many options were renamed. The old names still work and are removed in 5.0; when both are set, the new name wins. `renamed` below lists every pair.',
237
298
  'New: setOptions(), select:period with onSelectPeriod, switch:face with onSwitchFace, clock.type: "auto" (12h or 24h from the browser locale); ui.appendTo and ui.inline.container also accept an element.',
238
299
  'The focus ring is a single 3px ring in --tp-focus-ring; --tp-focus-ring-halo was removed.',
239
300
  'Deprecated (removed in 5.0): OptionTypes (use TimepickerOptions), labels.timezoneSelectorLabel, labels.format24Label, labels.toggleLabel, clock.currentTime.locales, getValue().degreesHours / degreesMinutes, the hide event, update() (use setOptions()), select:am / select:pm with onSelectAM / onSelectPM (use select:period / onSelectPeriod) and the renamed option names.',
240
301
  ];
241
- export const TROUBLESHOOTING = [
242
- {
243
- symptom: 'Picker opens but is completely unstyled.',
244
- cause: "The core stylesheet was not imported.",
245
- fix: "Always import 'timepicker-ui/main.css'. For a non-basic theme also import 'timepicker-ui/theme-<name>.css'.",
246
- },
247
- {
248
- symptom: "An 'error' event fires / a feature option is ignored.",
249
- cause: 'A plugin (range, timezone, wheel) was used without being registered.',
250
- fix: "Import the plugin and call PluginRegistry.register(Plugin) once at startup before creating the picker.",
251
- },
252
- {
253
- symptom: 'Wheel / compact-wheel mode renders as a normal clock.',
254
- cause: 'The wheel plugin is required for those modes.',
255
- fix: "Register WheelPlugin from 'timepicker-ui/plugins/wheel' and set ui.mode to 'wheel' or 'compact-wheel'.",
256
- },
257
- {
258
- symptom: 'Modal appears in the wrong place or behind other UI.',
259
- cause: 'It is appended to <body> by default.',
260
- fix: "Set ui.appendTo to a container selector or element, or check stacking context / z-index of ancestors.",
261
- },
262
- {
263
- symptom: 'Setting ui.theme to a custom name does nothing.',
264
- cause: 'The theme CSS was not loaded, or the data-theme name does not match.',
265
- fix: "Author [data-theme='name'] { --tp-*: ... }, import it after main.css, and pass ui.theme = 'name'.",
266
- },
267
- {
268
- symptom: 'Listeners or modal linger after the input is removed (e.g. SPA route change).',
269
- cause: 'The instance was not destroyed.',
270
- fix: 'Call picker.destroy() in your framework cleanup hook (useEffect cleanup, onBeforeUnmount, ngOnDestroy).',
271
- },
272
- {
273
- symptom: 'TypeScript complains about a custom theme name.',
274
- cause: 'ui.theme is a union of built-in theme names.',
275
- fix: "Cast the custom name: ui: { theme: 'ocean' as any }.",
276
- },
277
- ];
302
+ export const MIGRATION_V45_TO_V5 = {
303
+ status: '5.0 is not released and the guide is a draft: it lists what is decided so far. Every step under stepsNow works on 4.5.0 today.',
304
+ stepsNow: [
305
+ 'Upgrade to 4.5.0. Editors show deprecated APIs with a strikethrough; replace each one using `removed`.',
306
+ 'Move to the option names in `renamed`, and replace update() with setOptions().',
307
+ 'If you use callbacks.onSelectHour or select:hour to notice that the clock face changed, use switch:face or callbacks.onSwitchFace.',
308
+ 'Set behavior.returnFocus: true, so the 5.0 default does not change how your page behaves.',
309
+ 'Move code you pass as a callback to open() or destroy() to the line after the call. 5.0 removes these callback arguments.',
310
+ 'Keep custom CSS that targets tp-ui-* classes in one place, so the 5.0 class rename touches few files.',
311
+ ],
312
+ changedDefaults: [
313
+ 'behavior.returnFocus defaults to true in 5.0: on close, focus goes back to the input or to the data-open element that opened the picker. In 4.5.0 the default is false.',
314
+ "New ui.colorScheme defaults to 'auto', so the picker matches a light or dark page and basic shows its dark version on a dark page. Set ui.colorScheme: 'light' to keep the light picker everywhere.",
315
+ ],
316
+ otherChanges: [
317
+ 'select:hour, select:minute and select:second fire once when a value of their unit is picked, in every mode. In 4.5.0 clock mode they fire when a field is activated; listen to switch:face for face changes.',
318
+ 'ui.footer replaces ui.inline.showButtons and wheel.hideFooter.',
319
+ 'behavior.commitOnChange replaces ui.inline.autoUpdate and wheel.commitOnScroll: each picked value is written to the input and confirm fires with autoCommit: true.',
320
+ 'behavior.ignoreOutsideClick replaces wheel.ignoreOutsideClick and works in every mode.',
321
+ 'ui.cssClass is applied to one element instead of two. Check CSS that uses your class.',
322
+ 'TimepickerEventMap loses the internal events range:minute:commit and animation:*, and its catch-all index signature, so on("typo") becomes a type error.',
323
+ 'Removed without a 4.5.0 deprecation warning: behavior.delayHandler, TimepickerUI.isAvailable() (use document.querySelector(...) !== null), the instance getters hour / minutes / okButton / cancelButton / clockHand, the callback arguments of open() and destroy(), and the RangeManager / TimezoneManager / WheelManager exports.',
324
+ 'Plugins get a new registration API (not designed yet). Import paths timepicker-ui/plugins/range, /timezone and /wheel stay; the ./plugins/* wildcard export and the plugin UMD builds are removed.',
325
+ 'CSS classes keep the tp-ui- prefix but are renamed after Material Design component names. About 50 custom properties that no rule reads are removed.',
326
+ "The dark theme is removed: use ui.colorScheme: 'dark'. crane-straight becomes a variant of crane. ui.keyboardIcon and ui.clockIcon take an element or a factory function instead of an HTML string.",
327
+ ],
328
+ };
278
329
  export const IO_CONTRACT = {
279
- getValue: 'getValue() returns { hour: string; minutes: string; type?: string; time: string; degreesHours: number | null; degreesMinutes: number | null }.',
280
- setValue: 'setValue(time, updateInput?) accepts "HH:MM" (24h) or "h:MM AM/PM" (12h), e.g. "14:30" or "2:30 PM". updateInput defaults to writing the input value.',
330
+ getValue: 'getValue() returns { hour: string; minutes: string; seconds?: string; type?: string; time: string; degreesHours: number | null; degreesMinutes: number | null }. seconds is present only with clock.showSeconds; type is the period (AM/PM) on a 12h clock. degreesHours / degreesMinutes are deprecated (removed in 5.0). While the picker is open the value is the shown selection, otherwise the input value.',
331
+ setValue: 'setValue(time, updateInput = true) accepts "HH:MM" (24h) or "h:MM AM/PM" (12h); with clock.showSeconds also "HH:MM:SS" / "h:MM:SS AM/PM" (no seconds means :00). A valid time in the other clock format is converted ("2:30 PM" sets 14:30 on a 24h picker). An invalid string changes nothing and emits error; an empty string is ignored.',
281
332
  };
package/build/data.js CHANGED
@@ -1,4 +1,28 @@
1
- export const PACKAGE_VERSION = '4.5';
1
+ import { META } from './generated.js';
2
+ export const LIBRARY_VERSION = META.library;
3
+ export const SITE_URL = META.siteUrl;
4
+ export function docsUrl(path) {
5
+ return `${SITE_URL}${path}`;
6
+ }
7
+ export function optionGroupDocs(group) {
8
+ if (group === 'clearBehavior')
9
+ return docsUrl('/docs/features/clear-button');
10
+ return docsUrl(`/docs/api/options#${group.toLowerCase()}-options`);
11
+ }
12
+ export const DOCS = {
13
+ methods: docsUrl('/docs/api/methods'),
14
+ events: docsUrl('/docs/api/events#available-events'),
15
+ cssVariables: docsUrl('/docs/api/css-variables'),
16
+ cssClasses: docsUrl('/docs/api/css-classes'),
17
+ styling: docsUrl('/docs/advanced/styling'),
18
+ themes: docsUrl('/docs/features/themes'),
19
+ plugins: docsUrl('/docs/features/plugins'),
20
+ migrationV4: docsUrl('/docs/migration-guide'),
21
+ migrationV5: docsUrl('/docs/migration-guide-v5'),
22
+ renamed: docsUrl('/docs/api/options#renamed'),
23
+ troubleshooting: docsUrl('/docs/guides/troubleshooting'),
24
+ llms: docsUrl('/llms.txt'),
25
+ };
2
26
  export const THEMES = [
3
27
  { name: 'basic', cssImport: 'timepicker-ui/main.css', note: 'default theme, bundled in main.css' },
4
28
  { name: 'ai', cssImport: 'timepicker-ui/theme-ai.css' },
@@ -18,12 +42,12 @@ export const MODES = [
18
42
  { name: 'clock', description: 'Analog clock face with draggable hands (default).' },
19
43
  {
20
44
  name: 'wheel',
21
- description: 'Scrollable wheel picker for hours/minutes.',
45
+ description: 'Scroll wheel spinner.',
22
46
  requiresPlugin: 'wheel',
23
47
  },
24
48
  {
25
49
  name: 'compact-wheel',
26
- description: 'Condensed inline wheel variant.',
50
+ description: 'Wheel without the header. Opens as a modal, or as a popover next to the input with wheel.placement.',
27
51
  requiresPlugin: 'wheel',
28
52
  },
29
53
  ];
@@ -54,28 +78,54 @@ export const PLUGINS = [
54
78
  export const PLUGIN_NAMES = PLUGINS.map((p) => p.name);
55
79
  export const API_METHODS = [
56
80
  { signature: 'new TimepickerUI(selectorOrElement, options?)', description: 'Construct an instance.' },
57
- { signature: 'create()', description: 'Initialize and render the picker.' },
58
- { signature: 'open(callback?)', description: 'Open the modal.' },
59
- { signature: 'close(update?, callback?)', description: 'Close the modal.' },
81
+ { signature: 'create()', description: 'Initialize and render the picker. An inline picker mounts right away.' },
82
+ {
83
+ signature: 'open(callback?)',
84
+ description: 'Open the picker.',
85
+ deprecated: 'The callback argument is removed in 5.0. Run that code after the call or use picker.once("open", ...).',
86
+ },
87
+ {
88
+ signature: 'close(update?, callback?)',
89
+ description: 'Close the picker. update: true commits the selection like OK (updates the input and emits confirm); otherwise the selection is discarded.',
90
+ },
60
91
  {
61
92
  signature: 'destroy(options?)',
62
- description: 'Clean up DOM and listeners. options: { keepInputValue?, callback? }.',
93
+ description: 'Remove listeners and DOM. options: { keepInputValue?, callback? } or a callback.',
94
+ deprecated: 'The callback forms destroy(cb) and destroy({ callback }) are removed in 5.0.',
63
95
  },
64
96
  {
65
97
  signature: 'getValue()',
66
- description: 'Returns { hour, minutes, type?, time, degreesHours, degreesMinutes }.',
98
+ description: 'Returns { hour, minutes, seconds?, type?, time, degreesHours, degreesMinutes }. seconds is present only with clock.showSeconds. While open it is the shown selection, otherwise the input value.',
99
+ },
100
+ {
101
+ signature: 'setValue(time, updateInput = true)',
102
+ description: 'Set the time, e.g. "14:30", "2:30 PM" or "14:30:15". Invalid strings emit error and change nothing.',
67
103
  },
68
- { signature: 'setValue(time, updateInput?)', description: 'Set time, e.g. "14:30" or "2:30 PM".' },
69
104
  { signature: 'setOptions(options)', description: 'Change options at runtime; groups you leave out keep their values.' },
70
105
  {
71
106
  signature: 'update({ options, create? }, callback?)',
72
- description: 'Deprecated, use setOptions(). Removed in 5.0.',
107
+ description: 'Change options at runtime.',
108
+ deprecated: 'Use setOptions(options). Removed in 5.0.',
109
+ },
110
+ { signature: 'getElement()', description: 'The root .tp-ui element of the instance.' },
111
+ { signature: 'instanceId', description: 'Getter: the unique instance id.' },
112
+ { signature: 'options', description: 'Getter: the merged options. Returns the old option names until 5.0.' },
113
+ { signature: 'isInitialized / isDestroyed', description: 'Getters for the lifecycle state.' },
114
+ {
115
+ signature: 'hour / minutes / okButton / cancelButton / clockHand',
116
+ description: 'Undocumented getters that return internal DOM elements.',
117
+ deprecated: 'Removed in 5.0. Read the time with getValue().',
73
118
  },
74
119
  { signature: 'on(event, handler)', description: 'Subscribe to an event.' },
75
120
  { signature: 'once(event, handler)', description: 'Subscribe once.' },
76
121
  { signature: 'off(event, handler?)', description: 'Unsubscribe.' },
77
- { signature: 'TimepickerUI.getById(id)', description: 'Get an instance by custom behavior.id.', static: true },
122
+ { signature: 'TimepickerUI.getById(id)', description: 'Get an instance by its behavior.id.', static: true },
78
123
  { signature: 'TimepickerUI.getAllInstances()', description: 'All live instances.', static: true },
79
- { signature: 'TimepickerUI.isAvailable(selector)', description: 'Check the element exists.', static: true },
124
+ {
125
+ signature: 'TimepickerUI.isAvailable(selectorOrElement)',
126
+ description: 'Check the element exists.',
127
+ static: true,
128
+ deprecated: 'Removed in 5.0. Use document.querySelector(...) !== null.',
129
+ },
80
130
  { signature: 'TimepickerUI.destroyAll()', description: 'Destroy every instance.', static: true },
81
131
  ];