@assistant-ui/react-generative-ui 0.0.23 → 0.0.24

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.
@@ -1 +1 @@
1
- {"version":3,"file":"temporal.d.ts","sourceRoot":"","sources":["../src/temporal.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,iBAAiB,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/C;IACE,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,iBAAiB,CAAC;CAC9B,GACD,eAAe,CAAC;AAUpB,eAAO,MAAM,2BAA2B,GAAI,OAAO,MAAM,KAAG,MAS3D,CAAC;AASF,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,aA8BhD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,GAAG,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,KAChC,KAAK,GAAG,CAAC,GAAG,SAYd,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,EACb,SAAS,MAAM,KACd;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAA;CAAE,GAAG,SAiB/D,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,MAOjE,CAAC;AAWF,eAAO,MAAM,qBAAqB,GAChC,SAAS,eAAe,EACxB,eAAuB,EACvB,6BAA6B,KAC5B,MAUF,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,SAAS,MAAM,EACf,eAAY,KACX,MAAM,GAAG,SAaX,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,SAI5D,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,EACb,gBAAgB,MAAM,KACrB,MAcF,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,OAAO,MAAM,EACb,YAAY,iBAAiB,KAC5B,MAQF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,OAAO,MAAM,KAAG,MAUrD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,OAAO,MAAM,EACb,gBAAgB,MAAM,KACrB,MAsCF,CAAC"}
1
+ {"version":3,"file":"temporal.d.ts","sourceRoot":"","sources":["../src/temporal.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,iBAAiB,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/C;IACE,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,iBAAiB,CAAC;CAC9B,GACD,eAAe,CAAC;AAUpB,eAAO,MAAM,2BAA2B,GAAI,OAAO,MAAM,KAAG,MAS3D,CAAC;AASF,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,aA8BhD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,GAAG,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,KAChC,KAAK,GAAG,CAAC,GAAG,SAYd,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,EACb,SAAS,MAAM,KACd;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAA;CAAE,GAAG,SAiB/D,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,MAOjE,CAAC;AAWF,eAAO,MAAM,qBAAqB,GAChC,SAAS,eAAe,EACxB,eAAuB,EACvB,6BAA6B,KAC5B,MAUF,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,SAAS,MAAM,EACf,eAAY,KACX,MAAM,GAAG,SAaX,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,SAI5D,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,EACb,gBAAgB,MAAM,KACrB,MAcF,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,OAAO,MAAM,EACb,YAAY,iBAAiB,KAC5B,MAQF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,OAAO,MAAM,KAAG,MAUrD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,OAAO,MAAM,EACb,gBAAgB,MAAM,KACrB,MA0CF,CAAC"}
package/dist/temporal.js CHANGED
@@ -140,6 +140,7 @@ const fromLocalDateTime = (value, previousValue) => {
140
140
  return formatTemporalInstant({
141
141
  ...previous,
142
142
  epochMs: date.getTime(),
143
+ ...previous.precision === "minutes" && (temporal.precision === "seconds" || date.getUTCSeconds() !== 0) ? { precision: "seconds" } : {},
143
144
  ...date.getMilliseconds() === new Date(previous.epochMs).getUTCMilliseconds() ? {} : typeof temporal.precision === "number" ? {
144
145
  precision: Math.max(temporal.precision, typeof previous.precision === "number" ? previous.precision : 0),
145
146
  subMillisecondDigits: value.slice(value.indexOf(".") + 4)
@@ -1 +1 @@
1
- {"version":3,"file":"temporal.js","names":[],"sources":["../src/temporal.ts"],"sourcesContent":["export type TemporalPrecision = \"minutes\" | \"seconds\" | number;\n\nexport type TemporalInstant = {\n kind: \"instant\";\n epochMs: number;\n offset: string;\n precision: TemporalPrecision;\n subMillisecondDigits?: string;\n};\n\nexport type TemporalValue =\n | { kind: \"date\" | \"unparseable\"; value: string }\n | {\n kind: \"time\" | \"floating\";\n value: string;\n precision: TemporalPrecision;\n }\n | TemporalInstant;\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst TIME_PATTERN =\n /^([01]\\d|2[0-3]):([0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d{1,9}))?)?$/;\nconst DATETIME_PATTERN =\n /^(\\d{4}-\\d{2}-\\d{2})T([01]\\d|2[0-3]):([0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d+))?)?(Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)?$/;\nconst INPUT_PATTERN =\n /^((?:\\d{4}-\\d{2}-\\d{2}T)?(?:[01]\\d|2[0-3]):[0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d{1,9}))?)?$/;\n\nexport const normalizeTemporalInputValue = (value: string): string => {\n const match = INPUT_PATTERN.exec(value);\n if (!match || match[2] === undefined) return value;\n const digits = match[3]?.slice(0, 3) ?? \"\";\n let end = digits.length;\n while (end > 0 && digits[end - 1] === \"0\") end--;\n const fraction = digits.slice(0, end);\n if (match[2] === \"00\" && !fraction) return match[1]!;\n return `${match[1]}:${match[2]}${fraction ? `.${fraction}` : \"\"}`;\n};\n\nconst isCalendarDate = (value: string): boolean => {\n const date = new Date(`${value}T00:00:00Z`);\n return (\n Number.isFinite(date.getTime()) && date.toISOString().slice(0, 10) === value\n );\n};\n\nexport const classifyTemporal = (value: string): TemporalValue => {\n if (DATE_PATTERN.test(value) && isCalendarDate(value)) {\n return { kind: \"date\", value };\n }\n const time = TIME_PATTERN.exec(value);\n if (time) {\n return {\n kind: \"time\",\n value,\n precision:\n time[4]?.length ?? (time[3] === undefined ? \"minutes\" : \"seconds\"),\n };\n }\n const datetime = DATETIME_PATTERN.exec(value);\n if (!datetime || !isCalendarDate(datetime[1]!)) {\n return { kind: \"unparseable\", value };\n }\n const precision =\n datetime[5]?.length ?? (datetime[4] === undefined ? \"minutes\" : \"seconds\");\n const offset = datetime[6];\n if (offset === undefined) return { kind: \"floating\", value, precision };\n return {\n kind: \"instant\",\n epochMs: Date.parse(value),\n offset,\n precision,\n ...(datetime[5] && datetime[5].length > 3\n ? { subMillisecondDigits: datetime[5].slice(3) }\n : {}),\n };\n};\n\nexport const getTemporalInputStep = (\n ...values: (string | undefined)[]\n): \"any\" | 1 | undefined => {\n let step: 1 | undefined;\n for (const value of values) {\n const temporal = classifyTemporal(value ?? \"\");\n const precision =\n \"precision\" in temporal\n ? temporal.precision\n : INPUT_PATTERN.exec(value ?? \"\")?.[3]?.length;\n if (typeof precision === \"number\") return \"any\";\n if (precision === \"seconds\") step = 1;\n }\n return step;\n};\n\nexport const splitTemporalMinutes = (\n value: string,\n offset?: string,\n): { date?: string; time: string; droppedPrecision: boolean } | undefined => {\n const temporal = classifyTemporal(value);\n if (temporal.kind === \"date\" || temporal.kind === \"unparseable\")\n return undefined;\n const wallTime =\n temporal.kind === \"instant\"\n ? formatTemporalInstant(temporal, offset).slice(\n 0,\n -(offset ?? temporal.offset).length,\n )\n : temporal.value;\n const end = temporal.kind === \"time\" ? 5 : 16;\n return {\n ...(temporal.kind === \"time\" ? {} : { date: wallTime.slice(0, 10) }),\n time: wallTime.slice(end - 5, end),\n droppedPrecision: /[1-9]/.test(wallTime.slice(end)),\n };\n};\n\nexport const mergeTemporalMinutes = (date: string, time: string): string => {\n if (\n classifyTemporal(date).kind !== \"date\" ||\n classifyTemporal(time).kind !== \"time\"\n )\n return \"\";\n return `${date}T${time.slice(0, 5)}`;\n};\n\nconst pad = (value: number, digits = 2): string =>\n String(value).padStart(digits, \"0\");\n\nconst offsetMinutes = (offset: string): number =>\n offset === \"Z\"\n ? 0\n : (offset.startsWith(\"-\") ? -1 : 1) *\n (Number(offset.slice(1, 3)) * 60 + Number(offset.slice(4, 6)));\n\nexport const formatTemporalInstant = (\n instant: TemporalInstant,\n offset = instant.offset,\n precision = instant.precision,\n): string => {\n const date = new Date(instant.epochMs + offsetMinutes(offset) * 60_000);\n const iso = date.toISOString();\n const wallTime =\n precision === \"minutes\"\n ? iso.slice(0, 16)\n : precision === \"seconds\"\n ? iso.slice(0, 19)\n : `${iso.slice(0, 19)}.${(pad(date.getUTCMilliseconds(), 3) + (instant.subMillisecondDigits ?? \"\")).padEnd(precision, \"0\").slice(0, precision)}`;\n return `${wallTime}${offset}`;\n};\n\nexport const formatTemporalUnixSeconds = (\n seconds: number,\n offset = \"Z\",\n): string | undefined => {\n if (!Number.isInteger(seconds)) return undefined;\n try {\n const value = formatTemporalInstant({\n kind: \"instant\",\n epochMs: seconds * 1000,\n offset,\n precision: \"seconds\",\n });\n return classifyTemporal(value).kind === \"instant\" ? value : undefined;\n } catch {\n return undefined;\n }\n};\n\nexport const temporalOffsetLabel = (value: string): string | undefined => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return undefined;\n return temporal.offset === \"Z\" ? \"UTC\" : `UTC${temporal.offset}`;\n};\n\nexport const fromOffsetDateTime = (\n value: string,\n previousValue?: string,\n): string => {\n if (value === \"\") return \"\";\n if (classifyTemporal(value).kind !== \"floating\") return value;\n const previous = classifyTemporal(previousValue ?? \"\");\n if (previous.kind !== \"instant\") return value;\n const epochMs = Date.parse(`${value}${previous.offset}`);\n const minuteRemainder =\n previous.precision === \"minutes\"\n ? 0\n : ((previous.epochMs % 60_000) + 60_000) % 60_000;\n return formatTemporalInstant({\n ...previous,\n epochMs: epochMs + minuteRemainder,\n });\n};\n\nexport const toLocalDateTime = (\n value: string,\n precision?: TemporalPrecision,\n): string => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return value;\n const date = new Date(temporal.epochMs);\n const wallTime = `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;\n return (precision ?? temporal.precision) === \"minutes\"\n ? wallTime\n : `${wallTime}:${pad(date.getSeconds())}${typeof precision === \"number\" ? formatTemporalInstant(temporal, \"Z\", precision).slice(19, -1) : \"\"}`;\n};\n\nexport const toPickerLocalDateTime = (value: string): string => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return normalizeTemporalInputValue(value);\n const local = toLocalDateTime(\n value,\n typeof temporal.precision === \"number\" ? 3 : temporal.precision,\n );\n return typeof temporal.precision !== \"number\" || local.endsWith(\".000\")\n ? normalizeTemporalInputValue(local)\n : local;\n};\n\nexport const fromLocalDateTime = (\n value: string,\n previousValue?: string,\n): string => {\n const temporal = classifyTemporal(value);\n const previous = classifyTemporal(previousValue ?? \"\");\n if (temporal.kind !== \"floating\" || previous.kind === \"floating\") {\n return value;\n }\n const date = new Date(\n value.replace(\n /\\.(\\d{1,2})$/,\n (_, digits: string) => `.${digits.padEnd(3, \"0\")}`,\n ),\n );\n const minutes = -date.getTimezoneOffset();\n const offset = `${minutes < 0 ? \"-\" : \"+\"}${pad(Math.floor(Math.abs(minutes) / 60))}:${pad(Math.abs(minutes) % 60)}`;\n if (previous.kind !== \"instant\") {\n return formatTemporalInstant({\n kind: \"instant\",\n epochMs: date.getTime(),\n offset,\n precision: \"seconds\",\n });\n }\n return formatTemporalInstant({\n ...previous,\n epochMs: date.getTime(),\n ...(date.getMilliseconds() ===\n new Date(previous.epochMs).getUTCMilliseconds()\n ? {}\n : typeof temporal.precision === \"number\"\n ? {\n precision: Math.max(\n temporal.precision,\n typeof previous.precision === \"number\" ? previous.precision : 0,\n ),\n subMillisecondDigits: value.slice(value.indexOf(\".\") + 4),\n }\n : { subMillisecondDigits: \"\" }),\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"temporal.js","names":[],"sources":["../src/temporal.ts"],"sourcesContent":["export type TemporalPrecision = \"minutes\" | \"seconds\" | number;\n\nexport type TemporalInstant = {\n kind: \"instant\";\n epochMs: number;\n offset: string;\n precision: TemporalPrecision;\n subMillisecondDigits?: string;\n};\n\nexport type TemporalValue =\n | { kind: \"date\" | \"unparseable\"; value: string }\n | {\n kind: \"time\" | \"floating\";\n value: string;\n precision: TemporalPrecision;\n }\n | TemporalInstant;\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst TIME_PATTERN =\n /^([01]\\d|2[0-3]):([0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d{1,9}))?)?$/;\nconst DATETIME_PATTERN =\n /^(\\d{4}-\\d{2}-\\d{2})T([01]\\d|2[0-3]):([0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d+))?)?(Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)?$/;\nconst INPUT_PATTERN =\n /^((?:\\d{4}-\\d{2}-\\d{2}T)?(?:[01]\\d|2[0-3]):[0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d{1,9}))?)?$/;\n\nexport const normalizeTemporalInputValue = (value: string): string => {\n const match = INPUT_PATTERN.exec(value);\n if (!match || match[2] === undefined) return value;\n const digits = match[3]?.slice(0, 3) ?? \"\";\n let end = digits.length;\n while (end > 0 && digits[end - 1] === \"0\") end--;\n const fraction = digits.slice(0, end);\n if (match[2] === \"00\" && !fraction) return match[1]!;\n return `${match[1]}:${match[2]}${fraction ? `.${fraction}` : \"\"}`;\n};\n\nconst isCalendarDate = (value: string): boolean => {\n const date = new Date(`${value}T00:00:00Z`);\n return (\n Number.isFinite(date.getTime()) && date.toISOString().slice(0, 10) === value\n );\n};\n\nexport const classifyTemporal = (value: string): TemporalValue => {\n if (DATE_PATTERN.test(value) && isCalendarDate(value)) {\n return { kind: \"date\", value };\n }\n const time = TIME_PATTERN.exec(value);\n if (time) {\n return {\n kind: \"time\",\n value,\n precision:\n time[4]?.length ?? (time[3] === undefined ? \"minutes\" : \"seconds\"),\n };\n }\n const datetime = DATETIME_PATTERN.exec(value);\n if (!datetime || !isCalendarDate(datetime[1]!)) {\n return { kind: \"unparseable\", value };\n }\n const precision =\n datetime[5]?.length ?? (datetime[4] === undefined ? \"minutes\" : \"seconds\");\n const offset = datetime[6];\n if (offset === undefined) return { kind: \"floating\", value, precision };\n return {\n kind: \"instant\",\n epochMs: Date.parse(value),\n offset,\n precision,\n ...(datetime[5] && datetime[5].length > 3\n ? { subMillisecondDigits: datetime[5].slice(3) }\n : {}),\n };\n};\n\nexport const getTemporalInputStep = (\n ...values: (string | undefined)[]\n): \"any\" | 1 | undefined => {\n let step: 1 | undefined;\n for (const value of values) {\n const temporal = classifyTemporal(value ?? \"\");\n const precision =\n \"precision\" in temporal\n ? temporal.precision\n : INPUT_PATTERN.exec(value ?? \"\")?.[3]?.length;\n if (typeof precision === \"number\") return \"any\";\n if (precision === \"seconds\") step = 1;\n }\n return step;\n};\n\nexport const splitTemporalMinutes = (\n value: string,\n offset?: string,\n): { date?: string; time: string; droppedPrecision: boolean } | undefined => {\n const temporal = classifyTemporal(value);\n if (temporal.kind === \"date\" || temporal.kind === \"unparseable\")\n return undefined;\n const wallTime =\n temporal.kind === \"instant\"\n ? formatTemporalInstant(temporal, offset).slice(\n 0,\n -(offset ?? temporal.offset).length,\n )\n : temporal.value;\n const end = temporal.kind === \"time\" ? 5 : 16;\n return {\n ...(temporal.kind === \"time\" ? {} : { date: wallTime.slice(0, 10) }),\n time: wallTime.slice(end - 5, end),\n droppedPrecision: /[1-9]/.test(wallTime.slice(end)),\n };\n};\n\nexport const mergeTemporalMinutes = (date: string, time: string): string => {\n if (\n classifyTemporal(date).kind !== \"date\" ||\n classifyTemporal(time).kind !== \"time\"\n )\n return \"\";\n return `${date}T${time.slice(0, 5)}`;\n};\n\nconst pad = (value: number, digits = 2): string =>\n String(value).padStart(digits, \"0\");\n\nconst offsetMinutes = (offset: string): number =>\n offset === \"Z\"\n ? 0\n : (offset.startsWith(\"-\") ? -1 : 1) *\n (Number(offset.slice(1, 3)) * 60 + Number(offset.slice(4, 6)));\n\nexport const formatTemporalInstant = (\n instant: TemporalInstant,\n offset = instant.offset,\n precision = instant.precision,\n): string => {\n const date = new Date(instant.epochMs + offsetMinutes(offset) * 60_000);\n const iso = date.toISOString();\n const wallTime =\n precision === \"minutes\"\n ? iso.slice(0, 16)\n : precision === \"seconds\"\n ? iso.slice(0, 19)\n : `${iso.slice(0, 19)}.${(pad(date.getUTCMilliseconds(), 3) + (instant.subMillisecondDigits ?? \"\")).padEnd(precision, \"0\").slice(0, precision)}`;\n return `${wallTime}${offset}`;\n};\n\nexport const formatTemporalUnixSeconds = (\n seconds: number,\n offset = \"Z\",\n): string | undefined => {\n if (!Number.isInteger(seconds)) return undefined;\n try {\n const value = formatTemporalInstant({\n kind: \"instant\",\n epochMs: seconds * 1000,\n offset,\n precision: \"seconds\",\n });\n return classifyTemporal(value).kind === \"instant\" ? value : undefined;\n } catch {\n return undefined;\n }\n};\n\nexport const temporalOffsetLabel = (value: string): string | undefined => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return undefined;\n return temporal.offset === \"Z\" ? \"UTC\" : `UTC${temporal.offset}`;\n};\n\nexport const fromOffsetDateTime = (\n value: string,\n previousValue?: string,\n): string => {\n if (value === \"\") return \"\";\n if (classifyTemporal(value).kind !== \"floating\") return value;\n const previous = classifyTemporal(previousValue ?? \"\");\n if (previous.kind !== \"instant\") return value;\n const epochMs = Date.parse(`${value}${previous.offset}`);\n const minuteRemainder =\n previous.precision === \"minutes\"\n ? 0\n : ((previous.epochMs % 60_000) + 60_000) % 60_000;\n return formatTemporalInstant({\n ...previous,\n epochMs: epochMs + minuteRemainder,\n });\n};\n\nexport const toLocalDateTime = (\n value: string,\n precision?: TemporalPrecision,\n): string => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return value;\n const date = new Date(temporal.epochMs);\n const wallTime = `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;\n return (precision ?? temporal.precision) === \"minutes\"\n ? wallTime\n : `${wallTime}:${pad(date.getSeconds())}${typeof precision === \"number\" ? formatTemporalInstant(temporal, \"Z\", precision).slice(19, -1) : \"\"}`;\n};\n\nexport const toPickerLocalDateTime = (value: string): string => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return normalizeTemporalInputValue(value);\n const local = toLocalDateTime(\n value,\n typeof temporal.precision === \"number\" ? 3 : temporal.precision,\n );\n return typeof temporal.precision !== \"number\" || local.endsWith(\".000\")\n ? normalizeTemporalInputValue(local)\n : local;\n};\n\nexport const fromLocalDateTime = (\n value: string,\n previousValue?: string,\n): string => {\n const temporal = classifyTemporal(value);\n const previous = classifyTemporal(previousValue ?? \"\");\n if (temporal.kind !== \"floating\" || previous.kind === \"floating\") {\n return value;\n }\n const date = new Date(\n value.replace(\n /\\.(\\d{1,2})$/,\n (_, digits: string) => `.${digits.padEnd(3, \"0\")}`,\n ),\n );\n const minutes = -date.getTimezoneOffset();\n const offset = `${minutes < 0 ? \"-\" : \"+\"}${pad(Math.floor(Math.abs(minutes) / 60))}:${pad(Math.abs(minutes) % 60)}`;\n if (previous.kind !== \"instant\") {\n return formatTemporalInstant({\n kind: \"instant\",\n epochMs: date.getTime(),\n offset,\n precision: \"seconds\",\n });\n }\n return formatTemporalInstant({\n ...previous,\n epochMs: date.getTime(),\n ...(previous.precision === \"minutes\" &&\n (temporal.precision === \"seconds\" || date.getUTCSeconds() !== 0)\n ? { precision: \"seconds\" }\n : {}),\n ...(date.getMilliseconds() ===\n new Date(previous.epochMs).getUTCMilliseconds()\n ? {}\n : typeof temporal.precision === \"number\"\n ? {\n precision: Math.max(\n temporal.precision,\n typeof previous.precision === \"number\" ? previous.precision : 0,\n ),\n subMillisecondDigits: value.slice(value.indexOf(\".\") + 4),\n }\n : { subMillisecondDigits: \"\" }),\n });\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/vocabulary/interactive.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/vocabulary/interactive.tsx"],"names":[],"mappings":"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"}
@@ -178,13 +178,20 @@ function DatePickerRender({ inputType = "date", value, min, max, label, name, $a
178
178
  const [selection, setSelection] = React.useState({
179
179
  initialValue,
180
180
  value: initialValue,
181
- anchor: initialValue
182
- });
183
- if (selection.initialValue !== initialValue) setSelection({
184
- initialValue,
185
- value: initialValue,
186
- anchor: isBound && initialValue === selection.value ? selection.anchor : initialValue
181
+ anchor: initialValue,
182
+ committedValue: initialValue,
183
+ edited: false
187
184
  });
185
+ if (selection.initialValue !== initialValue) {
186
+ const isEcho = isBound && initialValue === selection.value;
187
+ setSelection({
188
+ initialValue,
189
+ value: initialValue,
190
+ anchor: isEcho ? selection.anchor : initialValue,
191
+ committedValue: isEcho ? selection.committedValue : initialValue,
192
+ edited: isEcho && selection.edited
193
+ });
194
+ }
188
195
  const currentValue = isBound ? initialValue : selection.value;
189
196
  const temporal = classifyTemporal(currentValue ?? "");
190
197
  const anchor = classifyTemporal(selection.anchor ?? "");
@@ -203,6 +210,21 @@ function DatePickerRender({ inputType = "date", value, min, max, label, name, $a
203
210
  const hydrated = React.useSyncExternalStore(subscribeToHydration, clientHydrationSnapshot, serverHydrationSnapshot);
204
211
  const showRawValue = inputType === "datetime" && temporal.kind === "instant" && !hydrated;
205
212
  const displayValue = currentValue === void 0 ? void 0 : inputType === "datetime" && hydrated ? toPickerLocalDateTime(currentValue) : normalizeTemporalInputValue(currentValue);
213
+ const keyboardActive = React.useRef(false);
214
+ const valueFromInput = (input) => {
215
+ const inputValue = normalizeTemporalInputValue(input.value);
216
+ return inputType === "datetime" ? fromLocalDateTime(inputValue, selection.anchor) : inputValue;
217
+ };
218
+ const commit = (input) => {
219
+ if (!selection.edited) return;
220
+ const nextValue = valueFromInput(input);
221
+ setSelection({
222
+ ...selection,
223
+ committedValue: nextValue,
224
+ edited: false
225
+ });
226
+ if (nextValue !== selection.committedValue) fire($action, $dispatch, nextValue, input);
227
+ };
206
228
  return /* @__PURE__ */ jsx("input", {
207
229
  type: showRawValue ? "text" : inputType === "datetime" ? "datetime-local" : inputType,
208
230
  readOnly: showRawValue || void 0,
@@ -214,20 +236,52 @@ function DatePickerRender({ inputType = "date", value, min, max, label, name, $a
214
236
  ...isBound || inputType === "datetime" ? { value: displayValue ?? "" } : { defaultValue: displayValue },
215
237
  min: inputType === "datetime" && hydrated && min !== void 0 ? normalizeTemporalInputValue(toLocalDateTime(min, minimum.kind === "instant" ? minimum.precision : void 0)) : min === void 0 ? void 0 : normalizeTemporalInputValue(min),
216
238
  max: inputType === "datetime" && hydrated && max !== void 0 ? normalizeTemporalInputValue(toLocalDateTime(max, maximum.kind === "instant" ? maximum.precision : void 0)) : max === void 0 ? void 0 : normalizeTemporalInputValue(max),
217
- step: inputType !== "date" ? getTemporalInputStep(selection.anchor, currentValue, min, max) : void 0,
239
+ step: inputType !== "date" ? getTemporalInputStep(selection.anchor, hasInstantAnchor ? displayValue : currentValue, min, max) : void 0,
218
240
  onChange: (e) => {
219
- const inputValue = normalizeTemporalInputValue(e.currentTarget.value);
220
- const nextValue = inputType === "datetime" ? fromLocalDateTime(inputValue, selection.anchor) : inputValue;
241
+ const nextValue = valueFromInput(e.currentTarget);
221
242
  const canonical = canonicalFieldValue(nextValue);
222
243
  if (canonical !== void 0) e.currentTarget.setAttribute(FIELD_VALUE_ATTR, canonical);
223
244
  else e.currentTarget.removeAttribute(FIELD_VALUE_ATTR);
245
+ const typed = keyboardActive.current;
246
+ keyboardActive.current = false;
224
247
  setSelection({
225
248
  initialValue,
226
249
  value: nextValue,
227
- anchor: selection.anchor
250
+ anchor: selection.anchor,
251
+ committedValue: typed ? selection.committedValue : nextValue,
252
+ edited: typed
228
253
  });
229
254
  updateBinding?.(nextValue);
230
- fire($action, $dispatch, nextValue, e.currentTarget);
255
+ if (!typed && nextValue !== selection.committedValue) fire($action, $dispatch, nextValue, e.currentTarget);
256
+ },
257
+ onBlur: (e) => {
258
+ keyboardActive.current = false;
259
+ commit(e.currentTarget);
260
+ },
261
+ onKeyDown: (e) => {
262
+ if (e.key === "Enter" && !e.nativeEvent.isComposing && !e.currentTarget.form) {
263
+ commit(e.currentTarget);
264
+ return;
265
+ }
266
+ keyboardActive.current = !e.altKey && !e.ctrlKey && !e.metaKey && (e.key.length === 1 || [
267
+ "ArrowDown",
268
+ "ArrowUp",
269
+ "Backspace",
270
+ "Delete",
271
+ "End",
272
+ "Home",
273
+ "PageDown",
274
+ "PageUp"
275
+ ].includes(e.key));
276
+ },
277
+ onKeyUp: () => {
278
+ keyboardActive.current = false;
279
+ },
280
+ onPaste: () => {
281
+ keyboardActive.current = true;
282
+ },
283
+ onCut: () => {
284
+ keyboardActive.current = true;
231
285
  }
232
286
  }, isBound ? void 0 : initialValue);
233
287
  }
@@ -465,7 +519,7 @@ const interactiveVocabulary = {
465
519
  render: InputRender
466
520
  },
467
521
  DatePicker: {
468
- description: "A date, datetime, or time input. Carries `$action` describing the on-select behavior.",
522
+ description: "A date, datetime, or time input. Carries `$action`, which runs once per committed value: a pick from the picker, or a typed value on blur or Enter.",
469
523
  properties: z.object({
470
524
  inputType: z.enum([
471
525
  "date",
@@ -1 +1 @@
1
- {"version":3,"file":"interactive.js","names":[],"sources":["../../src/vocabulary/interactive.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { z } from \"zod\";\nimport {\n CHECKBOX_GROUP_ATTR,\n FIELD_NAME_ATTR,\n FIELD_VALUE_ATTR,\n GENERATED_NAME_ATTR,\n} from \"../constants\";\nimport type { Action } from \"../ir\";\nimport { BUTTON_STYLES } from \"../ir\";\nimport type {\n GenerativeUIDispatch,\n GenerativeUILibrary,\n GenerativeUIStatus,\n} from \"../types\";\nimport { A2uiBindingContext, useA2uiBinding } from \"../a2ui/BindingContext\";\nimport { useAnsweredValue } from \"../answeredValues\";\nimport { actionAttr, fire } from \"./dispatch\";\nimport { toTextContent } from \"./toTextContent\";\nimport { useRadioGroupName } from \"../RadioGroupScope\";\nimport {\n classifyTemporal,\n fromLocalDateTime,\n getTemporalInputStep,\n normalizeTemporalInputValue,\n toLocalDateTime,\n toPickerLocalDateTime,\n} from \"../temporal\";\n\nconst optionSchema = z.object({\n label: z.string(),\n value: z.string(),\n});\n\nconst describedOptionSchema = optionSchema.extend({\n description: z.string().optional(),\n});\n\ntype Option = { label: string; description?: string; value: string };\n\nconst isOption = (option: unknown): option is Option =>\n option !== null &&\n typeof option === \"object\" &&\n \"label\" in option &&\n typeof option.label === \"string\" &&\n (!(\"description\" in option) || typeof option.description === \"string\") &&\n \"value\" in option &&\n typeof option.value === \"string\";\n\nconst mapOptions = (\n options: unknown,\n render: (option: Option, key: string) => React.ReactNode,\n) => {\n const occurrences = new Map<string, number>();\n return (Array.isArray(options) ? options : []).map((option) => {\n if (!isOption(option)) return null;\n const occurrence = occurrences.get(option.value) ?? 0;\n occurrences.set(option.value, occurrence + 1);\n return render(option, JSON.stringify([option.value, occurrence]));\n });\n};\n\ntype RadioGroupRenderProps = {\n value?: string;\n options: Option[];\n name?: string;\n label?: string;\n defaultValue?: string;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction RadioGroupRender({\n value,\n options,\n name,\n label,\n defaultValue,\n children,\n $action,\n $dispatch,\n}: RadioGroupRenderProps) {\n const groupName = useRadioGroupName(name);\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : defaultValue;\n return (\n <fieldset\n key={initialValue}\n data-aui=\"radiogroup\"\n data-aui-action={actionAttr($action)}\n aria-label={label}\n >\n {mapOptions(options, (option, key) => (\n <label key={key} data-aui=\"radiogroup-option\">\n <input\n type=\"radio\"\n name={groupName}\n {...{ [FIELD_NAME_ATTR]: name }}\n {...(name == null ? { [GENERATED_NAME_ATTR]: \"\" } : {})}\n value={option.value}\n {...(isBound\n ? { checked: (answeredValue ?? value) === option.value }\n : { defaultChecked: initialValue === option.value })}\n onChange={(e) => {\n updateBinding?.(option.value);\n fire($action, $dispatch, option.value, e.currentTarget);\n }}\n />\n {option.description ? (\n <span data-aui=\"option-content\">\n <span data-aui=\"option-label\">{option.label}</span>\n <span data-aui=\"option-description\">{option.description}</span>\n </span>\n ) : (\n option.label\n )}\n </label>\n ))}\n {children}\n </fieldset>\n );\n}\n\ntype CheckboxGroupRenderProps = {\n value?: string[];\n options: Option[];\n name?: string;\n label?: string;\n defaultValue?: string[];\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nconst checkedGroupValues = (input: HTMLInputElement): string[] =>\n Array.from(\n input\n .closest(\"fieldset\")\n ?.querySelectorAll<HTMLInputElement>(\n `:scope > label > input[${CHECKBOX_GROUP_ATTR}]:checked`,\n ) ?? [],\n (checkbox) => checkbox.value,\n );\n\nfunction CheckboxGroupRender({\n value,\n options,\n name,\n label,\n defaultValue,\n children,\n $action,\n $dispatch,\n}: CheckboxGroupRenderProps) {\n const generatedName = React.useId();\n const fieldName = name ?? generatedName;\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const checkedValues =\n Array.isArray(answeredValue) &&\n answeredValue.every((value): value is string => typeof value === \"string\")\n ? answeredValue\n : Array.isArray(defaultValue)\n ? defaultValue\n : [];\n return (\n <fieldset\n key={JSON.stringify(checkedValues)}\n data-aui=\"checkboxgroup\"\n data-aui-action={actionAttr($action)}\n aria-label={label}\n >\n {mapOptions(options, (option, key) => (\n <label key={key} data-aui=\"checkboxgroup-option\">\n <input\n type=\"checkbox\"\n name={fieldName}\n {...{ [CHECKBOX_GROUP_ATTR]: \"\" }}\n {...(name == null ? { [GENERATED_NAME_ATTR]: \"\" } : {})}\n value={option.value}\n {...(isBound\n ? {\n checked: (Array.isArray(answeredValue)\n ? checkedValues\n : (value ?? [])\n ).includes(option.value),\n }\n : { defaultChecked: checkedValues.includes(option.value) })}\n onChange={(e) => {\n const selected = checkedGroupValues(e.currentTarget);\n updateBinding?.(selected);\n fire($action, $dispatch, selected, e.currentTarget);\n }}\n />\n {option.description ? (\n <span data-aui=\"option-content\">\n <span data-aui=\"option-label\">{option.label}</span>\n <span data-aui=\"option-description\">{option.description}</span>\n </span>\n ) : (\n option.label\n )}\n </label>\n ))}\n {children}\n </fieldset>\n );\n}\n\ntype SelectRenderProps = {\n value?: string;\n options: Option[];\n placeholder?: string;\n label?: string;\n name?: string;\n defaultValue?: string;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction SelectRender({\n value,\n options,\n placeholder,\n label,\n name,\n defaultValue,\n $action,\n $dispatch,\n children,\n}: SelectRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const placeholderText = toTextContent(placeholder);\n const initialValue =\n typeof answeredValue === \"string\"\n ? answeredValue\n : typeof defaultValue === \"string\"\n ? defaultValue\n : \"\";\n return (\n <select\n key={initialValue}\n data-aui=\"select\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={(e) => {\n updateBinding?.(e.currentTarget.value);\n fire($action, $dispatch, e.currentTarget.value, e.currentTarget);\n }}\n >\n {placeholderText ? (\n <option value=\"\" disabled>\n {placeholderText}\n </option>\n ) : null}\n {mapOptions(options, (option, key) => (\n <option key={key} value={option.value}>\n {option.label}\n </option>\n ))}\n {children}\n </select>\n );\n}\n\ntype InputRenderProps = {\n value?: string;\n inputType?: \"text\" | \"password\" | \"number\";\n placeholder?: string;\n multiline?: boolean;\n label?: string;\n name?: string;\n defaultValue?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction InputRender({\n value,\n inputType,\n placeholder,\n multiline,\n label,\n name,\n defaultValue,\n $action,\n $dispatch,\n}: InputRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : defaultValue;\n const submit = (control: HTMLInputElement | HTMLTextAreaElement) =>\n fire($action, $dispatch, control.value, control);\n return multiline && (inputType === undefined || inputType === \"text\") ? (\n <textarea\n key={initialValue}\n data-aui=\"input\"\n data-aui-multiline\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n placeholder={placeholder}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={\n updateBinding ? (e) => updateBinding(e.currentTarget.value) : undefined\n }\n onKeyDown={(e) => {\n if (\n e.key !== \"Enter\" ||\n !(e.ctrlKey || e.metaKey) ||\n e.nativeEvent.isComposing\n )\n return;\n if (e.currentTarget.form) {\n e.preventDefault();\n HTMLFormElement.prototype.requestSubmit.call(e.currentTarget.form);\n } else {\n submit(e.currentTarget);\n }\n }}\n />\n ) : (\n <input\n key={initialValue}\n type={inputType}\n step={inputType === \"number\" ? \"any\" : undefined}\n data-aui=\"input\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n placeholder={placeholder}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={\n updateBinding ? (e) => updateBinding(e.currentTarget.value) : undefined\n }\n onKeyDown={(e) => {\n if (\n e.key === \"Enter\" &&\n !e.nativeEvent.isComposing &&\n !e.currentTarget.form\n )\n submit(e.currentTarget);\n }}\n />\n );\n}\n\ntype DatePickerRenderProps = {\n inputType?: \"date\" | \"datetime\" | \"time\";\n value?: string;\n min?: string;\n max?: string;\n label?: string;\n name?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nconst subscribeToHydration = () => () => {};\nconst clientHydrationSnapshot = () => true;\nconst serverHydrationSnapshot = () => false;\n\nfunction DatePickerRender({\n inputType = \"date\",\n value,\n min,\n max,\n label,\n name,\n $action,\n $dispatch,\n}: DatePickerRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : value;\n const [selection, setSelection] = React.useState({\n initialValue,\n value: initialValue,\n anchor: initialValue,\n });\n if (selection.initialValue !== initialValue) {\n setSelection({\n initialValue,\n value: initialValue,\n anchor:\n isBound && initialValue === selection.value\n ? selection.anchor\n : initialValue,\n });\n }\n const currentValue = isBound ? initialValue : selection.value;\n const temporal = classifyTemporal(currentValue ?? \"\");\n const anchor = classifyTemporal(selection.anchor ?? \"\");\n const hasInstantAnchor =\n inputType === \"datetime\" && anchor.kind === \"instant\";\n const canonicalFieldValue = (value: string | undefined) => {\n const current = value ?? \"\";\n const kind = classifyTemporal(current).kind;\n if (inputType === \"time\")\n return kind === \"time\" && normalizeTemporalInputValue(current) !== current\n ? value\n : undefined;\n if (inputType !== \"datetime\") return undefined;\n if (kind === \"instant\") return value;\n if (hasInstantAnchor) return selection.anchor;\n return kind === \"floating\" &&\n normalizeTemporalInputValue(current) !== current\n ? value\n : undefined;\n };\n const minimum = classifyTemporal(min ?? \"\");\n const maximum = classifyTemporal(max ?? \"\");\n const hydrated = React.useSyncExternalStore(\n subscribeToHydration,\n clientHydrationSnapshot,\n serverHydrationSnapshot,\n );\n const showRawValue =\n inputType === \"datetime\" && temporal.kind === \"instant\" && !hydrated;\n const displayValue =\n currentValue === undefined\n ? undefined\n : inputType === \"datetime\" && hydrated\n ? toPickerLocalDateTime(currentValue)\n : normalizeTemporalInputValue(currentValue);\n return (\n <input\n key={isBound ? undefined : initialValue}\n type={\n showRawValue\n ? \"text\"\n : inputType === \"datetime\"\n ? \"datetime-local\"\n : inputType\n }\n readOnly={showRawValue || undefined}\n data-aui=\"datepicker\"\n data-aui-action={actionAttr($action)}\n {...{\n [FIELD_VALUE_ATTR]: canonicalFieldValue(currentValue),\n }}\n name={name}\n aria-label={label}\n {...(isBound || inputType === \"datetime\"\n ? { value: displayValue ?? \"\" }\n : { defaultValue: displayValue })}\n min={\n inputType === \"datetime\" && hydrated && min !== undefined\n ? normalizeTemporalInputValue(\n toLocalDateTime(\n min,\n minimum.kind === \"instant\" ? minimum.precision : undefined,\n ),\n )\n : min === undefined\n ? undefined\n : normalizeTemporalInputValue(min)\n }\n max={\n inputType === \"datetime\" && hydrated && max !== undefined\n ? normalizeTemporalInputValue(\n toLocalDateTime(\n max,\n maximum.kind === \"instant\" ? maximum.precision : undefined,\n ),\n )\n : max === undefined\n ? undefined\n : normalizeTemporalInputValue(max)\n }\n step={\n inputType !== \"date\"\n ? getTemporalInputStep(selection.anchor, currentValue, min, max)\n : undefined\n }\n onChange={(e) => {\n const inputValue = normalizeTemporalInputValue(e.currentTarget.value);\n const nextValue =\n inputType === \"datetime\"\n ? fromLocalDateTime(inputValue, selection.anchor)\n : inputValue;\n const canonical = canonicalFieldValue(nextValue);\n if (canonical !== undefined) {\n e.currentTarget.setAttribute(FIELD_VALUE_ATTR, canonical);\n } else {\n e.currentTarget.removeAttribute(FIELD_VALUE_ATTR);\n }\n setSelection({\n initialValue,\n value: nextValue,\n anchor: selection.anchor,\n });\n updateBinding?.(nextValue);\n fire($action, $dispatch, nextValue, e.currentTarget);\n }}\n />\n );\n}\n\ntype CheckboxRenderProps = {\n checked?: boolean;\n label: string;\n name?: string;\n defaultChecked?: boolean;\n variant?: \"checkbox\" | \"switch\";\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction CheckboxRender({\n checked,\n label,\n name,\n defaultChecked,\n variant = \"checkbox\",\n $action,\n $dispatch,\n}: CheckboxRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialChecked =\n typeof answeredValue === \"boolean\" ? answeredValue : defaultChecked;\n return (\n <label\n data-aui=\"checkbox\"\n data-aui-variant={variant === \"switch\" ? variant : undefined}\n >\n <input\n key={String(initialChecked)}\n type=\"checkbox\"\n role={variant === \"switch\" ? \"switch\" : undefined}\n data-aui-action={actionAttr($action)}\n name={name}\n {...(isBound\n ? {\n checked:\n typeof answeredValue === \"boolean\"\n ? answeredValue\n : checked === true,\n }\n : { defaultChecked: initialChecked })}\n onChange={(e) => {\n updateBinding?.(e.currentTarget.checked);\n fire($action, $dispatch, e.currentTarget.checked, e.currentTarget);\n }}\n />\n <span data-aui=\"checkbox-label\">{toTextContent(label)}</span>\n </label>\n );\n}\n\nconst finiteNumber = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isFinite(value);\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.min(Math.max(value, min), max);\n\ntype SliderRenderProps = {\n value?: number;\n name?: string;\n label?: string;\n min: number;\n max: number;\n step?: number;\n defaultValue?: number;\n unit?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\ntype SliderControlProps = {\n controlledValue?: number;\n name?: string;\n label?: string;\n min: number;\n max: number;\n step: number;\n initialValue: number;\n unit?: string;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction SliderControl({\n controlledValue,\n name,\n label,\n min,\n max,\n step,\n initialValue,\n unit,\n $action,\n $dispatch,\n}: SliderControlProps) {\n const updateBinding = useA2uiBinding(name);\n const [localValue, setValue] = React.useState(initialValue);\n const value = controlledValue ?? localValue;\n const pointerActive = React.useRef(false);\n const keyboardActive = React.useRef(false);\n const lastCommitted = React.useRef(initialValue);\n const currentValue = (input: HTMLInputElement) =>\n clamp(Number(input.value), min, max);\n const commit = (input: HTMLInputElement) => {\n if (controlledValue !== undefined) return;\n const nextValue = currentValue(input);\n setValue(nextValue);\n if (lastCommitted.current === nextValue) return;\n lastCommitted.current = nextValue;\n fire($action, $dispatch, nextValue, input);\n };\n\n return (\n <label data-aui=\"slider-field\">\n {label ? <span data-aui=\"slider-label\">{label}</span> : null}\n <input\n type=\"range\"\n data-aui=\"slider\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n aria-valuetext={unit ? `${value} ${unit}` : String(value)}\n min={min}\n max={max}\n step={step}\n {...(controlledValue !== undefined\n ? { value }\n : { defaultValue: initialValue })}\n onInput={(e) => {\n const nextValue = currentValue(e.currentTarget);\n if (controlledValue !== undefined) updateBinding?.(nextValue);\n else setValue(nextValue);\n }}\n onPointerDown={() => {\n pointerActive.current = true;\n }}\n onPointerUp={(e) => {\n pointerActive.current = false;\n commit(e.currentTarget);\n }}\n onKeyDown={(e) => {\n keyboardActive.current = [\n \"ArrowDown\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"ArrowUp\",\n \"End\",\n \"Home\",\n \"PageDown\",\n \"PageUp\",\n ].includes(e.key);\n }}\n onKeyUp={(e) => {\n if (!keyboardActive.current) return;\n keyboardActive.current = false;\n commit(e.currentTarget);\n }}\n onChange={(e) => {\n if (!pointerActive.current && !keyboardActive.current)\n commit(e.currentTarget);\n }}\n />\n <output data-aui=\"slider-value\">\n {value}\n {unit ? ` ${unit}` : \"\"}\n </output>\n </label>\n );\n}\n\nfunction SliderRender({\n value,\n name,\n label,\n min,\n max,\n step,\n defaultValue,\n unit,\n $action,\n $dispatch,\n}: SliderRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const safeMin = finiteNumber(min) ? min : 0;\n const safeMax = finiteNumber(max) ? Math.max(max, safeMin) : safeMin + 100;\n const safeStep = finiteNumber(step) && step > 0 ? step : 1;\n const defaultNumber = finiteNumber(isBound ? value : defaultValue)\n ? isBound\n ? value!\n : defaultValue!\n : safeMin;\n const initialValue = clamp(\n finiteNumber(answeredValue) ? answeredValue : defaultNumber,\n safeMin,\n safeMax,\n );\n return (\n <SliderControl\n key={`${safeMin}:${safeMax}:${safeStep}:${isBound ? \"bound\" : initialValue}`}\n {...(isBound ? { controlledValue: initialValue } : {})}\n min={safeMin}\n max={safeMax}\n step={safeStep}\n initialValue={initialValue}\n {...(name !== undefined ? { name } : {})}\n {...(label !== undefined ? { label } : {})}\n {...(unit !== undefined ? { unit } : {})}\n {...($action !== undefined ? { $action } : {})}\n {...($dispatch !== undefined ? { $dispatch } : {})}\n />\n );\n}\n\nconst UNDO_WINDOW_SECONDS = 5;\n\ntype ButtonRenderProps = {\n label: string;\n buttonStyle?: (typeof BUTTON_STYLES)[number];\n block?: boolean;\n submit?: boolean;\n undoable?: boolean;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction ButtonRender({\n label,\n buttonStyle,\n block,\n submit,\n undoable,\n children,\n $action,\n $dispatch,\n}: ButtonRenderProps) {\n const bindings = React.useContext(A2uiBindingContext!)?.fields;\n const bindingsRef = React.useRef(bindings);\n bindingsRef.current = bindings;\n const [remaining, setRemaining] = React.useState<number | undefined>(\n undefined,\n );\n const labelText = String(toTextContent(label) ?? \"\");\n const undoLabel = labelText.trim();\n const pendingAction = React.useRef<\n | {\n $action: Action;\n $dispatch: GenerativeUIDispatch;\n source: HTMLButtonElement;\n }\n | undefined\n >(undefined);\n const canUndo =\n undoable && !submit && $action !== undefined && $dispatch !== undefined;\n\n React.useEffect(() => {\n if (remaining === undefined) return;\n const timer = setTimeout(() => {\n if (remaining > 1) {\n setRemaining(remaining - 1);\n return;\n }\n const action = pendingAction.current;\n pendingAction.current = undefined;\n setRemaining(undefined);\n if (action)\n fire(\n action.$action,\n action.$dispatch,\n undefined,\n action.source,\n bindingsRef.current,\n );\n }, 1_000);\n return () => clearTimeout(timer);\n }, [remaining]);\n\n const cancelUndo = () => {\n pendingAction.current = undefined;\n setRemaining(undefined);\n };\n\n return (\n <button\n type={submit ? \"submit\" : \"button\"}\n data-aui=\"button\"\n data-aui-style={buttonStyle}\n data-aui-block={block || undefined}\n data-aui-submit={submit || undefined}\n data-aui-state={remaining === undefined ? undefined : \"pending\"}\n data-aui-action={actionAttr($action)}\n aria-label={\n remaining === undefined\n ? undefined\n : undoLabel\n ? `Undo ${undoLabel}`\n : \"Undo\"\n }\n onClick={\n submit\n ? undefined\n : canUndo\n ? (e) => {\n if (remaining !== undefined) {\n cancelUndo();\n return;\n }\n pendingAction.current = {\n $action,\n $dispatch,\n source: e.currentTarget,\n };\n setRemaining(UNDO_WINDOW_SECONDS);\n }\n : (e) =>\n fire($action, $dispatch, undefined, e.currentTarget, bindings)\n }\n onKeyDown={\n canUndo\n ? (e) => {\n if (e.key !== \"Escape\" || remaining === undefined) return;\n e.preventDefault();\n cancelUndo();\n }\n : undefined\n }\n >\n {remaining === undefined ? (\n <>\n {labelText}\n {children}\n </>\n ) : (\n <>\n Undo <span data-aui=\"button-countdown\">{remaining}</span>\n <span data-aui=\"button-undo-status\" role=\"status\">\n {labelText} in {UNDO_WINDOW_SECONDS} seconds\n </span>\n </>\n )}\n </button>\n );\n}\n\nexport const interactiveVocabulary = {\n Button: {\n description:\n \"A clickable button. Carries `$action` describing the side effect or resume value. Set `submit` to submit an ancestor Form/Card instead of firing `$action` on click.\",\n properties: z.object({\n label: z.string().describe(\"Button label.\"),\n buttonStyle: z.enum(BUTTON_STYLES).optional().describe(\"Visual style.\"),\n block: z\n .boolean()\n .optional()\n .describe(\"Whether the button spans the full width.\"),\n submit: z\n .boolean()\n .optional()\n .describe(\n \"Render as a submit button for an ancestor Form/Card instead of a click button.\",\n ),\n undoable: z\n .boolean()\n .optional()\n .describe(\n \"Delay the action with an undo control, for sends and other hard-to-reverse actions.\",\n ),\n }),\n render: ButtonRender,\n },\n Select: {\n description:\n \"A dropdown selector. Carries `$action` describing the on-select behavior.\",\n properties: z.object({\n options: z.array(optionSchema).describe(\"Selectable options.\"),\n placeholder: z\n .string()\n .optional()\n .describe(\"Placeholder shown when nothing is selected.\"),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultValue: z.string().optional().describe(\"Initially selected value.\"),\n }),\n render: SelectRender,\n },\n Input: {\n description:\n \"A text input. Carries `$action` describing the on-submit behavior.\",\n properties: z.object({\n inputType: z\n .enum([\"text\", \"password\", \"number\"])\n .optional()\n .describe(\"Single-line input type. Values remain strings.\"),\n placeholder: z.string().optional().describe(\"Placeholder text.\"),\n multiline: z\n .boolean()\n .optional()\n .describe(\n 'Render a textarea instead of a single-line input; ignored when `inputType` is `\"password\"` or `\"number\"`.',\n ),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultValue: z.string().optional().describe(\"Initial text.\"),\n }),\n render: InputRender,\n },\n DatePicker: {\n description:\n \"A date, datetime, or time input. Carries `$action` describing the on-select behavior.\",\n properties: z.object({\n inputType: z\n .enum([\"date\", \"datetime\", \"time\"])\n .optional()\n .describe(\n 'Temporal input type. Defaults to \"date\". Values remain strings.',\n ),\n value: z\n .string()\n .optional()\n .describe(\n \"Initial value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. A datetime with Z or ±HH:mm is an instant, displayed in the viewer's time zone and submitted with the same offset and precision. A datetime without an offset is local and submitted unchanged. An empty datetime submits with the viewer's offset and seconds.\",\n ),\n min: z\n .string()\n .optional()\n .describe(\n \"Minimum value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. Datetimes with Z or ±HH:mm are converted to the viewer's time zone.\",\n ),\n max: z\n .string()\n .optional()\n .describe(\n \"Maximum value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. Datetimes with Z or ±HH:mm are converted to the viewer's time zone.\",\n ),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n }),\n render: DatePickerRender,\n },\n Checkbox: {\n description:\n \"A checkbox with a label. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n label: z.string().describe(\"Label text next to the checkbox.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultChecked: z\n .boolean()\n .optional()\n .describe(\"Whether the checkbox starts checked.\"),\n variant: z\n .enum([\"checkbox\", \"switch\"])\n .optional()\n .describe(\"Whether to render a checkbox or switch.\"),\n }),\n render: CheckboxRender,\n },\n Slider: {\n description:\n \"A numeric range control. Carries `$action` describing the committed value.\",\n properties: z.object({\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n min: z.number().describe(\"Minimum value.\"),\n max: z.number().describe(\"Maximum value.\"),\n step: z.number().optional().describe(\"Value increment. Defaults to 1.\"),\n defaultValue: z.number().optional().describe(\"Initial value.\"),\n unit: z.string().optional().describe(\"Unit shown with the value.\"),\n }),\n render: SliderRender,\n },\n RadioGroup: {\n description:\n \"A group of mutually exclusive radio options. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n options: z.array(describedOptionSchema).describe(\"Selectable options.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z.string().optional().describe(\"Accessible name for the group.\"),\n defaultValue: z.string().optional().describe(\"Initially selected value.\"),\n }),\n render: RadioGroupRender,\n },\n CheckboxGroup: {\n description:\n \"A group of checkbox options where any number can be checked. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n options: z.array(describedOptionSchema).describe(\"Selectable options.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z.string().optional().describe(\"Accessible name for the group.\"),\n defaultValue: z\n .array(z.string())\n .optional()\n .describe(\"Initially checked values.\"),\n }),\n render: CheckboxGroupRender,\n },\n} satisfies GenerativeUILibrary;\n"],"mappings":"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"}
1
+ {"version":3,"file":"interactive.js","names":[],"sources":["../../src/vocabulary/interactive.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { z } from \"zod\";\nimport {\n CHECKBOX_GROUP_ATTR,\n FIELD_NAME_ATTR,\n FIELD_VALUE_ATTR,\n GENERATED_NAME_ATTR,\n} from \"../constants\";\nimport type { Action } from \"../ir\";\nimport { BUTTON_STYLES } from \"../ir\";\nimport type {\n GenerativeUIDispatch,\n GenerativeUILibrary,\n GenerativeUIStatus,\n} from \"../types\";\nimport { A2uiBindingContext, useA2uiBinding } from \"../a2ui/BindingContext\";\nimport { useAnsweredValue } from \"../answeredValues\";\nimport { actionAttr, fire } from \"./dispatch\";\nimport { toTextContent } from \"./toTextContent\";\nimport { useRadioGroupName } from \"../RadioGroupScope\";\nimport {\n classifyTemporal,\n fromLocalDateTime,\n getTemporalInputStep,\n normalizeTemporalInputValue,\n toLocalDateTime,\n toPickerLocalDateTime,\n} from \"../temporal\";\n\nconst optionSchema = z.object({\n label: z.string(),\n value: z.string(),\n});\n\nconst describedOptionSchema = optionSchema.extend({\n description: z.string().optional(),\n});\n\ntype Option = { label: string; description?: string; value: string };\n\nconst isOption = (option: unknown): option is Option =>\n option !== null &&\n typeof option === \"object\" &&\n \"label\" in option &&\n typeof option.label === \"string\" &&\n (!(\"description\" in option) || typeof option.description === \"string\") &&\n \"value\" in option &&\n typeof option.value === \"string\";\n\nconst mapOptions = (\n options: unknown,\n render: (option: Option, key: string) => React.ReactNode,\n) => {\n const occurrences = new Map<string, number>();\n return (Array.isArray(options) ? options : []).map((option) => {\n if (!isOption(option)) return null;\n const occurrence = occurrences.get(option.value) ?? 0;\n occurrences.set(option.value, occurrence + 1);\n return render(option, JSON.stringify([option.value, occurrence]));\n });\n};\n\ntype RadioGroupRenderProps = {\n value?: string;\n options: Option[];\n name?: string;\n label?: string;\n defaultValue?: string;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction RadioGroupRender({\n value,\n options,\n name,\n label,\n defaultValue,\n children,\n $action,\n $dispatch,\n}: RadioGroupRenderProps) {\n const groupName = useRadioGroupName(name);\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : defaultValue;\n return (\n <fieldset\n key={initialValue}\n data-aui=\"radiogroup\"\n data-aui-action={actionAttr($action)}\n aria-label={label}\n >\n {mapOptions(options, (option, key) => (\n <label key={key} data-aui=\"radiogroup-option\">\n <input\n type=\"radio\"\n name={groupName}\n {...{ [FIELD_NAME_ATTR]: name }}\n {...(name == null ? { [GENERATED_NAME_ATTR]: \"\" } : {})}\n value={option.value}\n {...(isBound\n ? { checked: (answeredValue ?? value) === option.value }\n : { defaultChecked: initialValue === option.value })}\n onChange={(e) => {\n updateBinding?.(option.value);\n fire($action, $dispatch, option.value, e.currentTarget);\n }}\n />\n {option.description ? (\n <span data-aui=\"option-content\">\n <span data-aui=\"option-label\">{option.label}</span>\n <span data-aui=\"option-description\">{option.description}</span>\n </span>\n ) : (\n option.label\n )}\n </label>\n ))}\n {children}\n </fieldset>\n );\n}\n\ntype CheckboxGroupRenderProps = {\n value?: string[];\n options: Option[];\n name?: string;\n label?: string;\n defaultValue?: string[];\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nconst checkedGroupValues = (input: HTMLInputElement): string[] =>\n Array.from(\n input\n .closest(\"fieldset\")\n ?.querySelectorAll<HTMLInputElement>(\n `:scope > label > input[${CHECKBOX_GROUP_ATTR}]:checked`,\n ) ?? [],\n (checkbox) => checkbox.value,\n );\n\nfunction CheckboxGroupRender({\n value,\n options,\n name,\n label,\n defaultValue,\n children,\n $action,\n $dispatch,\n}: CheckboxGroupRenderProps) {\n const generatedName = React.useId();\n const fieldName = name ?? generatedName;\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const checkedValues =\n Array.isArray(answeredValue) &&\n answeredValue.every((value): value is string => typeof value === \"string\")\n ? answeredValue\n : Array.isArray(defaultValue)\n ? defaultValue\n : [];\n return (\n <fieldset\n key={JSON.stringify(checkedValues)}\n data-aui=\"checkboxgroup\"\n data-aui-action={actionAttr($action)}\n aria-label={label}\n >\n {mapOptions(options, (option, key) => (\n <label key={key} data-aui=\"checkboxgroup-option\">\n <input\n type=\"checkbox\"\n name={fieldName}\n {...{ [CHECKBOX_GROUP_ATTR]: \"\" }}\n {...(name == null ? { [GENERATED_NAME_ATTR]: \"\" } : {})}\n value={option.value}\n {...(isBound\n ? {\n checked: (Array.isArray(answeredValue)\n ? checkedValues\n : (value ?? [])\n ).includes(option.value),\n }\n : { defaultChecked: checkedValues.includes(option.value) })}\n onChange={(e) => {\n const selected = checkedGroupValues(e.currentTarget);\n updateBinding?.(selected);\n fire($action, $dispatch, selected, e.currentTarget);\n }}\n />\n {option.description ? (\n <span data-aui=\"option-content\">\n <span data-aui=\"option-label\">{option.label}</span>\n <span data-aui=\"option-description\">{option.description}</span>\n </span>\n ) : (\n option.label\n )}\n </label>\n ))}\n {children}\n </fieldset>\n );\n}\n\ntype SelectRenderProps = {\n value?: string;\n options: Option[];\n placeholder?: string;\n label?: string;\n name?: string;\n defaultValue?: string;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction SelectRender({\n value,\n options,\n placeholder,\n label,\n name,\n defaultValue,\n $action,\n $dispatch,\n children,\n}: SelectRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const placeholderText = toTextContent(placeholder);\n const initialValue =\n typeof answeredValue === \"string\"\n ? answeredValue\n : typeof defaultValue === \"string\"\n ? defaultValue\n : \"\";\n return (\n <select\n key={initialValue}\n data-aui=\"select\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={(e) => {\n updateBinding?.(e.currentTarget.value);\n fire($action, $dispatch, e.currentTarget.value, e.currentTarget);\n }}\n >\n {placeholderText ? (\n <option value=\"\" disabled>\n {placeholderText}\n </option>\n ) : null}\n {mapOptions(options, (option, key) => (\n <option key={key} value={option.value}>\n {option.label}\n </option>\n ))}\n {children}\n </select>\n );\n}\n\ntype InputRenderProps = {\n value?: string;\n inputType?: \"text\" | \"password\" | \"number\";\n placeholder?: string;\n multiline?: boolean;\n label?: string;\n name?: string;\n defaultValue?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction InputRender({\n value,\n inputType,\n placeholder,\n multiline,\n label,\n name,\n defaultValue,\n $action,\n $dispatch,\n}: InputRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : defaultValue;\n const submit = (control: HTMLInputElement | HTMLTextAreaElement) =>\n fire($action, $dispatch, control.value, control);\n return multiline && (inputType === undefined || inputType === \"text\") ? (\n <textarea\n key={initialValue}\n data-aui=\"input\"\n data-aui-multiline\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n placeholder={placeholder}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={\n updateBinding ? (e) => updateBinding(e.currentTarget.value) : undefined\n }\n onKeyDown={(e) => {\n if (\n e.key !== \"Enter\" ||\n !(e.ctrlKey || e.metaKey) ||\n e.nativeEvent.isComposing\n )\n return;\n if (e.currentTarget.form) {\n e.preventDefault();\n HTMLFormElement.prototype.requestSubmit.call(e.currentTarget.form);\n } else {\n submit(e.currentTarget);\n }\n }}\n />\n ) : (\n <input\n key={initialValue}\n type={inputType}\n step={inputType === \"number\" ? \"any\" : undefined}\n data-aui=\"input\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n placeholder={placeholder}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={\n updateBinding ? (e) => updateBinding(e.currentTarget.value) : undefined\n }\n onKeyDown={(e) => {\n if (\n e.key === \"Enter\" &&\n !e.nativeEvent.isComposing &&\n !e.currentTarget.form\n )\n submit(e.currentTarget);\n }}\n />\n );\n}\n\ntype DatePickerRenderProps = {\n inputType?: \"date\" | \"datetime\" | \"time\";\n value?: string;\n min?: string;\n max?: string;\n label?: string;\n name?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nconst subscribeToHydration = () => () => {};\nconst clientHydrationSnapshot = () => true;\nconst serverHydrationSnapshot = () => false;\n\nfunction DatePickerRender({\n inputType = \"date\",\n value,\n min,\n max,\n label,\n name,\n $action,\n $dispatch,\n}: DatePickerRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : value;\n const [selection, setSelection] = React.useState({\n initialValue,\n value: initialValue,\n anchor: initialValue,\n committedValue: initialValue,\n edited: false,\n });\n if (selection.initialValue !== initialValue) {\n const isEcho = isBound && initialValue === selection.value;\n setSelection({\n initialValue,\n value: initialValue,\n anchor: isEcho ? selection.anchor : initialValue,\n committedValue: isEcho ? selection.committedValue : initialValue,\n edited: isEcho && selection.edited,\n });\n }\n const currentValue = isBound ? initialValue : selection.value;\n const temporal = classifyTemporal(currentValue ?? \"\");\n const anchor = classifyTemporal(selection.anchor ?? \"\");\n const hasInstantAnchor =\n inputType === \"datetime\" && anchor.kind === \"instant\";\n const canonicalFieldValue = (value: string | undefined) => {\n const current = value ?? \"\";\n const kind = classifyTemporal(current).kind;\n if (inputType === \"time\")\n return kind === \"time\" && normalizeTemporalInputValue(current) !== current\n ? value\n : undefined;\n if (inputType !== \"datetime\") return undefined;\n if (kind === \"instant\") return value;\n if (hasInstantAnchor) return selection.anchor;\n return kind === \"floating\" &&\n normalizeTemporalInputValue(current) !== current\n ? value\n : undefined;\n };\n const minimum = classifyTemporal(min ?? \"\");\n const maximum = classifyTemporal(max ?? \"\");\n const hydrated = React.useSyncExternalStore(\n subscribeToHydration,\n clientHydrationSnapshot,\n serverHydrationSnapshot,\n );\n const showRawValue =\n inputType === \"datetime\" && temporal.kind === \"instant\" && !hydrated;\n const displayValue =\n currentValue === undefined\n ? undefined\n : inputType === \"datetime\" && hydrated\n ? toPickerLocalDateTime(currentValue)\n : normalizeTemporalInputValue(currentValue);\n const keyboardActive = React.useRef(false);\n const valueFromInput = (input: HTMLInputElement) => {\n const inputValue = normalizeTemporalInputValue(input.value);\n return inputType === \"datetime\"\n ? fromLocalDateTime(inputValue, selection.anchor)\n : inputValue;\n };\n const commit = (input: HTMLInputElement) => {\n if (!selection.edited) return;\n const nextValue = valueFromInput(input);\n setSelection({ ...selection, committedValue: nextValue, edited: false });\n if (nextValue !== selection.committedValue)\n fire($action, $dispatch, nextValue, input);\n };\n return (\n <input\n key={isBound ? undefined : initialValue}\n type={\n showRawValue\n ? \"text\"\n : inputType === \"datetime\"\n ? \"datetime-local\"\n : inputType\n }\n readOnly={showRawValue || undefined}\n data-aui=\"datepicker\"\n data-aui-action={actionAttr($action)}\n {...{\n [FIELD_VALUE_ATTR]: canonicalFieldValue(currentValue),\n }}\n name={name}\n aria-label={label}\n {...(isBound || inputType === \"datetime\"\n ? { value: displayValue ?? \"\" }\n : { defaultValue: displayValue })}\n min={\n inputType === \"datetime\" && hydrated && min !== undefined\n ? normalizeTemporalInputValue(\n toLocalDateTime(\n min,\n minimum.kind === \"instant\" ? minimum.precision : undefined,\n ),\n )\n : min === undefined\n ? undefined\n : normalizeTemporalInputValue(min)\n }\n max={\n inputType === \"datetime\" && hydrated && max !== undefined\n ? normalizeTemporalInputValue(\n toLocalDateTime(\n max,\n maximum.kind === \"instant\" ? maximum.precision : undefined,\n ),\n )\n : max === undefined\n ? undefined\n : normalizeTemporalInputValue(max)\n }\n step={\n inputType !== \"date\"\n ? getTemporalInputStep(\n selection.anchor,\n hasInstantAnchor ? displayValue : currentValue,\n min,\n max,\n )\n : undefined\n }\n onChange={(e) => {\n const nextValue = valueFromInput(e.currentTarget);\n const canonical = canonicalFieldValue(nextValue);\n if (canonical !== undefined) {\n e.currentTarget.setAttribute(FIELD_VALUE_ATTR, canonical);\n } else {\n e.currentTarget.removeAttribute(FIELD_VALUE_ATTR);\n }\n // A change without an editing key down is a pick from the native picker, which commits at once; a typed value waits for blur or Enter.\n const typed = keyboardActive.current;\n keyboardActive.current = false;\n setSelection({\n initialValue,\n value: nextValue,\n anchor: selection.anchor,\n committedValue: typed ? selection.committedValue : nextValue,\n edited: typed,\n });\n updateBinding?.(nextValue);\n if (!typed && nextValue !== selection.committedValue)\n fire($action, $dispatch, nextValue, e.currentTarget);\n }}\n onBlur={(e) => {\n keyboardActive.current = false;\n commit(e.currentTarget);\n }}\n onKeyDown={(e) => {\n if (\n e.key === \"Enter\" &&\n !e.nativeEvent.isComposing &&\n !e.currentTarget.form\n ) {\n commit(e.currentTarget);\n return;\n }\n keyboardActive.current =\n !e.altKey &&\n !e.ctrlKey &&\n !e.metaKey &&\n (e.key.length === 1 ||\n [\n \"ArrowDown\",\n \"ArrowUp\",\n \"Backspace\",\n \"Delete\",\n \"End\",\n \"Home\",\n \"PageDown\",\n \"PageUp\",\n ].includes(e.key));\n }}\n onKeyUp={() => {\n keyboardActive.current = false;\n }}\n onPaste={() => {\n keyboardActive.current = true;\n }}\n onCut={() => {\n keyboardActive.current = true;\n }}\n />\n );\n}\n\ntype CheckboxRenderProps = {\n checked?: boolean;\n label: string;\n name?: string;\n defaultChecked?: boolean;\n variant?: \"checkbox\" | \"switch\";\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction CheckboxRender({\n checked,\n label,\n name,\n defaultChecked,\n variant = \"checkbox\",\n $action,\n $dispatch,\n}: CheckboxRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialChecked =\n typeof answeredValue === \"boolean\" ? answeredValue : defaultChecked;\n return (\n <label\n data-aui=\"checkbox\"\n data-aui-variant={variant === \"switch\" ? variant : undefined}\n >\n <input\n key={String(initialChecked)}\n type=\"checkbox\"\n role={variant === \"switch\" ? \"switch\" : undefined}\n data-aui-action={actionAttr($action)}\n name={name}\n {...(isBound\n ? {\n checked:\n typeof answeredValue === \"boolean\"\n ? answeredValue\n : checked === true,\n }\n : { defaultChecked: initialChecked })}\n onChange={(e) => {\n updateBinding?.(e.currentTarget.checked);\n fire($action, $dispatch, e.currentTarget.checked, e.currentTarget);\n }}\n />\n <span data-aui=\"checkbox-label\">{toTextContent(label)}</span>\n </label>\n );\n}\n\nconst finiteNumber = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isFinite(value);\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.min(Math.max(value, min), max);\n\ntype SliderRenderProps = {\n value?: number;\n name?: string;\n label?: string;\n min: number;\n max: number;\n step?: number;\n defaultValue?: number;\n unit?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\ntype SliderControlProps = {\n controlledValue?: number;\n name?: string;\n label?: string;\n min: number;\n max: number;\n step: number;\n initialValue: number;\n unit?: string;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction SliderControl({\n controlledValue,\n name,\n label,\n min,\n max,\n step,\n initialValue,\n unit,\n $action,\n $dispatch,\n}: SliderControlProps) {\n const updateBinding = useA2uiBinding(name);\n const [localValue, setValue] = React.useState(initialValue);\n const value = controlledValue ?? localValue;\n const pointerActive = React.useRef(false);\n const keyboardActive = React.useRef(false);\n const lastCommitted = React.useRef(initialValue);\n const currentValue = (input: HTMLInputElement) =>\n clamp(Number(input.value), min, max);\n const commit = (input: HTMLInputElement) => {\n if (controlledValue !== undefined) return;\n const nextValue = currentValue(input);\n setValue(nextValue);\n if (lastCommitted.current === nextValue) return;\n lastCommitted.current = nextValue;\n fire($action, $dispatch, nextValue, input);\n };\n\n return (\n <label data-aui=\"slider-field\">\n {label ? <span data-aui=\"slider-label\">{label}</span> : null}\n <input\n type=\"range\"\n data-aui=\"slider\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n aria-valuetext={unit ? `${value} ${unit}` : String(value)}\n min={min}\n max={max}\n step={step}\n {...(controlledValue !== undefined\n ? { value }\n : { defaultValue: initialValue })}\n onInput={(e) => {\n const nextValue = currentValue(e.currentTarget);\n if (controlledValue !== undefined) updateBinding?.(nextValue);\n else setValue(nextValue);\n }}\n onPointerDown={() => {\n pointerActive.current = true;\n }}\n onPointerUp={(e) => {\n pointerActive.current = false;\n commit(e.currentTarget);\n }}\n onKeyDown={(e) => {\n keyboardActive.current = [\n \"ArrowDown\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"ArrowUp\",\n \"End\",\n \"Home\",\n \"PageDown\",\n \"PageUp\",\n ].includes(e.key);\n }}\n onKeyUp={(e) => {\n if (!keyboardActive.current) return;\n keyboardActive.current = false;\n commit(e.currentTarget);\n }}\n onChange={(e) => {\n if (!pointerActive.current && !keyboardActive.current)\n commit(e.currentTarget);\n }}\n />\n <output data-aui=\"slider-value\">\n {value}\n {unit ? ` ${unit}` : \"\"}\n </output>\n </label>\n );\n}\n\nfunction SliderRender({\n value,\n name,\n label,\n min,\n max,\n step,\n defaultValue,\n unit,\n $action,\n $dispatch,\n}: SliderRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const safeMin = finiteNumber(min) ? min : 0;\n const safeMax = finiteNumber(max) ? Math.max(max, safeMin) : safeMin + 100;\n const safeStep = finiteNumber(step) && step > 0 ? step : 1;\n const defaultNumber = finiteNumber(isBound ? value : defaultValue)\n ? isBound\n ? value!\n : defaultValue!\n : safeMin;\n const initialValue = clamp(\n finiteNumber(answeredValue) ? answeredValue : defaultNumber,\n safeMin,\n safeMax,\n );\n return (\n <SliderControl\n key={`${safeMin}:${safeMax}:${safeStep}:${isBound ? \"bound\" : initialValue}`}\n {...(isBound ? { controlledValue: initialValue } : {})}\n min={safeMin}\n max={safeMax}\n step={safeStep}\n initialValue={initialValue}\n {...(name !== undefined ? { name } : {})}\n {...(label !== undefined ? { label } : {})}\n {...(unit !== undefined ? { unit } : {})}\n {...($action !== undefined ? { $action } : {})}\n {...($dispatch !== undefined ? { $dispatch } : {})}\n />\n );\n}\n\nconst UNDO_WINDOW_SECONDS = 5;\n\ntype ButtonRenderProps = {\n label: string;\n buttonStyle?: (typeof BUTTON_STYLES)[number];\n block?: boolean;\n submit?: boolean;\n undoable?: boolean;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction ButtonRender({\n label,\n buttonStyle,\n block,\n submit,\n undoable,\n children,\n $action,\n $dispatch,\n}: ButtonRenderProps) {\n const bindings = React.useContext(A2uiBindingContext!)?.fields;\n const bindingsRef = React.useRef(bindings);\n bindingsRef.current = bindings;\n const [remaining, setRemaining] = React.useState<number | undefined>(\n undefined,\n );\n const labelText = String(toTextContent(label) ?? \"\");\n const undoLabel = labelText.trim();\n const pendingAction = React.useRef<\n | {\n $action: Action;\n $dispatch: GenerativeUIDispatch;\n source: HTMLButtonElement;\n }\n | undefined\n >(undefined);\n const canUndo =\n undoable && !submit && $action !== undefined && $dispatch !== undefined;\n\n React.useEffect(() => {\n if (remaining === undefined) return;\n const timer = setTimeout(() => {\n if (remaining > 1) {\n setRemaining(remaining - 1);\n return;\n }\n const action = pendingAction.current;\n pendingAction.current = undefined;\n setRemaining(undefined);\n if (action)\n fire(\n action.$action,\n action.$dispatch,\n undefined,\n action.source,\n bindingsRef.current,\n );\n }, 1_000);\n return () => clearTimeout(timer);\n }, [remaining]);\n\n const cancelUndo = () => {\n pendingAction.current = undefined;\n setRemaining(undefined);\n };\n\n return (\n <button\n type={submit ? \"submit\" : \"button\"}\n data-aui=\"button\"\n data-aui-style={buttonStyle}\n data-aui-block={block || undefined}\n data-aui-submit={submit || undefined}\n data-aui-state={remaining === undefined ? undefined : \"pending\"}\n data-aui-action={actionAttr($action)}\n aria-label={\n remaining === undefined\n ? undefined\n : undoLabel\n ? `Undo ${undoLabel}`\n : \"Undo\"\n }\n onClick={\n submit\n ? undefined\n : canUndo\n ? (e) => {\n if (remaining !== undefined) {\n cancelUndo();\n return;\n }\n pendingAction.current = {\n $action,\n $dispatch,\n source: e.currentTarget,\n };\n setRemaining(UNDO_WINDOW_SECONDS);\n }\n : (e) =>\n fire($action, $dispatch, undefined, e.currentTarget, bindings)\n }\n onKeyDown={\n canUndo\n ? (e) => {\n if (e.key !== \"Escape\" || remaining === undefined) return;\n e.preventDefault();\n cancelUndo();\n }\n : undefined\n }\n >\n {remaining === undefined ? (\n <>\n {labelText}\n {children}\n </>\n ) : (\n <>\n Undo <span data-aui=\"button-countdown\">{remaining}</span>\n <span data-aui=\"button-undo-status\" role=\"status\">\n {labelText} in {UNDO_WINDOW_SECONDS} seconds\n </span>\n </>\n )}\n </button>\n );\n}\n\nexport const interactiveVocabulary = {\n Button: {\n description:\n \"A clickable button. Carries `$action` describing the side effect or resume value. Set `submit` to submit an ancestor Form/Card instead of firing `$action` on click.\",\n properties: z.object({\n label: z.string().describe(\"Button label.\"),\n buttonStyle: z.enum(BUTTON_STYLES).optional().describe(\"Visual style.\"),\n block: z\n .boolean()\n .optional()\n .describe(\"Whether the button spans the full width.\"),\n submit: z\n .boolean()\n .optional()\n .describe(\n \"Render as a submit button for an ancestor Form/Card instead of a click button.\",\n ),\n undoable: z\n .boolean()\n .optional()\n .describe(\n \"Delay the action with an undo control, for sends and other hard-to-reverse actions.\",\n ),\n }),\n render: ButtonRender,\n },\n Select: {\n description:\n \"A dropdown selector. Carries `$action` describing the on-select behavior.\",\n properties: z.object({\n options: z.array(optionSchema).describe(\"Selectable options.\"),\n placeholder: z\n .string()\n .optional()\n .describe(\"Placeholder shown when nothing is selected.\"),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultValue: z.string().optional().describe(\"Initially selected value.\"),\n }),\n render: SelectRender,\n },\n Input: {\n description:\n \"A text input. Carries `$action` describing the on-submit behavior.\",\n properties: z.object({\n inputType: z\n .enum([\"text\", \"password\", \"number\"])\n .optional()\n .describe(\"Single-line input type. Values remain strings.\"),\n placeholder: z.string().optional().describe(\"Placeholder text.\"),\n multiline: z\n .boolean()\n .optional()\n .describe(\n 'Render a textarea instead of a single-line input; ignored when `inputType` is `\"password\"` or `\"number\"`.',\n ),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultValue: z.string().optional().describe(\"Initial text.\"),\n }),\n render: InputRender,\n },\n DatePicker: {\n description:\n \"A date, datetime, or time input. Carries `$action`, which runs once per committed value: a pick from the picker, or a typed value on blur or Enter.\",\n properties: z.object({\n inputType: z\n .enum([\"date\", \"datetime\", \"time\"])\n .optional()\n .describe(\n 'Temporal input type. Defaults to \"date\". Values remain strings.',\n ),\n value: z\n .string()\n .optional()\n .describe(\n \"Initial value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. A datetime with Z or ±HH:mm is an instant, displayed in the viewer's time zone and submitted with the same offset and precision. A datetime without an offset is local and submitted unchanged. An empty datetime submits with the viewer's offset and seconds.\",\n ),\n min: z\n .string()\n .optional()\n .describe(\n \"Minimum value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. Datetimes with Z or ±HH:mm are converted to the viewer's time zone.\",\n ),\n max: z\n .string()\n .optional()\n .describe(\n \"Maximum value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. Datetimes with Z or ±HH:mm are converted to the viewer's time zone.\",\n ),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n }),\n render: DatePickerRender,\n },\n Checkbox: {\n description:\n \"A checkbox with a label. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n label: z.string().describe(\"Label text next to the checkbox.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultChecked: z\n .boolean()\n .optional()\n .describe(\"Whether the checkbox starts checked.\"),\n variant: z\n .enum([\"checkbox\", \"switch\"])\n .optional()\n .describe(\"Whether to render a checkbox or switch.\"),\n }),\n render: CheckboxRender,\n },\n Slider: {\n description:\n \"A numeric range control. Carries `$action` describing the committed value.\",\n properties: z.object({\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n min: z.number().describe(\"Minimum value.\"),\n max: z.number().describe(\"Maximum value.\"),\n step: z.number().optional().describe(\"Value increment. Defaults to 1.\"),\n defaultValue: z.number().optional().describe(\"Initial value.\"),\n unit: z.string().optional().describe(\"Unit shown with the value.\"),\n }),\n render: SliderRender,\n },\n RadioGroup: {\n description:\n \"A group of mutually exclusive radio options. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n options: z.array(describedOptionSchema).describe(\"Selectable options.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z.string().optional().describe(\"Accessible name for the group.\"),\n defaultValue: z.string().optional().describe(\"Initially selected value.\"),\n }),\n render: RadioGroupRender,\n },\n CheckboxGroup: {\n description:\n \"A group of checkbox options where any number can be checked. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n options: z.array(describedOptionSchema).describe(\"Selectable options.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z.string().optional().describe(\"Accessible name for the group.\"),\n defaultValue: z\n .array(z.string())\n .optional()\n .describe(\"Initially checked values.\"),\n }),\n render: CheckboxGroupRender,\n },\n} satisfies GenerativeUILibrary;\n"],"mappings":";;;;;;;;;;;;AA6BA,MAAM,eAAe,EAAE,OAAO;CAC5B,OAAO,EAAE,OAAO;CAChB,OAAO,EAAE,OAAO;AAClB,CAAC;AAED,MAAM,wBAAwB,aAAa,OAAO,EAChD,aAAa,EAAE,OAAO,CAAC,CAAC,SAAS,EACnC,CAAC;AAID,MAAM,YAAY,WAChB,WAAW,QACX,OAAO,WAAW,YAClB,WAAW,UACX,OAAO,OAAO,UAAU,aACvB,EAAE,iBAAiB,WAAW,OAAO,OAAO,gBAAgB,aAC7D,WAAW,UACX,OAAO,OAAO,UAAU;AAE1B,MAAM,cACJ,SACA,WACG;CACH,MAAM,8BAAc,IAAI,IAAoB;CAC5C,QAAQ,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,EAAA,CAAG,KAAK,WAAW;EAC7D,IAAI,CAAC,SAAS,MAAM,GAAG,OAAO;EAC9B,MAAM,aAAa,YAAY,IAAI,OAAO,KAAK,KAAK;EACpD,YAAY,IAAI,OAAO,OAAO,aAAa,CAAC;EAC5C,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,OAAO,OAAO,UAAU,CAAC,CAAC;CAClE,CAAC;AACH;AAcA,SAAS,iBAAiB,EACxB,OACA,SACA,MACA,OACA,cACA,UACA,SACA,aACwB;CACxB,MAAM,YAAY,kBAAkB,IAAI;CACxC,MAAM,gBAAgB,iBAAiB,IAAI;CAC3C,MAAM,gBAAgB,eAAe,IAAI;CACzC,MAAM,UAAU,kBAAkB,KAAA;CAClC,MAAM,eACJ,OAAO,kBAAkB,WAAW,gBAAgB;CACtD,OACE,qBAAC,YAAD;EAEE,YAAS;EACT,mBAAiB,WAAW,OAAO;EACnC,cAAY;EAJd,UAAA,CAMG,WAAW,UAAU,QAAQ,QAC5B,qBAAC,SAAD;GAAiB,YAAS;GAA1B,UAAA,CACE,oBAAC,SAAD;IACE,MAAK;IACL,MAAM;KACC,kBAAkB;IACzB,GAAK,QAAQ,OAAO,GAAG,sBAAsB,GAAG,IAAI,CAAC;IACrD,OAAO,OAAO;IACd,GAAK,UACD,EAAE,UAAU,iBAAiB,WAAW,OAAO,MAAM,IACrD,EAAE,gBAAgB,iBAAiB,OAAO,MAAM;IACpD,WAAW,MAAM;KACf,gBAAgB,OAAO,KAAK;KAC5B,KAAK,SAAS,WAAW,OAAO,OAAO,EAAE,aAAa;IACxD;GACD,CAAA,GACA,OAAO,cACN,qBAAC,QAAD;IAAM,YAAS;IAAf,UAAA,CACE,oBAAC,QAAD;KAAM,YAAS;KAAgB,UAAA,OAAO;IAAY,CAAA,GAClD,oBAAC,QAAD;KAAM,YAAS;KAAsB,UAAA,OAAO;IAAkB,CAAA,CAC1D;GAEN,CAAA,IAAA,OAAO,KAEJ;EAvBK,GAAA,GAuBL,CACR,GACA,QACO;CAhCH,GAAA,YAgCG;AAEd;AAcA,MAAM,sBAAsB,UAC1B,MAAM,KACJ,MACG,QAAQ,UAAU,CAAC,EAClB,iBACA,yDACF,KAAK,CAAC,IACP,aAAa,SAAS,KACzB;AAEF,SAAS,oBAAoB,EAC3B,OACA,SACA,MACA,OACA,cACA,UACA,SACA,aAC2B;CAC3B,MAAM,gBAAgB,MAAM,MAAM;CAClC,MAAM,YAAY,QAAQ;CAC1B,MAAM,gBAAgB,iBAAiB,IAAI;CAC3C,MAAM,gBAAgB,eAAe,IAAI;CACzC,MAAM,UAAU,kBAAkB,KAAA;CAClC,MAAM,gBACJ,MAAM,QAAQ,aAAa,KAC3B,cAAc,OAAO,UAA2B,OAAO,UAAU,QAAQ,IACrE,gBACA,MAAM,QAAQ,YAAY,IACxB,eACA,CAAC;CACT,OACE,qBAAC,YAAD;EAEE,YAAS;EACT,mBAAiB,WAAW,OAAO;EACnC,cAAY;EAJd,UAAA,CAMG,WAAW,UAAU,QAAQ,QAC5B,qBAAC,SAAD;GAAiB,YAAS;GAA1B,UAAA,CACE,oBAAC,SAAD;IACE,MAAK;IACL,MAAM;KACC,sBAAsB;IAC7B,GAAK,QAAQ,OAAO,GAAG,sBAAsB,GAAG,IAAI,CAAC;IACrD,OAAO,OAAO;IACd,GAAK,UACD,EACE,UAAU,MAAM,QAAQ,aAAa,IACjC,gBACC,SAAS,CAAC,EAAA,CACb,SAAS,OAAO,KAAK,EACzB,IACA,EAAE,gBAAgB,cAAc,SAAS,OAAO,KAAK,EAAE;IAC3D,WAAW,MAAM;KACf,MAAM,WAAW,mBAAmB,EAAE,aAAa;KACnD,gBAAgB,QAAQ;KACxB,KAAK,SAAS,WAAW,UAAU,EAAE,aAAa;IACpD;GACD,CAAA,GACA,OAAO,cACN,qBAAC,QAAD;IAAM,YAAS;IAAf,UAAA,CACE,oBAAC,QAAD;KAAM,YAAS;KAAgB,UAAA,OAAO;IAAY,CAAA,GAClD,oBAAC,QAAD;KAAM,YAAS;KAAsB,UAAA,OAAO;IAAkB,CAAA,CAC1D;GAEN,CAAA,IAAA,OAAO,KAEJ;EA7BK,GAAA,GA6BL,CACR,GACA,QACO;CAtCH,GAAA,KAAK,UAAU,aAAa,CAsCzB;AAEd;AAeA,SAAS,aAAa,EACpB,OACA,SACA,aACA,OACA,MACA,cACA,SACA,WACA,YACoB;CACpB,MAAM,gBAAgB,iBAAiB,IAAI;CAC3C,MAAM,gBAAgB,eAAe,IAAI;CACzC,MAAM,UAAU,kBAAkB,KAAA;CAClC,MAAM,kBAAkB,cAAc,WAAW;CACjD,MAAM,eACJ,OAAO,kBAAkB,WACrB,gBACA,OAAO,iBAAiB,WACtB,eACA;CACR,OACE,qBAAC,UAAD;EAEE,YAAS;EACT,mBAAiB,WAAW,OAAO;EAC7B;EACN,cAAY;EACZ,GAAK,UACD,EACE,OACE,OAAO,kBAAkB,WAAW,gBAAiB,SAAS,GAClE,IACA,EAAE,cAAc,aAAa;EACjC,WAAW,MAAM;GACf,gBAAgB,EAAE,cAAc,KAAK;GACrC,KAAK,SAAS,WAAW,EAAE,cAAc,OAAO,EAAE,aAAa;EACjE;EAfF,UAAA;GAiBG,kBACC,oBAAC,UAAD;IAAQ,OAAM;IAAG,UAAA;IACd,UAAA;GACK,CAAA,IACN;GACH,WAAW,UAAU,QAAQ,QAC5B,oBAAC,UAAD;IAAkB,OAAO,OAAO;IAC7B,UAAA,OAAO;GACF,GAFK,GAEL,CACT;GACA;EACK;CA3BD,GAAA,YA2BC;AAEZ;AAeA,SAAS,YAAY,EACnB,OACA,WACA,aACA,WACA,OACA,MACA,cACA,SACA,aACmB;CACnB,MAAM,gBAAgB,iBAAiB,IAAI;CAC3C,MAAM,gBAAgB,eAAe,IAAI;CACzC,MAAM,UAAU,kBAAkB,KAAA;CAClC,MAAM,eACJ,OAAO,kBAAkB,WAAW,gBAAgB;CACtD,MAAM,UAAU,YACd,KAAK,SAAS,WAAW,QAAQ,OAAO,OAAO;CACjD,OAAO,cAAc,cAAc,KAAA,KAAa,cAAc,UAC5D,oBAAC,YAAD;EAEE,YAAS;EACT,sBAAA;EACA,mBAAiB,WAAW,OAAO;EAC7B;EACN,cAAY;EACC;EACb,GAAK,UACD,EACE,OACE,OAAO,kBAAkB,WAAW,gBAAiB,SAAS,GAClE,IACA,EAAE,cAAc,aAAa;EACjC,UACE,iBAAiB,MAAM,cAAc,EAAE,cAAc,KAAK,IAAI,KAAA;EAEhE,YAAY,MAAM;GAChB,IACE,EAAE,QAAQ,WACV,EAAE,EAAE,WAAW,EAAE,YACjB,EAAE,YAAY,aAEd;GACF,IAAI,EAAE,cAAc,MAAM;IACxB,EAAE,eAAe;IACjB,gBAAgB,UAAU,cAAc,KAAK,EAAE,cAAc,IAAI;GACnE,OACE,OAAO,EAAE,aAAa;EAE1B;CACD,GA9BM,YA8BN,IAED,oBAAC,SAAD;EAEE,MAAM;EACN,MAAM,cAAc,WAAW,QAAQ,KAAA;EACvC,YAAS;EACT,mBAAiB,WAAW,OAAO;EAC7B;EACN,cAAY;EACC;EACb,GAAK,UACD,EACE,OACE,OAAO,kBAAkB,WAAW,gBAAiB,SAAS,GAClE,IACA,EAAE,cAAc,aAAa;EACjC,UACE,iBAAiB,MAAM,cAAc,EAAE,cAAc,KAAK,IAAI,KAAA;EAEhE,YAAY,MAAM;GAChB,IACE,EAAE,QAAQ,WACV,CAAC,EAAE,YAAY,eACf,CAAC,EAAE,cAAc,MAEjB,OAAO,EAAE,aAAa;EAC1B;CACD,GAzBM,YAyBN;AAEL;AAcA,MAAM,mCAAmC,CAAC;AAC1C,MAAM,gCAAgC;AACtC,MAAM,gCAAgC;AAEtC,SAAS,iBAAiB,EACxB,YAAY,QACZ,OACA,KACA,KACA,OACA,MACA,SACA,aACwB;CACxB,MAAM,gBAAgB,iBAAiB,IAAI;CAC3C,MAAM,gBAAgB,eAAe,IAAI;CACzC,MAAM,UAAU,kBAAkB,KAAA;CAClC,MAAM,eACJ,OAAO,kBAAkB,WAAW,gBAAgB;CACtD,MAAM,CAAC,WAAW,gBAAgB,MAAM,SAAS;EAC/C;EACA,OAAO;EACP,QAAQ;EACR,gBAAgB;EAChB,QAAQ;CACV,CAAC;CACD,IAAI,UAAU,iBAAiB,cAAc;EAC3C,MAAM,SAAS,WAAW,iBAAiB,UAAU;EACrD,aAAa;GACX;GACA,OAAO;GACP,QAAQ,SAAS,UAAU,SAAS;GACpC,gBAAgB,SAAS,UAAU,iBAAiB;GACpD,QAAQ,UAAU,UAAU;EAC9B,CAAC;CACH;CACA,MAAM,eAAe,UAAU,eAAe,UAAU;CACxD,MAAM,WAAW,iBAAiB,gBAAgB,EAAE;CACpD,MAAM,SAAS,iBAAiB,UAAU,UAAU,EAAE;CACtD,MAAM,mBACJ,cAAc,cAAc,OAAO,SAAS;CAC9C,MAAM,uBAAuB,UAA8B;EACzD,MAAM,UAAU,SAAS;EACzB,MAAM,OAAO,iBAAiB,OAAO,CAAC,CAAC;EACvC,IAAI,cAAc,QAChB,OAAO,SAAS,UAAU,4BAA4B,OAAO,MAAM,UAC/D,QACA,KAAA;EACN,IAAI,cAAc,YAAY,OAAO,KAAA;EACrC,IAAI,SAAS,WAAW,OAAO;EAC/B,IAAI,kBAAkB,OAAO,UAAU;EACvC,OAAO,SAAS,cACd,4BAA4B,OAAO,MAAM,UACvC,QACA,KAAA;CACN;CACA,MAAM,UAAU,iBAAiB,OAAO,EAAE;CAC1C,MAAM,UAAU,iBAAiB,OAAO,EAAE;CAC1C,MAAM,WAAW,MAAM,qBACrB,sBACA,yBACA,uBACF;CACA,MAAM,eACJ,cAAc,cAAc,SAAS,SAAS,aAAa,CAAC;CAC9D,MAAM,eACJ,iBAAiB,KAAA,IACb,KAAA,IACA,cAAc,cAAc,WAC1B,sBAAsB,YAAY,IAClC,4BAA4B,YAAY;CAChD,MAAM,iBAAiB,MAAM,OAAO,KAAK;CACzC,MAAM,kBAAkB,UAA4B;EAClD,MAAM,aAAa,4BAA4B,MAAM,KAAK;EAC1D,OAAO,cAAc,aACjB,kBAAkB,YAAY,UAAU,MAAM,IAC9C;CACN;CACA,MAAM,UAAU,UAA4B;EAC1C,IAAI,CAAC,UAAU,QAAQ;EACvB,MAAM,YAAY,eAAe,KAAK;EACtC,aAAa;GAAE,GAAG;GAAW,gBAAgB;GAAW,QAAQ;EAAM,CAAC;EACvE,IAAI,cAAc,UAAU,gBAC1B,KAAK,SAAS,WAAW,WAAW,KAAK;CAC7C;CACA,OACE,oBAAC,SAAD;EAEE,MACE,eACI,SACA,cAAc,aACZ,mBACA;EAER,UAAU,gBAAgB,KAAA;EAC1B,YAAS;EACT,mBAAiB,WAAW,OAAO;GAEhC,mBAAmB,oBAAoB,YAAY;EAEhD;EACN,cAAY;EACZ,GAAK,WAAW,cAAc,aAC1B,EAAE,OAAO,gBAAgB,GAAG,IAC5B,EAAE,cAAc,aAAa;EACjC,KACE,cAAc,cAAc,YAAY,QAAQ,KAAA,IAC5C,4BACE,gBACE,KACA,QAAQ,SAAS,YAAY,QAAQ,YAAY,KAAA,CACnD,CACF,IACA,QAAQ,KAAA,IACN,KAAA,IACA,4BAA4B,GAAG;EAEvC,KACE,cAAc,cAAc,YAAY,QAAQ,KAAA,IAC5C,4BACE,gBACE,KACA,QAAQ,SAAS,YAAY,QAAQ,YAAY,KAAA,CACnD,CACF,IACA,QAAQ,KAAA,IACN,KAAA,IACA,4BAA4B,GAAG;EAEvC,MACE,cAAc,SACV,qBACE,UAAU,QACV,mBAAmB,eAAe,cAClC,KACA,GACF,IACA,KAAA;EAEN,WAAW,MAAM;GACf,MAAM,YAAY,eAAe,EAAE,aAAa;GAChD,MAAM,YAAY,oBAAoB,SAAS;GAC/C,IAAI,cAAc,KAAA,GAChB,EAAE,cAAc,aAAa,kBAAkB,SAAS;QAExD,EAAE,cAAc,gBAAgB,gBAAgB;GAGlD,MAAM,QAAQ,eAAe;GAC7B,eAAe,UAAU;GACzB,aAAa;IACX;IACA,OAAO;IACP,QAAQ,UAAU;IAClB,gBAAgB,QAAQ,UAAU,iBAAiB;IACnD,QAAQ;GACV,CAAC;GACD,gBAAgB,SAAS;GACzB,IAAI,CAAC,SAAS,cAAc,UAAU,gBACpC,KAAK,SAAS,WAAW,WAAW,EAAE,aAAa;EACvD;EACA,SAAS,MAAM;GACb,eAAe,UAAU;GACzB,OAAO,EAAE,aAAa;EACxB;EACA,YAAY,MAAM;GAChB,IACE,EAAE,QAAQ,WACV,CAAC,EAAE,YAAY,eACf,CAAC,EAAE,cAAc,MACjB;IACA,OAAO,EAAE,aAAa;IACtB;GACF;GACA,eAAe,UACb,CAAC,EAAE,UACH,CAAC,EAAE,WACH,CAAC,EAAE,YACF,EAAE,IAAI,WAAW,KAChB;IACE;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACF,CAAC,CAAC,SAAS,EAAE,GAAG;EACtB;EACA,eAAe;GACb,eAAe,UAAU;EAC3B;EACA,eAAe;GACb,eAAe,UAAU;EAC3B;EACA,aAAa;GACX,eAAe,UAAU;EAC3B;CACD,GAjHM,UAAU,KAAA,IAAY,YAiH5B;AAEL;AAaA,SAAS,eAAe,EACtB,SACA,OACA,MACA,gBACA,UAAU,YACV,SACA,aACsB;CACtB,MAAM,gBAAgB,iBAAiB,IAAI;CAC3C,MAAM,gBAAgB,eAAe,IAAI;CACzC,MAAM,UAAU,kBAAkB,KAAA;CAClC,MAAM,iBACJ,OAAO,kBAAkB,YAAY,gBAAgB;CACvD,OACE,qBAAC,SAAD;EACE,YAAS;EACT,oBAAkB,YAAY,WAAW,UAAU,KAAA;EAFrD,UAAA,CAIE,oBAAC,SAAD;GAEE,MAAK;GACL,MAAM,YAAY,WAAW,WAAW,KAAA;GACxC,mBAAiB,WAAW,OAAO;GAC7B;GACN,GAAK,UACD,EACE,SACE,OAAO,kBAAkB,YACrB,gBACA,YAAY,KACpB,IACA,EAAE,gBAAgB,eAAe;GACrC,WAAW,MAAM;IACf,gBAAgB,EAAE,cAAc,OAAO;IACvC,KAAK,SAAS,WAAW,EAAE,cAAc,SAAS,EAAE,aAAa;GACnE;EACD,GAjBM,OAAO,cAAc,CAiB3B,GACD,oBAAC,QAAD;GAAM,YAAS;GAAkB,UAAA,cAAc,KAAK;EAAQ,CAAA,CACvD;;AAEX;AAEA,MAAM,gBAAgB,UACpB,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AAEpD,MAAM,SAAS,OAAe,KAAa,QACzC,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG;AA6BpC,SAAS,cAAc,EACrB,iBACA,MACA,OACA,KACA,KACA,MACA,cACA,MACA,SACA,aACqB;CACrB,MAAM,gBAAgB,eAAe,IAAI;CACzC,MAAM,CAAC,YAAY,YAAY,MAAM,SAAS,YAAY;CAC1D,MAAM,QAAQ,mBAAmB;CACjC,MAAM,gBAAgB,MAAM,OAAO,KAAK;CACxC,MAAM,iBAAiB,MAAM,OAAO,KAAK;CACzC,MAAM,gBAAgB,MAAM,OAAO,YAAY;CAC/C,MAAM,gBAAgB,UACpB,MAAM,OAAO,MAAM,KAAK,GAAG,KAAK,GAAG;CACrC,MAAM,UAAU,UAA4B;EAC1C,IAAI,oBAAoB,KAAA,GAAW;EACnC,MAAM,YAAY,aAAa,KAAK;EACpC,SAAS,SAAS;EAClB,IAAI,cAAc,YAAY,WAAW;EACzC,cAAc,UAAU;EACxB,KAAK,SAAS,WAAW,WAAW,KAAK;CAC3C;CAEA,OACE,qBAAC,SAAD;EAAO,YAAS;EAAhB,UAAA;GACG,QAAQ,oBAAC,QAAD;IAAM,YAAS;IAAgB,UAAA;GAAY,CAAA,IAAI;GACxD,oBAAC,SAAD;IACE,MAAK;IACL,YAAS;IACT,mBAAiB,WAAW,OAAO;IAC7B;IACN,cAAY;IACZ,kBAAgB,OAAO,GAAG,MAAM,GAAG,SAAS,OAAO,KAAK;IACnD;IACA;IACC;IACN,GAAK,oBAAoB,KAAA,IACrB,EAAE,MAAM,IACR,EAAE,cAAc,aAAa;IACjC,UAAU,MAAM;KACd,MAAM,YAAY,aAAa,EAAE,aAAa;KAC9C,IAAI,oBAAoB,KAAA,GAAW,gBAAgB,SAAS;UACvD,SAAS,SAAS;IACzB;IACA,qBAAqB;KACnB,cAAc,UAAU;IAC1B;IACA,cAAc,MAAM;KAClB,cAAc,UAAU;KACxB,OAAO,EAAE,aAAa;IACxB;IACA,YAAY,MAAM;KAChB,eAAe,UAAU;MACvB;MACA;MACA;MACA;MACA;MACA;MACA;MACA;KACF,CAAC,CAAC,SAAS,EAAE,GAAG;IAClB;IACA,UAAU,MAAM;KACd,IAAI,CAAC,eAAe,SAAS;KAC7B,eAAe,UAAU;KACzB,OAAO,EAAE,aAAa;IACxB;IACA,WAAW,MAAM;KACf,IAAI,CAAC,cAAc,WAAW,CAAC,eAAe,SAC5C,OAAO,EAAE,aAAa;IAC1B;GACD,CAAA;GACD,qBAAC,UAAD;IAAQ,YAAS;IAAjB,UAAA,CACG,OACA,OAAO,IAAI,SAAS,EACf;;EACH;;AAEX;AAEA,SAAS,aAAa,EACpB,OACA,MACA,OACA,KACA,KACA,MACA,cACA,MACA,SACA,aACoB;CACpB,MAAM,gBAAgB,iBAAiB,IAAI;CAE3C,MAAM,UADgB,eAAe,IACT,MAAM,KAAA;CAClC,MAAM,UAAU,aAAa,GAAG,IAAI,MAAM;CAC1C,MAAM,UAAU,aAAa,GAAG,IAAI,KAAK,IAAI,KAAK,OAAO,IAAI,UAAU;CACvE,MAAM,WAAW,aAAa,IAAI,KAAK,OAAO,IAAI,OAAO;CACzD,MAAM,gBAAgB,aAAa,UAAU,QAAQ,YAAY,IAC7D,UACE,QACA,eACF;CACJ,MAAM,eAAe,MACnB,aAAa,aAAa,IAAI,gBAAgB,eAC9C,SACA,OACF;CACA,OACE,oBAAC,eAAD;EAEE,GAAK,UAAU,EAAE,iBAAiB,aAAa,IAAI,CAAC;EACpD,KAAK;EACL,KAAK;EACL,MAAM;EACQ;EACd,GAAK,SAAS,KAAA,IAAY,EAAE,KAAK,IAAI,CAAC;EACtC,GAAK,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;EACxC,GAAK,SAAS,KAAA,IAAY,EAAE,KAAK,IAAI,CAAC;EACtC,GAAK,YAAY,KAAA,IAAY,EAAE,QAAQ,IAAI,CAAC;EAC5C,GAAK,cAAc,KAAA,IAAY,EAAE,UAAU,IAAI,CAAC;CACjD,GAXM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,UAAU,cAW/D;AAEL;AAEA,MAAM,sBAAsB;AAc5B,SAAS,aAAa,EACpB,OACA,aACA,OACA,QACA,UACA,UACA,SACA,aACoB;CACpB,MAAM,WAAW,MAAM,WAAW,kBAAmB,CAAC,EAAE;CACxD,MAAM,cAAc,MAAM,OAAO,QAAQ;CACzC,YAAY,UAAU;CACtB,MAAM,CAAC,WAAW,gBAAgB,MAAM,SACtC,KAAA,CACF;CACA,MAAM,YAAY,OAAO,cAAc,KAAK,KAAK,EAAE;CACnD,MAAM,YAAY,UAAU,KAAK;CACjC,MAAM,gBAAgB,MAAM,OAO1B,KAAA,CAAS;CACX,MAAM,UACJ,YAAY,CAAC,UAAU,YAAY,KAAA,KAAa,cAAc,KAAA;CAEhE,MAAM,gBAAgB;EACpB,IAAI,cAAc,KAAA,GAAW;EAC7B,MAAM,QAAQ,iBAAiB;GAC7B,IAAI,YAAY,GAAG;IACjB,aAAa,YAAY,CAAC;IAC1B;GACF;GACA,MAAM,SAAS,cAAc;GAC7B,cAAc,UAAU,KAAA;GACxB,aAAa,KAAA,CAAS;GACtB,IAAI,QACF,KACE,OAAO,SACP,OAAO,WACP,KAAA,GACA,OAAO,QACP,YAAY,OACd;EACJ,GAAG,GAAK;EACR,aAAa,aAAa,KAAK;CACjC,GAAG,CAAC,SAAS,CAAC;CAEd,MAAM,mBAAmB;EACvB,cAAc,UAAU,KAAA;EACxB,aAAa,KAAA,CAAS;CACxB;CAEA,OACE,oBAAC,UAAD;EACE,MAAM,SAAS,WAAW;EAC1B,YAAS;EACT,kBAAgB;EAChB,kBAAgB,SAAS,KAAA;EACzB,mBAAiB,UAAU,KAAA;EAC3B,kBAAgB,cAAc,KAAA,IAAY,KAAA,IAAY;EACtD,mBAAiB,WAAW,OAAO;EACnC,cACE,cAAc,KAAA,IACV,KAAA,IACA,YACE,QAAQ,cACR;EAER,SACE,SACI,KAAA,IACA,WACG,MAAM;GACL,IAAI,cAAc,KAAA,GAAW;IAC3B,WAAW;IACX;GACF;GACA,cAAc,UAAU;IACtB;IACA;IACA,QAAQ,EAAE;GACZ;GACA,aAAa,mBAAmB;EAClC,KACC,MACC,KAAK,SAAS,WAAW,KAAA,GAAW,EAAE,eAAe,QAAQ;EAEvE,WACE,WACK,MAAM;GACL,IAAI,EAAE,QAAQ,YAAY,cAAc,KAAA,GAAW;GACnD,EAAE,eAAe;GACjB,WAAW;EACb,IACA,KAAA;EAGL,UAAA,cAAc,KAAA,IACb,qBAAA,YAAA,EAAA,UAAA,CACG,WACA,QACD,EAAA,CAAA,IAEF,qBAAA,YAAA,EAAA,UAAA;GAAE;GACK,oBAAC,QAAD;IAAM,YAAS;IAAoB,UAAA;GAAgB,CAAA;GACxD,qBAAC,QAAD;IAAM,YAAS;IAAqB,MAAK;IAAzC,UAAA;KACG;KAAU;KAAK;KAAoB;IAChC;;EACN,EAAA,CAAA;CAEE,CAAA;AAEZ;AAEA,MAAa,wBAAwB;CACnC,QAAQ;EACN,aACE;EACF,YAAY,EAAE,OAAO;GACnB,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,eAAe;GAC1C,aAAa,EAAE,KAAK,aAAa,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,eAAe;GACtE,OAAO,EACJ,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,0CAA0C;GACtD,QAAQ,EACL,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SACC,gFACF;GACF,UAAU,EACP,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SACC,qFACF;EACJ,CAAC;EACD,QAAQ;CACV;CACA,QAAQ;EACN,aACE;EACF,YAAY,EAAE,OAAO;GACnB,SAAS,EAAE,MAAM,YAAY,CAAC,CAAC,SAAS,qBAAqB;GAC7D,aAAa,EACV,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SAAS,6CAA6C;GACzD,OAAO,EACJ,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SAAS,mCAAmC;GAC/C,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC;GACrE,cAAc,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,2BAA2B;EAC1E,CAAC;EACD,QAAQ;CACV;CACA,OAAO;EACL,aACE;EACF,YAAY,EAAE,OAAO;GACnB,WAAW,EACR,KAAK;IAAC;IAAQ;IAAY;GAAQ,CAAC,CAAC,CACpC,SAAS,CAAC,CACV,SAAS,gDAAgD;GAC5D,aAAa,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,mBAAmB;GAC/D,WAAW,EACR,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SACC,+GACF;GACF,OAAO,EACJ,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SAAS,mCAAmC;GAC/C,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC;GACrE,cAAc,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,eAAe;EAC9D,CAAC;EACD,QAAQ;CACV;CACA,YAAY;EACV,aACE;EACF,YAAY,EAAE,OAAO;GACnB,WAAW,EACR,KAAK;IAAC;IAAQ;IAAY;GAAM,CAAC,CAAC,CAClC,SAAS,CAAC,CACV,SACC,mEACF;GACF,OAAO,EACJ,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SACC,8bACF;GACF,KAAK,EACF,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SACC,kQACF;GACF,KAAK,EACF,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SACC,kQACF;GACF,OAAO,EACJ,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SAAS,mCAAmC;GAC/C,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC;EACvE,CAAC;EACD,QAAQ;CACV;CACA,UAAU;EACR,aACE;EACF,YAAY,EAAE,OAAO;GACnB,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,kCAAkC;GAC7D,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC;GACrE,gBAAgB,EACb,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,sCAAsC;GAClD,SAAS,EACN,KAAK,CAAC,YAAY,QAAQ,CAAC,CAAC,CAC5B,SAAS,CAAC,CACV,SAAS,yCAAyC;EACvD,CAAC;EACD,QAAQ;CACV;CACA,QAAQ;EACN,aACE;EACF,YAAY,EAAE,OAAO;GACnB,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC;GACrE,OAAO,EACJ,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SAAS,mCAAmC;GAC/C,KAAK,EAAE,OAAO,CAAC,CAAC,SAAS,gBAAgB;GACzC,KAAK,EAAE,OAAO,CAAC,CAAC,SAAS,gBAAgB;GACzC,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,iCAAiC;GACtE,cAAc,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gBAAgB;GAC7D,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,4BAA4B;EACnE,CAAC;EACD,QAAQ;CACV;CACA,YAAY;EACV,aACE;EACF,YAAY,EAAE,OAAO;GACnB,SAAS,EAAE,MAAM,qBAAqB,CAAC,CAAC,SAAS,qBAAqB;GACtE,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC;GACrE,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC;GACtE,cAAc,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,2BAA2B;EAC1E,CAAC;EACD,QAAQ;CACV;CACA,eAAe;EACb,aACE;EACF,YAAY,EAAE,OAAO;GACnB,SAAS,EAAE,MAAM,qBAAqB,CAAC,CAAC,SAAS,qBAAqB;GACtE,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC;GACrE,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gCAAgC;GACtE,cAAc,EACX,MAAM,EAAE,OAAO,CAAC,CAAC,CACjB,SAAS,CAAC,CACV,SAAS,2BAA2B;EACzC,CAAC;EACD,QAAQ;CACV;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-generative-ui",
3
- "version": "0.0.23",
3
+ "version": "0.0.24",
4
4
  "description": "Generative UI components for assistant-ui",
5
5
  "keywords": [
6
6
  "generative-ui",
@@ -51,7 +51,7 @@
51
51
  "sideEffects": false,
52
52
  "dependencies": {
53
53
  "@types/json-schema": "^7.0.15",
54
- "assistant-stream": "^0.3.47"
54
+ "assistant-stream": "^0.3.48"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "@assistant-ui/react": "^0.15.0",
@@ -65,7 +65,7 @@
65
65
  }
66
66
  },
67
67
  "devDependencies": {
68
- "@assistant-ui/react": "0.15.24",
68
+ "@assistant-ui/react": "0.15.25",
69
69
  "@assistant-ui/x-buildutils": "0.0.30",
70
70
  "@assistant-ui/x-react18": "0.0.0",
71
71
  "@types/react": "^19.3.0",
@@ -91,6 +91,7 @@
91
91
  "scripts": {
92
92
  "build": "aui-build",
93
93
  "test": "vitest run",
94
+ "test:coverage": "vitest run --coverage --coverage.reporter=text --coverage.reporter=html --coverage.reporter=json-summary",
94
95
  "typecheck": "tsc --noEmit",
95
96
  "test:peer-react18": "vitest run --config vitest.peer-react18.config.ts",
96
97
  "test:types:peer-react18": "aui-react18-types",
@@ -10,6 +10,7 @@ import {
10
10
  splitTemporalMinutes,
11
11
  temporalOffsetLabel,
12
12
  toLocalDateTime,
13
+ toPickerLocalDateTime,
13
14
  } from "./temporal";
14
15
 
15
16
  describe("temporal values", () => {
@@ -139,6 +140,23 @@ describe("temporal values", () => {
139
140
  expect(new Date("2026-07-15T12:00Z").getTimezoneOffset()).toBe(240);
140
141
  });
141
142
 
143
+ it.each(["0002", "0020", "0202"])(
144
+ "preserves minute edits in %s when the zone offset has seconds",
145
+ (year) => {
146
+ const local = `${year}-12-15T12:00`;
147
+ const value = fromLocalDateTime(local, "2025-12-15T17:00Z");
148
+ expect(value).toBe(`${year}-12-15T16:56:02Z`);
149
+ expect(toPickerLocalDateTime(value)).toBe(local);
150
+ },
151
+ );
152
+
153
+ it("preserves entered seconds with a minute precision anchor", () => {
154
+ const local = "2025-12-15T12:00:30";
155
+ const value = fromLocalDateTime(local, "2025-12-15T17:00Z");
156
+ expect(value).toBe("2025-12-15T17:00:30Z");
157
+ expect(toPickerLocalDateTime(value)).toBe(local);
158
+ });
159
+
142
160
  it.each([
143
161
  ["2026-07-15", "date"],
144
162
  ["2024-02-29", "date"],
package/src/temporal.ts CHANGED
@@ -243,6 +243,10 @@ export const fromLocalDateTime = (
243
243
  return formatTemporalInstant({
244
244
  ...previous,
245
245
  epochMs: date.getTime(),
246
+ ...(previous.precision === "minutes" &&
247
+ (temporal.precision === "seconds" || date.getUTCSeconds() !== 0)
248
+ ? { precision: "seconds" }
249
+ : {}),
246
250
  ...(date.getMilliseconds() ===
247
251
  new Date(previous.epochMs).getUTCMilliseconds()
248
252
  ? {}
@@ -55,6 +55,28 @@ const change = async (input: HTMLInputElement, value: string) => {
55
55
  });
56
56
  };
57
57
 
58
+ const type = async (input: HTMLInputElement, value: string) => {
59
+ await React.act(async () => {
60
+ input.dispatchEvent(
61
+ new KeyboardEvent("keydown", { key: "1", bubbles: true }),
62
+ );
63
+ Object.getOwnPropertyDescriptor(
64
+ HTMLInputElement.prototype,
65
+ "value",
66
+ )!.set!.call(input, value);
67
+ input.dispatchEvent(new Event("input", { bubbles: true }));
68
+ input.dispatchEvent(
69
+ new KeyboardEvent("keyup", { key: "1", bubbles: true }),
70
+ );
71
+ });
72
+ };
73
+
74
+ const blur = async (input: HTMLInputElement) => {
75
+ await React.act(async () => {
76
+ input.dispatchEvent(new FocusEvent("focusout", { bubbles: true }));
77
+ });
78
+ };
79
+
58
80
  const nativeValue = (type: string, value: string) => {
59
81
  const input = document.createElement("input");
60
82
  input.type = type;
@@ -66,6 +88,249 @@ const normalizedNativeValue = (type: string, value: string) =>
66
88
  normalizeTemporalInputValue(nativeValue(type, value));
67
89
 
68
90
  describe("DatePicker temporal contract", () => {
91
+ it("dispatches once for the final value on blur and resolves current fields", async () => {
92
+ const dispatch = vi.fn();
93
+ const container = await mount(
94
+ <div data-aui="root">
95
+ {view(
96
+ {
97
+ inputType: "datetime",
98
+ value: "2025-12-15T17:00Z",
99
+ $action: {
100
+ type: "save",
101
+ when: { $field: "when" },
102
+ note: { $field: "note" },
103
+ },
104
+ },
105
+ dispatch,
106
+ )}
107
+ <input name="note" defaultValue="before" />
108
+ </div>,
109
+ );
110
+ const input = container.querySelector<HTMLInputElement>(
111
+ '[data-aui="datepicker"]',
112
+ )!;
113
+ const note = container.querySelector<HTMLInputElement>('[name="note"]')!;
114
+ await type(input, "0002-12-15T12:00");
115
+ expect(dispatch).not.toHaveBeenCalled();
116
+ await type(input, "0020-12-15T12:00");
117
+ expect(dispatch).not.toHaveBeenCalled();
118
+ await type(input, "2025-12-16T12:00");
119
+ note.value = "after";
120
+ expect(dispatch).not.toHaveBeenCalled();
121
+ await blur(input);
122
+ expect(dispatch).toHaveBeenCalledExactlyOnceWith({
123
+ type: "save",
124
+ when: "2025-12-16T17:00Z",
125
+ note: "after",
126
+ $input: "2025-12-16T17:00Z",
127
+ });
128
+ await blur(input);
129
+ expect(dispatch).toHaveBeenCalledTimes(1);
130
+ });
131
+
132
+ it("dispatches a pick from the native picker at once", async () => {
133
+ const dispatch = vi.fn();
134
+ const container = await mount(
135
+ view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
136
+ );
137
+ const input = container.querySelector("input")!;
138
+ await change(input, "2025-12-20");
139
+ expect(dispatch).toHaveBeenCalledExactlyOnceWith({
140
+ type: "save",
141
+ $input: "2025-12-20",
142
+ });
143
+ await blur(input);
144
+ expect(dispatch).toHaveBeenCalledTimes(1);
145
+ });
146
+
147
+ it("dispatches a pick at once after the keyboard opened the picker", async () => {
148
+ const dispatch = vi.fn();
149
+ const container = await mount(
150
+ view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
151
+ );
152
+ const input = container.querySelector("input")!;
153
+ await React.act(async () => {
154
+ input.dispatchEvent(
155
+ new KeyboardEvent("keydown", {
156
+ key: "ArrowDown",
157
+ altKey: true,
158
+ bubbles: true,
159
+ }),
160
+ );
161
+ });
162
+ await change(input, "2025-12-21");
163
+ expect(dispatch).toHaveBeenCalledExactlyOnceWith({
164
+ type: "save",
165
+ $input: "2025-12-21",
166
+ });
167
+ });
168
+
169
+ it.each(["PageUp", "PageDown", "Home", "End"])(
170
+ "holds a value stepped with %s for blur",
171
+ async (key) => {
172
+ const dispatch = vi.fn();
173
+ const container = await mount(
174
+ view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
175
+ );
176
+ const input = container.querySelector("input")!;
177
+ await React.act(async () => {
178
+ input.dispatchEvent(
179
+ new KeyboardEvent("keydown", { key, bubbles: true }),
180
+ );
181
+ });
182
+ await change(input, "2025-12-25");
183
+ expect(dispatch).not.toHaveBeenCalled();
184
+ await blur(input);
185
+ expect(dispatch).toHaveBeenCalledExactlyOnceWith({
186
+ type: "save",
187
+ $input: "2025-12-25",
188
+ });
189
+ },
190
+ );
191
+
192
+ it.each(["paste", "cut"])("holds a %s for blur", async (type) => {
193
+ const dispatch = vi.fn();
194
+ const container = await mount(
195
+ view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
196
+ );
197
+ const input = container.querySelector("input")!;
198
+ await React.act(async () => {
199
+ input.dispatchEvent(new Event(type, { bubbles: true }));
200
+ });
201
+ await change(input, "2025-12-26");
202
+ expect(dispatch).not.toHaveBeenCalled();
203
+ await blur(input);
204
+ expect(dispatch).toHaveBeenCalledExactlyOnceWith({
205
+ type: "save",
206
+ $input: "2025-12-26",
207
+ });
208
+ });
209
+
210
+ it("dispatches a pick at once after a typed digit whose keyup never arrived", async () => {
211
+ const dispatch = vi.fn();
212
+ const container = await mount(
213
+ view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
214
+ );
215
+ const input = container.querySelector("input")!;
216
+ await React.act(async () => {
217
+ input.dispatchEvent(
218
+ new KeyboardEvent("keydown", { key: "1", bubbles: true }),
219
+ );
220
+ });
221
+ await change(input, "2025-12-11");
222
+ expect(dispatch).not.toHaveBeenCalled();
223
+ await change(input, "2025-12-24");
224
+ expect(dispatch).toHaveBeenCalledExactlyOnceWith({
225
+ type: "save",
226
+ $input: "2025-12-24",
227
+ });
228
+ });
229
+
230
+ it("lets a pick replace a typed value that was never committed", async () => {
231
+ const dispatch = vi.fn();
232
+ const container = await mount(
233
+ view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
234
+ );
235
+ const input = container.querySelector("input")!;
236
+ await type(input, "2025-12-16");
237
+ expect(dispatch).not.toHaveBeenCalled();
238
+ await change(input, "2025-12-22");
239
+ expect(dispatch).toHaveBeenCalledExactlyOnceWith({
240
+ type: "save",
241
+ $input: "2025-12-22",
242
+ });
243
+ await blur(input);
244
+ expect(dispatch).toHaveBeenCalledTimes(1);
245
+ });
246
+
247
+ it("commits nothing when the user leaves without editing", async () => {
248
+ const dispatch = vi.fn();
249
+ const container = await mount(
250
+ <div data-aui="root">
251
+ {view({ $action: { type: "save" } }, dispatch)}
252
+ {view({ inputType: "time", $action: { type: "save" } }, dispatch)}
253
+ {view(
254
+ {
255
+ inputType: "datetime",
256
+ value: "2025-12-15T17:00:00.000Z",
257
+ $action: { type: "save" },
258
+ },
259
+ dispatch,
260
+ )}
261
+ </div>,
262
+ );
263
+ for (const input of container.querySelectorAll<HTMLInputElement>(
264
+ '[data-aui="datepicker"]',
265
+ )) {
266
+ await blur(input);
267
+ await React.act(async () => {
268
+ input.dispatchEvent(
269
+ new KeyboardEvent("keydown", { key: "Enter", bubbles: true }),
270
+ );
271
+ });
272
+ }
273
+ expect(dispatch).not.toHaveBeenCalled();
274
+ });
275
+
276
+ it("commits on Enter outside a form unless composing", async () => {
277
+ const dispatch = vi.fn();
278
+ const container = await mount(
279
+ view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
280
+ );
281
+ const input = container.querySelector("input")!;
282
+ await type(input, "2025-12-16");
283
+ await React.act(async () => {
284
+ input.dispatchEvent(
285
+ new KeyboardEvent("keydown", {
286
+ key: "Enter",
287
+ isComposing: true,
288
+ bubbles: true,
289
+ }),
290
+ );
291
+ });
292
+ expect(dispatch).not.toHaveBeenCalled();
293
+ await React.act(async () => {
294
+ input.dispatchEvent(
295
+ new KeyboardEvent("keydown", { key: "Enter", bubbles: true }),
296
+ );
297
+ });
298
+ expect(dispatch).toHaveBeenCalledExactlyOnceWith({
299
+ type: "save",
300
+ $input: "2025-12-16",
301
+ });
302
+ await blur(input);
303
+ expect(dispatch).toHaveBeenCalledTimes(1);
304
+ });
305
+
306
+ it("resets the committed baseline when the initial value changes", async () => {
307
+ const dispatch = vi.fn();
308
+ const container = await mount(
309
+ view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
310
+ );
311
+ let input = container.querySelector("input")!;
312
+ await blur(input);
313
+ expect(dispatch).not.toHaveBeenCalled();
314
+ await type(input, "2025-12-16");
315
+ await blur(input);
316
+ expect(dispatch).toHaveBeenCalledTimes(1);
317
+ await React.act(async () =>
318
+ root!.render(
319
+ view({ value: "2025-12-17", $action: { type: "save" } }, dispatch),
320
+ ),
321
+ );
322
+ input = container.querySelector("input")!;
323
+ await blur(input);
324
+ expect(dispatch).toHaveBeenCalledTimes(1);
325
+ await type(input, "2025-12-16");
326
+ await blur(input);
327
+ expect(dispatch).toHaveBeenLastCalledWith({
328
+ type: "save",
329
+ $input: "2025-12-16",
330
+ });
331
+ expect(dispatch).toHaveBeenCalledTimes(2);
332
+ });
333
+
69
334
  it.each([
70
335
  ["2026-07-15T12:34Z", "2026-07-16T13:45Z", ""],
71
336
  ["2026-07-15T12:34:56+08:00", "2026-07-16T21:45:00+08:00", "1"],
@@ -88,10 +353,12 @@ describe("DatePicker temporal contract", () => {
88
353
  await change(input, "2026-07-16T08:30");
89
354
  expect(input.step).toBe(step);
90
355
  await change(input, "");
356
+ await blur(input);
91
357
  expect(dispatch).toHaveBeenLastCalledWith({ type: "save", $input: "" });
92
358
  expect(collectFormValues([input])).toEqual({ when: "" });
93
359
  expect(input.step).toBe(step);
94
360
  await change(input, "2026-07-16T09:45");
361
+ await blur(input);
95
362
  expect(dispatch).toHaveBeenLastCalledWith({
96
363
  type: "save",
97
364
  $input: emitted,
@@ -115,6 +382,7 @@ describe("DatePicker temporal contract", () => {
115
382
  );
116
383
  const input = container.querySelector("input")!;
117
384
  await change(input, "2025-12-15T12:00:00.750");
385
+ await blur(input);
118
386
  expect(dispatch).toHaveBeenLastCalledWith({
119
387
  type: "save",
120
388
  $input: "2025-12-15T17:00:00.750Z",
@@ -168,6 +436,7 @@ describe("DatePicker temporal contract", () => {
168
436
  expect(collectFormValues([input])).toEqual({ when: value });
169
437
  }
170
438
  await change(input, edited);
439
+ await blur(input);
171
440
  expect(dispatch).toHaveBeenLastCalledWith({
172
441
  type: "save",
173
442
  $input: emitted,
@@ -190,6 +459,7 @@ describe("DatePicker temporal contract", () => {
190
459
  );
191
460
  const input = container.querySelector("input")!;
192
461
  await change(input, "2026-07-16T08:34:56.789");
462
+ await blur(input);
193
463
  expect(dispatch).toHaveBeenLastCalledWith({
194
464
  type: "save",
195
465
  $input: "2026-07-16T12:34:56.789000Z",
@@ -279,8 +549,10 @@ describe("DatePicker temporal contract", () => {
279
549
  const container = await mount(<Surface />);
280
550
  const input = container.querySelector("input")!;
281
551
  expect(input.getAttribute("value")).toBe(shown);
282
- await change(input, edited);
552
+ await type(input, edited);
283
553
  expect(update).toHaveBeenLastCalledWith("when", emitted);
554
+ expect(dispatch).not.toHaveBeenCalled();
555
+ await blur(input);
284
556
  expect(dispatch).toHaveBeenLastCalledWith({
285
557
  type: "save",
286
558
  $input: emitted,
@@ -370,19 +642,23 @@ describe("DatePicker temporal contract", () => {
370
642
  };
371
643
  const container = await mount(<Surface />);
372
644
  const input = container.querySelector("input")!;
373
- await change(input, "2026-07-16T08:30");
374
- await change(input, "");
645
+ await type(input, "2026-07-16T08:30");
646
+ await type(input, "");
375
647
  expect(input.step).toBe("");
376
- await change(input, "2026-07-16T09:45");
648
+ await type(input, "2026-07-16T09:45");
649
+ await blur(input);
377
650
  expect(dispatch).toHaveBeenLastCalledWith({
378
651
  type: "save",
379
652
  $input: "2026-07-16T13:45-00:00",
380
653
  });
381
654
  expect(input.step).toBe("");
382
655
  await React.act(async () => replace!("2026-07-17T12:34:56+08:00"));
383
- await change(input, "");
656
+ await blur(input);
657
+ expect(dispatch).toHaveBeenCalledTimes(1);
658
+ await type(input, "");
384
659
  expect(input.step).toBe("1");
385
- await change(input, "2026-07-18T09:45:30");
660
+ await type(input, "2026-07-18T09:45:30");
661
+ await blur(input);
386
662
  expect(dispatch).toHaveBeenLastCalledWith({
387
663
  type: "save",
388
664
  $input: "2026-07-18T21:45:30+08:00",
@@ -516,6 +792,7 @@ describe("DatePicker temporal contract", () => {
516
792
  $input: { when: initial },
517
793
  });
518
794
  await change(input, "2025-12-16T09:30");
795
+ await blur(input);
519
796
  expect(dispatch).toHaveBeenLastCalledWith({
520
797
  type: "picker",
521
798
  when: edited,
@@ -721,6 +998,27 @@ describe("DatePicker temporal contract", () => {
721
998
  expect(new Date("2026-07-15T12:00Z").getTimezoneOffset()).toBe(240);
722
999
  });
723
1000
 
1001
+ it("keeps year edits stable across historical second-level offsets", async () => {
1002
+ const container = await mount(
1003
+ view({ inputType: "datetime", value: "2025-12-15T17:00Z" }),
1004
+ );
1005
+ const input = container.querySelector<HTMLInputElement>("input")!;
1006
+
1007
+ for (const [year, canonical] of [
1008
+ ["0002", "0002-12-15T16:56:02Z"],
1009
+ ["0020", "0020-12-15T16:56:02Z"],
1010
+ ["0202", "0202-12-15T16:56:02Z"],
1011
+ ["2025", "2025-12-15T17:00Z"],
1012
+ ]) {
1013
+ const local = `${year}-12-15T12:00`;
1014
+ await change(input, local);
1015
+ expect(input.value).toBe(local);
1016
+ expect(input.step).toBe("");
1017
+ expect(input.getAttribute(FIELD_VALUE_ATTR)).toBe(canonical);
1018
+ expect(collectFormValues([input])).toEqual({ when: canonical });
1019
+ }
1020
+ });
1021
+
724
1022
  it("declares the three temporal input types and defaults to date", async () => {
725
1023
  const schema = interactiveVocabulary.DatePicker.properties;
726
1024
  for (const inputType of ["date", "datetime", "time"]) {
@@ -785,6 +1083,7 @@ describe("DatePicker temporal contract", () => {
785
1083
  expect(input.name).toBe("when");
786
1084
  expect(input.readOnly).toBe(false);
787
1085
  await change(input, edited);
1086
+ await blur(input);
788
1087
  expect(dispatch).toHaveBeenLastCalledWith({
789
1088
  type: "save",
790
1089
  $input: emitted,
@@ -847,6 +1146,7 @@ describe("DatePicker temporal contract", () => {
847
1146
  expect(input.type).toBe("datetime-local");
848
1147
  expect(input.value).toBe("");
849
1148
  await change(input, edited!);
1149
+ await blur(input);
850
1150
  expect(dispatch).toHaveBeenLastCalledWith({
851
1151
  type: "save",
852
1152
  $input: emitted,
@@ -863,13 +1163,16 @@ describe("DatePicker temporal contract", () => {
863
1163
  const input = container.querySelector("input")!;
864
1164
  await change(input, "2026-01-15T12:34");
865
1165
  await change(input, "2026-07-15T12:34");
1166
+ await blur(input);
866
1167
  expect(dispatch).toHaveBeenLastCalledWith({
867
1168
  type: "save",
868
1169
  $input: "2026-07-15T12:34:00-04:00",
869
1170
  });
870
1171
  await change(input, "");
1172
+ await blur(input);
871
1173
  expect(dispatch).toHaveBeenLastCalledWith({ type: "save", $input: "" });
872
1174
  await change(input, "2026-07-16T12:34");
1175
+ await blur(input);
873
1176
  expect(dispatch).toHaveBeenLastCalledWith({
874
1177
  type: "save",
875
1178
  $input: "2026-07-16T12:34:00-04:00",
@@ -949,6 +1252,7 @@ describe("DatePicker temporal contract", () => {
949
1252
  expect(input.max).toBe(max);
950
1253
  expect(input.step).toBe(step);
951
1254
  await change(input, edited);
1255
+ await blur(input);
952
1256
  expect(dispatch).toHaveBeenLastCalledWith({
953
1257
  type: "save",
954
1258
  $input: normalizedNativeValue(input.type, edited),
@@ -1025,6 +1329,7 @@ describe("DatePicker temporal contract", () => {
1025
1329
  );
1026
1330
  await change(input, edited);
1027
1331
  expect(update).toHaveBeenLastCalledWith("when", emitted);
1332
+ await blur(input);
1028
1333
  expect(dispatch).toHaveBeenLastCalledWith({
1029
1334
  type: "save",
1030
1335
  $input: emitted,
@@ -1038,6 +1343,7 @@ describe("DatePicker temporal contract", () => {
1038
1343
  expect(input.value).toBe(nativeValue(input.type, edited));
1039
1344
  await change(input, "");
1040
1345
  expect(update).toHaveBeenLastCalledWith("when", "");
1346
+ await blur(input);
1041
1347
  expect(dispatch).toHaveBeenLastCalledWith({ type: "save", $input: "" });
1042
1348
  },
1043
1349
  );
@@ -1061,6 +1367,7 @@ describe("DatePicker temporal contract", () => {
1061
1367
  nativeValue("datetime-local", "2026-07-15T14:04:56"),
1062
1368
  );
1063
1369
  await change(input, "2026-07-16T09:45:30");
1370
+ await blur(input);
1064
1371
  expect(dispatch).toHaveBeenLastCalledWith({
1065
1372
  type: "save",
1066
1373
  $input: "2026-07-16T08:15:30-05:30",
@@ -1084,6 +1391,7 @@ describe("DatePicker temporal contract", () => {
1084
1391
  nativeValue("datetime-local", "2026-07-17T00:34:56"),
1085
1392
  );
1086
1393
  await change(input, "2026-07-18T09:45:30");
1394
+ await blur(input);
1087
1395
  expect(dispatch).toHaveBeenLastCalledWith({
1088
1396
  type: "save",
1089
1397
  $input: "2026-07-18T21:45:30+08:00",
@@ -1119,6 +1427,7 @@ describe("DatePicker temporal contract", () => {
1119
1427
  nativeValue("datetime-local", "2026-07-15T00:34:56.123"),
1120
1428
  );
1121
1429
  await change(input, "2026-07-16T09:45:30");
1430
+ await blur(input);
1122
1431
  expect(dispatch).toHaveBeenLastCalledWith({
1123
1432
  type: "save",
1124
1433
  $input: "2026-07-16T21:45:30.000+08:00",
@@ -411,15 +411,17 @@ function DatePickerRender({
411
411
  initialValue,
412
412
  value: initialValue,
413
413
  anchor: initialValue,
414
+ committedValue: initialValue,
415
+ edited: false,
414
416
  });
415
417
  if (selection.initialValue !== initialValue) {
418
+ const isEcho = isBound && initialValue === selection.value;
416
419
  setSelection({
417
420
  initialValue,
418
421
  value: initialValue,
419
- anchor:
420
- isBound && initialValue === selection.value
421
- ? selection.anchor
422
- : initialValue,
422
+ anchor: isEcho ? selection.anchor : initialValue,
423
+ committedValue: isEcho ? selection.committedValue : initialValue,
424
+ edited: isEcho && selection.edited,
423
425
  });
424
426
  }
425
427
  const currentValue = isBound ? initialValue : selection.value;
@@ -457,6 +459,20 @@ function DatePickerRender({
457
459
  : inputType === "datetime" && hydrated
458
460
  ? toPickerLocalDateTime(currentValue)
459
461
  : normalizeTemporalInputValue(currentValue);
462
+ const keyboardActive = React.useRef(false);
463
+ const valueFromInput = (input: HTMLInputElement) => {
464
+ const inputValue = normalizeTemporalInputValue(input.value);
465
+ return inputType === "datetime"
466
+ ? fromLocalDateTime(inputValue, selection.anchor)
467
+ : inputValue;
468
+ };
469
+ const commit = (input: HTMLInputElement) => {
470
+ if (!selection.edited) return;
471
+ const nextValue = valueFromInput(input);
472
+ setSelection({ ...selection, committedValue: nextValue, edited: false });
473
+ if (nextValue !== selection.committedValue)
474
+ fire($action, $dispatch, nextValue, input);
475
+ };
460
476
  return (
461
477
  <input
462
478
  key={isBound ? undefined : initialValue}
@@ -504,28 +520,73 @@ function DatePickerRender({
504
520
  }
505
521
  step={
506
522
  inputType !== "date"
507
- ? getTemporalInputStep(selection.anchor, currentValue, min, max)
523
+ ? getTemporalInputStep(
524
+ selection.anchor,
525
+ hasInstantAnchor ? displayValue : currentValue,
526
+ min,
527
+ max,
528
+ )
508
529
  : undefined
509
530
  }
510
531
  onChange={(e) => {
511
- const inputValue = normalizeTemporalInputValue(e.currentTarget.value);
512
- const nextValue =
513
- inputType === "datetime"
514
- ? fromLocalDateTime(inputValue, selection.anchor)
515
- : inputValue;
532
+ const nextValue = valueFromInput(e.currentTarget);
516
533
  const canonical = canonicalFieldValue(nextValue);
517
534
  if (canonical !== undefined) {
518
535
  e.currentTarget.setAttribute(FIELD_VALUE_ATTR, canonical);
519
536
  } else {
520
537
  e.currentTarget.removeAttribute(FIELD_VALUE_ATTR);
521
538
  }
539
+ // A change without an editing key down is a pick from the native picker, which commits at once; a typed value waits for blur or Enter.
540
+ const typed = keyboardActive.current;
541
+ keyboardActive.current = false;
522
542
  setSelection({
523
543
  initialValue,
524
544
  value: nextValue,
525
545
  anchor: selection.anchor,
546
+ committedValue: typed ? selection.committedValue : nextValue,
547
+ edited: typed,
526
548
  });
527
549
  updateBinding?.(nextValue);
528
- fire($action, $dispatch, nextValue, e.currentTarget);
550
+ if (!typed && nextValue !== selection.committedValue)
551
+ fire($action, $dispatch, nextValue, e.currentTarget);
552
+ }}
553
+ onBlur={(e) => {
554
+ keyboardActive.current = false;
555
+ commit(e.currentTarget);
556
+ }}
557
+ onKeyDown={(e) => {
558
+ if (
559
+ e.key === "Enter" &&
560
+ !e.nativeEvent.isComposing &&
561
+ !e.currentTarget.form
562
+ ) {
563
+ commit(e.currentTarget);
564
+ return;
565
+ }
566
+ keyboardActive.current =
567
+ !e.altKey &&
568
+ !e.ctrlKey &&
569
+ !e.metaKey &&
570
+ (e.key.length === 1 ||
571
+ [
572
+ "ArrowDown",
573
+ "ArrowUp",
574
+ "Backspace",
575
+ "Delete",
576
+ "End",
577
+ "Home",
578
+ "PageDown",
579
+ "PageUp",
580
+ ].includes(e.key));
581
+ }}
582
+ onKeyUp={() => {
583
+ keyboardActive.current = false;
584
+ }}
585
+ onPaste={() => {
586
+ keyboardActive.current = true;
587
+ }}
588
+ onCut={() => {
589
+ keyboardActive.current = true;
529
590
  }}
530
591
  />
531
592
  );
@@ -952,7 +1013,7 @@ export const interactiveVocabulary = {
952
1013
  },
953
1014
  DatePicker: {
954
1015
  description:
955
- "A date, datetime, or time input. Carries `$action` describing the on-select behavior.",
1016
+ "A date, datetime, or time input. Carries `$action`, which runs once per committed value: a pick from the picker, or a typed value on blur or Enter.",
956
1017
  properties: z.object({
957
1018
  inputType: z
958
1019
  .enum(["date", "datetime", "time"])