@stina/extension-api 0.52.0 → 0.53.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/runtime.cjs +2 -2
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +2 -2
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.js +2 -2
- package/dist/runtime.js.map +1 -1
- package/dist/schemas/index.cjs +545 -2
- package/dist/schemas/index.cjs.map +1 -1
- package/dist/schemas/index.d.cts +735 -35
- package/dist/schemas/index.d.ts +735 -35
- package/dist/schemas/index.js +520 -1
- package/dist/schemas/index.js.map +1 -1
- package/dist/{types.tools-XpXCoPrJ.d.cts → types.tools-BL9WPsi_.d.cts} +314 -2
- package/dist/{types.tools-XpXCoPrJ.d.ts → types.tools-BL9WPsi_.d.ts} +314 -2
- package/package.json +1 -1
- package/src/index.ts +20 -0
- package/src/runtime.ts +2 -2
- package/src/schemas/card.schema.test.ts +235 -0
- package/src/schemas/card.schema.ts +606 -0
- package/src/schemas/components.schema.ts +241 -0
- package/src/schemas/index.ts +30 -0
- package/src/types.components.ts +336 -0
- package/src/types.context.ts +15 -1
- package/src/types.tools.ts +15 -0
|
@@ -518,6 +518,228 @@ export const ListPropsSchema = z
|
|
|
518
518
|
.passthrough()
|
|
519
519
|
.describe('List component')
|
|
520
520
|
|
|
521
|
+
// =============================================================================
|
|
522
|
+
// Purpose-built Display Components
|
|
523
|
+
// =============================================================================
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* The skies the weather components know how to draw. Closed on purpose: the
|
|
527
|
+
* icon is chosen from this list, and a string outside it has no picture.
|
|
528
|
+
*/
|
|
529
|
+
export const WeatherConditionSchema = z
|
|
530
|
+
.enum([
|
|
531
|
+
'clear',
|
|
532
|
+
'partly-cloudy',
|
|
533
|
+
'cloudy',
|
|
534
|
+
'overcast',
|
|
535
|
+
'fog',
|
|
536
|
+
'light-rain',
|
|
537
|
+
'rain',
|
|
538
|
+
'heavy-rain',
|
|
539
|
+
'sleet',
|
|
540
|
+
'light-snow',
|
|
541
|
+
'snow',
|
|
542
|
+
'heavy-snow',
|
|
543
|
+
'thunderstorm',
|
|
544
|
+
'hail',
|
|
545
|
+
'windy',
|
|
546
|
+
])
|
|
547
|
+
.describe('Weather condition')
|
|
548
|
+
|
|
549
|
+
export const WeatherWindSchema = z
|
|
550
|
+
.object({
|
|
551
|
+
speed: z.number().describe('Sustained wind speed'),
|
|
552
|
+
unit: z.string().optional().describe('Unit written after the speed. Defaults to m/s'),
|
|
553
|
+
direction: z.string().optional().describe('Compass direction, in the caller\'s own words'),
|
|
554
|
+
gust: z.number().optional().describe('Gust speed, in the same unit'),
|
|
555
|
+
})
|
|
556
|
+
.passthrough()
|
|
557
|
+
.describe('Wind reading')
|
|
558
|
+
|
|
559
|
+
export const WeatherNowPropsSchema = z
|
|
560
|
+
.object({
|
|
561
|
+
component: z.literal('WeatherNow'),
|
|
562
|
+
place: z.string().describe('Where the reading is from'),
|
|
563
|
+
condition: WeatherConditionSchema,
|
|
564
|
+
conditionLabel: z.string().optional().describe('The condition in the user\'s own language'),
|
|
565
|
+
temperature: z.number().describe('Current temperature'),
|
|
566
|
+
feelsLike: z.number().optional().describe('Apparent temperature'),
|
|
567
|
+
unit: z.string().optional().describe('Written after every temperature. Defaults to °'),
|
|
568
|
+
wind: WeatherWindSchema.optional(),
|
|
569
|
+
night: z.boolean().optional().describe('Draw the night variant of the icon'),
|
|
570
|
+
summary: z.string().optional().describe('One line of context under the numbers'),
|
|
571
|
+
style: ExtensionComponentStyleSchema.optional(),
|
|
572
|
+
})
|
|
573
|
+
.passthrough()
|
|
574
|
+
.describe('Current weather component')
|
|
575
|
+
|
|
576
|
+
export const WeatherForecastStepSchema = z
|
|
577
|
+
.object({
|
|
578
|
+
label: z.string().describe('Short step label, e.g. 14 or Tors'),
|
|
579
|
+
condition: WeatherConditionSchema,
|
|
580
|
+
conditionLabel: z.string().optional(),
|
|
581
|
+
high: z.number().describe('The warmer temperature, or the only one'),
|
|
582
|
+
low: z.number().optional().describe('The colder temperature'),
|
|
583
|
+
night: z.boolean().optional(),
|
|
584
|
+
precipitation: z.number().min(0).max(100).optional().describe('Chance of precipitation, 0-100'),
|
|
585
|
+
current: z.boolean().optional().describe('The step the reader is at; drawn larger'),
|
|
586
|
+
})
|
|
587
|
+
.passthrough()
|
|
588
|
+
.describe('One forecast step')
|
|
589
|
+
|
|
590
|
+
export const WeatherForecastPropsSchema = z
|
|
591
|
+
.object({
|
|
592
|
+
component: z.literal('WeatherForecast'),
|
|
593
|
+
title: z.string().optional().describe('Heading above the row'),
|
|
594
|
+
icon: z.string().optional().describe('Icon shown to the left of the title'),
|
|
595
|
+
unit: z.string().optional().describe('Written after every temperature. Defaults to °'),
|
|
596
|
+
steps: z.array(WeatherForecastStepSchema).min(1).describe('Steps, hourly or daily'),
|
|
597
|
+
style: ExtensionComponentStyleSchema.optional(),
|
|
598
|
+
})
|
|
599
|
+
.passthrough()
|
|
600
|
+
.describe('Weather forecast component')
|
|
601
|
+
|
|
602
|
+
export const ChartKindSchema = z.enum(['line', 'bar', 'area']).describe('How the chart draws')
|
|
603
|
+
|
|
604
|
+
export const ChartSeriesSchema = z
|
|
605
|
+
.object({
|
|
606
|
+
name: z.string().optional().describe('Named in the legend'),
|
|
607
|
+
points: z
|
|
608
|
+
.array(z.number().nullable())
|
|
609
|
+
.describe('One value per label. null is a gap, drawn as one'),
|
|
610
|
+
})
|
|
611
|
+
.passthrough()
|
|
612
|
+
.describe('One chart series')
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Eight series is the ceiling because the host palette has eight slots, assigned
|
|
616
|
+
* in a fixed order so a series keeps its colour wherever it appears. A ninth
|
|
617
|
+
* would need a generated hue, and a generated hue is how a chart stops being
|
|
618
|
+
* readable to colour-blind viewers.
|
|
619
|
+
*/
|
|
620
|
+
export const ChartPropsSchema = z
|
|
621
|
+
.object({
|
|
622
|
+
component: z.literal('Chart'),
|
|
623
|
+
chart: ChartKindSchema.optional().describe('Defaults to line'),
|
|
624
|
+
series: z.array(ChartSeriesSchema).min(1).max(8).describe('At most eight series'),
|
|
625
|
+
labels: z.array(z.string()).optional().describe('One per point, along the horizontal axis'),
|
|
626
|
+
title: z.string().optional().describe('Heading above the plot'),
|
|
627
|
+
icon: z.string().optional().describe('Icon shown to the left of the title'),
|
|
628
|
+
unit: z.string().optional().describe('Written after every value'),
|
|
629
|
+
zeroBaseline: z.boolean().optional().describe('Force the value axis to include zero'),
|
|
630
|
+
height: z.number().min(4).max(24).optional().describe('Plot height in rem. Defaults to 9'),
|
|
631
|
+
style: ExtensionComponentStyleSchema.optional(),
|
|
632
|
+
})
|
|
633
|
+
.passthrough()
|
|
634
|
+
.describe('Chart component')
|
|
635
|
+
|
|
636
|
+
export const StatTrendSchema = z.enum(['up', 'down', 'flat']).describe('Direction of change')
|
|
637
|
+
|
|
638
|
+
export const StatTilePropsSchema = z
|
|
639
|
+
.object({
|
|
640
|
+
component: z.literal('StatTile'),
|
|
641
|
+
label: z.string().describe('What the number is'),
|
|
642
|
+
value: z.union([z.string(), z.number()]).describe('The number itself'),
|
|
643
|
+
unit: z.string().optional().describe('Written after the value in smaller type'),
|
|
644
|
+
caption: z.string().optional().describe('One quiet line under the number'),
|
|
645
|
+
icon: z.string().optional().describe('Icon name'),
|
|
646
|
+
trend: StatTrendSchema.optional(),
|
|
647
|
+
trendLabel: z.string().optional().describe('The change in words'),
|
|
648
|
+
trendIsGood: z.boolean().optional().describe('Whether up is the good direction. Defaults to true'),
|
|
649
|
+
style: ExtensionComponentStyleSchema.optional(),
|
|
650
|
+
})
|
|
651
|
+
.passthrough()
|
|
652
|
+
.describe('Single-number stat tile component')
|
|
653
|
+
|
|
654
|
+
export const KeyValueRowSchema = z
|
|
655
|
+
.object({
|
|
656
|
+
label: z.string(),
|
|
657
|
+
value: z.string(),
|
|
658
|
+
icon: z.string().optional().describe('Icon name'),
|
|
659
|
+
})
|
|
660
|
+
.passthrough()
|
|
661
|
+
.describe('One key/value row')
|
|
662
|
+
|
|
663
|
+
export const KeyValueListPropsSchema = z
|
|
664
|
+
.object({
|
|
665
|
+
component: z.literal('KeyValueList'),
|
|
666
|
+
rows: z.array(KeyValueRowSchema).min(1).describe('Facts in two aligned columns'),
|
|
667
|
+
style: ExtensionComponentStyleSchema.optional(),
|
|
668
|
+
})
|
|
669
|
+
.passthrough()
|
|
670
|
+
.describe('Key/value list component')
|
|
671
|
+
|
|
672
|
+
export const TimelineVariantSchema = z
|
|
673
|
+
.enum(['default', 'accent', 'success', 'warning', 'danger'])
|
|
674
|
+
.describe('How much a timeline entry stands out')
|
|
675
|
+
|
|
676
|
+
export const TimelineEntrySchema = z
|
|
677
|
+
.object({
|
|
678
|
+
time: z.string().describe('When it happens, worded as it should be read'),
|
|
679
|
+
title: z.string(),
|
|
680
|
+
description: z.string().optional().describe('A line under the title'),
|
|
681
|
+
icon: z.string().optional().describe('Icon name'),
|
|
682
|
+
duration: z.string().optional().describe('How long it lasts, in words'),
|
|
683
|
+
badge: z.string().optional().describe('Trailing badge'),
|
|
684
|
+
current: z.boolean().optional().describe('The entry the reader is at'),
|
|
685
|
+
past: z.boolean().optional().describe('Already behind us; drawn recessive'),
|
|
686
|
+
variant: TimelineVariantSchema.optional(),
|
|
687
|
+
})
|
|
688
|
+
.passthrough()
|
|
689
|
+
.describe('One timeline entry')
|
|
690
|
+
|
|
691
|
+
export const TimelinePropsSchema = z
|
|
692
|
+
.object({
|
|
693
|
+
component: z.literal('Timeline'),
|
|
694
|
+
title: z.string().optional().describe('Heading above the rail'),
|
|
695
|
+
icon: z.string().optional().describe('Icon shown to the left of the title'),
|
|
696
|
+
entries: z.array(TimelineEntrySchema).min(1).describe('Entries, in the order they happen'),
|
|
697
|
+
style: ExtensionComponentStyleSchema.optional(),
|
|
698
|
+
})
|
|
699
|
+
.passthrough()
|
|
700
|
+
.describe('Vertical timeline component')
|
|
701
|
+
|
|
702
|
+
export const NoteVariantSchema = z
|
|
703
|
+
.enum(['default', 'accent', 'success', 'warning', 'danger'])
|
|
704
|
+
.describe('How much a note stands out')
|
|
705
|
+
|
|
706
|
+
export const NotePropsSchema = z
|
|
707
|
+
.object({
|
|
708
|
+
component: z.literal('Note'),
|
|
709
|
+
title: z.string().optional(),
|
|
710
|
+
icon: z.string().optional().describe('Icon shown to the left of the title'),
|
|
711
|
+
badge: z.string().optional().describe('Trailing badge next to the title'),
|
|
712
|
+
content: z.string().describe('The body, as markdown'),
|
|
713
|
+
footer: z.string().optional().describe('One quiet line at the bottom'),
|
|
714
|
+
variant: NoteVariantSchema.optional(),
|
|
715
|
+
style: ExtensionComponentStyleSchema.optional(),
|
|
716
|
+
})
|
|
717
|
+
.passthrough()
|
|
718
|
+
.describe('Titled block of prose component')
|
|
719
|
+
|
|
720
|
+
export const CalendarEventStatusSchema = z
|
|
721
|
+
.enum(['confirmed', 'tentative', 'cancelled'])
|
|
722
|
+
.describe('Whether an event is going ahead')
|
|
723
|
+
|
|
724
|
+
export const CalendarEventPropsSchema = z
|
|
725
|
+
.object({
|
|
726
|
+
component: z.literal('CalendarEvent'),
|
|
727
|
+
title: z.string(),
|
|
728
|
+
start: z.string().describe('ISO 8601 with an offset'),
|
|
729
|
+
end: z.string().optional().describe('ISO 8601 with an offset'),
|
|
730
|
+
allDay: z.boolean().optional(),
|
|
731
|
+
location: z.string().optional(),
|
|
732
|
+
organizer: z.string().optional(),
|
|
733
|
+
attendees: z.array(z.string()).optional(),
|
|
734
|
+
calendar: z.string().optional().describe('Which calendar it sits in; shown as a badge'),
|
|
735
|
+
status: CalendarEventStatusSchema.optional(),
|
|
736
|
+
recurrence: z.string().optional().describe('How it repeats, in words'),
|
|
737
|
+
notes: z.string().optional(),
|
|
738
|
+
style: ExtensionComponentStyleSchema.optional(),
|
|
739
|
+
})
|
|
740
|
+
.passthrough()
|
|
741
|
+
.describe('Single calendar event component')
|
|
742
|
+
|
|
521
743
|
// =============================================================================
|
|
522
744
|
// Type Exports
|
|
523
745
|
// =============================================================================
|
|
@@ -562,3 +784,22 @@ export type ConditionalGroupProps = z.infer<typeof ConditionalGroupPropsSchema>
|
|
|
562
784
|
export type FrameVariant = z.infer<typeof FrameVariantSchema>
|
|
563
785
|
export type FrameProps = z.infer<typeof FramePropsSchema>
|
|
564
786
|
export type ListProps = z.infer<typeof ListPropsSchema>
|
|
787
|
+
export type WeatherCondition = z.infer<typeof WeatherConditionSchema>
|
|
788
|
+
export type WeatherWind = z.infer<typeof WeatherWindSchema>
|
|
789
|
+
export type WeatherNowProps = z.infer<typeof WeatherNowPropsSchema>
|
|
790
|
+
export type WeatherForecastStep = z.infer<typeof WeatherForecastStepSchema>
|
|
791
|
+
export type WeatherForecastProps = z.infer<typeof WeatherForecastPropsSchema>
|
|
792
|
+
export type ChartKind = z.infer<typeof ChartKindSchema>
|
|
793
|
+
export type ChartSeries = z.infer<typeof ChartSeriesSchema>
|
|
794
|
+
export type ChartProps = z.infer<typeof ChartPropsSchema>
|
|
795
|
+
export type StatTrend = z.infer<typeof StatTrendSchema>
|
|
796
|
+
export type StatTileProps = z.infer<typeof StatTilePropsSchema>
|
|
797
|
+
export type KeyValueRow = z.infer<typeof KeyValueRowSchema>
|
|
798
|
+
export type KeyValueListProps = z.infer<typeof KeyValueListPropsSchema>
|
|
799
|
+
export type TimelineVariant = z.infer<typeof TimelineVariantSchema>
|
|
800
|
+
export type TimelineEntry = z.infer<typeof TimelineEntrySchema>
|
|
801
|
+
export type TimelineProps = z.infer<typeof TimelinePropsSchema>
|
|
802
|
+
export type NoteVariant = z.infer<typeof NoteVariantSchema>
|
|
803
|
+
export type NoteProps = z.infer<typeof NotePropsSchema>
|
|
804
|
+
export type CalendarEventStatus = z.infer<typeof CalendarEventStatusSchema>
|
|
805
|
+
export type CalendarEventProps = z.infer<typeof CalendarEventPropsSchema>
|
package/src/schemas/index.ts
CHANGED
|
@@ -118,6 +118,25 @@ export {
|
|
|
118
118
|
MarkdownPropsSchema,
|
|
119
119
|
ModalPropsSchema,
|
|
120
120
|
ConditionalGroupPropsSchema,
|
|
121
|
+
WeatherConditionSchema,
|
|
122
|
+
WeatherWindSchema,
|
|
123
|
+
WeatherNowPropsSchema,
|
|
124
|
+
WeatherForecastStepSchema,
|
|
125
|
+
WeatherForecastPropsSchema,
|
|
126
|
+
ChartKindSchema,
|
|
127
|
+
ChartSeriesSchema,
|
|
128
|
+
ChartPropsSchema,
|
|
129
|
+
StatTrendSchema,
|
|
130
|
+
StatTilePropsSchema,
|
|
131
|
+
KeyValueRowSchema,
|
|
132
|
+
KeyValueListPropsSchema,
|
|
133
|
+
TimelineVariantSchema,
|
|
134
|
+
TimelineEntrySchema,
|
|
135
|
+
TimelinePropsSchema,
|
|
136
|
+
NoteVariantSchema,
|
|
137
|
+
NotePropsSchema,
|
|
138
|
+
CalendarEventStatusSchema,
|
|
139
|
+
CalendarEventPropsSchema,
|
|
121
140
|
type ExtensionComponentData,
|
|
122
141
|
type ExtensionComponentStyle,
|
|
123
142
|
type ExtensionActionCall,
|
|
@@ -156,3 +175,14 @@ export {
|
|
|
156
175
|
type ModalProps,
|
|
157
176
|
type ConditionalGroupProps,
|
|
158
177
|
} from './components.schema.js'
|
|
178
|
+
|
|
179
|
+
// Chat card profile - the subset of the DSL a tool may put in a conversation
|
|
180
|
+
export {
|
|
181
|
+
ChatCardSchema,
|
|
182
|
+
ChatCardComponentSchema,
|
|
183
|
+
CHAT_CARD_COMPONENTS,
|
|
184
|
+
validateChatCard,
|
|
185
|
+
describeChatCardProfile,
|
|
186
|
+
type ChatCardComponent,
|
|
187
|
+
type ChatCardValidation,
|
|
188
|
+
} from './card.schema.js'
|
package/src/types.components.ts
CHANGED
|
@@ -536,3 +536,339 @@ export interface ListProps extends ExtensionComponentData {
|
|
|
536
536
|
/** Child components to render as list items. Supports iteration. */
|
|
537
537
|
children: ExtensionComponentChildren
|
|
538
538
|
}
|
|
539
|
+
|
|
540
|
+
// =============================================================================
|
|
541
|
+
// Purpose-built display components
|
|
542
|
+
// =============================================================================
|
|
543
|
+
|
|
544
|
+
/*
|
|
545
|
+
* The components below differ in kind from the ones above. Those are building
|
|
546
|
+
* blocks — a stack, a label, a button — and what they mean is whatever the
|
|
547
|
+
* author arranges them into. These name a *subject*: weather, a measurement,
|
|
548
|
+
* a series over time. The caller supplies the facts and the host decides how
|
|
549
|
+
* they look, which is what lets the same declaration render as HTML on the web
|
|
550
|
+
* and as native geometry on a phone without either side guessing.
|
|
551
|
+
*
|
|
552
|
+
* That is also why they take no colors and no layout. A chart that let its
|
|
553
|
+
* author pick series colors would be eight different charts across eight
|
|
554
|
+
* extensions; the palette is the host's, assigned in a fixed order, and it has
|
|
555
|
+
* been validated for colour-vision deficiency against the app's own surfaces.
|
|
556
|
+
*/
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* A sky the DSL knows how to draw.
|
|
560
|
+
*
|
|
561
|
+
* A closed list rather than free text: the drawing has to be chosen from it, and
|
|
562
|
+
* an unrecognised string would leave a hole where the icon goes. Providers name
|
|
563
|
+
* conditions in wildly different vocabularies, so the caller maps theirs onto
|
|
564
|
+
* this list and passes the provider's own words as `conditionLabel` — the enum
|
|
565
|
+
* picks the picture, the label is what gets read.
|
|
566
|
+
*/
|
|
567
|
+
export type WeatherCondition =
|
|
568
|
+
| 'clear'
|
|
569
|
+
| 'partly-cloudy'
|
|
570
|
+
| 'cloudy'
|
|
571
|
+
| 'overcast'
|
|
572
|
+
| 'fog'
|
|
573
|
+
| 'light-rain'
|
|
574
|
+
| 'rain'
|
|
575
|
+
| 'heavy-rain'
|
|
576
|
+
| 'sleet'
|
|
577
|
+
| 'light-snow'
|
|
578
|
+
| 'snow'
|
|
579
|
+
| 'heavy-snow'
|
|
580
|
+
| 'thunderstorm'
|
|
581
|
+
| 'hail'
|
|
582
|
+
| 'windy'
|
|
583
|
+
|
|
584
|
+
/** Wind, as shown by the weather components. */
|
|
585
|
+
export interface WeatherWind {
|
|
586
|
+
speed: number
|
|
587
|
+
/** Written after the speed. Defaults to `m/s`. */
|
|
588
|
+
unit?: string
|
|
589
|
+
/** Compass direction as the caller words it, e.g. `NV` or `sydväst`. */
|
|
590
|
+
direction?: string
|
|
591
|
+
/** Gust speed, in the same unit. Shown next to the sustained speed when present. */
|
|
592
|
+
gust?: number
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/** The extension API properties for the WeatherNow component. */
|
|
596
|
+
export interface WeatherNowProps extends ExtensionComponentData {
|
|
597
|
+
component: 'WeatherNow'
|
|
598
|
+
/** Where the reading is from. */
|
|
599
|
+
place: string
|
|
600
|
+
condition: WeatherCondition
|
|
601
|
+
/** The condition in the user's own language. Falls back to the host's wording for `condition`. */
|
|
602
|
+
conditionLabel?: string
|
|
603
|
+
temperature: number
|
|
604
|
+
/** What it feels like, when that differs enough to be worth saying. */
|
|
605
|
+
feelsLike?: number
|
|
606
|
+
/** Written after every temperature. Defaults to `°`. */
|
|
607
|
+
unit?: string
|
|
608
|
+
wind?: WeatherWind
|
|
609
|
+
/** Draws the night variant of the icon. Only affects conditions that have one. */
|
|
610
|
+
night?: boolean
|
|
611
|
+
/** One line of context under the numbers, e.g. "Regn från tretiden". */
|
|
612
|
+
summary?: string
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* One step of a forecast.
|
|
617
|
+
*
|
|
618
|
+
* Deliberately the same shape whether the steps are hours or days — the only
|
|
619
|
+
* difference is what `label` says and whether there are two temperatures. A
|
|
620
|
+
* separate hourly component would have been the same component twice.
|
|
621
|
+
*/
|
|
622
|
+
export interface WeatherForecastStep {
|
|
623
|
+
/** Short: `14`, `Tors`, `Nu`. */
|
|
624
|
+
label: string
|
|
625
|
+
condition: WeatherCondition
|
|
626
|
+
conditionLabel?: string
|
|
627
|
+
/** The warmer of the two, or the only one for a single-temperature step. */
|
|
628
|
+
high: number
|
|
629
|
+
/** The colder one. Absent when the step has a single temperature. */
|
|
630
|
+
low?: number
|
|
631
|
+
night?: boolean
|
|
632
|
+
/** Chance of precipitation as a percentage, 0–100. */
|
|
633
|
+
precipitation?: number
|
|
634
|
+
/**
|
|
635
|
+
* The step the reader is at — this hour, today. Drawn larger than its
|
|
636
|
+
* neighbours, so the eye lands on it before reading the row.
|
|
637
|
+
*
|
|
638
|
+
* Same word as `TimelineEntry.current`, and the same meaning: mark one, and
|
|
639
|
+
* only one. Marking several is marking none.
|
|
640
|
+
*/
|
|
641
|
+
current?: boolean
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/** The extension API properties for the WeatherForecast component. */
|
|
645
|
+
export interface WeatherForecastProps extends ExtensionComponentData {
|
|
646
|
+
component: 'WeatherForecast'
|
|
647
|
+
/** Heading above the row, e.g. "Kommande timmar". */
|
|
648
|
+
title?: string
|
|
649
|
+
/** Icon shown to the left of the title. */
|
|
650
|
+
icon?: HugeIconName
|
|
651
|
+
/** Written after every temperature. Defaults to `°`. */
|
|
652
|
+
unit?: string
|
|
653
|
+
steps: WeatherForecastStep[]
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/** How a chart draws its series. */
|
|
657
|
+
export type ChartKind = 'line' | 'bar' | 'area'
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* One series of a chart.
|
|
661
|
+
*
|
|
662
|
+
* `points` lines up with the chart's `labels` by position. A `null` is a gap in
|
|
663
|
+
* the data and is drawn as one — a missing reading joined up as if it were a
|
|
664
|
+
* value is a lie the chart would tell on the caller's behalf.
|
|
665
|
+
*/
|
|
666
|
+
export interface ChartSeries {
|
|
667
|
+
/** Named in the legend. Required in practice once there is more than one series. */
|
|
668
|
+
name?: string
|
|
669
|
+
points: Array<number | null>
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* The extension API properties for the Chart component.
|
|
674
|
+
*
|
|
675
|
+
* There is no colour option and no second axis, on purpose. Colours come from
|
|
676
|
+
* the host's categorical palette in slot order, so the same series keeps the
|
|
677
|
+
* same colour everywhere and the whole set stays legible to colour-blind
|
|
678
|
+
* readers. Two measurements of different magnitude are two charts.
|
|
679
|
+
*/
|
|
680
|
+
export interface ChartProps extends ExtensionComponentData {
|
|
681
|
+
component: 'Chart'
|
|
682
|
+
/** Defaults to `line`. */
|
|
683
|
+
chart?: ChartKind
|
|
684
|
+
/** At most eight. Past that the ninth would need a colour the palette does not have. */
|
|
685
|
+
series: ChartSeries[]
|
|
686
|
+
/** One per point, along the horizontal axis. */
|
|
687
|
+
labels?: string[]
|
|
688
|
+
/** Heading above the plot. With a single series this is what names it, so no legend is drawn. */
|
|
689
|
+
title?: string
|
|
690
|
+
/** Icon shown to the left of the title. */
|
|
691
|
+
icon?: HugeIconName
|
|
692
|
+
/** Written after every value, e.g. `°`, ` mm`, ` kr`. */
|
|
693
|
+
unit?: string
|
|
694
|
+
/**
|
|
695
|
+
* Whether the value axis must include zero. Defaults to true for `bar` and
|
|
696
|
+
* `area`, where the fill's height is the reading, and false for `line`, where
|
|
697
|
+
* forcing zero can flatten the shape the chart exists to show.
|
|
698
|
+
*/
|
|
699
|
+
zeroBaseline?: boolean
|
|
700
|
+
/** Plot height in rem. Defaults to 9. */
|
|
701
|
+
height?: number
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/** Which way a stat tile's trend points. */
|
|
705
|
+
export type StatTrend = 'up' | 'down' | 'flat'
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* The extension API properties for the StatTile component.
|
|
709
|
+
*
|
|
710
|
+
* For the single number that does not need a plot. A chart of one value is a
|
|
711
|
+
* chart that spends a lot of space saying very little.
|
|
712
|
+
*/
|
|
713
|
+
export interface StatTileProps extends ExtensionComponentData {
|
|
714
|
+
component: 'StatTile'
|
|
715
|
+
/** What the number is, e.g. "Förbrukning i går". */
|
|
716
|
+
label: string
|
|
717
|
+
value: string | number
|
|
718
|
+
/** Written after the value in smaller type, e.g. `kWh`. */
|
|
719
|
+
unit?: string
|
|
720
|
+
/** One quiet line under the number. */
|
|
721
|
+
caption?: string
|
|
722
|
+
icon?: HugeIconName
|
|
723
|
+
/** `flat` draws no arrow. */
|
|
724
|
+
trend?: StatTrend
|
|
725
|
+
/** The change in words, e.g. "+12 % mot förra veckan". Shown beside the arrow. */
|
|
726
|
+
trendLabel?: string
|
|
727
|
+
/**
|
|
728
|
+
* Whether up is the good direction. Defaults to true. Colours the trend;
|
|
729
|
+
* the arrow and the label carry the same meaning without it.
|
|
730
|
+
*/
|
|
731
|
+
trendIsGood?: boolean
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/** One row of a KeyValueList. */
|
|
735
|
+
export interface KeyValueRow {
|
|
736
|
+
label: string
|
|
737
|
+
value: string
|
|
738
|
+
icon?: HugeIconName
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* The extension API properties for the KeyValueList component.
|
|
743
|
+
*
|
|
744
|
+
* Facts in two columns, aligned. What a stack of `HorizontalStack`s was always
|
|
745
|
+
* being used for, without each caller having to get the alignment right.
|
|
746
|
+
*/
|
|
747
|
+
export interface KeyValueListProps extends ExtensionComponentData {
|
|
748
|
+
component: 'KeyValueList'
|
|
749
|
+
rows: KeyValueRow[]
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/** How much a timeline entry stands out. */
|
|
753
|
+
export type TimelineVariant = 'default' | 'accent' | 'success' | 'warning' | 'danger'
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* One entry on a timeline.
|
|
757
|
+
*
|
|
758
|
+
* `time` is a string, not a timestamp, because the component draws order rather
|
|
759
|
+
* than duration: entries sit evenly on the rail whether they are minutes or
|
|
760
|
+
* decades apart. That is what makes the same component work for today's
|
|
761
|
+
* calendar and for a project's milestones, and it means the caller writes the
|
|
762
|
+
* time the way it should be read — `09:00`, `Tors 14 mar`, `2019`.
|
|
763
|
+
*/
|
|
764
|
+
export interface TimelineEntry {
|
|
765
|
+
/** When it happens, worded as it should be read. */
|
|
766
|
+
time: string
|
|
767
|
+
title: string
|
|
768
|
+
/** A line under the title. */
|
|
769
|
+
description?: string
|
|
770
|
+
icon?: HugeIconName
|
|
771
|
+
/** How long it lasts, in the caller's own words: `1 h`, `3 dagar`. */
|
|
772
|
+
duration?: string
|
|
773
|
+
/** Trailing badge, e.g. a place or a category. */
|
|
774
|
+
badge?: string
|
|
775
|
+
/** Marks the entry the reader is at. Drawn with a filled marker and a heavier rule. */
|
|
776
|
+
current?: boolean
|
|
777
|
+
/** Already behind us. Drawn recessive, so what is left to come reads first. */
|
|
778
|
+
past?: boolean
|
|
779
|
+
variant?: TimelineVariant
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* The extension API properties for the Timeline component.
|
|
784
|
+
*
|
|
785
|
+
* A vertical rail with one marker per entry. For a day's events, or for the
|
|
786
|
+
* order things happened in over a longer stretch.
|
|
787
|
+
*/
|
|
788
|
+
export interface TimelineProps extends ExtensionComponentData {
|
|
789
|
+
component: 'Timeline'
|
|
790
|
+
/** Heading above the rail, e.g. "I dag". */
|
|
791
|
+
title?: string
|
|
792
|
+
/** Icon shown to the left of the title. */
|
|
793
|
+
icon?: HugeIconName
|
|
794
|
+
entries: TimelineEntry[]
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/** How much a note stands out. */
|
|
798
|
+
export type NoteVariant = 'default' | 'accent' | 'success' | 'warning' | 'danger'
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* The extension API properties for the Note component.
|
|
802
|
+
*
|
|
803
|
+
* A titled block of prose: a heading, markdown, and a quiet line at the bottom.
|
|
804
|
+
* For the answer that *is* text but wants a shape — one of five news summaries,
|
|
805
|
+
* one of three things worth doing this weekend — where a single long reply would
|
|
806
|
+
* run them all together and the reader would lose which sentence belonged to
|
|
807
|
+
* which item.
|
|
808
|
+
*
|
|
809
|
+
* The body is markdown rather than components because prose is what it is for.
|
|
810
|
+
* A note that wants a chart in it is a `Frame` with a `Note` and a `Chart`
|
|
811
|
+
* inside; keeping those separate is what stops this from becoming a second,
|
|
812
|
+
* worse layout system.
|
|
813
|
+
*/
|
|
814
|
+
export interface NoteProps extends ExtensionComponentData {
|
|
815
|
+
component: 'Note'
|
|
816
|
+
title?: string
|
|
817
|
+
/** Icon shown to the left of the title. */
|
|
818
|
+
icon?: HugeIconName
|
|
819
|
+
/** Trailing badge next to the title: a source, a category, a time. */
|
|
820
|
+
badge?: string
|
|
821
|
+
/** The body, as markdown. */
|
|
822
|
+
content: string
|
|
823
|
+
/** One quiet line at the bottom: where it came from, when, or a caveat. */
|
|
824
|
+
footer?: string
|
|
825
|
+
/** Colours the rail down the left edge. Defaults to none. */
|
|
826
|
+
variant?: NoteVariant
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/** Whether an event is going ahead. */
|
|
830
|
+
export type CalendarEventStatus = 'confirmed' | 'tentative' | 'cancelled'
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* The extension API properties for the CalendarEvent component.
|
|
834
|
+
*
|
|
835
|
+
* One event, shown properly. It is the thing Stina most often has to say
|
|
836
|
+
* something *about* — a meeting starting shortly, a booking she is reminding you
|
|
837
|
+
* of — and a sentence carrying a time, a place and four names is a sentence
|
|
838
|
+
* nobody reads to the end.
|
|
839
|
+
*
|
|
840
|
+
* For a whole day, use `Timeline`. This is the single event that has the floor.
|
|
841
|
+
*
|
|
842
|
+
* `start` and `end` are timestamps rather than the worded strings `Timeline`
|
|
843
|
+
* takes, and that is the one place in the DSL where live computation is right:
|
|
844
|
+
* the card is *about* a moment, so the host works out "om tolv minuter" and
|
|
845
|
+
* "pågår nu" and keeps them true. A caller who wrote those as text would have
|
|
846
|
+
* written a card that lies the moment it is scrolled back to.
|
|
847
|
+
*/
|
|
848
|
+
export interface CalendarEventProps extends ExtensionComponentData {
|
|
849
|
+
component: 'CalendarEvent'
|
|
850
|
+
title: string
|
|
851
|
+
/** ISO 8601, with an offset. The host formats it in the reader's own locale. */
|
|
852
|
+
start: string
|
|
853
|
+
/** ISO 8601, with an offset. Absent for an event with no stated end. */
|
|
854
|
+
end?: string
|
|
855
|
+
/** Draws a day rather than a clock time. */
|
|
856
|
+
allDay?: boolean
|
|
857
|
+
location?: string
|
|
858
|
+
/** Who called it. */
|
|
859
|
+
organizer?: string
|
|
860
|
+
/** Names as the calendar has them. Shown in full up to a few, then counted. */
|
|
861
|
+
attendees?: string[]
|
|
862
|
+
/** Which calendar it sits in. Shown as a badge. */
|
|
863
|
+
calendar?: string
|
|
864
|
+
/** Defaults to `confirmed`. */
|
|
865
|
+
status?: CalendarEventStatus
|
|
866
|
+
/**
|
|
867
|
+
* How it repeats, in words: `Varje tisdag`. The recurrence rule itself is not
|
|
868
|
+
* something this component parses — the caller already knows what it means,
|
|
869
|
+
* and a component guessing at an RRULE would get the exceptions wrong.
|
|
870
|
+
*/
|
|
871
|
+
recurrence?: string
|
|
872
|
+
/** One short line of context. */
|
|
873
|
+
notes?: string
|
|
874
|
+
}
|
package/src/types.context.ts
CHANGED
|
@@ -320,7 +320,21 @@ export interface UserProfile {
|
|
|
320
320
|
* User API for profile access
|
|
321
321
|
*/
|
|
322
322
|
export interface UserAPI {
|
|
323
|
-
|
|
323
|
+
/**
|
|
324
|
+
* Read a user's profile, including the timezone their clock times are written in.
|
|
325
|
+
*
|
|
326
|
+
* Pass the id of the user this work belongs to — `ctx.userId` in a background task,
|
|
327
|
+
* `context.userId` in a tool call, the fired job's `userId` in a scheduler callback.
|
|
328
|
+
* A host serving more than one user cannot guess which profile was meant, and answers
|
|
329
|
+
* with an empty profile when the id is left out.
|
|
330
|
+
*
|
|
331
|
+
* This matters most for `timezone`. An extension that turns a wall-clock time into an
|
|
332
|
+
* instant without it falls back to the machine's timezone, which on a server is UTC,
|
|
333
|
+
* and schedules the user's 11:30 for 11:30 UTC.
|
|
334
|
+
*
|
|
335
|
+
* @param userId The user whose profile to read. Optional only for single-user hosts.
|
|
336
|
+
*/
|
|
337
|
+
getProfile(userId?: string): Promise<UserProfile>
|
|
324
338
|
listIds(): Promise<string[]>
|
|
325
339
|
}
|
|
326
340
|
|
package/src/types.tools.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ExecutionContext } from './types.context.js'
|
|
8
|
+
import type { ExtensionComponentData } from './types.components.js'
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Tool implementation
|
|
@@ -39,6 +40,20 @@ export interface ToolResult {
|
|
|
39
40
|
message?: string
|
|
40
41
|
/** Error message if failed */
|
|
41
42
|
error?: string
|
|
43
|
+
/**
|
|
44
|
+
* Something to show the user that is not text.
|
|
45
|
+
*
|
|
46
|
+
* A component from the chat card profile — a chart, a weather reading, a
|
|
47
|
+
* timeline. It is rendered in the conversation where the tool ran, and it is
|
|
48
|
+
* *not* part of what goes back to the model: the model already knows what it
|
|
49
|
+
* asked to be shown, and sending the drawing back would spend the tokens
|
|
50
|
+
* twice and invite it to summarise its own picture.
|
|
51
|
+
*
|
|
52
|
+
* Validate with `validateChatCard` before returning one. A card that fails the
|
|
53
|
+
* profile is dropped by the host rather than half-rendered, so a tool that
|
|
54
|
+
* skips the check finds out from the user, which is the wrong end.
|
|
55
|
+
*/
|
|
56
|
+
display?: ExtensionComponentData
|
|
42
57
|
}
|
|
43
58
|
|
|
44
59
|
/**
|