@llmnative/react 1.4.0 → 1.5.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.
Files changed (32) hide show
  1. package/dist/index.css +1 -1
  2. package/dist/index.js +13 -12
  3. package/dist/index.js.map +1 -0
  4. package/dist/index.mjs +3095 -2619
  5. package/dist/index.mjs.map +1 -0
  6. package/dist/logHeader-Busbc8OO.mjs +2 -0
  7. package/dist/logHeader-Busbc8OO.mjs.map +1 -0
  8. package/dist/logHeader-CbzOhx-u.js +2 -1
  9. package/dist/logHeader-CbzOhx-u.js.map +1 -0
  10. package/dist/types/src/App.d.ts +6 -3
  11. package/dist/types/src/I18n.d.ts +7 -0
  12. package/dist/types/src/Theme.d.ts +6 -0
  13. package/dist/types/src/components/index.d.ts +1 -0
  14. package/dist/types/src/components/ui/Card.d.ts +3 -1
  15. package/dist/types/src/components/ui/Loader.d.ts +3 -1
  16. package/dist/types/src/components/ui/fields/AddressAutocomplete.d.ts +80 -0
  17. package/dist/types/src/components/widgets/grid-core/GridCore.d.ts +1 -1
  18. package/dist/types/src/components/widgets/grid-core/GridTableView.d.ts +1 -1
  19. package/dist/types/src/components/widgets/grid-core/types.d.ts +57 -0
  20. package/dist/types/src/index.d.ts +4 -1
  21. package/dist/types/src/providers/auth/AuthProvider.d.ts +1 -1
  22. package/dist/types/src/providers/auth/firebase/FirebaseAuthProvider.d.ts +1 -1
  23. package/dist/types/src/providers/googleMaps/GoogleMapsProviderContext.d.ts +8 -0
  24. package/dist/types/src/providers/googleMaps/loadGoogleMaps.d.ts +20 -0
  25. package/dist/vite.js +2 -1
  26. package/dist/vite.js.map +1 -0
  27. package/dist/vite.mjs +2 -0
  28. package/dist/vite.mjs.map +1 -0
  29. package/package.json +2 -1
  30. package/themes/cyber.ts +6 -0
  31. package/themes/default.ts +6 -0
  32. package/themes/flat.ts +6 -0
@@ -2,3 +2,5 @@
2
2
  var e = "x-llmnative-log-id";
3
3
  //#endregion
4
4
  export { e as t };
5
+
6
+ //# sourceMappingURL=logHeader-Busbc8OO.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logHeader-Busbc8OO.mjs","names":[],"sources":["../src/providers/proxy/logHeader.ts"],"sourcesContent":["/** Nome dell'header che trasporta l'id di conversazione dal client (provider AI, via\n * `AICompleteRequest.logId`) al proxy dev di Vite (`vite.ts`), che lo usa per accodare\n * request/response allo stesso file di log per l'intera conversazione. File separato e\n * privo di import Node-specific — i provider AI lato browser (anthropic.ts/gemini.ts/\n * openaiCompatible.ts) lo importano per impostare l'header, senza portarsi dietro le\n * dipendenze Node di vite.ts nel proprio bundle. */\nexport const LLM_LOG_ID_HEADER = 'x-llmnative-log-id';\n"],"mappings":";AAMA,IAAa,IAAoB"}
@@ -1 +1,2 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),l=`x-llmnative-log-id`;Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return l}});
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),l=`x-llmnative-log-id`;Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return l}});
2
+ //# sourceMappingURL=logHeader-CbzOhx-u.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logHeader-CbzOhx-u.js","names":[],"sources":["../src/providers/proxy/logHeader.ts"],"sourcesContent":["/** Nome dell'header che trasporta l'id di conversazione dal client (provider AI, via\n * `AICompleteRequest.logId`) al proxy dev di Vite (`vite.ts`), che lo usa per accodare\n * request/response allo stesso file di log per l'intera conversazione. File separato e\n * privo di import Node-specific — i provider AI lato browser (anthropic.ts/gemini.ts/\n * openaiCompatible.ts) lo importano per impostare l'header, senza portarsi dietro le\n * dipendenze Node di vite.ts nel proprio bundle. */\nexport const LLM_LOG_ID_HEADER = 'x-llmnative-log-id';\n"],"mappings":"slBAMa,EAAoB"}
@@ -8,6 +8,7 @@ import type { AuthProviderAdapter } from "./providers/auth/AuthProvider";
8
8
  import type { EmailProviderAdapter } from "./providers/email/EmailProvider";
9
9
  import type { CredentialsAdapter } from "./providers/credentials/CredentialsProvider";
10
10
  import { type AppIconProviderConfig } from "./providers/icon/IconProviderContext";
11
+ import type { GoogleMapsConfig } from "./providers/googleMaps/loadGoogleMaps";
11
12
  import { type I18nConfig } from "./I18n";
12
13
  import { type ServicesConfig, type GoogleProviderConfig, type SupabaseProviderConfig, type MockProviderConfig } from "./providers/manifest";
13
14
  interface MenuItem {
@@ -81,6 +82,8 @@ export type AppProviderProps = {
81
82
  providers?: AppProvidersConfig;
82
83
  /** Icon provider registry config. String shorthand selects the default provider id. */
83
84
  iconProvider?: AppIconProviderConfig;
85
+ /** Google Maps/Places config consumed by `<AddressAutocomplete>`. Omit to leave it unconfigured. */
86
+ googleMaps?: GoogleMapsConfig;
84
87
  /** Theme registry config. String shorthand selects the active theme id. */
85
88
  themeProvider?: AppThemeProviderConfig;
86
89
  /** Internationalization config: locale and per-locale translation overrides. */
@@ -110,8 +113,8 @@ export declare const getContextMenu: () => string[];
110
113
  *
111
114
  * Wraps children with: ErrorBoundary → BrowserRouter → RuntimeProvider →
112
115
  * AuthProvider → CredentialsProvider → DataProvider → StorageProvider →
113
- * EmailProvider → AIProvider → IconProvider → HeadProviderI18nProvider
114
- * ThemeProvider → [contextProviders] → children.
116
+ * EmailProvider → AIProvider → IconProvider → GoogleMapsProviderHeadProvider
117
+ * I18nProvider → ThemeProvider → [contextProviders] → children.
115
118
  *
116
119
  * `contextProviders` runs inside DataProvider, so vertical-specific providers
117
120
  * mounted there can call `useDataProvider()`, `useI18n()`, etc. directly.
@@ -119,7 +122,7 @@ export declare const getContextMenu: () => string[];
119
122
  * `App` uses `AppProvider` internally. Use `AppProvider` directly only when
120
123
  * you need the provider stack without the routing layer (e.g. tests, Storybook).
121
124
  */
122
- export declare function AppProvider({ contextProviders, providers, appName, scrapeConfig, iconProvider, themeProvider, i18n, children, errorReportUrl, debug, basename, }: AppProviderProps): React.JSX.Element;
125
+ export declare function AppProvider({ contextProviders, providers, appName, scrapeConfig, iconProvider, googleMaps, themeProvider, i18n, children, errorReportUrl, debug, basename, }: AppProviderProps): React.JSX.Element;
123
126
  declare function App({ importPage, LayoutDefault, menuConfig, children, ...providerProps }: AppProps): React.JSX.Element;
124
127
  export declare const useMenu: (type: string) => UseMenuItem[];
125
128
  export default App;
@@ -156,6 +156,13 @@ export interface I18nDict {
156
156
  dragToResize: string;
157
157
  dragHere: string;
158
158
  };
159
+ addressAutocomplete: {
160
+ placeholder: string;
161
+ noResults: string;
162
+ searching: string;
163
+ notConfigured: string;
164
+ loadError: string;
165
+ };
159
166
  }
160
167
  type DeepPartial<T> = {
161
168
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
@@ -271,6 +271,12 @@ export interface ThemeConfig {
271
271
  wrapperClassName?: string;
272
272
  className?: string;
273
273
  };
274
+ AddressAutocomplete?: {
275
+ wrapperClassName?: string;
276
+ className?: string;
277
+ menuClassName?: string;
278
+ itemClassName?: string;
279
+ };
274
280
  Form?: {
275
281
  wrapperClassName?: string;
276
282
  buttonSaveClass?: string;
@@ -43,6 +43,7 @@ export { default as Icon } from './ui/Icon';
43
43
  export { LayoutBuilder } from './ui/LayoutBuilder';
44
44
  export * from './ui/fields/Input';
45
45
  export * from './ui/fields/Select';
46
+ export * from './ui/fields/AddressAutocomplete';
46
47
  export * from './ui/fields/Upload';
47
48
  export * from './ui/fields/Crop';
48
49
  export * from './ui/fields/UploadCSV';
@@ -12,8 +12,10 @@ export interface CardProps extends UIProps {
12
12
  footerClassName?: string;
13
13
  /** Show a loading skeleton instead of content. */
14
14
  loading?: boolean;
15
+ /** Minimum height reserved for the loading overlay. Defaults to `200px`. */
16
+ loaderMinHeight?: string | number;
15
17
  before?: React.ReactNode;
16
18
  after?: React.ReactNode;
17
19
  }
18
- declare const Card: ({ children, title, header, footer, loading, wrapperClassName, className, headerClassName, bodyClassName, footerClassName, before, after, }: CardProps) => React.JSX.Element;
20
+ declare const Card: ({ children, title, header, footer, loading, loaderMinHeight, wrapperClassName, className, headerClassName, bodyClassName, footerClassName, before, after, }: CardProps) => React.JSX.Element;
19
21
  export default Card;
@@ -8,6 +8,8 @@ interface LoaderProps extends UIProps {
8
8
  icon?: string;
9
9
  title?: string;
10
10
  description?: string;
11
+ /** Minimum height reserved for the overlay while there is no content to size against. Defaults to `200px`. */
12
+ minHeight?: string | number;
11
13
  }
12
- declare function Loader({ children, show, icon, title, description, before, after, wrapperClassName, className }: LoaderProps): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.JSX.Element | null | undefined;
14
+ declare function Loader({ children, show, icon, title, description, minHeight, before, after, wrapperClassName, className }: LoaderProps): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.JSX.Element | null | undefined;
13
15
  export default Loader;
@@ -0,0 +1,80 @@
1
+ import React from 'react';
2
+ import { FormFieldProps } from '../../widgets/Form';
3
+ import type { FieldValue } from '../../../providers/data/DataProvider';
4
+ export interface ParsedAddress {
5
+ street?: string;
6
+ city?: string;
7
+ /** Province (administrative_area_level_2), falling back to the region when a country has no province level. */
8
+ state?: string;
9
+ zip?: string;
10
+ country?: string;
11
+ countryCode?: string;
12
+ lat?: number;
13
+ lng?: number;
14
+ formatted?: string;
15
+ }
16
+ /**
17
+ * Overrides the default sub-field paths. Each key defaults to `${name}.<key>` (e.g. `name="address"`
18
+ * → `address.street`) — set any of these to an unrelated, absolute field path to write there instead.
19
+ */
20
+ export interface AddressAutocompleteFieldNames {
21
+ street?: string;
22
+ city?: string;
23
+ state?: string;
24
+ zip?: string;
25
+ country?: string;
26
+ countryCode?: string;
27
+ lat?: string;
28
+ lng?: string;
29
+ zoom?: string;
30
+ formatted?: string;
31
+ }
32
+ export interface AddressAutocompleteMapOptions {
33
+ /** Where the map renders relative to the search box. Defaults to `'below'`. */
34
+ position?: 'above' | 'below';
35
+ /** Zoom applied when a location is first placed (via search selection or map click) and no zoom is stored yet. Defaults to `15`. */
36
+ defaultZoom?: number;
37
+ /** Map height in pixels. Defaults to `240`. */
38
+ height?: number;
39
+ }
40
+ export interface AddressAutocompleteProps extends FormFieldProps {
41
+ /** Base field path; see {@link AddressAutocompleteFieldNames} for the default sub-field layout. */
42
+ name: string;
43
+ placeholder?: string;
44
+ disabled?: boolean;
45
+ feedback?: string;
46
+ /** Restrict suggestions to these ISO 3166-1 alpha-2 country codes, e.g. `['it']`. */
47
+ countries?: string[];
48
+ /** Overrides the provider-level language/region bias for this instance only. */
49
+ language?: string;
50
+ region?: string;
51
+ validator?: (value: FieldValue) => string | undefined | Promise<string | undefined>;
52
+ /** Called with the fully parsed address whenever it changes (search selection, pin drag/click)
53
+ * — use this if the consumer needs fields beyond the ones auto-written. */
54
+ onSelect?: (address: ParsedAddress) => void;
55
+ /** Remaps the sub-field paths this component reads/writes. Omit to use `${name}.<field>`. */
56
+ fieldNames?: AddressAutocompleteFieldNames;
57
+ /**
58
+ * Shows an embeddable map with a draggable/clickable pin, so the user can fine-tune the exact
59
+ * location beyond what the address search resolves. Pass `true` for defaults or an options
60
+ * object. Requires the **Geocoding API** enabled in the same Google Cloud project, in addition
61
+ * to Places API (New) + Maps JavaScript API.
62
+ */
63
+ map?: boolean | AddressAutocompleteMapOptions;
64
+ id?: string;
65
+ labelClassName?: string;
66
+ menuClassName?: string;
67
+ itemClassName?: string;
68
+ mapClassName?: string;
69
+ }
70
+ /**
71
+ * Address search field backed by the Google Places Autocomplete (New) API. Selecting a
72
+ * suggestion fills the structured subfields below `name` — they stay independently editable
73
+ * afterward, same as every other CRM-override field in this app. Optionally pairs with an
74
+ * embeddable map (`map` prop) with a draggable/clickable pin for fine adjustment.
75
+ *
76
+ * Requires `<App googleMaps={{ apiKey }}>` (or `<GoogleMapsProvider config={...}>`) somewhere
77
+ * above in the tree; without it the field renders disabled with an explanatory message.
78
+ */
79
+ export declare const AddressAutocomplete: ({ name, label, required, placeholder, disabled, feedback, countries, language, region, validator, onSelect, fieldNames, map, id, labelClassName, inheritWrapperClassName, wrapperClassName, className, menuClassName, itemClassName, mapClassName, }: AddressAutocompleteProps) => React.ReactElement;
80
+ export default AddressAutocomplete;
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
2
  import { type RecordProps } from "../../../providers/data/DataProvider";
3
3
  import { type GridCoreProps } from "./types";
4
- declare function GridCore<TRecord extends RecordProps>({ records, recordId, sourcePath, columns, actions, form, editDeepLink, header, footer, view, views, sticky, wrapperClassName, loading, title, before, after, sortable, pagination, selection, onRowClick, reorderable, onReorder, groupBy, onSave, onDelete, onComplete, audit, onLoad, }: GridCoreProps<TRecord>): React.JSX.Element;
4
+ declare function GridCore<TRecord extends RecordProps>({ records, recordId, sourcePath, columns, actions, form, editDeepLink, header, footer, view, views, sticky, wrapperClassName, cardClassName, bodyClassName, loading, title, before, after, sortable, pagination, selection, onRowClick, reorderable, onReorder, groupBy, searchable, onSave, onDelete, onComplete, audit, onLoad, }: GridCoreProps<TRecord>): React.JSX.Element;
5
5
  export default GridCore;
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
2
  import { type RecordProps } from "../../../providers/data/DataProvider";
3
3
  import { type GridTableViewProps } from "./types";
4
- declare function GridTableView<TRecord extends RecordProps>({ records, recordId, columns, runAction, sortable, pagination, selection, selectedKeys, onSelectionChange, onRowClick, reorderable, onReorder, activeKey, groupBy, wrapperClassName, before, after, }: GridTableViewProps<TRecord>): React.JSX.Element;
4
+ declare function GridTableView<TRecord extends RecordProps>({ records, recordId, columns, runAction, sortable, pagination, selection, selectedKeys, onSelectionChange, onRowClick, reorderable, onReorder, activeKey, groupBy, wrapperClassName, className, heightClassName, scrollClassName, headerClassName, before, after, }: GridTableViewProps<TRecord>): React.JSX.Element;
5
5
  export default GridTableView;
@@ -29,6 +29,26 @@ export type GridGalleryField<TRecord> = {
29
29
  export type GridTableViewConfig = {
30
30
  /** Show a built-in "Columns" dropdown letting the user show/hide table columns. Default `false`. */
31
31
  columnPicker?: boolean;
32
+ /** CSS classes on the underlying `<table>` element — forwarded to `Table`'s own `className`.
33
+ * A `<table>` with `table-layout: auto` (the default) shrinks its columns to fit the
34
+ * container instead of overflowing it, so wide/many-column content never triggers a
35
+ * horizontal scrollbar on its own — set a `min-w-[…]` here (combined with `views.table`'s
36
+ * ancestor allowing horizontal overflow, e.g. via `wrapperClassName="overflow-x-auto"`) to
37
+ * force the table wider than its container when needed. */
38
+ className?: string;
39
+ /** Tailwind height/max-height class for the inner viewport — forwarded to `Table`'s own
40
+ * `heightClassName`. When set, `Table` enables internal vertical scrolling automatically AND
41
+ * pins its own `<thead>`/`<tfoot>` to the top/bottom of that same scrolling viewport, so only
42
+ * the body rows move — no extra prop needed for that, it's `Table`'s own default behavior
43
+ * whenever it is height-bound. */
44
+ heightClassName?: string;
45
+ /** Additional class on the inner scrolling viewport — forwarded to `Table`'s own
46
+ * `scrollClassName`. Use as an addon for overflow styling or fine-grained tweaks. */
47
+ scrollClassName?: string;
48
+ /** Optional extra classes on `<thead>` — forwarded to `Table`'s own `headerClassName`. Purely
49
+ * cosmetic (e.g. extra padding/typography); the sticky-header-when-scrollable behavior itself
50
+ * is automatic (see `heightClassName` above) and does not depend on this prop being set. */
51
+ headerClassName?: string;
32
52
  };
33
53
  /** Gallery-specific view options, grouped under `views.gallery`. */
34
54
  export type GridGalleryViewConfig<TRecord> = {
@@ -220,6 +240,22 @@ export type GridPresentation<TRecord> = {
220
240
  sticky?: GridSticky;
221
241
  /** CSS classes on the outermost wrapper element. */
222
242
  wrapperClassName?: string;
243
+ /** CSS classes on the Card's own root box — the bordered element itself (`ui/Card.tsx`'s
244
+ * `cardRootClassName`), distinct from `wrapperClassName` (the element AROUND the card,
245
+ * e.g. for `before`/`after` layout) and `bodyClassName` (the padded area inside the card).
246
+ * The root box has no `display: flex`/height by default — its body only actually gets a
247
+ * bounded height (and therefore an internal scrollbar) when THIS box is also a flex column
248
+ * with a real height, e.g. `"flex h-full min-h-0 flex-col"`. Needed together with
249
+ * `bodyClassName`/`views.table.heightClassName` for a full-height Grid to scroll internally
250
+ * instead of growing past its container. */
251
+ cardClassName?: string;
252
+ /** CSS classes on the Card body — the container directly around the table/gallery view.
253
+ * Grid always renders inside a `Card` (`ui/Card.tsx`); its body defaults to a fixed padding
254
+ * with no height constraint. Use this (typically with `flex-1 min-h-0 overflow-hidden p-0`,
255
+ * paired with `wrapperClassName`/`cardClassName` making the card itself a bounded flex
256
+ * column, and `views.table.heightClassName`) to make the table's own internal scroll fill
257
+ * the remaining height of a flex ancestor instead of growing the page. */
258
+ bodyClassName?: string;
223
259
  /** Show a loading skeleton instead of rows. */
224
260
  loading?: boolean;
225
261
  /** Title rendered in the Grid header area. */
@@ -229,6 +265,15 @@ export type GridPresentation<TRecord> = {
229
265
  /** Content rendered after the Grid. */
230
266
  after?: React.ReactNode;
231
267
  };
268
+ /** Config for `GridBehavior.searchable` beyond a plain `true` — restrict which fields match,
269
+ * or override the input's placeholder. */
270
+ export type GridSearchConfig<TRecord> = {
271
+ /** Placeholder text for the search input — defaults to the localized `common.search`
272
+ * ("Search"). */
273
+ placeholder?: string;
274
+ /** Field keys to match against — omit to search every string-valued field on each record. */
275
+ fields?: Array<keyof TRecord | string>;
276
+ };
232
277
  /** Interaction / behaviour props for `<Grid>`. */
233
278
  export type GridBehavior<TRecord> = {
234
279
  /** Enable global sorting. Pass an `OrderConfig` to set a default sort. */
@@ -245,6 +290,14 @@ export type GridBehavior<TRecord> = {
245
290
  onReorder?: GridReorderHandler<TRecord>;
246
291
  /** Group rows by a field or array of fields. */
247
292
  groupBy?: keyof TRecord | string | Array<keyof TRecord | string>;
293
+ /** Enable a built-in search box in Grid's own default header — filters the records actually
294
+ * rendered (table/gallery, and whatever they in turn compute: sort/pagination/selection all
295
+ * see the FILTERED set, not the original one) by a case-insensitive substring match. `true`
296
+ * searches every string-valued field on each record; pass a `GridSearchConfig` to restrict to
297
+ * specific fields or customize the placeholder. Only rendered as part of Grid's OWN default
298
+ * header — same as `views.table.columnPicker`/`views.toggle`, a fully custom `header` prop
299
+ * bypasses it entirely (render your own search input there instead). */
300
+ searchable?: boolean | GridSearchConfig<TRecord>;
248
301
  };
249
302
  /** Data-mutation hooks for `<Grid>`. */
250
303
  export type GridPersistence<TRecord> = {
@@ -329,6 +382,10 @@ export type GridTableViewProps<TRecord extends RecordProps> = {
329
382
  activeKey?: string | null;
330
383
  groupBy?: keyof TRecord | string | Array<keyof TRecord | string>;
331
384
  wrapperClassName?: string;
385
+ className?: string;
386
+ heightClassName?: string;
387
+ scrollClassName?: string;
388
+ headerClassName?: string;
332
389
  before?: React.ReactNode;
333
390
  after?: React.ReactNode;
334
391
  };
@@ -21,7 +21,7 @@ export { PromptUtils } from './libs/promptUtils';
21
21
  export type { PromptAction, PromptStatusItem, PromptRunStats } from './components/widgets/Prompt';
22
22
  export type { TableHeaderProp, TableReorderHandler, TableReorderMeta, TableSelectionChangeHandler, TableSelectionState } from './components/ui/Table';
23
23
  export type { GalleryRecord, GallerySelectionChangeHandler, GallerySelectionState } from './components/ui/Gallery';
24
- export type { GridAction, GridActions, GridAfterActionArgs, GridAfterActionHandler, GridArrayProps, GridColumn, GridCoreProps, GridDBPath, GridDBQuery, GridDBProps, GridFooterContext, GridFormContext, GridGalleryField, GridGalleryViewProps, GridGalleryViewConfig, GridHeaderContext, GridLayout, GridMutationDeleteArgs, GridMutationDeleteHandler, GridMutationSaveArgs, GridMutationSaveHandler, GridReorderHandler, GridReorderMeta, GridRecordKey, GridProps, GridSelectionChangeHandler, GridSelectionMode, GridSelectionState, GridSticky, GridTableViewProps, GridTableViewConfig, GridViewsConfig, GridCellContext, } from './components/widgets/Grid';
24
+ export type { GridAction, GridActions, GridAfterActionArgs, GridAfterActionHandler, GridArrayProps, GridColumn, GridCoreProps, GridDBPath, GridDBQuery, GridDBProps, GridFooterContext, GridFormContext, GridGalleryField, GridGalleryViewProps, GridGalleryViewConfig, GridHeaderContext, GridLayout, GridMutationDeleteArgs, GridMutationDeleteHandler, GridMutationSaveArgs, GridMutationSaveHandler, GridReorderHandler, GridReorderMeta, GridRecordKey, GridProps, GridSearchConfig, GridSelectionChangeHandler, GridSelectionMode, GridSelectionState, GridSticky, GridTableViewProps, GridTableViewConfig, GridViewsConfig, GridCellContext, } from './components/widgets/Grid';
25
25
  export type { ProviderConfigurable, ProviderConfigurationState } from './providers/ProviderConfiguration';
26
26
  export { getProviderConfigurationState } from './providers/ProviderConfiguration';
27
27
  export type { ProviderAdapterMap, ProviderService, SetProviderFn, ProviderRegistrySnapshot } from './providers/ProviderRegistryContext';
@@ -73,6 +73,9 @@ export { PhosphorIconProvider } from './providers/icon/PhosphorIconProvider';
73
73
  export type { PhosphorWeight } from './providers/icon/PhosphorIconProvider';
74
74
  export { IconProvider, useIconProvider, useIconController } from './providers/icon/IconProviderContext';
75
75
  export type { AppIconProviderConfig, IconController } from './providers/icon/IconProviderContext';
76
+ export { GoogleMapsProvider, useGoogleMapsConfig } from './providers/googleMaps/GoogleMapsProviderContext';
77
+ export { loadGoogleMaps } from './providers/googleMaps/loadGoogleMaps';
78
+ export type { GoogleMapsConfig } from './providers/googleMaps/loadGoogleMaps';
76
79
  export { useEmailProvider, EmailProvider } from './providers/email/EmailProviderContext';
77
80
  export { GmailEmailProvider } from './providers/email/google/GmailEmailProvider';
78
81
  export type { AIRequestOptions, AIAttachment, AIProviderAdapter, AIKeyValidationResult, AIModelDescriptor, AIProviderCapabilities, AIModelCatalog, AIProviderDefinition, AICompleteRequest, AICompleteResult, AIConversationTurn, AIToolDefinition, AIToolCall, AIToolResult, } from './providers/ai';
@@ -26,5 +26,5 @@ export interface AuthProviderAdapter extends ProviderConfigurable {
26
26
  * Read-only/UI convenience — never a security boundary, the server always
27
27
  * re-verifies the raw token itself rather than trusting client-read claims.
28
28
  */
29
- getIdTokenClaims?(): Promise<Record<string, unknown> | null>;
29
+ getIdTokenClaims?(forceRefresh?: boolean): Promise<Record<string, unknown> | null>;
30
30
  }
@@ -45,5 +45,5 @@ export declare class FirebaseAuthProvider implements AuthProviderAdapter {
45
45
  signOut(): Promise<void>;
46
46
  onAuthChange(callback: (user: UserProfile | null) => void): () => void;
47
47
  getAccessToken(): Promise<string>;
48
- getIdTokenClaims(): Promise<Record<string, unknown> | null>;
48
+ getIdTokenClaims(forceRefresh?: boolean): Promise<Record<string, unknown> | null>;
49
49
  }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import type { GoogleMapsConfig } from './loadGoogleMaps';
3
+ export declare function GoogleMapsProvider({ config, children }: {
4
+ config?: GoogleMapsConfig;
5
+ children: React.ReactNode;
6
+ }): React.JSX.Element;
7
+ /** `undefined` when `<App>`/`<GoogleMapsProvider>` was not given a `googleMaps` config. */
8
+ export declare function useGoogleMapsConfig(): GoogleMapsConfig | undefined;
@@ -0,0 +1,20 @@
1
+ export interface GoogleMapsConfig {
2
+ /** Google Cloud API key with the "Places API (New)" enabled. */
3
+ apiKey: string;
4
+ /** BCP-47 language for results, e.g. `'it'`. Defaults to the browser's language. */
5
+ language?: string;
6
+ /** Region bias (ccTLD), e.g. `'it'`. */
7
+ region?: string;
8
+ }
9
+ declare global {
10
+ interface Window {
11
+ [key: `__googleMapsLoader_${string}`]: (() => void) | undefined;
12
+ }
13
+ }
14
+ /**
15
+ * Loads the Google Maps JS API (places library) exactly once per page, regardless of how
16
+ * many `<AddressAutocomplete>` instances mount. Safe to call repeatedly with the same config;
17
+ * throws if called again with a *different* apiKey than the one already loading/loaded, since
18
+ * the script tag can't be swapped out once injected.
19
+ */
20
+ export declare function loadGoogleMaps(config: GoogleMapsConfig): Promise<typeof google>;
package/dist/vite.js CHANGED
@@ -1,2 +1,3 @@
1
1
  Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./logHeader-CbzOhx-u.js");let t=require("node:fs/promises"),n=require("node:path");n=e.r(n);var r=`x-llmnative-proxy`,i=`/api/proxy`,a=async e=>{let t=[];for await(let n of e)t.push(Buffer.isBuffer(n)?n:Buffer.from(n));return t.length===0?void 0:Buffer.concat(t)};function o(e){return e.replace(/[^a-zA-Z0-9_-]/g,``).slice(0,128)||`unknown`}function s(e){if(!e.trim())return`(empty body)`;try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}async function c(e,r,i,a,c,l){try{await(0,t.mkdir)(e.dir,{recursive:!0});let u=[``,`=`.repeat(80),`Turn @ ${new Date().toISOString()}`,`Target: ${i}`,`--- REQUEST BODY ---`,s(a),`--- RESPONSE BODY (status ${c}) ---`,s(l),`=`.repeat(80),``].join(`
2
- `);await(0,t.appendFile)(n.default.join(e.dir,`${o(r)}.txt`),u,`utf-8`)}catch(e){console.warn(`[llmnative-dev-proxy] Failed to write LLM log:`,e)}}var l=async(t,n,i,o)=>{let s=new URL(t.url||`/`,`http://localhost`).searchParams.get(`url`);if(t.headers[r]!==`1`){n.statusCode=403,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Proxy access denied.`}));return}if(!s){n.statusCode=400,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Missing "url" query parameter.`}));return}try{let l=new URL(s);if(l.pathname.startsWith(i)&&[`localhost`,`127.0.0.1`].includes(l.hostname)){n.statusCode=508,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Recursive proxy target detected.`,target:s}));return}let u=await a(t),d=t.headers[e.t],f=new Headers,p=new Set([r,e.t,`host`,`origin`,`referer`,`content-length`,`transfer-encoding`,`connection`]);Object.entries(t.headers).forEach(([e,t])=>{!t||p.has(e.toLowerCase())||f.set(e,Array.isArray(t)?t.join(`, `):t)});let m=await fetch(s,{method:t.method||`GET`,headers:f,body:u&&![`GET`,`HEAD`].includes((t.method||`GET`).toUpperCase())?Uint8Array.from(u):void 0,redirect:`follow`}),h=Buffer.from(await m.arrayBuffer());o?.enabled&&typeof d==`string`&&d&&c(o,d,s,u?.toString(`utf-8`)??``,m.status,h.toString(`utf-8`)),n.statusCode=m.status,m.headers.forEach((e,t)=>{t.toLowerCase()!==`content-encoding`&&n.setHeader(t,e)}),n.end(h)}catch(e){n.statusCode=502,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:e instanceof Error?e.message:`Proxy request failed.`}))}},u=(e=i,t)=>({name:`llmnative-dev-proxy`,configureServer(n){n.middlewares.use(e,(n,r)=>{l(n,r,e,t)})},configurePreviewServer(n){n.middlewares.use(e,(n,r)=>{l(n,r,e,t)})}});exports.PROXY_PATH=i,exports.createProxyPlugin=u;
2
+ `);await(0,t.appendFile)(n.default.join(e.dir,`${o(r)}.txt`),u,`utf-8`)}catch(e){console.warn(`[llmnative-dev-proxy] Failed to write LLM log:`,e)}}var l=async(t,n,i,o)=>{let s=new URL(t.url||`/`,`http://localhost`).searchParams.get(`url`);if(t.headers[r]!==`1`){n.statusCode=403,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Proxy access denied.`}));return}if(!s){n.statusCode=400,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Missing "url" query parameter.`}));return}try{let l=new URL(s);if(l.pathname.startsWith(i)&&[`localhost`,`127.0.0.1`].includes(l.hostname)){n.statusCode=508,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Recursive proxy target detected.`,target:s}));return}let u=await a(t),d=t.headers[e.t],f=new Headers,p=new Set([r,e.t,`host`,`origin`,`referer`,`content-length`,`transfer-encoding`,`connection`]);Object.entries(t.headers).forEach(([e,t])=>{!t||p.has(e.toLowerCase())||f.set(e,Array.isArray(t)?t.join(`, `):t)});let m=await fetch(s,{method:t.method||`GET`,headers:f,body:u&&![`GET`,`HEAD`].includes((t.method||`GET`).toUpperCase())?Uint8Array.from(u):void 0,redirect:`follow`}),h=Buffer.from(await m.arrayBuffer());o?.enabled&&typeof d==`string`&&d&&c(o,d,s,u?.toString(`utf-8`)??``,m.status,h.toString(`utf-8`)),n.statusCode=m.status,m.headers.forEach((e,t)=>{t.toLowerCase()!==`content-encoding`&&n.setHeader(t,e)}),n.end(h)}catch(e){n.statusCode=502,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:e instanceof Error?e.message:`Proxy request failed.`}))}},u=(e=i,t)=>({name:`llmnative-dev-proxy`,configureServer(n){n.middlewares.use(e,(n,r)=>{l(n,r,e,t)})},configurePreviewServer(n){n.middlewares.use(e,(n,r)=>{l(n,r,e,t)})}});exports.PROXY_PATH=i,exports.createProxyPlugin=u;
3
+ //# sourceMappingURL=vite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.js","names":[],"sources":["../src/providers/proxy/vite.ts"],"sourcesContent":["import { appendFile, mkdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { Plugin } from 'vite';\nimport { LLM_LOG_ID_HEADER } from './logHeader';\n\nconst PROXY_CALLER_HEADER = 'x-llmnative-proxy';\nexport const PROXY_PATH = '/api/proxy';\n\n/** Logging file di richiesta/risposta LLM — SOLO dev, mai attivo di default. Un file per\n * conversazione: il client (useAgent, CMS) genera un `logId` stabile per l'intera sessione e\n * lo manda su ogni chiamata come header `x-llmnative-log-id`; il proxy accoda ogni turno allo\n * stesso file, non lo sovrascrive. Vedi AICompleteRequest.logId per il lato client. */\nexport interface ProxyLogOptions {\n enabled: boolean;\n /** Cartella di destinazione, relativa alla cwd del dev server (il progetto consumer, es.\n * il CMS) o assoluta. Creata se assente. */\n dir: string;\n}\n\nconst readRequestBody = async (req: IncomingMessage): Promise<Buffer | undefined> => {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return chunks.length === 0 ? undefined : Buffer.concat(chunks);\n};\n\n/** Solo caratteri sicuri per un nome file — l'id è generato da noi (crypto.randomUUID() lato\n * client), ma niente path traversal per costruzione anche se un client si comportasse male. */\nfunction sanitizeLogId(logId: string): string {\n return logId.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 128) || 'unknown';\n}\n\nfunction prettyJsonOrRaw(text: string): string {\n if (!text.trim()) return '(empty body)';\n try {\n return JSON.stringify(JSON.parse(text), null, 2);\n } catch {\n return text;\n }\n}\n\nasync function appendLogBlock(logOptions: ProxyLogOptions, logId: string, target: string, requestBody: string, status: number, responseBody: string) {\n try {\n await mkdir(logOptions.dir, { recursive: true });\n const block = [\n '',\n '='.repeat(80),\n `Turn @ ${new Date().toISOString()}`,\n `Target: ${target}`,\n '--- REQUEST BODY ---',\n prettyJsonOrRaw(requestBody),\n `--- RESPONSE BODY (status ${status}) ---`,\n prettyJsonOrRaw(responseBody),\n '='.repeat(80),\n '',\n ].join('\\n');\n await appendFile(path.join(logOptions.dir, `${sanitizeLogId(logId)}.txt`), block, 'utf-8');\n } catch (error) {\n // Un log fallito non deve mai far fallire la vera richiesta — solo un avviso in console.\n console.warn('[llmnative-dev-proxy] Failed to write LLM log:', error);\n }\n}\n\nconst writeProxyResponse = async (req: IncomingMessage, res: ServerResponse, route: string, logOptions?: ProxyLogOptions) => {\n const requestUrl = new URL(req.url || '/', 'http://localhost');\n const target = requestUrl.searchParams.get('url');\n\n if (req.headers[PROXY_CALLER_HEADER] !== '1') {\n res.statusCode = 403;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Proxy access denied.' }));\n return;\n }\n\n if (!target) {\n res.statusCode = 400;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Missing \"url\" query parameter.' }));\n return;\n }\n\n try {\n const targetUrl = new URL(target);\n if (targetUrl.pathname.startsWith(route) && ['localhost', '127.0.0.1'].includes(targetUrl.hostname)) {\n res.statusCode = 508;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Recursive proxy target detected.', target }));\n return;\n }\n\n const body = await readRequestBody(req);\n const logId = req.headers[LLM_LOG_ID_HEADER];\n const headers = new Headers();\n // L'header di log è consumato QUI, mai inoltrato al provider upstream (stesso\n // trattamento del PROXY_CALLER_HEADER) — è un dettaglio nostro, non dell'API esterna.\n const skipHeaders = new Set([PROXY_CALLER_HEADER, LLM_LOG_ID_HEADER, 'host', 'origin', 'referer', 'content-length', 'transfer-encoding', 'connection']);\n\n Object.entries(req.headers).forEach(([key, value]) => {\n if (!value || skipHeaders.has(key.toLowerCase())) return;\n headers.set(key, Array.isArray(value) ? value.join(', ') : value);\n });\n\n const upstream = await fetch(target, {\n method: req.method || 'GET',\n headers,\n body: body && !['GET', 'HEAD'].includes((req.method || 'GET').toUpperCase())\n ? Uint8Array.from(body)\n : undefined,\n redirect: 'follow',\n });\n\n const responseBuffer = Buffer.from(await upstream.arrayBuffer());\n\n if (logOptions?.enabled && typeof logId === 'string' && logId) {\n void appendLogBlock(logOptions, logId, target, body?.toString('utf-8') ?? '', upstream.status, responseBuffer.toString('utf-8'));\n }\n\n res.statusCode = upstream.status;\n upstream.headers.forEach((value, key) => {\n if (key.toLowerCase() === 'content-encoding') return;\n res.setHeader(key, value);\n });\n res.end(responseBuffer);\n } catch (error) {\n res.statusCode = 502;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: error instanceof Error ? error.message : 'Proxy request failed.' }));\n }\n};\n\nexport const createProxyPlugin = (route = PROXY_PATH, logOptions?: ProxyLogOptions): Plugin => ({\n name: 'llmnative-dev-proxy',\n configureServer(server) {\n server.middlewares.use(route, (req, res) => { void writeProxyResponse(req, res, route, logOptions); });\n },\n configurePreviewServer(server) {\n server.middlewares.use(route, (req, res) => { void writeProxyResponse(req, res, route, logOptions); });\n },\n});\n"],"mappings":"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"}
package/dist/vite.mjs CHANGED
@@ -97,3 +97,5 @@ var u = async (t, n, r, a) => {
97
97
  });
98
98
  //#endregion
99
99
  export { a as PROXY_PATH, d as createProxyPlugin };
100
+
101
+ //# sourceMappingURL=vite.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.mjs","names":[],"sources":["../src/providers/proxy/vite.ts"],"sourcesContent":["import { appendFile, mkdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { Plugin } from 'vite';\nimport { LLM_LOG_ID_HEADER } from './logHeader';\n\nconst PROXY_CALLER_HEADER = 'x-llmnative-proxy';\nexport const PROXY_PATH = '/api/proxy';\n\n/** Logging file di richiesta/risposta LLM — SOLO dev, mai attivo di default. Un file per\n * conversazione: il client (useAgent, CMS) genera un `logId` stabile per l'intera sessione e\n * lo manda su ogni chiamata come header `x-llmnative-log-id`; il proxy accoda ogni turno allo\n * stesso file, non lo sovrascrive. Vedi AICompleteRequest.logId per il lato client. */\nexport interface ProxyLogOptions {\n enabled: boolean;\n /** Cartella di destinazione, relativa alla cwd del dev server (il progetto consumer, es.\n * il CMS) o assoluta. Creata se assente. */\n dir: string;\n}\n\nconst readRequestBody = async (req: IncomingMessage): Promise<Buffer | undefined> => {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return chunks.length === 0 ? undefined : Buffer.concat(chunks);\n};\n\n/** Solo caratteri sicuri per un nome file — l'id è generato da noi (crypto.randomUUID() lato\n * client), ma niente path traversal per costruzione anche se un client si comportasse male. */\nfunction sanitizeLogId(logId: string): string {\n return logId.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 128) || 'unknown';\n}\n\nfunction prettyJsonOrRaw(text: string): string {\n if (!text.trim()) return '(empty body)';\n try {\n return JSON.stringify(JSON.parse(text), null, 2);\n } catch {\n return text;\n }\n}\n\nasync function appendLogBlock(logOptions: ProxyLogOptions, logId: string, target: string, requestBody: string, status: number, responseBody: string) {\n try {\n await mkdir(logOptions.dir, { recursive: true });\n const block = [\n '',\n '='.repeat(80),\n `Turn @ ${new Date().toISOString()}`,\n `Target: ${target}`,\n '--- REQUEST BODY ---',\n prettyJsonOrRaw(requestBody),\n `--- RESPONSE BODY (status ${status}) ---`,\n prettyJsonOrRaw(responseBody),\n '='.repeat(80),\n '',\n ].join('\\n');\n await appendFile(path.join(logOptions.dir, `${sanitizeLogId(logId)}.txt`), block, 'utf-8');\n } catch (error) {\n // Un log fallito non deve mai far fallire la vera richiesta — solo un avviso in console.\n console.warn('[llmnative-dev-proxy] Failed to write LLM log:', error);\n }\n}\n\nconst writeProxyResponse = async (req: IncomingMessage, res: ServerResponse, route: string, logOptions?: ProxyLogOptions) => {\n const requestUrl = new URL(req.url || '/', 'http://localhost');\n const target = requestUrl.searchParams.get('url');\n\n if (req.headers[PROXY_CALLER_HEADER] !== '1') {\n res.statusCode = 403;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Proxy access denied.' }));\n return;\n }\n\n if (!target) {\n res.statusCode = 400;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Missing \"url\" query parameter.' }));\n return;\n }\n\n try {\n const targetUrl = new URL(target);\n if (targetUrl.pathname.startsWith(route) && ['localhost', '127.0.0.1'].includes(targetUrl.hostname)) {\n res.statusCode = 508;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Recursive proxy target detected.', target }));\n return;\n }\n\n const body = await readRequestBody(req);\n const logId = req.headers[LLM_LOG_ID_HEADER];\n const headers = new Headers();\n // L'header di log è consumato QUI, mai inoltrato al provider upstream (stesso\n // trattamento del PROXY_CALLER_HEADER) — è un dettaglio nostro, non dell'API esterna.\n const skipHeaders = new Set([PROXY_CALLER_HEADER, LLM_LOG_ID_HEADER, 'host', 'origin', 'referer', 'content-length', 'transfer-encoding', 'connection']);\n\n Object.entries(req.headers).forEach(([key, value]) => {\n if (!value || skipHeaders.has(key.toLowerCase())) return;\n headers.set(key, Array.isArray(value) ? value.join(', ') : value);\n });\n\n const upstream = await fetch(target, {\n method: req.method || 'GET',\n headers,\n body: body && !['GET', 'HEAD'].includes((req.method || 'GET').toUpperCase())\n ? Uint8Array.from(body)\n : undefined,\n redirect: 'follow',\n });\n\n const responseBuffer = Buffer.from(await upstream.arrayBuffer());\n\n if (logOptions?.enabled && typeof logId === 'string' && logId) {\n void appendLogBlock(logOptions, logId, target, body?.toString('utf-8') ?? '', upstream.status, responseBuffer.toString('utf-8'));\n }\n\n res.statusCode = upstream.status;\n upstream.headers.forEach((value, key) => {\n if (key.toLowerCase() === 'content-encoding') return;\n res.setHeader(key, value);\n });\n res.end(responseBuffer);\n } catch (error) {\n res.statusCode = 502;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: error instanceof Error ? error.message : 'Proxy request failed.' }));\n }\n};\n\nexport const createProxyPlugin = (route = PROXY_PATH, logOptions?: ProxyLogOptions): Plugin => ({\n name: 'llmnative-dev-proxy',\n configureServer(server) {\n server.middlewares.use(route, (req, res) => { void writeProxyResponse(req, res, route, logOptions); });\n },\n configurePreviewServer(server) {\n server.middlewares.use(route, (req, res) => { void writeProxyResponse(req, res, route, logOptions); });\n },\n});\n"],"mappings":";;;;AAMA,IAAM,IAAsB,qBACf,IAAa,cAapB,IAAkB,OAAO,MAAsD;CACjF,IAAM,IAAmB,CAAC;CAC1B,WAAW,IAAM,KAAS,GACtB,EAAO,KAAK,OAAO,SAAS,CAAK,IAAI,IAAQ,OAAO,KAAK,CAAK,CAAC;CAEnE,OAAO,EAAO,WAAW,IAAI,KAAA,IAAY,OAAO,OAAO,CAAM;AACjE;AAIA,SAAS,EAAc,GAAuB;CAC1C,OAAO,EAAM,QAAQ,mBAAmB,EAAE,CAAC,CAAC,MAAM,GAAG,GAAG,KAAK;AACjE;AAEA,SAAS,EAAgB,GAAsB;CAC3C,IAAI,CAAC,EAAK,KAAK,GAAG,OAAO;CACzB,IAAI;EACA,OAAO,KAAK,UAAU,KAAK,MAAM,CAAI,GAAG,MAAM,CAAC;CACnD,QAAQ;EACJ,OAAO;CACX;AACJ;AAEA,eAAe,EAAe,GAA6B,GAAe,GAAgB,GAAqB,GAAgB,GAAsB;CACjJ,IAAI;EACA,MAAM,EAAM,EAAW,KAAK,EAAE,WAAW,GAAK,CAAC;EAC/C,IAAM,IAAQ;GACV;GACA,IAAI,OAAO,EAAE;GACb,2BAAU,IAAI,KAAK,EAAA,CAAE,YAAY;GACjC,WAAW;GACX;GACA,EAAgB,CAAW;GAC3B,6BAA6B,EAAO;GACpC,EAAgB,CAAY;GAC5B,IAAI,OAAO,EAAE;GACb;EACJ,CAAC,CAAC,KAAK,IAAI;EACX,MAAM,EAAW,EAAK,KAAK,EAAW,KAAK,GAAG,EAAc,CAAK,EAAE,KAAK,GAAG,GAAO,OAAO;CAC7F,SAAS,GAAO;EAEZ,QAAQ,KAAK,kDAAkD,CAAK;CACxE;AACJ;AAEA,IAAM,IAAqB,OAAO,GAAsB,GAAqB,GAAe,MAAiC;CAEzH,IAAM,IAAS,IADQ,IAAI,EAAI,OAAO,KAAK,kBAC5B,CAAA,CAAW,aAAa,IAAI,KAAK;CAEhD,IAAI,EAAI,QAAQ,OAAyB,KAAK;EAG1C,AAFA,EAAI,aAAa,KACjB,EAAI,UAAU,gBAAgB,kBAAkB,GAChD,EAAI,IAAI,KAAK,UAAU,EAAE,OAAO,uBAAuB,CAAC,CAAC;EACzD;CACJ;CAEA,IAAI,CAAC,GAAQ;EAGT,AAFA,EAAI,aAAa,KACjB,EAAI,UAAU,gBAAgB,kBAAkB,GAChD,EAAI,IAAI,KAAK,UAAU,EAAE,OAAO,mCAAiC,CAAC,CAAC;EACnE;CACJ;CAEA,IAAI;EACA,IAAM,IAAY,IAAI,IAAI,CAAM;EAChC,IAAI,EAAU,SAAS,WAAW,CAAK,KAAK,CAAC,aAAa,WAAW,CAAC,CAAC,SAAS,EAAU,QAAQ,GAAG;GAGjG,AAFA,EAAI,aAAa,KACjB,EAAI,UAAU,gBAAgB,kBAAkB,GAChD,EAAI,IAAI,KAAK,UAAU;IAAE,OAAO;IAAoC;GAAO,CAAC,CAAC;GAC7E;EACJ;EAEA,IAAM,IAAO,MAAM,EAAgB,CAAG,GAChC,IAAQ,EAAI,QAAQ,IACpB,IAAU,IAAI,QAAQ,GAGtB,oBAAc,IAAI,IAAI;GAAC;GAAqB;GAAmB;GAAQ;GAAU;GAAW;GAAkB;GAAqB;EAAY,CAAC;EAEtJ,OAAO,QAAQ,EAAI,OAAO,CAAC,CAAC,SAAS,CAAC,GAAK,OAAW;GAC9C,CAAC,KAAS,EAAY,IAAI,EAAI,YAAY,CAAC,KAC/C,EAAQ,IAAI,GAAK,MAAM,QAAQ,CAAK,IAAI,EAAM,KAAK,IAAI,IAAI,CAAK;EACpE,CAAC;EAED,IAAM,IAAW,MAAM,MAAM,GAAQ;GACjC,QAAQ,EAAI,UAAU;GACtB;GACA,MAAM,KAAQ,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,UAAU,EAAI,UAAU,MAAA,CAAO,YAAY,CAAC,IACrE,WAAW,KAAK,CAAI,IACpB,KAAA;GACN,UAAU;EACd,CAAC,GAEK,IAAiB,OAAO,KAAK,MAAM,EAAS,YAAY,CAAC;EAW/D,AATI,GAAY,WAAW,OAAO,KAAU,YAAY,KACpD,EAAoB,GAAY,GAAO,GAAQ,GAAM,SAAS,OAAO,KAAK,IAAI,EAAS,QAAQ,EAAe,SAAS,OAAO,CAAC,GAGnI,EAAI,aAAa,EAAS,QAC1B,EAAS,QAAQ,SAAS,GAAO,MAAQ;GACjC,EAAI,YAAY,MAAM,sBAC1B,EAAI,UAAU,GAAK,CAAK;EAC5B,CAAC,GACD,EAAI,IAAI,CAAc;CAC1B,SAAS,GAAO;EAGZ,AAFA,EAAI,aAAa,KACjB,EAAI,UAAU,gBAAgB,kBAAkB,GAChD,EAAI,IAAI,KAAK,UAAU,EAAE,OAAO,aAAiB,QAAQ,EAAM,UAAU,wBAAwB,CAAC,CAAC;CACvG;AACJ,GAEa,KAAqB,IAAQ,GAAY,OAA0C;CAC5F,MAAM;CACN,gBAAgB,GAAQ;EACpB,EAAO,YAAY,IAAI,IAAQ,GAAK,MAAQ;GAAE,EAAwB,GAAK,GAAK,GAAO,CAAU;EAAG,CAAC;CACzG;CACA,uBAAuB,GAAQ;EAC3B,EAAO,YAAY,IAAI,IAAQ,GAAK,MAAQ;GAAE,EAAwB,GAAK,GAAK,GAAO,CAAU;EAAG,CAAC;CACzG;AACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmnative/react",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "description": "LLM-native React framework for deterministic UI generation. Data-driven by default, schema-driven optional. Ultra-low token consumption — built for AI agents, not just humans.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -101,6 +101,7 @@
101
101
  "@testing-library/jest-dom": "^7.0.0",
102
102
  "@testing-library/react": "^16.3.2",
103
103
  "@testing-library/user-event": "^14.6.1",
104
+ "@types/google.maps": "^3.65.4",
104
105
  "@types/papaparse": "^5.5.2",
105
106
  "@types/prismjs": "^1.26.6",
106
107
  "@types/react": "^19.2.17",
package/themes/cyber.ts CHANGED
@@ -318,6 +318,12 @@ export const components: Theme = {
318
318
  wrapperClassName: '',
319
319
  className: '',
320
320
  },
321
+ AddressAutocomplete: {
322
+ wrapperClassName: '',
323
+ className: '',
324
+ menuClassName: 'rounded-none border-border/70 bg-popover/95 shadow-[0_0_0_1px_hsl(var(--border)/0.55),0_12px_32px_rgba(0,0,0,0.35)] supports-[backdrop-filter]:bg-popover/92',
325
+ itemClassName: '',
326
+ },
321
327
  Form: {
322
328
  wrapperClassName: '',
323
329
  buttonSaveClass: 'btn-outline-primary',
package/themes/default.ts CHANGED
@@ -332,6 +332,12 @@ export const components: Theme = {
332
332
  wrapperClassName: '',
333
333
  className: '',
334
334
  },
335
+ AddressAutocomplete: {
336
+ wrapperClassName: '',
337
+ className: '',
338
+ menuClassName: 'border-border/60 bg-popover/95 shadow-xl shadow-black/5 backdrop-blur supports-[backdrop-filter]:bg-popover/90 dark:shadow-black/20',
339
+ itemClassName: '',
340
+ },
335
341
  Form: {
336
342
  wrapperClassName: '',
337
343
  buttonSaveClass: 'btn-outline-primary',
package/themes/flat.ts CHANGED
@@ -318,6 +318,12 @@ export const components: Theme = {
318
318
  wrapperClassName: '',
319
319
  className: '',
320
320
  },
321
+ AddressAutocomplete: {
322
+ wrapperClassName: '',
323
+ className: '',
324
+ menuClassName: 'border-border/60 bg-popover/95 shadow-lg shadow-black/5 supports-[backdrop-filter]:bg-popover/92',
325
+ itemClassName: '',
326
+ },
321
327
  Form: {
322
328
  wrapperClassName: '',
323
329
  buttonSaveClass: 'btn-outline-primary',