@warkypublic/svelix 0.2.1 → 0.2.3

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 (29) hide show
  1. package/README.md +10 -2
  2. package/dist/components/Boxer/Boxer.svelte +11 -0
  3. package/dist/components/Boxer/types.d.ts +2 -0
  4. package/dist/components/ContentEditor/index.d.ts +12 -0
  5. package/dist/components/ContentEditor/index.js +12 -0
  6. package/dist/components/FormerControllers/CheckBoxCtrl.svelte +77 -0
  7. package/dist/components/FormerControllers/CheckBoxCtrl.svelte.d.ts +11 -0
  8. package/dist/components/FormerControllers/FileInputCtrl.svelte +106 -0
  9. package/dist/components/FormerControllers/FileInputCtrl.svelte.d.ts +9 -0
  10. package/dist/components/FormerControllers/InlineWrapper.svelte +2 -2
  11. package/dist/components/FormerControllers/JsonCtrl/JsonCtrl.svelte +61 -0
  12. package/dist/components/FormerControllers/JsonCtrl/JsonCtrl.svelte.d.ts +8 -0
  13. package/dist/components/FormerControllers/JsonCtrl/JsonCtrlEditorDialog.svelte +72 -0
  14. package/dist/components/FormerControllers/JsonCtrl/JsonCtrlEditorDialog.svelte.d.ts +9 -0
  15. package/dist/components/FormerControllers/JsonCtrl/JsonCtrlMonaco.svelte +45 -0
  16. package/dist/components/FormerControllers/JsonCtrl/JsonCtrlMonaco.svelte.d.ts +8 -0
  17. package/dist/components/FormerControllers/JsonCtrl/index.d.ts +1 -0
  18. package/dist/components/FormerControllers/JsonCtrl/index.js +1 -0
  19. package/dist/components/FormerControllers/index.d.ts +3 -0
  20. package/dist/components/FormerControllers/index.js +3 -0
  21. package/dist/components/Icons/IconEye.svelte +6 -0
  22. package/dist/components/Icons/IconEye.svelte.d.ts +7 -0
  23. package/dist/components/Icons/index.d.ts +1 -0
  24. package/dist/components/Icons/index.js +1 -0
  25. package/dist/components/Icons/strings.d.ts +1 -0
  26. package/dist/components/Icons/strings.js +1 -0
  27. package/llm/FORMER_CONTROLLERS.md +190 -0
  28. package/llm/README.md +4 -1
  29. package/package.json +3 -1
package/README.md CHANGED
@@ -32,8 +32,11 @@ import { Button, Former, Gridler } from '@warkypublic/svelix';
32
32
  | `ButtonCtrl`, `IconButtonCtrl` | Button and icon-button form controls |
33
33
  | `TextInputCtrl`, `TextAreaCtrl`, `PasswordInputCtrl` | Text input controls |
34
34
  | `NumberInputCtrl`, `NativeSelectCtrl` | Number and select controls |
35
- | `SwitchCtrl`, `DateTimeCtrl` | Toggle and date/time picker controls |
36
- | `InlineWrapper` | Inline layout wrapper for form controls |
35
+ | `CheckBoxCtrl`, `SwitchCtrl` | Checkbox and toggle controls; `CheckBoxCtrl` supports checkbox or switch presentation and boolean, integer, or string values |
36
+ | `FileInputCtrl` | Single-file picker with a compact file-type or image thumbnail, filename, and size |
37
+ | `JsonCtrl` | JSON editor control with a dialog editor and preview |
38
+ | `DateTimeCtrl` | Date/time picker control |
39
+ | `CtrlFieldShell`, `InlineWrapper` | Field shell and inline layout wrapper for form controls |
37
40
 
38
41
  ### Data Grids
39
42
  | Export | Description |
@@ -59,6 +62,10 @@ import { Button, Former, Gridler } from '@warkypublic/svelix';
59
62
  | Export | Description |
60
63
  |---|---|
61
64
  | `ContentEditor` | Rich content editor (Carta Markdown + Monaco) |
65
+ | `TextEditor`, `MarkdownViewer`, `MonacoEditor` | Text, Markdown, and code editing components |
66
+ | `CollaboraEditor`, `Office365Editor` | Office document editor integrations |
67
+ | `ImageViewer`, `AudioPlayer`, `VideoPlayer`, `PdfViewer` | File media and PDF viewers |
68
+ | `EmailViewer`, `ZipViewer`, `UnknownFile` | Fallback file viewers |
62
69
 
63
70
  ### Trees
64
71
  | Export | Description |
@@ -98,6 +105,7 @@ Extended architecture and implementation docs are published with the package und
98
105
  node_modules/@warkypublic/svelix/llm/README.md
99
106
  node_modules/@warkypublic/svelix/llm/ARCHITECTURE.md
100
107
  node_modules/@warkypublic/svelix/llm/COMPONENT_GUIDE.md
108
+ node_modules/@warkypublic/svelix/llm/FORMER_CONTROLLERS.md
101
109
  ```
102
110
 
103
111
  ## Development
@@ -405,6 +405,9 @@ function onOptionPointerDown(e, index) {
405
405
  function syncInsideDialogFlag() {
406
406
  insideDialog = !!anchorEl?.closest("dialog, [role=\"dialog\"]");
407
407
  }
408
+ function isSvgIcon(value) {
409
+ return value?.trimStart?.().startsWith?.("<svg");
410
+ }
408
411
  // Public API via bind:this
409
412
  export function clear() {
410
413
  onClear();
@@ -625,6 +628,14 @@ $effect(() => {
625
628
  type="checkbox"
626
629
  />
627
630
  {/if}
631
+ {#if item?.icon}
632
+ {#if isSvgIcon(item.icon)}
633
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
634
+ <span class="size-4 flex items-center shrink-0">{@html item.icon}</span>
635
+ {:else}
636
+ <span class="shrink-0">{item.icon}</span>
637
+ {/if}
638
+ {/if}
628
639
  <span>{item?.label}</span>
629
640
  </div>
630
641
  {/each}
@@ -42,6 +42,8 @@ export type BoxerItem = {
42
42
  [key: string]: any;
43
43
  label: string;
44
44
  value: any;
45
+ /** Optional icon rendered before the label in the dropdown list. Accepts an SVG string (rendered raw) or plain text/emoji. */
46
+ icon?: string;
45
47
  };
46
48
  export interface BoxerProps {
47
49
  /** Server adapter (ResolveSpec or HeaderSpec). Automatically sets `dataSource` to `'server'` when provided. */
@@ -1,2 +1,14 @@
1
1
  export { default as ContentEditor } from './ContentEditor.svelte';
2
+ export { default as AudioPlayer } from './subcomponents/AudioPlayer.svelte';
3
+ export { default as CollaboraEditor } from './subcomponents/CollaboraEditor.svelte';
4
+ export { default as EmailViewer } from './subcomponents/EmailViewer.svelte';
5
+ export { default as ImageViewer } from './subcomponents/ImageViewer.svelte';
6
+ export { default as MarkdownViewer } from './subcomponents/MarkdownViewer.svelte';
7
+ export { default as MonacoEditor } from './subcomponents/MonacoEditor.svelte';
8
+ export { default as Office365Editor } from './subcomponents/Office365Editor.svelte';
9
+ export { default as PdfViewer } from './subcomponents/PdfViewer.svelte';
10
+ export { default as TextEditor } from './subcomponents/TextEditor.svelte';
11
+ export { default as UnknownFile } from './subcomponents/UnknownFile.svelte';
12
+ export { default as VideoPlayer } from './subcomponents/VideoPlayer.svelte';
13
+ export { default as ZipViewer } from './subcomponents/ZipViewer.svelte';
2
14
  export * from './types.js';
@@ -1,2 +1,14 @@
1
1
  export { default as ContentEditor } from './ContentEditor.svelte';
2
+ export { default as AudioPlayer } from './subcomponents/AudioPlayer.svelte';
3
+ export { default as CollaboraEditor } from './subcomponents/CollaboraEditor.svelte';
4
+ export { default as EmailViewer } from './subcomponents/EmailViewer.svelte';
5
+ export { default as ImageViewer } from './subcomponents/ImageViewer.svelte';
6
+ export { default as MarkdownViewer } from './subcomponents/MarkdownViewer.svelte';
7
+ export { default as MonacoEditor } from './subcomponents/MonacoEditor.svelte';
8
+ export { default as Office365Editor } from './subcomponents/Office365Editor.svelte';
9
+ export { default as PdfViewer } from './subcomponents/PdfViewer.svelte';
10
+ export { default as TextEditor } from './subcomponents/TextEditor.svelte';
11
+ export { default as UnknownFile } from './subcomponents/UnknownFile.svelte';
12
+ export { default as VideoPlayer } from './subcomponents/VideoPlayer.svelte';
13
+ export { default as ZipViewer } from './subcomponents/ZipViewer.svelte';
2
14
  export * from './types.js';
@@ -0,0 +1,77 @@
1
+ <script lang="ts">import { makeFieldId } from "./utils";
2
+ let { checked = $bindable(false), dataFormat = "boolean", disabled, error, label, name, onchange, required, sid, tooltip, type = "checkbox" } = $props();
3
+ const id = $derived(makeFieldId(name, sid));
4
+ const errorId = $derived(`${id}-error`);
5
+ const inputChecked = $derived(toBoolean(checked, dataFormat));
6
+ function toBoolean(value, format) {
7
+ if (format === "integer") return value === 1;
8
+ if (format === "string") {
9
+ return typeof value === "string" && [
10
+ "1",
11
+ "true",
12
+ "yes"
13
+ ].includes(value.trim().toLowerCase());
14
+ }
15
+ return value === true;
16
+ }
17
+ function formatValue(value) {
18
+ if (dataFormat === "integer") return value ? 1 : 0;
19
+ if (dataFormat === "string") return value ? "true" : "false";
20
+ return value;
21
+ }
22
+ function handleChange(event) {
23
+ checked = formatValue(event.currentTarget.checked);
24
+ onchange?.(checked);
25
+ }
26
+ </script>
27
+
28
+ <div class="flex flex-col gap-1" title={tooltip}>
29
+ {#if type === 'switch'}
30
+ <label class="flex w-fit cursor-pointer items-center gap-2" for={id}>
31
+ <input
32
+ {id}
33
+ {disabled}
34
+ {name}
35
+ {required}
36
+ aria-describedby={error ? errorId : undefined}
37
+ aria-invalid={!!error}
38
+ aria-checked={inputChecked}
39
+ checked={inputChecked}
40
+ class="peer sr-only"
41
+ role="switch"
42
+ type="checkbox"
43
+ onchange={handleChange}
44
+ />
45
+ <span
46
+ aria-hidden="true"
47
+ class="flex h-6 w-10 items-center rounded-full bg-surface-300-700 p-0.5 transition-colors peer-checked:[&>span]:translate-x-4 peer-checked:bg-primary-500 peer-disabled:cursor-not-allowed peer-disabled:opacity-50 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-primary-500"
48
+ >
49
+ <span class="size-5 rounded-full bg-surface-50 transition-transform"></span>
50
+ </span>
51
+ {#if label}
52
+ <span class="label text-sm">{label}{#if required}<span class="text-error-500">*</span>{/if}</span>
53
+ {/if}
54
+ </label>
55
+ {:else}
56
+ <div class="flex items-center gap-2">
57
+ <input
58
+ {id}
59
+ {disabled}
60
+ {name}
61
+ {required}
62
+ aria-describedby={error ? errorId : undefined}
63
+ aria-invalid={!!error}
64
+ checked={inputChecked}
65
+ class="checkbox"
66
+ type="checkbox"
67
+ onchange={handleChange}
68
+ />
69
+ {#if label}
70
+ <label class="label text-sm" for={id}>{label}{#if required}<span class="text-error-500">*</span>{/if}</label>
71
+ {/if}
72
+ </div>
73
+ {/if}
74
+ {#if error}
75
+ <p class="text-error-500 text-xs" id={errorId}>{error}</p>
76
+ {/if}
77
+ </div>
@@ -0,0 +1,11 @@
1
+ import type { CtrlBaseProps } from './types';
2
+ export type CheckBoxCtrlValue = boolean | 0 | 1 | string | null;
3
+ export interface Props extends Omit<CtrlBaseProps, 'placeholder'> {
4
+ checked?: CheckBoxCtrlValue;
5
+ dataFormat?: 'boolean' | 'integer' | 'string';
6
+ onchange?: (checked: CheckBoxCtrlValue) => void;
7
+ type?: 'checkbox' | 'switch';
8
+ }
9
+ declare const CheckBoxCtrl: import("svelte").Component<Props, {}, "checked">;
10
+ type CheckBoxCtrl = ReturnType<typeof CheckBoxCtrl>;
11
+ export default CheckBoxCtrl;
@@ -0,0 +1,106 @@
1
+ <script lang="ts">import CtrlFieldShell from "./CtrlFieldShell.svelte";
2
+ import { makeFieldId } from "./utils";
3
+ let { accept, disabled, error, label, name, onchange, required, sid, tooltip, value = $bindable(null) } = $props();
4
+ let imageUrl = $state(null);
5
+ const id = $derived(makeFieldId(name, sid));
6
+ const isImage = $derived(value?.type.startsWith("image/") ?? false);
7
+ const fileType = $derived(getFileType(value));
8
+ $effect(() => {
9
+ if (!value || !isImage) {
10
+ imageUrl = null;
11
+ return;
12
+ }
13
+ const url = URL.createObjectURL(value);
14
+ imageUrl = url;
15
+ return () => URL.revokeObjectURL(url);
16
+ });
17
+ function handleChange(event) {
18
+ value = event.currentTarget.files?.[0] ?? null;
19
+ onchange?.(value);
20
+ }
21
+ function getFileType(file) {
22
+ if (!file) return "FILE";
23
+ if (file.type.startsWith("image/")) return "IMG";
24
+ if (file.type.startsWith("video/")) return "VID";
25
+ if (file.type.startsWith("audio/")) return "AUD";
26
+ if (file.type === "application/pdf") return "PDF";
27
+ const extension = file.name.split(".").pop()?.toLowerCase();
28
+ if ([
29
+ "doc",
30
+ "docx",
31
+ "odt",
32
+ "rtf"
33
+ ].includes(extension ?? "")) return "DOC";
34
+ if ([
35
+ "xls",
36
+ "xlsx",
37
+ "ods",
38
+ "csv"
39
+ ].includes(extension ?? "")) return "SHEET";
40
+ if ([
41
+ "ppt",
42
+ "pptx",
43
+ "odp"
44
+ ].includes(extension ?? "")) return "SLIDE";
45
+ if ([
46
+ "zip",
47
+ "rar",
48
+ "7z",
49
+ "tar",
50
+ "gz"
51
+ ].includes(extension ?? "")) return "ZIP";
52
+ if ([
53
+ "txt",
54
+ "md",
55
+ "json",
56
+ "xml",
57
+ "yaml",
58
+ "yml",
59
+ "js",
60
+ "ts",
61
+ "css",
62
+ "html"
63
+ ].includes(extension ?? "")) {
64
+ return "TEXT";
65
+ }
66
+ return extension?.slice(0, 5).toUpperCase() || "FILE";
67
+ }
68
+ function formatSize(bytes) {
69
+ if (bytes < 1024) return `${bytes} B`;
70
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
71
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
72
+ }
73
+ </script>
74
+
75
+ <CtrlFieldShell {error} {id} {label} {required} {tooltip}>
76
+ <div class="flex min-w-0 items-center gap-3 rounded-container-token border border-surface-200-800 bg-surface-50-900 p-2">
77
+ <input {accept} {disabled} {id} {name} {required} class="sr-only" type="file" onchange={handleChange} />
78
+ {#if imageUrl && value}
79
+ <img alt={`Preview of ${value.name}`} class="size-9 shrink-0 rounded object-cover" src={imageUrl} />
80
+ {:else}
81
+ <div
82
+ aria-label={`${fileType} file preview`}
83
+ class="flex size-9 shrink-0 items-center justify-center rounded bg-primary-100-900 text-center text-[10px] font-bold tracking-wide text-primary-700-300"
84
+ role="img"
85
+ >
86
+ {fileType}
87
+ </div>
88
+ {/if}
89
+ <div class="min-w-0 flex-1">
90
+ {#if value}
91
+ <p class="truncate text-sm font-medium" title={value.name}>{value.name}</p>
92
+ <p class="text-xs text-surface-500-400">{formatSize(value.size)}</p>
93
+ {:else}
94
+ <p class="truncate text-sm text-surface-500-400">No file selected</p>
95
+ {/if}
96
+ </div>
97
+ <label
98
+ class="btn preset-outlined-primary-500 shrink-0"
99
+ class:cursor-not-allowed={disabled}
100
+ class:opacity-50={disabled}
101
+ for={id}
102
+ >
103
+ {value ? 'Replace file' : 'Choose file'}
104
+ </label>
105
+ </div>
106
+ </CtrlFieldShell>
@@ -0,0 +1,9 @@
1
+ import type { CtrlBaseProps } from './types';
2
+ export interface Props extends Omit<CtrlBaseProps, 'placeholder'> {
3
+ accept?: string;
4
+ onchange?: (value: File | null) => void;
5
+ value?: File | null;
6
+ }
7
+ declare const FileInputCtrl: import("svelte").Component<Props, {}, "value">;
8
+ type FileInputCtrl = ReturnType<typeof FileInputCtrl>;
9
+ export default FileInputCtrl;
@@ -16,13 +16,13 @@ const showWarningIcon = $derived(!error && !!required && isEmpty);
16
16
  <div class="flex flex-nowrap gap-0">
17
17
  {#if labelWidth && (label || promptArea)}
18
18
  <div
19
- class="flex items-center px-3 border border-surface-300 dark:border-surface-600 rounded-l bg-surface-100 dark:bg-surface-800"
19
+ class="flex items-center px-3 bg-primary-100-900 text-primary-950-50 select-none"
20
20
  style:min-width={labelWidth}
21
21
  >
22
22
  {#if promptArea}
23
23
  {@render promptArea()}
24
24
  {:else if typeof label === 'string'}
25
- <span class="inline-flex items-center gap-1.5 text-xs font-medium text-surface-700 dark:text-surface-300">
25
+ <span class="inline-flex items-center gap-1.5 text-xs font-medium">
26
26
  {label}{#if required && isEmpty}<span class="text-error-500">*</span>{/if}
27
27
  {#if showErrorIcon}
28
28
  <IconAlertCircle size={14} class="text-error-500" />
@@ -0,0 +1,61 @@
1
+ <script lang="ts">import CtrlFieldShell from "../CtrlFieldShell.svelte";
2
+ import { makeFieldId } from "../utils";
3
+ import { iconEyeSvg } from "../../Icons/strings";
4
+ import JsonCtrlEditorDialog from "./JsonCtrlEditorDialog.svelte";
5
+ let { disabled, error, label, name, onchange, required, sid, tooltip, value = $bindable(undefined) } = $props();
6
+ const id = $derived(makeFieldId(name, sid));
7
+ let dialogOpen = $state(false);
8
+ const preview = $derived.by(() => {
9
+ if (value === undefined) return {
10
+ text: "Empty",
11
+ empty: true
12
+ };
13
+ try {
14
+ const text = JSON.stringify(value);
15
+ return {
16
+ text: text.length > 80 ? `${text.slice(0, 80)}…` : text,
17
+ empty: false
18
+ };
19
+ } catch {
20
+ return {
21
+ text: "(unserializable)",
22
+ empty: true
23
+ };
24
+ }
25
+ });
26
+ function openDialog() {
27
+ if (disabled) return;
28
+ dialogOpen = true;
29
+ }
30
+ function handleSave(parsed) {
31
+ value = parsed;
32
+ onchange?.(parsed);
33
+ }
34
+ </script>
35
+
36
+ <CtrlFieldShell {error} {id} {label} {required} {tooltip}>
37
+ <button
38
+ {id}
39
+ {disabled}
40
+ class="input flex items-center justify-between gap-2 text-left"
41
+ class:input-error={!!error}
42
+ type="button"
43
+ onclick={openDialog}
44
+ >
45
+ <code
46
+ class="truncate text-xs font-mono"
47
+ class:text-surface-500={preview.empty}
48
+ >{preview.text}</code>
49
+ <span class="shrink-0 flex items-center" aria-hidden="true">
50
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
51
+ {@html iconEyeSvg}
52
+ </span>
53
+ </button>
54
+ </CtrlFieldShell>
55
+
56
+ <JsonCtrlEditorDialog
57
+ bind:open={dialogOpen}
58
+ {label}
59
+ {value}
60
+ onsave={handleSave}
61
+ />
@@ -0,0 +1,8 @@
1
+ import type { CtrlBaseProps } from '../types';
2
+ export interface Props extends CtrlBaseProps {
3
+ onchange?: (value: unknown) => void;
4
+ value?: unknown;
5
+ }
6
+ declare const JsonCtrl: import("svelte").Component<Props, {}, "value">;
7
+ type JsonCtrl = ReturnType<typeof JsonCtrl>;
8
+ export default JsonCtrl;
@@ -0,0 +1,72 @@
1
+ <script lang="ts">import { Dialog, Portal } from "@skeletonlabs/skeleton-svelte";
2
+ import { createOverlayZIndex } from "../../Former/formerOverlayHelpers.svelte";
3
+ import JsonCtrlMonaco from "./JsonCtrlMonaco.svelte";
4
+ let { label, open = $bindable(false), value, onsave } = $props();
5
+ let draftText = $state("");
6
+ let parseError = $state(undefined);
7
+ let wasOpen = false;
8
+ $effect(() => {
9
+ if (open && !wasOpen) {
10
+ draftText = JSON.stringify(value ?? null, null, 2);
11
+ parseError = undefined;
12
+ }
13
+ wasOpen = open;
14
+ });
15
+ const overlayZ = createOverlayZIndex("dialog", () => open);
16
+ function handleSave() {
17
+ try {
18
+ const parsed = draftText.trim() === "" ? null : JSON.parse(draftText);
19
+ parseError = undefined;
20
+ onsave?.(parsed);
21
+ open = false;
22
+ } catch (err) {
23
+ parseError = err instanceof Error ? err.message : "Invalid JSON";
24
+ }
25
+ }
26
+ function handleCancel() {
27
+ open = false;
28
+ }
29
+ </script>
30
+
31
+ <Dialog
32
+ open={open}
33
+ onOpenChange={(e) => {
34
+ open = e.open;
35
+ }}
36
+ closeOnInteractOutside={false}
37
+ modal
38
+ >
39
+ <Portal>
40
+ <Dialog.Backdrop
41
+ class="fixed inset-0 bg-black/50 backdrop-blur-sm"
42
+ style={`z-index:${overlayZ.backdropZ};`}
43
+ />
44
+ <Dialog.Positioner class="fixed inset-0 flex" style={`z-index:${overlayZ.contentZ};`}>
45
+ <Dialog.Content class="bg-surface-100-900 shadow-2xl w-full h-full flex flex-col overflow-hidden">
46
+ <header class="flex items-center justify-between px-6 pt-5 pb-4 border-b border-surface-300-600 shrink-0">
47
+ <Dialog.Title class="text-lg font-semibold">{label ? `Edit ${label}` : 'Edit JSON'}</Dialog.Title>
48
+ <Dialog.CloseTrigger class="btn-icon btn-icon-sm preset-tonal" aria-label="Close dialog" onclick={handleCancel}>
49
+
50
+ </Dialog.CloseTrigger>
51
+ </header>
52
+
53
+ <div class="flex-1 min-h-0 overflow-hidden p-4">
54
+ <JsonCtrlMonaco value={draftText} onchange={(text) => (draftText = text)} height="100%" />
55
+ </div>
56
+
57
+ {#if parseError}
58
+ <p class="text-error-500 text-xs px-6 pb-2" role="alert">{parseError}</p>
59
+ {/if}
60
+
61
+ <div class="flex items-center justify-end gap-2 p-4 border-t border-surface-300-600 shrink-0">
62
+ <button class="btn preset-filled-secondary-500" type="button" onclick={handleCancel}>
63
+ ✕ Cancel
64
+ </button>
65
+ <button class="btn preset-filled-primary-500" type="button" onclick={handleSave}>
66
+ 💾 Save
67
+ </button>
68
+ </div>
69
+ </Dialog.Content>
70
+ </Dialog.Positioner>
71
+ </Portal>
72
+ </Dialog>
@@ -0,0 +1,9 @@
1
+ export interface Props {
2
+ label?: string;
3
+ open?: boolean;
4
+ value?: unknown;
5
+ onsave?: (value: unknown) => void;
6
+ }
7
+ declare const JsonCtrlEditorDialog: import("svelte").Component<Props, {}, "open">;
8
+ type JsonCtrlEditorDialog = ReturnType<typeof JsonCtrlEditorDialog>;
9
+ export default JsonCtrlEditorDialog;
@@ -0,0 +1,45 @@
1
+ <script lang="ts">import { onMount } from "svelte";
2
+ import * as monaco from "monaco-editor";
3
+ import { watchSkeletonDarkMode } from "../../ContentEditor/utils";
4
+ let { value, height = "320px", onchange } = $props();
5
+ let container = $state(undefined);
6
+ let editor;
7
+ let changeGuard = false;
8
+ let isDark = $state(false);
9
+ onMount(() => {
10
+ const stopWatchingTheme = watchSkeletonDarkMode(container, (nextIsDark) => {
11
+ isDark = nextIsDark;
12
+ });
13
+ if (!container) return;
14
+ editor = monaco.editor.create(container, {
15
+ value,
16
+ language: "json",
17
+ theme: isDark ? "vs-dark" : "vs",
18
+ automaticLayout: true,
19
+ minimap: { enabled: false }
20
+ });
21
+ const createdEditor = editor;
22
+ createdEditor.onDidChangeModelContent(() => {
23
+ if (changeGuard) return;
24
+ onchange?.(createdEditor.getValue());
25
+ });
26
+ return () => {
27
+ stopWatchingTheme();
28
+ editor?.dispose();
29
+ editor = undefined;
30
+ };
31
+ });
32
+ // Sync external value changes (e.g. dialog re-opened with different data) into the editor.
33
+ $effect(() => {
34
+ const next = value;
35
+ if (!editor || editor.getValue() === next) return;
36
+ changeGuard = true;
37
+ editor.setValue(next);
38
+ changeGuard = false;
39
+ });
40
+ $effect(() => {
41
+ monaco.editor.setTheme(isDark ? "vs-dark" : "vs");
42
+ });
43
+ </script>
44
+
45
+ <div bind:this={container} style="width: 100%; height: {height};"></div>
@@ -0,0 +1,8 @@
1
+ export interface Props {
2
+ value: string;
3
+ height?: string;
4
+ onchange?: (text: string) => void;
5
+ }
6
+ declare const JsonCtrlMonaco: import("svelte").Component<Props, {}, "">;
7
+ type JsonCtrlMonaco = ReturnType<typeof JsonCtrlMonaco>;
8
+ export default JsonCtrlMonaco;
@@ -0,0 +1 @@
1
+ export { default as JsonCtrl } from './JsonCtrl.svelte';
@@ -0,0 +1 @@
1
+ export { default as JsonCtrl } from './JsonCtrl.svelte';
@@ -1,8 +1,11 @@
1
1
  export { default as ButtonCtrl } from './ButtonCtrl.svelte';
2
+ export { default as CheckBoxCtrl } from './CheckBoxCtrl.svelte';
2
3
  export { default as CtrlFieldShell } from './CtrlFieldShell.svelte';
3
4
  export { DateTimeCtrl } from './DateTimeCtrl/index';
5
+ export { default as FileInputCtrl } from './FileInputCtrl.svelte';
4
6
  export { default as IconButtonCtrl } from './IconButtonCtrl.svelte';
5
7
  export { default as InlineWrapper } from './InlineWrapper.svelte';
8
+ export { JsonCtrl } from './JsonCtrl/index';
6
9
  export { default as NativeSelectCtrl } from './NativeSelectCtrl.svelte';
7
10
  export { default as NumberInputCtrl } from './NumberInputCtrl.svelte';
8
11
  export { default as PasswordInputCtrl } from './PasswordInputCtrl.svelte';
@@ -1,8 +1,11 @@
1
1
  export { default as ButtonCtrl } from './ButtonCtrl.svelte';
2
+ export { default as CheckBoxCtrl } from './CheckBoxCtrl.svelte';
2
3
  export { default as CtrlFieldShell } from './CtrlFieldShell.svelte';
3
4
  export { DateTimeCtrl } from './DateTimeCtrl/index';
5
+ export { default as FileInputCtrl } from './FileInputCtrl.svelte';
4
6
  export { default as IconButtonCtrl } from './IconButtonCtrl.svelte';
5
7
  export { default as InlineWrapper } from './InlineWrapper.svelte';
8
+ export { JsonCtrl } from './JsonCtrl/index';
6
9
  export { default as NativeSelectCtrl } from './NativeSelectCtrl.svelte';
7
10
  export { default as NumberInputCtrl } from './NumberInputCtrl.svelte';
8
11
  export { default as PasswordInputCtrl } from './PasswordInputCtrl.svelte';
@@ -0,0 +1,6 @@
1
+ <script lang="ts">const { size = 16, class: cls = "" } = $props();
2
+ </script>
3
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={cls} aria-hidden="true">
4
+ <path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/>
5
+ <circle cx="12" cy="12" r="3"/>
6
+ </svg>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ size?: number;
3
+ class?: string;
4
+ }
5
+ declare const IconEye: import("svelte").Component<Props, {}, "">;
6
+ type IconEye = ReturnType<typeof IconEye>;
7
+ export default IconEye;
@@ -4,6 +4,7 @@ export { default as IconAlertTriangle } from './IconAlertTriangle.svelte';
4
4
  export { default as IconCamera } from './IconCamera.svelte';
5
5
  export { default as IconClose } from './IconClose.svelte';
6
6
  export { default as IconEdit } from './IconEdit.svelte';
7
+ export { default as IconEye } from './IconEye.svelte';
7
8
  export { default as IconFilter } from './IconFilter.svelte';
8
9
  export { default as IconSearch } from './IconSearch.svelte';
9
10
  export { default as IconSort } from './IconSort.svelte';
@@ -4,6 +4,7 @@ export { default as IconAlertTriangle } from './IconAlertTriangle.svelte';
4
4
  export { default as IconCamera } from './IconCamera.svelte';
5
5
  export { default as IconClose } from './IconClose.svelte';
6
6
  export { default as IconEdit } from './IconEdit.svelte';
7
+ export { default as IconEye } from './IconEye.svelte';
7
8
  export { default as IconFilter } from './IconFilter.svelte';
8
9
  export { default as IconSearch } from './IconSearch.svelte';
9
10
  export { default as IconSort } from './IconSort.svelte';
@@ -9,3 +9,4 @@ export declare const iconFilterSvg = "<svg width=\"16\" height=\"16\" viewBox=\"
9
9
  export declare const iconCameraSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z\"/><circle cx=\"12\" cy=\"13\" r=\"3\"/></svg>";
10
10
  export declare const iconSortAscSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"M7 14l5-5 5 5H7z\"/></svg>";
11
11
  export declare const iconSortDescSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"M7 10l5 5 5-5H7z\"/></svg>";
12
+ export declare const iconEyeSvg = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex-shrink:0\" aria-hidden=\"true\"><path d=\"M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>";
@@ -13,3 +13,4 @@ export const iconFilterSvg = `<svg ${F}><path d="M3 5a1 1 0 0 1 1-1h16a1 1 0 0 1
13
13
  export const iconCameraSvg = `<svg ${S}><path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/></svg>`;
14
14
  export const iconSortAscSvg = `<svg ${F}><path d="M7 14l5-5 5 5H7z"/></svg>`;
15
15
  export const iconSortDescSvg = `<svg ${F}><path d="M7 10l5 5 5-5H7z"/></svg>`;
16
+ export const iconEyeSvg = `<svg ${S}><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>`;
@@ -0,0 +1,190 @@
1
+ # Svelix Components Quick Reference
2
+
3
+ This reference lists every Svelte component exported from the package root. Import components
4
+ from `@warkypublic/svelix`; adapters, stores, utility functions, and types are documented in
5
+ [ARCHITECTURE.md](./ARCHITECTURE.md) and [COMPONENT_GUIDE.md](./COMPONENT_GUIDE.md).
6
+
7
+ ## Import Pattern
8
+
9
+ ```svelte
10
+ <script lang="ts">
11
+ import {
12
+ Button,
13
+ CheckBoxCtrl,
14
+ FileInputCtrl,
15
+ Former,
16
+ JsonCtrl,
17
+ TextInputCtrl,
18
+ VTree
19
+ } from '@warkypublic/svelix';
20
+ </script>
21
+ ```
22
+
23
+ ## Form Controllers
24
+
25
+ Most field controllers accept `label`, `name`, `disabled`, `error`, `required`, `sid`, and
26
+ `tooltip`. Use `bind:value` or `bind:checked` when the component supports it. Use `onchange`
27
+ when form state needs to be updated manually.
28
+
29
+ | Component | Use |
30
+ |---|---|
31
+ | `TextInputCtrl` | Single-line text input; set `type` for email, URL, and other native input types. |
32
+ | `TextAreaCtrl` | Multi-line text input; set `rows` as needed. |
33
+ | `PasswordInputCtrl` | Password input with a visibility toggle. |
34
+ | `NumberInputCtrl` | Numeric input with `min`, `max`, and `step`. |
35
+ | `NativeSelectCtrl` | Native select; `options` accepts strings or `{ label, value }` objects. |
36
+ | `DateTimeCtrl` | Date, time, or datetime picker; configure `mode`, constraints, and formats. |
37
+ | `CheckBoxCtrl` | Checkbox or switch with boolean, integer, or string serialization. |
38
+ | `SwitchCtrl` | Basic boolean checkbox-style toggle. |
39
+ | `FileInputCtrl` | Single-file picker with file-type/image preview, name, and size. |
40
+ | `JsonCtrl` | JSON value preview with a dialog editor. |
41
+ | `ButtonCtrl` | Form action button. |
42
+ | `IconButtonCtrl` | Compact icon action button. |
43
+ | `CtrlFieldShell` | Label, required marker, tooltip, and error wrapper for custom controls. |
44
+ | `InlineWrapper` | Places a field with optional left and right content in an inline row. |
45
+
46
+ ## CheckBoxCtrl
47
+
48
+ `CheckBoxCtrl` renders a checkbox by default. Set `type="switch"` for a switch presentation.
49
+ The bound `checked` value uses `dataFormat="boolean"` by default.
50
+
51
+ ```svelte
52
+ <script lang="ts">
53
+ let enabled = $state(false);
54
+ let published = $state<0 | 1 | null>(null);
55
+ let verified = $state<string | null>(null);
56
+ </script>
57
+
58
+ <CheckBoxCtrl bind:checked={enabled} label="Enable notifications" name="notifications" />
59
+
60
+ <CheckBoxCtrl
61
+ bind:checked={published}
62
+ dataFormat="integer"
63
+ label="Published"
64
+ name="published"
65
+ type="switch"
66
+ />
67
+
68
+ <CheckBoxCtrl
69
+ bind:checked={verified}
70
+ dataFormat="string"
71
+ label="Verified"
72
+ name="verified"
73
+ />
74
+ ```
75
+
76
+ Formats:
77
+
78
+ - `boolean`: reads and writes `true` / `false`.
79
+ - `integer`: reads `1`, `0`, or `null`; writes `1` / `0` after user interaction.
80
+ - `string`: reads `1`, `true`, `yes`, `0`, `false`, `no`, or `null` case-insensitively; writes
81
+ `"true"` / `"false"` after user interaction.
82
+
83
+ ## FileInputCtrl
84
+
85
+ `FileInputCtrl` manages one `File`. Its compact row shows an image thumbnail or file-type badge,
86
+ the filename and size, and a choose/replace button.
87
+
88
+ ```svelte
89
+ <script lang="ts">
90
+ let attachment = $state<File | null>(null);
91
+ </script>
92
+
93
+ <FileInputCtrl
94
+ bind:value={attachment}
95
+ accept="image/*,application/pdf"
96
+ label="Attachment"
97
+ name="attachment"
98
+ required
99
+ />
100
+ ```
101
+
102
+ Use `onchange={(file) => { ... }}` when the selected file must be copied into external form
103
+ state. `value` is `File | null`.
104
+
105
+ ## JsonCtrl
106
+
107
+ `JsonCtrl` displays a compact JSON preview and opens a dialog editor. Bind arbitrary JSON-safe
108
+ data through `value`.
109
+
110
+ ```svelte
111
+ <script lang="ts">
112
+ let metadata = $state<unknown>({ status: 'draft' });
113
+ </script>
114
+
115
+ <JsonCtrl bind:value={metadata} label="Metadata" name="metadata" />
116
+ ```
117
+
118
+ Use `onchange={(value) => { ... }}` when updating parent form state manually.
119
+
120
+ ## Form Containers
121
+
122
+ | Component | Use |
123
+ |---|---|
124
+ | `Former` | Form state and CRUD lifecycle container. Supply fields through its `children` snippet. |
125
+ | `FormerModal` | `Former` rendered in a modal. |
126
+ | `FormerDrawer` | `Former` rendered in a side drawer. |
127
+ | `FormerButtonArea` | Standard save, delete, cancel, and keep-open action area. |
128
+
129
+ Use `bind:values`, `request`, `onAPICall`, and the `children(state)` snippet with `Former`.
130
+ For state helpers, use `createFormerState`; for data adapters, use `FormerResolveSpecAPI` or
131
+ `FormerRestHeadSpecAPI`.
132
+
133
+ ## Content Editors And Viewers
134
+
135
+ `ContentEditor` selects a viewer/editor from `filename` and `contentType`. Use a subcomponent
136
+ directly only when the file type is already known.
137
+
138
+ | Component | Use |
139
+ |---|---|
140
+ | `ContentEditor` | Routes a file to the appropriate editor or viewer. |
141
+ | `TextEditor` | Plain text editing. |
142
+ | `MarkdownViewer` | Markdown display/editing with Carta. |
143
+ | `MonacoEditor` | Source-code editing with Monaco. |
144
+ | `CollaboraEditor` | Collabora office-document integration. |
145
+ | `Office365Editor` | Office 365 document integration. |
146
+ | `ImageViewer` | Image display. |
147
+ | `AudioPlayer` | Audio playback. |
148
+ | `VideoPlayer` | Video playback. |
149
+ | `PdfViewer` | PDF display. |
150
+ | `EmailViewer` | Email-file fallback viewer. |
151
+ | `ZipViewer` | Archive-file fallback viewer. |
152
+ | `UnknownFile` | Metadata and binary-header fallback viewer. |
153
+
154
+ ## Data And Selection Components
155
+
156
+ | Component | Use |
157
+ |---|---|
158
+ | `Gridler` | Canvas-backed grid for large data sets, selection, editing, sorting, and filtering. |
159
+ | `GridlerFull` | Full grid workflow with paging, searching, menus, and Former integration. |
160
+ | `SvarkGrid` | SVAR grid alternative with column mapping and context menus. |
161
+ | `CardGrid` | Filterable and sortable card collection. |
162
+ | `DefaultCard` | Default card renderer for `CardGrid`. |
163
+ | `CardGridFilterPanel` | Filter controls for `CardGrid`. |
164
+ | `Boxer` | Lookup/select field backed by client or server data. |
165
+ | `BoxerTarget` | Renders a selected Boxer value target. |
166
+ | `VTree` | Virtualized hierarchical tree. |
167
+
168
+ Use the corresponding `*ResolveSpecAdapter` or `*RestHeaderSpecAdapter` only when connecting
169
+ `Gridler`, `SvarkGrid`, or `Boxer` to a supported data source.
170
+
171
+ ## Layout, Feedback, And Utilities
172
+
173
+ | Component | Use |
174
+ |---|---|
175
+ | `Button` | General-purpose base button. |
176
+ | `Portal` | Renders children outside the current DOM subtree. |
177
+ | `ErrorBoundary` | Displays a fallback when child rendering fails. |
178
+ | `BetterMenu` | Enhanced menu/dropdown control. |
179
+ | `MenuRenderer` | Renders BetterMenu items when composing a custom menu. |
180
+ | `GlobalStateStoreProvider` | Provides the global state store to descendants. |
181
+ | `CaptureScreenshotComponent` | UI component for screenshot capture. |
182
+
183
+ Use `createBetterMenuStore`, `useGlobalStateStoreContext`, `CaptureScreen`, and `overlayStack`
184
+ for their related non-component APIs.
185
+
186
+ ## Icons
187
+
188
+ The package exports `IconAdd`, `IconAlertCircle`, `IconAlertTriangle`, `IconCamera`, `IconClose`,
189
+ `IconEdit`, `IconEye`, `IconFilter`, `IconSearch`, `IconSort`, and `IconTrash`. Each is a Svelte
190
+ component that accepts `size` and `class` props.
package/llm/README.md CHANGED
@@ -7,6 +7,7 @@ This folder contains AI-readable documentation that ships with the package.
7
7
  - [ARCHITECTURE.md](./ARCHITECTURE.md)
8
8
  - [SETUP.md](./SETUP.md)
9
9
  - [COMPONENT_GUIDE.md](./COMPONENT_GUIDE.md)
10
+ - [FORMER_CONTROLLERS.md](./FORMER_CONTROLLERS.md) - quick reference for public components and controls
10
11
  - [IMPLEMENTATION_PLAN.md](./IMPLEMENTATION_PLAN.md)
11
12
  - [ROADMAP.md](./ROADMAP.md)
12
13
  - [ESLINT_GUIDE.md](./ESLINT_GUIDE.md)
@@ -118,5 +119,7 @@ When `@warkypublic/svelix` is installed, this documentation is available at:
118
119
  - `node_modules/@warkypublic/svelix/llm/README.md`
119
120
  - `node_modules/@warkypublic/svelix/llm/ARCHITECTURE.md`
120
121
  - `node_modules/@warkypublic/svelix/llm/SETUP.md`
122
+ - `node_modules/@warkypublic/svelix/llm/FORMER_CONTROLLERS.md`
121
123
 
122
- Package subpath access is also exported for `@warkypublic/svelix/llm/*`.
124
+ Package subpath access is exported for `@warkypublic/svelix/llm/*`, including the direct
125
+ `@warkypublic/svelix/llm/components` and `@warkypublic/svelix/llm/former-controllers` references.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warkypublic/svelix",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Svelte 5 component library with Skeleton UI and Tailwind CSS",
5
5
  "license": "Apache-2.0",
6
6
  "exports": {
@@ -9,6 +9,8 @@
9
9
  "svelte": "./dist/index.js"
10
10
  },
11
11
  "./llm": "./llm/README.md",
12
+ "./llm/components": "./llm/FORMER_CONTROLLERS.md",
13
+ "./llm/former-controllers": "./llm/FORMER_CONTROLLERS.md",
12
14
  "./llm/*": "./llm/*",
13
15
  "./css/tailwind-source.css": "./dist/css/tailwind-source.css",
14
16
  "./css/*.css": "./dist/css/*.css",