laif-ds 0.2.80 → 0.2.83

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/CHANGELOG.md CHANGED
@@ -5,6 +5,22 @@ All notable technical changes to the laif-ds core will be documented in this fil
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [laif-ds@0.2.82]
9
+
10
+ ### 🚀 Added
11
+
12
+ - **Chat**: Added a `placeholder` prop forwarded to the message input, allowing a custom composer placeholder (defaults to `"Ask AI..."`)
13
+
14
+ ## [laif-ds@0.2.81]
15
+
16
+ ### 🚀 Added
17
+
18
+ - **FilePreviewer**: Added a `size` option to `PreviewOptions` (`sm` | `default` | `lg` | `xl` | `full`, defaults to `xl`) to control the preview dialog size per call
19
+
20
+ ### 🔧 Changed
21
+
22
+ - **FilePreviewer**: Migrated from the low-level `Dialog` to `AppDialog`, simplifying the markup and relying on its built-in header and configurable sizing
23
+
8
24
  ## [laif-ds@0.2.80]
9
25
 
10
26
  ### 🚀 Added
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-05-05T15:26:14.216Z",
3
+ "generatedAt": "2026-06-09T07:56:53.942Z",
4
4
  "package": {
5
5
  "name": "laif-ds",
6
- "version": "0.2.80"
6
+ "version": "0.2.83"
7
7
  },
8
8
  "summary": {
9
9
  "scannedFiles": 394,
@@ -25,6 +25,7 @@ Complete chat interface: message list with markdown rendering, auto-scroll and t
25
25
  | `suggestions` | `string[]` | `undefined` | Suggestions displayed when empty. Requires `append`. |
26
26
  | `className` | `string` | `undefined` | Wrapper classes. |
27
27
  | `welcomeTitle` | `string` | `"Da dove iniziamo?"` | Title shown in empty state. |
28
+ | `placeholder` | `string` | `"Ask AI..."` | Placeholder text for the composer input. |
28
29
 
29
30
  ---
30
31
 
@@ -12,7 +12,8 @@ Drag-and-drop uploader with keyboard activation, file type filtering, limits (co
12
12
  | ------------------- | ------------------------- | -------------------------------------------------- | --------------------------------------------------------- |
13
13
  | `extensions` | `AcceptItem[]` | `["pdf","image","video","audio"]` | Allowed extensions/categories (maps to accept attribute). |
14
14
  | `multiple` | `boolean` | `false` | Enables multiple file selection. |
15
- | `onUpload` | `(files: File[]) => void` | `undefined` | Called whenever selected files change. |
15
+ | `onUpload` | `(files: File[]) => void` | `undefined` | Called when files are **added**, with the resulting list. |
16
+ | `onRemove` | `(removed: File[], remaining: File[]) => void` | `undefined` | Called when files are **removed** (single or remove-all), with the removed files and the list that remains. |
16
17
  | `description` | `string` | `"Trascina un file o clicca per selezionare"` | Dropzone hint. |
17
18
  | `formatDescription` | `string` | `"Formato accettato: PDF, Immagini, Video, Audio"` | Accepted formats hint. |
18
19
  | `selectedLabel` | `string` | `"File selezionati"` | Title shown above selected files list. |
@@ -30,7 +31,7 @@ Drag-and-drop uploader with keyboard activation, file type filtering, limits (co
30
31
  - **Filtering**: `extensions` map to accept string; only matching files are added.
31
32
  - **Limits**: Enforces `maxFiles` and `maxTotalSize` (with compact error caption).
32
33
  - **Previews**: Renders selected files with `FilePreview`; supports remove per file and remove-all.
33
- - **Integration**: `onUpload` provides the current `File[]`; perform the actual upload externally.
34
+ - **Integration**: `onUpload` fires on **add** with the resulting `File[]`; `onRemove` fires on **removal** (single file or remove-all) with the removed files and the remaining list. Removals do **not** trigger `onUpload` — a consumer mirroring the list in its own state must handle both callbacks (see example below).
34
35
 
35
36
  ---
36
37
 
@@ -127,7 +128,11 @@ export function ExternalUploadExample() {
127
128
 
128
129
  return (
129
130
  <div className="space-y-4">
130
- <FileUploader multiple onUpload={setFiles} />
131
+ <FileUploader
132
+ multiple
133
+ onUpload={setFiles}
134
+ onRemove={(_removed, remaining) => setFiles(remaining)}
135
+ />
131
136
  {files.length > 0 && (
132
137
  <div className="flex justify-end">
133
138
  <Button onClick={handleSubmit} isLoading={loading} iconLeft="Upload">
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": "1.1.0",
3
- "generatedAt": "2026-05-05T15:26:13.593Z",
3
+ "generatedAt": "2026-06-09T07:56:53.318Z",
4
4
  "package": {
5
5
  "name": "laif-ds",
6
- "version": "0.2.80"
6
+ "version": "0.2.83"
7
7
  },
8
8
  "stats": {
9
9
  "documentedComponentCount": 92,
@@ -1934,10 +1934,10 @@
1934
1934
  },
1935
1935
  "metadata": {
1936
1936
  "props": {
1937
- "totalProps": 15,
1937
+ "totalProps": 16,
1938
1938
  "requiredPropsCount": 4,
1939
- "typedPropsCount": 15,
1940
- "describedPropsCount": 15
1939
+ "typedPropsCount": 16,
1940
+ "describedPropsCount": 16
1941
1941
  },
1942
1942
  "accessibility": {
1943
1943
  "hasCoverage": false
@@ -3169,10 +3169,10 @@
3169
3169
  },
3170
3170
  "metadata": {
3171
3171
  "props": {
3172
- "totalProps": 9,
3172
+ "totalProps": 10,
3173
3173
  "requiredPropsCount": 0,
3174
- "typedPropsCount": 9,
3175
- "describedPropsCount": 9
3174
+ "typedPropsCount": 10,
3175
+ "describedPropsCount": 10
3176
3176
  },
3177
3177
  "accessibility": {
3178
3178
  "hasCoverage": true
@@ -6061,5 +6061,5 @@
6061
6061
  ]
6062
6062
  }
6063
6063
  ],
6064
- "checksum": "5365e3750a199ac2ef62a230e7ec5a26fa46e3494a332e48d9f832d003ac6a1b"
6064
+ "checksum": "911fd5d24f66568dc9007439161320c179ac69eaed81a631fe5b38ffc901e9c7"
6065
6065
  }
@@ -1,19 +1,19 @@
1
1
  "use client";
2
2
  import { jsxs as b, Fragment as y, jsx as t } from "react/jsx-runtime";
3
- import { forwardRef as A, useRef as M, useCallback as k, useState as O } from "react";
3
+ import { forwardRef as A, useRef as O, useCallback as k, useState as q } from "react";
4
4
  import { Button as v } from "./button.js";
5
5
  import { CopyButton as S } from "./copy-button.js";
6
- import { MessageInput as q } from "./message-input.js";
7
- import { MessageList as H } from "./message-list.js";
8
- import { PromptSuggestions as J } from "./prompt-suggestions.js";
9
- import { useAutoScroll as K } from "../../hooks/use-auto-scroll.js";
10
- import { cn as Q } from "../../lib/utils.js";
6
+ import { MessageInput as H } from "./message-input.js";
7
+ import { MessageList as J } from "./message-list.js";
8
+ import { PromptSuggestions as K } from "./prompt-suggestions.js";
9
+ import { useAutoScroll as Q } from "../../hooks/use-auto-scroll.js";
10
+ import { cn as V } from "../../lib/utils.js";
11
11
  import { Typo as z } from "./typo.js";
12
12
  import j from "../../node_modules/lucide-react/dist/esm/icons/save.js";
13
- import V from "../../node_modules/lucide-react/dist/esm/icons/thumbs-up.js";
14
- import W from "../../node_modules/lucide-react/dist/esm/icons/thumbs-down.js";
15
- import X from "../../node_modules/lucide-react/dist/esm/icons/arrow-down.js";
16
- function Y({
13
+ import W from "../../node_modules/lucide-react/dist/esm/icons/thumbs-up.js";
14
+ import X from "../../node_modules/lucide-react/dist/esm/icons/thumbs-down.js";
15
+ import Y from "../../node_modules/lucide-react/dist/esm/icons/arrow-down.js";
16
+ function Z({
17
17
  messages: o,
18
18
  welcomeTitle: n = "Da dove iniziamo?",
19
19
  handleSubmit: i,
@@ -29,11 +29,12 @@ function Y({
29
29
  transcribeAudio: D,
30
30
  allowAttachments: L = !1,
31
31
  onEdit: B,
32
- onMessageSave: d
32
+ onMessageSave: d,
33
+ placeholder: P
33
34
  }) {
34
- const P = o.at(-1), T = o.length === 0, I = P?.role === "user", C = M(o);
35
+ const U = o.at(-1), T = o.length === 0, I = U?.role === "user", C = O(o);
35
36
  C.current = o;
36
- const U = k(() => {
37
+ const E = k(() => {
37
38
  if (s?.(), !x) return;
38
39
  const e = [...C.current], l = e.findLast(
39
40
  (a) => a.role === "assistant"
@@ -81,7 +82,7 @@ function Y({
81
82
  );
82
83
  a !== -1 && (e[a] = p, x(e));
83
84
  }
84
- }, [s, x, C]), E = k(
85
+ }, [s, x, C]), M = k(
85
86
  (e) => ({
86
87
  actions: N ? /* @__PURE__ */ b(y, { children: [
87
88
  /* @__PURE__ */ t("div", { className: "border-d-border border-r pr-1", children: /* @__PURE__ */ t(
@@ -109,7 +110,7 @@ function Y({
109
110
  variant: "ghost",
110
111
  className: "h-6 w-6",
111
112
  onClick: () => N(e.id, "thumbs-up"),
112
- children: /* @__PURE__ */ t(V, { className: "h-4 w-4" })
113
+ children: /* @__PURE__ */ t(W, { className: "h-4 w-4" })
113
114
  }
114
115
  ),
115
116
  /* @__PURE__ */ t(
@@ -119,7 +120,7 @@ function Y({
119
120
  variant: "ghost",
120
121
  className: "h-6 w-6",
121
122
  onClick: () => N(e.id, "thumbs-down"),
122
- children: /* @__PURE__ */ t(W, { className: "h-4 w-4" })
123
+ children: /* @__PURE__ */ t(X, { className: "h-4 w-4" })
123
124
  }
124
125
  )
125
126
  ] }) : /* @__PURE__ */ b(y, { children: [
@@ -150,7 +151,7 @@ function Y({
150
151
  T && /* @__PURE__ */ t("div", { className: "flex flex-1 flex-col items-center justify-center p-8", children: m && u && /* @__PURE__ */ b(y, { children: [
151
152
  /* @__PURE__ */ t(z, { variant: "h3", className: "mb-8 text-center", children: n }),
152
153
  /* @__PURE__ */ t(
153
- J,
154
+ K,
154
155
  {
155
156
  label: "",
156
157
  append: m,
@@ -158,12 +159,12 @@ function Y({
158
159
  }
159
160
  )
160
161
  ] }) }),
161
- o.length > 0 ? /* @__PURE__ */ t(Z, { children: /* @__PURE__ */ t(
162
- H,
162
+ o.length > 0 ? /* @__PURE__ */ t($, { children: /* @__PURE__ */ t(
163
+ J,
163
164
  {
164
165
  messages: o,
165
166
  isTyping: I,
166
- messageOptions: E,
167
+ messageOptions: M,
167
168
  onEdit: B,
168
169
  onMessageSave: d
169
170
  }
@@ -176,31 +177,32 @@ function Y({
176
177
  isPending: w || I,
177
178
  handleSubmit: i,
178
179
  children: ({ files: e, setFiles: l }) => /* @__PURE__ */ t(
179
- q,
180
+ H,
180
181
  {
181
182
  value: f,
182
183
  onChange: c,
183
184
  allowAttachments: L,
184
185
  files: e,
185
186
  setFiles: l,
186
- stop: U,
187
+ stop: E,
187
188
  isGenerating: w,
188
- transcribeAudio: D
189
+ transcribeAudio: D,
190
+ placeholder: P
189
191
  }
190
192
  )
191
193
  }
192
194
  )
193
195
  ] });
194
196
  }
195
- Y.displayName = "Chat";
196
- function Z({ children: o }) {
197
+ Z.displayName = "Chat";
198
+ function $({ children: o }) {
197
199
  const {
198
200
  containerRef: n,
199
201
  scrollToBottom: i,
200
202
  handleScroll: f,
201
203
  shouldAutoScroll: c,
202
204
  handleTouchStart: s
203
- } = K();
205
+ } = Q();
204
206
  return /* @__PURE__ */ b(
205
207
  "div",
206
208
  {
@@ -217,7 +219,7 @@ function Z({ children: o }) {
217
219
  className: "animate-in fade-in-0 slide-in-from-bottom-1 pointer-events-auto h-8 w-8 rounded-full ease-in-out",
218
220
  size: "icon",
219
221
  variant: "ghost",
220
- children: /* @__PURE__ */ t(X, { className: "h-4 w-4" })
222
+ children: /* @__PURE__ */ t(Y, { className: "h-4 w-4" })
221
223
  }
222
224
  ) }) })
223
225
  ]
@@ -228,26 +230,26 @@ const F = A(({ className: o, ...n }, i) => /* @__PURE__ */ t(
228
230
  "div",
229
231
  {
230
232
  ref: i,
231
- className: Q("flex h-full max-h-full w-full flex-col", o),
233
+ className: V("flex h-full max-h-full w-full flex-col", o),
232
234
  ...n
233
235
  }
234
236
  ));
235
237
  F.displayName = "ChatContainer";
236
238
  const _ = A(
237
239
  ({ children: o, handleSubmit: n, className: i }, f) => {
238
- const [c, s] = O(null);
240
+ const [c, s] = q(null);
239
241
  return /* @__PURE__ */ t("form", { ref: f, onSubmit: (m) => {
240
242
  if (!c) {
241
243
  n(m);
242
244
  return;
243
245
  }
244
- const u = $(c);
246
+ const u = G(c);
245
247
  n(m, { experimental_attachments: u }), s(null);
246
248
  }, className: i, children: o({ files: c, setFiles: s }) });
247
249
  }
248
250
  );
249
251
  _.displayName = "ChatForm";
250
- function $(o) {
252
+ function G(o) {
251
253
  if (typeof window > "u")
252
254
  return {};
253
255
  const n = new DataTransfer();
@@ -256,8 +258,8 @@ function $(o) {
256
258
  return n.files;
257
259
  }
258
260
  export {
259
- Y as Chat,
261
+ Z as Chat,
260
262
  F as ChatContainer,
261
263
  _ as ChatForm,
262
- Z as ChatMessages
264
+ $ as ChatMessages
263
265
  };
@@ -1,112 +1,117 @@
1
1
  "use client";
2
- import { jsxs as f, jsx as r } from "react/jsx-runtime";
2
+ import { jsxs as h, jsx as r } from "react/jsx-runtime";
3
3
  import { useMemo as c } from "react";
4
- import { createAsk as w } from "../../node_modules/use-ask/dist/index.js";
5
- import { Dialog as x, DialogContent as g, DialogHeader as N, DialogTitle as y } from "./dialog.js";
6
- import { Icon as P } from "./icon.js";
7
- import { SecurePdfViewer as b } from "./secure-pdf-viewer.js";
8
- import { guessKind as A, isHttpUrl as D, getOfficeEmbedUrl as O } from "../../lib/file-preview.js";
9
- import { cn as F } from "../../lib/utils.js";
10
- const [p, T] = w({});
11
- function h(e) {
4
+ import { createAsk as v } from "../../node_modules/use-ask/dist/index.js";
5
+ import { AppDialog as x } from "./app-dialog.js";
6
+ import { Icon as g } from "./icon.js";
7
+ import { SecurePdfViewer as N } from "./secure-pdf-viewer.js";
8
+ import { guessKind as y, isHttpUrl as P, getOfficeEmbedUrl as b } from "../../lib/file-preview.js";
9
+ const [d, A] = v({});
10
+ function p(e) {
12
11
  return typeof e == "string" ? { url: e } : e || {};
13
12
  }
14
- async function q(e) {
15
- return p.ask(h(e));
13
+ async function S(e) {
14
+ return d.ask(p(e));
16
15
  }
17
- function B(e) {
18
- return p.safeAsk(h(e));
16
+ function U(e) {
17
+ return d.safeAsk(p(e));
19
18
  }
20
- const E = () => {
21
- const [{ payload: e }, { asking: i, ok: n }] = T(), l = e?.url, a = e?.readOnly, t = e?.page, o = c(() => e?.filename ? e?.filename : (e?.url || "").split(/[?#]/)[0].split("/").pop() || "" || "document", [e?.filename, e?.url]), m = c(() => {
19
+ const _ = () => {
20
+ const [{ payload: e }, { asking: l, ok: i }] = A(), n = e?.url, s = e?.readOnly, t = e?.page, o = c(() => e?.filename ? e?.filename : (e?.url || "").split(/[?#]/)[0].split("/").pop() || "" || "document", [e?.filename, e?.url]), u = c(() => {
22
21
  if (!e?.url) return null;
23
- const s = new URL(e?.url), u = s.searchParams.get("response-content-type") || s.searchParams.get("content-type"), d = e?.mimeType || u || "";
24
- return A({
25
- mimeType: d,
22
+ const a = new URL(e?.url), m = a.searchParams.get("response-content-type") || a.searchParams.get("content-type"), f = e?.mimeType || m || "";
23
+ return y({
24
+ mimeType: f,
26
25
  filename: o,
27
26
  url: e?.url
28
27
  });
29
- }, [e?.mimeType, o, e?.url]), v = c(
30
- () => /* @__PURE__ */ f("span", { className: "flex items-center gap-2", children: [
31
- /* @__PURE__ */ r(P, { name: "File", className: "h-4 w-4" }),
28
+ }, [e?.mimeType, o, e?.url]), w = c(
29
+ () => /* @__PURE__ */ h("span", { className: "flex items-center gap-2", children: [
30
+ /* @__PURE__ */ r(g, { name: "File", className: "h-4 w-4" }),
32
31
  e?.title || o
33
32
  ] }),
34
33
  [e?.title, o]
35
34
  );
36
- return /* @__PURE__ */ r(x, { open: i, onOpenChange: (s) => !s && n(), children: /* @__PURE__ */ f(g, { size: "xl", className: "sm:max-w-4xl", children: [
37
- /* @__PURE__ */ r(N, { children: /* @__PURE__ */ r(y, { children: v }) }),
38
- /* @__PURE__ */ r("div", { className: F("relative", "max-h-[80vh] overflow-auto p-1"), children: m && l ? /* @__PURE__ */ r(
39
- j,
40
- {
41
- kind: m,
42
- src: l,
43
- officeUrl: l,
44
- readOnly: a,
45
- page: t
46
- }
47
- ) : null })
48
- ] }) });
49
- }, j = ({
35
+ return /* @__PURE__ */ r(
36
+ x,
37
+ {
38
+ open: l,
39
+ onOpenChange: (a) => !a && i(),
40
+ title: w,
41
+ size: e?.size ?? "xl",
42
+ children: u && n ? /* @__PURE__ */ r(
43
+ O,
44
+ {
45
+ kind: u,
46
+ src: n,
47
+ officeUrl: n,
48
+ readOnly: s,
49
+ page: t
50
+ }
51
+ ) : null
52
+ }
53
+ );
54
+ }, O = ({
50
55
  kind: e,
51
- src: i,
52
- officeUrl: n,
53
- readOnly: l = !1,
54
- page: a = 0
56
+ src: l,
57
+ officeUrl: i,
58
+ readOnly: n = !1,
59
+ page: s = 0
55
60
  }) => {
56
- if (!i && e !== "text" && e !== "office")
61
+ if (!l && e !== "text" && e !== "office")
57
62
  return /* @__PURE__ */ r("div", { className: "text-d-secondary-foreground", children: "Nessuna sorgente disponibile" });
58
63
  switch (e) {
59
64
  case "image":
60
- return /* @__PURE__ */ r("div", { className: "grid place-items-center", children: /* @__PURE__ */ r(
65
+ return /* @__PURE__ */ r("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ r(
61
66
  "img",
62
67
  {
63
- src: i,
68
+ src: l,
64
69
  alt: "preview",
65
- className: "max-h-[78vh] w-auto max-w-full object-contain"
70
+ className: "max-h-full w-auto max-w-full object-contain"
66
71
  }
67
72
  ) });
68
73
  case "pdf": {
69
- if (l && i)
74
+ if (n && l)
70
75
  return /* @__PURE__ */ r(
71
- b,
76
+ N,
72
77
  {
73
- url: i,
74
- initialPage: a || 1,
75
- className: "w-full"
78
+ url: l,
79
+ initialPage: s || 1,
80
+ className: "h-full w-full"
76
81
  }
77
82
  );
78
- let t = i;
79
- return a > 0 && (t += "#page=" + a), /* @__PURE__ */ r(
83
+ let t = l;
84
+ return s > 0 && (t += "#page=" + s), /* @__PURE__ */ r(
80
85
  "iframe",
81
86
  {
82
87
  src: t,
83
88
  title: "PDF Preview",
84
- className: "h-[78vh] w-full rounded"
89
+ className: "h-full w-full rounded"
85
90
  }
86
91
  );
87
92
  }
88
93
  case "audio":
89
- return /* @__PURE__ */ r("div", { className: "grid place-items-center", children: /* @__PURE__ */ r("audio", { controls: !0, className: "w-full max-w-2xl", children: /* @__PURE__ */ r("source", { src: i }) }) });
94
+ return /* @__PURE__ */ r("div", { className: "grid h-full place-items-center", children: /* @__PURE__ */ r("audio", { controls: !0, className: "w-full max-w-2xl", children: /* @__PURE__ */ r("source", { src: l }) }) });
90
95
  case "video":
91
- return /* @__PURE__ */ r("div", { className: "grid place-items-center", children: /* @__PURE__ */ r("video", { controls: !0, className: "max-h-[78vh] w-full max-w-3xl rounded", children: /* @__PURE__ */ r("source", { src: i }) }) });
96
+ return /* @__PURE__ */ r("div", { className: "grid h-full place-items-center", children: /* @__PURE__ */ r("video", { controls: !0, className: "max-h-full w-full max-w-3xl rounded", children: /* @__PURE__ */ r("source", { src: l }) }) });
92
97
  case "text":
93
98
  return /* @__PURE__ */ r(
94
99
  "iframe",
95
100
  {
96
- src: i,
101
+ src: l,
97
102
  title: "Text Preview",
98
- className: "h-[78vh] w-full rounded"
103
+ className: "h-full w-full rounded"
99
104
  }
100
105
  );
101
106
  case "office": {
102
- if (n && D(n)) {
103
- const t = O(n);
107
+ if (i && P(i)) {
108
+ const t = b(i);
104
109
  return /* @__PURE__ */ r(
105
110
  "iframe",
106
111
  {
107
112
  src: t,
108
113
  title: "Office Preview",
109
- className: "h-[78vh] w-full rounded"
114
+ className: "h-full w-full rounded"
110
115
  }
111
116
  );
112
117
  }
@@ -117,7 +122,7 @@ const E = () => {
117
122
  }
118
123
  };
119
124
  export {
120
- E as FilePreviewer,
121
- q as previewFileModal,
122
- B as safePreviewFileModal
125
+ _ as FilePreviewer,
126
+ S as previewFileModal,
127
+ U as safePreviewFileModal
123
128
  };