@webskill/chatbot 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -76,27 +76,18 @@ probe (`pnpm test:install-smoke`), against the **published** `.d.ts`.
76
76
  <!-- typecheck: chatbot-minimal.tsx -->
77
77
 
78
78
  ```tsx
79
- import { Chatbot, type ChatbotHostAdapter, type LlmConfig } from '@webskill/chatbot';
79
+ import { Chatbot, type ChatbotHostAdapter } from '@webskill/chatbot';
80
80
  import { OpfsProvider } from '@webskill/sdk/browser';
81
81
  import '@webskill/chatbot/chatbot.css';
82
82
 
83
- // ChatbotHostAdapter: wire storage, skill roots, an LLM config store, and a
84
- // navigation port (openConsole / openConsoleTrace). <Chatbot> constructs its own
85
- // ChatEngine from the adapter; pass config for options like generativeUi.
86
- let saved: LlmConfig | undefined;
87
-
83
+ // ChatbotHostAdapter: wire storage, skill roots and a navigation port
84
+ // (openConsole / openConsoleTrace). <Chatbot> constructs its own ChatEngine
85
+ // from the adapter; pass config for options like generativeUi.
86
+ // Model configuration lives in the runtime config store (config.runtimeConfig):
87
+ // RuntimeConfig.llm.entries holds the model list, llm.defaultId the default one.
88
88
  const adapter: ChatbotHostAdapter = {
89
89
  storage: new OpfsProvider({ rootName: 'my-app' }),
90
90
  skillRoots: ['/skills'],
91
- llmConfig: {
92
- load: async () => saved,
93
- save: async (config) => {
94
- saved = config;
95
- },
96
- clear: async () => {
97
- saved = undefined;
98
- }
99
- },
100
91
  navigation: {
101
92
  openConsole() {
102
93
  /* route to your console surface */
@@ -118,6 +109,23 @@ export function App() {
118
109
  See `examples/chatbot-playground` for a complete `ChatbotHostAdapter`
119
110
  implementation (OPFS storage, skills, demo/model providers, console embedding).
120
111
 
112
+ ## Session list ordering and paging
113
+
114
+ `ChatEngine.listSessions()` forwards straight to the `SessionStore`, so the
115
+ default `FsSessionStore` decides what you see:
116
+
117
+ - Sessions are sorted by `createdAt` **ascending**, and a page is taken from the
118
+ **tail** of that list. The first screen therefore holds the *newest* sessions,
119
+ and the oldest session is not on it.
120
+ - The page size is the store's (`FS_SESSION_PAGE_SIZE`, 50). `<Chatbot>` does
121
+ not override it — pass your own `sessionStore` if you need another size.
122
+ - `nextCursor` points further back in history. While it is set, the session list
123
+ shows a **Load earlier sessions** entry; when it is absent the list is at the
124
+ end and the entry disappears, so "no button" always means "nothing older".
125
+ - Session files that cannot be parsed are skipped rather than failing the whole
126
+ list. Their file names come back in `skipped` and are shown above the list, so
127
+ a session you expected to see but cannot is always accounted for.
128
+
121
129
  ## Embedding models
122
130
 
123
131
  - **As a component** inside an existing web app — mount `<Chatbot>` in your own
package/dist/chatbot.css CHANGED
@@ -200,6 +200,9 @@
200
200
  }
201
201
  }
202
202
  @layer utilities {
203
+ .pointer-events-auto {
204
+ pointer-events: auto;
205
+ }
203
206
  .pointer-events-none {
204
207
  pointer-events: none;
205
208
  }
@@ -229,12 +232,21 @@
229
232
  .relative {
230
233
  position: relative;
231
234
  }
235
+ .static {
236
+ position: static;
237
+ }
232
238
  .inset-0 {
233
239
  inset: 0px;
234
240
  }
241
+ .inset-x-0 {
242
+ inset-inline: 0px;
243
+ }
235
244
  .inset-y-0 {
236
245
  inset-block: 0px;
237
246
  }
247
+ .top-0 {
248
+ top: 0px;
249
+ }
238
250
  .top-1\.5 {
239
251
  top: calc(var(--spacing) * 1.5);
240
252
  }
@@ -268,6 +280,9 @@
268
280
  .right-4 {
269
281
  right: calc(var(--spacing) * 4);
270
282
  }
283
+ .-bottom-7 {
284
+ bottom: calc(var(--spacing) * -7);
285
+ }
271
286
  .bottom-4 {
272
287
  bottom: calc(var(--spacing) * 4);
273
288
  }
@@ -310,6 +325,9 @@
310
325
  .m-3 {
311
326
  margin: calc(var(--spacing) * 3);
312
327
  }
328
+ .mx-1 {
329
+ margin-inline: var(--spacing);
330
+ }
313
331
  .mx-2 {
314
332
  margin-inline: calc(var(--spacing) * 2);
315
333
  }
@@ -322,6 +340,9 @@
322
340
  .my-0 {
323
341
  margin-block: 0px;
324
342
  }
343
+ .my-1 {
344
+ margin-block: var(--spacing);
345
+ }
325
346
  .my-2 {
326
347
  margin-block: calc(var(--spacing) * 2);
327
348
  }
@@ -349,8 +370,8 @@
349
370
  .mb-2 {
350
371
  margin-bottom: calc(var(--spacing) * 2);
351
372
  }
352
- .mb-4 {
353
- margin-bottom: calc(var(--spacing) * 4);
373
+ .mb-7 {
374
+ margin-bottom: calc(var(--spacing) * 7);
354
375
  }
355
376
  .block {
356
377
  display: block;
@@ -423,6 +444,9 @@
423
444
  width: 100%;
424
445
  height: 100%;
425
446
  }
447
+ .h-0 {
448
+ height: 0px;
449
+ }
426
450
  .h-2\.5 {
427
451
  height: calc(var(--spacing) * 2.5);
428
452
  }
@@ -483,23 +507,32 @@
483
507
  .w-\(--sidebar-width\) {
484
508
  width: var(--sidebar-width);
485
509
  }
510
+ .w-0 {
511
+ width: 0px;
512
+ }
486
513
  .w-2 {
487
514
  width: calc(var(--spacing) * 2);
488
515
  }
489
516
  .w-2\.5 {
490
517
  width: calc(var(--spacing) * 2.5);
491
518
  }
519
+ .w-3\.5 {
520
+ width: calc(var(--spacing) * 3.5);
521
+ }
492
522
  .w-5 {
493
523
  width: calc(var(--spacing) * 5);
494
524
  }
495
525
  .w-9 {
496
526
  width: calc(var(--spacing) * 9);
497
527
  }
528
+ .w-40 {
529
+ width: calc(var(--spacing) * 40);
530
+ }
498
531
  .w-72 {
499
532
  width: calc(var(--spacing) * 72);
500
533
  }
501
- .w-96 {
502
- width: calc(var(--spacing) * 96);
534
+ .w-\[44rem\] {
535
+ width: 44rem;
503
536
  }
504
537
  .w-\[min\(22rem\,calc\(100vw-2rem\)\)\] {
505
538
  width: min(22rem, calc(100vw - 2rem));
@@ -528,8 +561,11 @@
528
561
  .max-w-3xl {
529
562
  max-width: var(--container-3xl);
530
563
  }
531
- .max-w-56 {
532
- max-width: calc(var(--spacing) * 56);
564
+ .max-w-48 {
565
+ max-width: calc(var(--spacing) * 48);
566
+ }
567
+ .max-w-72 {
568
+ max-width: calc(var(--spacing) * 72);
533
569
  }
534
570
  .max-w-\[calc\(100vw-2rem\)\] {
535
571
  max-width: calc(100vw - 2rem);
@@ -641,6 +677,9 @@
641
677
  .grid-cols-1 {
642
678
  grid-template-columns: repeat(1, minmax(0, 1fr));
643
679
  }
680
+ .grid-cols-2 {
681
+ grid-template-columns: repeat(2, minmax(0, 1fr));
682
+ }
644
683
  .grid-cols-\[1fr_auto\] {
645
684
  grid-template-columns: 1fr auto;
646
685
  }
@@ -659,9 +698,6 @@
659
698
  .items-center {
660
699
  align-items: center;
661
700
  }
662
- .items-end {
663
- align-items: flex-end;
664
- }
665
701
  .items-start {
666
702
  align-items: flex-start;
667
703
  }
@@ -695,9 +731,6 @@
695
731
  .gap-4 {
696
732
  gap: calc(var(--spacing) * 4);
697
733
  }
698
- .gap-6 {
699
- gap: calc(var(--spacing) * 6);
700
- }
701
734
  :where(.space-y-1 > :not(:last-child)) {
702
735
  --tw-space-y-reverse: 0;
703
736
  margin-block-start: calc(var(--spacing) * var(--tw-space-y-reverse));
@@ -782,6 +815,10 @@
782
815
  border-style: var(--tw-border-style);
783
816
  border-width: 0px;
784
817
  }
818
+ .border-e {
819
+ border-inline-end-style: var(--tw-border-style);
820
+ border-inline-end-width: 1px;
821
+ }
785
822
  .border-t {
786
823
  border-top-style: var(--tw-border-style);
787
824
  border-top-width: 1px;
@@ -874,6 +911,12 @@
874
911
  .bg-danger-soft {
875
912
  background-color: var(--destructive-soft);
876
913
  }
914
+ .bg-danger\/10 {
915
+ background-color: var(--destructive);
916
+ @supports (color: color-mix(in lab, red, red)) {
917
+ background-color: color-mix(in oklab, var(--destructive) 10%, transparent);
918
+ }
919
+ }
877
920
  .bg-destructive {
878
921
  background-color: var(--destructive);
879
922
  }
@@ -973,9 +1016,6 @@
973
1016
  .p-4 {
974
1017
  padding: calc(var(--spacing) * 4);
975
1018
  }
976
- .p-5 {
977
- padding: calc(var(--spacing) * 5);
978
- }
979
1019
  .px-1 {
980
1020
  padding-inline: var(--spacing);
981
1021
  }
@@ -1045,9 +1085,6 @@
1045
1085
  .pt-4 {
1046
1086
  padding-top: calc(var(--spacing) * 4);
1047
1087
  }
1048
- .pt-5 {
1049
- padding-top: calc(var(--spacing) * 5);
1050
- }
1051
1088
  .pr-2 {
1052
1089
  padding-right: calc(var(--spacing) * 2);
1053
1090
  }
@@ -1081,6 +1118,12 @@
1081
1118
  .text-left {
1082
1119
  text-align: left;
1083
1120
  }
1121
+ .text-right {
1122
+ text-align: right;
1123
+ }
1124
+ .text-start {
1125
+ text-align: start;
1126
+ }
1084
1127
  .font-display {
1085
1128
  font-family: var(--font-sans);
1086
1129
  }
@@ -1113,6 +1156,9 @@
1113
1156
  .text-\[0\.85em\] {
1114
1157
  font-size: 0.85em;
1115
1158
  }
1159
+ .text-\[10px\] {
1160
+ font-size: 10px;
1161
+ }
1116
1162
  .text-\[11px\] {
1117
1163
  font-size: 11px;
1118
1164
  }
@@ -1173,6 +1219,9 @@
1173
1219
  .text-ink {
1174
1220
  color: var(--foreground);
1175
1221
  }
1222
+ .text-input {
1223
+ color: var(--input);
1224
+ }
1176
1225
  .text-muted {
1177
1226
  color: var(--muted-foreground);
1178
1227
  }
@@ -1210,6 +1259,9 @@
1210
1259
  --tw-numeric-spacing: tabular-nums;
1211
1260
  font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
1212
1261
  }
1262
+ .line-through {
1263
+ text-decoration-line: line-through;
1264
+ }
1213
1265
  .underline {
1214
1266
  text-decoration-line: underline;
1215
1267
  }
@@ -1321,6 +1373,9 @@
1321
1373
  -webkit-user-select: none;
1322
1374
  user-select: none;
1323
1375
  }
1376
+ .group-focus-within\:opacity-100:is(:where(.group):focus-within *) {
1377
+ opacity: 100%;
1378
+ }
1324
1379
  .group-focus-within\/menu-item\:opacity-100:is(:where(.group\/menu-item):focus-within *) {
1325
1380
  opacity: 100%;
1326
1381
  }
@@ -1447,9 +1502,6 @@
1447
1502
  .hover\:bg-accent:hover {
1448
1503
  background-color: var(--accent);
1449
1504
  }
1450
- .hover\:bg-danger-soft:hover {
1451
- background-color: var(--destructive-soft);
1452
- }
1453
1505
  .hover\:bg-destructive-soft:hover {
1454
1506
  background-color: var(--destructive-soft);
1455
1507
  }
@@ -1481,9 +1533,6 @@
1481
1533
  .hover\:bg-surface:hover {
1482
1534
  background-color: var(--card);
1483
1535
  }
1484
- .hover\:text-danger:hover {
1485
- color: var(--destructive);
1486
- }
1487
1536
  .hover\:text-foreground:hover {
1488
1537
  color: var(--foreground);
1489
1538
  }
@@ -1571,6 +1620,9 @@
1571
1620
  .disabled\:opacity-60:disabled {
1572
1621
  opacity: 60%;
1573
1622
  }
1623
+ .has-\[\[aria-expanded\=true\]\]\:opacity-100:has(:is([aria-expanded=true])) {
1624
+ opacity: 100%;
1625
+ }
1574
1626
  .aria-disabled\:pointer-events-none[aria-disabled="true"] {
1575
1627
  pointer-events: none;
1576
1628
  }
@@ -1976,19 +2028,11 @@
1976
2028
  background: color-mix(in oklab, var(--muted-foreground) 60%, transparent);
1977
2029
  }
1978
2030
  }
1979
- ::-webkit-scrollbar {
1980
- width: 4px;
1981
- height: 4px;
2031
+ [data-webskill-theme='light'] {
2032
+ color-scheme: light;
1982
2033
  }
1983
- ::-webkit-scrollbar-track {
1984
- background: transparent;
1985
- }
1986
- ::-webkit-scrollbar-thumb {
1987
- background: rgb(148 163 184 / 0.25);
1988
- border-radius: 999px;
1989
- }
1990
- ::-webkit-scrollbar-thumb:hover {
1991
- background: rgb(148 163 184 / 0.45);
2034
+ [data-webskill-theme='dark'] {
2035
+ color-scheme: dark;
1992
2036
  }
1993
2037
  .webskill-surface-list {
1994
2038
  display: flex;