@lucasvu/scope-ui 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -15
- package/dist/index.cjs +497 -6
- package/dist/index.d.cts +107 -5
- package/dist/index.d.ts +107 -5
- package/dist/index.js +489 -7
- package/dist/styles.css +2 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -435,6 +435,7 @@ var Button = forwardRef(
|
|
|
435
435
|
disabled,
|
|
436
436
|
startIcon,
|
|
437
437
|
endIcon,
|
|
438
|
+
block = false,
|
|
438
439
|
className,
|
|
439
440
|
style,
|
|
440
441
|
children,
|
|
@@ -447,14 +448,14 @@ var Button = forwardRef(
|
|
|
447
448
|
const describedBy = [helperId, errorId].filter(Boolean).join(" ") || void 0;
|
|
448
449
|
const content = children ?? label;
|
|
449
450
|
const isDisabled = disabled || loading;
|
|
450
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", style, children: [
|
|
451
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1", block && "w-full"), style, children: [
|
|
451
452
|
/* @__PURE__ */ jsxs(
|
|
452
453
|
"button",
|
|
453
454
|
{
|
|
454
455
|
ref,
|
|
455
456
|
id: buttonId,
|
|
456
457
|
type: type ?? "button",
|
|
457
|
-
className: cn(buttonVariants({ variant, size, className })),
|
|
458
|
+
className: cn(buttonVariants({ variant, size, className }), block && "w-full"),
|
|
458
459
|
disabled: isDisabled,
|
|
459
460
|
"aria-describedby": describedBy,
|
|
460
461
|
"aria-busy": loading || void 0,
|
|
@@ -875,6 +876,94 @@ var NumericInput = forwardRef(
|
|
|
875
876
|
}
|
|
876
877
|
);
|
|
877
878
|
NumericInput.displayName = "NumericInput";
|
|
879
|
+
var textareaVariants = cva(
|
|
880
|
+
"flex min-h-[104px] w-full resize-y rounded-md border px-3 py-2 text-sm font-medium transition placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60",
|
|
881
|
+
{
|
|
882
|
+
variants: {
|
|
883
|
+
variant: {
|
|
884
|
+
default: "border-input bg-background text-foreground hover:border-border",
|
|
885
|
+
ghost: "border-transparent bg-transparent text-foreground focus-visible:border-border/60",
|
|
886
|
+
outline: "border border-border bg-transparent text-foreground"
|
|
887
|
+
},
|
|
888
|
+
size: {
|
|
889
|
+
sm: "min-h-[88px] text-sm",
|
|
890
|
+
md: "min-h-[104px] text-sm",
|
|
891
|
+
lg: "min-h-[120px] text-base"
|
|
892
|
+
}
|
|
893
|
+
},
|
|
894
|
+
defaultVariants: {
|
|
895
|
+
variant: "default",
|
|
896
|
+
size: "md"
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
);
|
|
900
|
+
var Textarea = forwardRef(
|
|
901
|
+
({
|
|
902
|
+
variant = "default",
|
|
903
|
+
size = "md",
|
|
904
|
+
label,
|
|
905
|
+
helperText,
|
|
906
|
+
errorMessage,
|
|
907
|
+
loading,
|
|
908
|
+
disabled,
|
|
909
|
+
className,
|
|
910
|
+
style,
|
|
911
|
+
textareaClassName,
|
|
912
|
+
id,
|
|
913
|
+
onChange,
|
|
914
|
+
onValueChange,
|
|
915
|
+
suffix,
|
|
916
|
+
rows,
|
|
917
|
+
...rest
|
|
918
|
+
}, ref) => {
|
|
919
|
+
const generatedId = useId();
|
|
920
|
+
const textareaId = id ?? generatedId;
|
|
921
|
+
const helperId = helperText ? `${textareaId}-helper` : void 0;
|
|
922
|
+
const errorId = errorMessage ? `${textareaId}-error` : void 0;
|
|
923
|
+
const hasRightAccessory = Boolean(suffix) || Boolean(loading);
|
|
924
|
+
return /* @__PURE__ */ jsx(
|
|
925
|
+
FieldWrapper,
|
|
926
|
+
{
|
|
927
|
+
label,
|
|
928
|
+
helperText,
|
|
929
|
+
errorMessage,
|
|
930
|
+
helperId,
|
|
931
|
+
errorId,
|
|
932
|
+
htmlFor: textareaId,
|
|
933
|
+
className,
|
|
934
|
+
style,
|
|
935
|
+
children: /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
|
|
936
|
+
/* @__PURE__ */ jsx(
|
|
937
|
+
"textarea",
|
|
938
|
+
{
|
|
939
|
+
id: textareaId,
|
|
940
|
+
ref,
|
|
941
|
+
rows: rows ?? 4,
|
|
942
|
+
className: cn(
|
|
943
|
+
textareaVariants({ variant, size }),
|
|
944
|
+
hasRightAccessory && "pr-10",
|
|
945
|
+
textareaClassName
|
|
946
|
+
),
|
|
947
|
+
disabled: disabled || loading,
|
|
948
|
+
"aria-invalid": Boolean(errorMessage),
|
|
949
|
+
"aria-describedby": [helperId, errorId].filter(Boolean).join(" ") || void 0,
|
|
950
|
+
onChange: (event) => {
|
|
951
|
+
onChange?.(event);
|
|
952
|
+
onValueChange?.(event.target.value);
|
|
953
|
+
},
|
|
954
|
+
...rest
|
|
955
|
+
}
|
|
956
|
+
),
|
|
957
|
+
hasRightAccessory ? /* @__PURE__ */ jsxs("span", { className: "pointer-events-none absolute right-3 top-3 flex items-center gap-2 text-muted-foreground", children: [
|
|
958
|
+
loading ? /* @__PURE__ */ jsx(LoadingSpinner, { size: "xs" }) : null,
|
|
959
|
+
suffix ? /* @__PURE__ */ jsx("span", { children: suffix }) : null
|
|
960
|
+
] }) : null
|
|
961
|
+
] })
|
|
962
|
+
}
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
);
|
|
966
|
+
Textarea.displayName = "Textarea";
|
|
878
967
|
function Card({ className, ...props }) {
|
|
879
968
|
return /* @__PURE__ */ jsx(
|
|
880
969
|
"div",
|
|
@@ -1780,9 +1869,18 @@ function CheckMark() {
|
|
|
1780
1869
|
function Form({ className, ...props }) {
|
|
1781
1870
|
return /* @__PURE__ */ jsx("form", { className: cn("space-y-6", className), ...props });
|
|
1782
1871
|
}
|
|
1783
|
-
var FormField = forwardRef(
|
|
1784
|
-
|
|
1785
|
-
|
|
1872
|
+
var FormField = forwardRef(
|
|
1873
|
+
({ className, label, helperText, errorMessage, required, htmlFor, children, ...props }, ref) => {
|
|
1874
|
+
return /* @__PURE__ */ jsxs("div", { ref, className: cn("grid gap-2", className), ...props, children: [
|
|
1875
|
+
label ? /* @__PURE__ */ jsxs("label", { htmlFor, className: "text-sm font-semibold leading-none text-foreground", children: [
|
|
1876
|
+
label,
|
|
1877
|
+
required ? /* @__PURE__ */ jsx("span", { className: "ml-1 text-destructive", children: "*" }) : null
|
|
1878
|
+
] }) : null,
|
|
1879
|
+
children,
|
|
1880
|
+
errorMessage ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: errorMessage }) : helperText ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: helperText }) : null
|
|
1881
|
+
] });
|
|
1882
|
+
}
|
|
1883
|
+
);
|
|
1786
1884
|
FormField.displayName = "FormField";
|
|
1787
1885
|
var FormItem = forwardRef(({ className, ...props }, ref) => {
|
|
1788
1886
|
return /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col space-y-2", className), ...props });
|
|
@@ -2009,6 +2107,13 @@ var SearchableSelect = forwardRef(
|
|
|
2009
2107
|
return candidate.toLowerCase().includes(normalized);
|
|
2010
2108
|
});
|
|
2011
2109
|
}, [options, searchTerm]);
|
|
2110
|
+
const normalizeValue = useCallback(
|
|
2111
|
+
(raw) => {
|
|
2112
|
+
const match = options.find((option) => String(option.value) === raw);
|
|
2113
|
+
return match ? match.value : raw;
|
|
2114
|
+
},
|
|
2115
|
+
[options]
|
|
2116
|
+
);
|
|
2012
2117
|
const handleOpenChange = useCallback(
|
|
2013
2118
|
(next) => {
|
|
2014
2119
|
setIsOpen(next);
|
|
@@ -2052,7 +2157,7 @@ var SearchableSelect = forwardRef(
|
|
|
2052
2157
|
{
|
|
2053
2158
|
value: value !== void 0 ? String(value) : void 0,
|
|
2054
2159
|
defaultValue: defaultValue !== void 0 ? String(defaultValue) : void 0,
|
|
2055
|
-
onValueChange: (next) => onChange?.(next),
|
|
2160
|
+
onValueChange: (next) => onChange?.(normalizeValue(next)),
|
|
2056
2161
|
disabled: disabled || loading,
|
|
2057
2162
|
onOpenChange: handleOpenChange,
|
|
2058
2163
|
...props,
|
|
@@ -6513,4 +6618,381 @@ function PageTitle2({
|
|
|
6513
6618
|
);
|
|
6514
6619
|
}
|
|
6515
6620
|
|
|
6516
|
-
|
|
6621
|
+
// src/ai-manifest.ts
|
|
6622
|
+
var uiAiManifest = {
|
|
6623
|
+
packageName: "@lucasvu/scope-ui",
|
|
6624
|
+
styleImport: "@lucasvu/scope-ui/styles.css",
|
|
6625
|
+
rules: [
|
|
6626
|
+
"Import the stylesheet once at the app entry before rendering any component.",
|
|
6627
|
+
"Prefer the canonical component for each intent instead of mixing legacy MainFe components.",
|
|
6628
|
+
"Use Input/Textarea/Select label props directly for simple fields; use Field only to wrap custom controls or grouped content.",
|
|
6629
|
+
"Use Select for small fixed option lists, SearchableSelect for larger local lists, Combobox for type-and-pick flows, and AsyncCombobox for remote search.",
|
|
6630
|
+
'Always provide a stable rowKey to DataTable and use sortMode="server" when sorting happens on the backend.',
|
|
6631
|
+
"Prefer Card as the outer layout section and keep alerts, stats, and tables inside CardContent when building dashboards or forms.",
|
|
6632
|
+
"Do not import MainFe components unless the target explicitly asks for legacy main-fe styling."
|
|
6633
|
+
],
|
|
6634
|
+
components: [
|
|
6635
|
+
{
|
|
6636
|
+
name: "Primary action",
|
|
6637
|
+
importName: "Button",
|
|
6638
|
+
purpose: "Render the main clickable action.",
|
|
6639
|
+
useWhen: [
|
|
6640
|
+
"Submitting a form",
|
|
6641
|
+
"Triggering create/save/confirm flows",
|
|
6642
|
+
"Need loading or block width support"
|
|
6643
|
+
],
|
|
6644
|
+
avoidWhen: [
|
|
6645
|
+
"Pure navigation text links without button semantics"
|
|
6646
|
+
],
|
|
6647
|
+
props: [
|
|
6648
|
+
{ name: "variant", type: "'default' | 'secondary' | 'ghost' | 'outline' | 'destructive' | 'link' | 'confirm' | 'create'", description: "Chooses the visual emphasis." },
|
|
6649
|
+
{ name: "size", type: "'sm' | 'md' | 'lg' | 'icon'", description: "Chooses the button size." },
|
|
6650
|
+
{ name: "block", type: "boolean", description: "Expands the button to full width." },
|
|
6651
|
+
{ name: "loading", type: "boolean", description: "Shows a spinner and disables interaction." }
|
|
6652
|
+
],
|
|
6653
|
+
example: "<Button variant='create' block>Save changes</Button>",
|
|
6654
|
+
related: ["Alert", "Card"]
|
|
6655
|
+
},
|
|
6656
|
+
{
|
|
6657
|
+
name: "Single-line text input",
|
|
6658
|
+
importName: "Input",
|
|
6659
|
+
purpose: "Capture short text, email, password, number-like strings, or search text.",
|
|
6660
|
+
useWhen: [
|
|
6661
|
+
"A single text value is needed",
|
|
6662
|
+
"You need label/helper/error handling built in",
|
|
6663
|
+
"You need prefix, suffix, or password toggle support"
|
|
6664
|
+
],
|
|
6665
|
+
props: [
|
|
6666
|
+
{ name: "label", type: "ReactNode", description: "Field label rendered above the control." },
|
|
6667
|
+
{ name: "helperText", type: "ReactNode", description: "Supplemental guidance shown below." },
|
|
6668
|
+
{ name: "errorMessage", type: "ReactNode", description: "Validation message shown below in error state." },
|
|
6669
|
+
{ name: "prefix", type: "ReactNode", description: "Visual prefix inside the field." },
|
|
6670
|
+
{ name: "suffix", type: "ReactNode", description: "Visual suffix inside the field." }
|
|
6671
|
+
],
|
|
6672
|
+
example: "<Input label='Email' type='email' placeholder='you@example.com' />",
|
|
6673
|
+
related: ["Textarea", "NumericInput", "Field"]
|
|
6674
|
+
},
|
|
6675
|
+
{
|
|
6676
|
+
name: "Multiline text input",
|
|
6677
|
+
importName: "Textarea",
|
|
6678
|
+
purpose: "Capture longer free-form content.",
|
|
6679
|
+
useWhen: [
|
|
6680
|
+
"The user needs multiple lines of text",
|
|
6681
|
+
"A description, note, or comment field is needed"
|
|
6682
|
+
],
|
|
6683
|
+
props: [
|
|
6684
|
+
{ name: "label", type: "ReactNode", description: "Field label rendered above the textarea." },
|
|
6685
|
+
{ name: "rows", type: "number", description: "Controls initial textarea height." },
|
|
6686
|
+
{ name: "helperText", type: "ReactNode", description: "Supplemental guidance shown below." },
|
|
6687
|
+
{ name: "errorMessage", type: "ReactNode", description: "Validation message shown below in error state." }
|
|
6688
|
+
],
|
|
6689
|
+
example: "<Textarea label='Description' rows={5} placeholder='Write the details...' />",
|
|
6690
|
+
related: ["Input", "Field"]
|
|
6691
|
+
},
|
|
6692
|
+
{
|
|
6693
|
+
name: "Single select",
|
|
6694
|
+
importName: "Select",
|
|
6695
|
+
purpose: "Pick one value from a small fixed option set.",
|
|
6696
|
+
useWhen: [
|
|
6697
|
+
"Fewer options and no search is required",
|
|
6698
|
+
"A simple dropdown is enough"
|
|
6699
|
+
],
|
|
6700
|
+
props: [
|
|
6701
|
+
{ name: "options", type: "SelectOption[]", required: true, description: "List of selectable options." },
|
|
6702
|
+
{ name: "label", type: "ReactNode", description: "Field label rendered above the trigger." },
|
|
6703
|
+
{ name: "placeholder", type: "string", description: "Placeholder shown when no value is selected." },
|
|
6704
|
+
{ name: "onChange", type: "(value) => void", description: "Returns the original option value type." }
|
|
6705
|
+
],
|
|
6706
|
+
example: "<Select label='Plan' options={[{ label: 'Starter', value: 'starter' }]} />",
|
|
6707
|
+
related: ["SearchableSelect", "Combobox"]
|
|
6708
|
+
},
|
|
6709
|
+
{
|
|
6710
|
+
name: "Searchable local select",
|
|
6711
|
+
importName: "SearchableSelect",
|
|
6712
|
+
purpose: "Pick one value from a larger in-memory list with search.",
|
|
6713
|
+
useWhen: [
|
|
6714
|
+
"More than a handful of options exist",
|
|
6715
|
+
"Local filtering is enough"
|
|
6716
|
+
],
|
|
6717
|
+
props: [
|
|
6718
|
+
{ name: "options", type: "SearchableSelectOption[]", required: true, description: "List of selectable options." },
|
|
6719
|
+
{ name: "searchPlaceholder", type: "string", description: "Placeholder inside the search field." },
|
|
6720
|
+
{ name: "onSearch", type: "(query) => void", description: "Receives the local search query." }
|
|
6721
|
+
],
|
|
6722
|
+
example: "<SearchableSelect label='Country' searchPlaceholder='Search country' options={countries} />",
|
|
6723
|
+
related: ["Select", "Combobox", "AsyncCombobox"]
|
|
6724
|
+
},
|
|
6725
|
+
{
|
|
6726
|
+
name: "Type and pick",
|
|
6727
|
+
importName: "Combobox",
|
|
6728
|
+
purpose: "Let the user type and choose from a filtered list.",
|
|
6729
|
+
useWhen: [
|
|
6730
|
+
"A command-palette-like input is needed",
|
|
6731
|
+
"The typed text should stay visible in the input"
|
|
6732
|
+
],
|
|
6733
|
+
props: [
|
|
6734
|
+
{ name: "options", type: "ComboboxOption[]", required: true, description: "List of options to filter." },
|
|
6735
|
+
{ name: "value", type: "string | number", description: "Selected value." },
|
|
6736
|
+
{ name: "onChange", type: "(value) => void", description: "Selected value callback." }
|
|
6737
|
+
],
|
|
6738
|
+
example: "<Combobox label='Assignee' options={users} value={assigneeId} onChange={setAssigneeId} />",
|
|
6739
|
+
related: ["SearchableSelect", "AsyncCombobox"]
|
|
6740
|
+
},
|
|
6741
|
+
{
|
|
6742
|
+
name: "Remote search combobox",
|
|
6743
|
+
importName: "AsyncCombobox",
|
|
6744
|
+
purpose: "Search and pick from server-backed data.",
|
|
6745
|
+
useWhen: [
|
|
6746
|
+
"Options are loaded from an API",
|
|
6747
|
+
"Infinite scroll or async fetching is needed"
|
|
6748
|
+
],
|
|
6749
|
+
props: [
|
|
6750
|
+
{ name: "options", type: "ComboboxOption[]", required: true, description: "Currently loaded options." },
|
|
6751
|
+
{ name: "loading", type: "boolean", description: "Shows async loading state." },
|
|
6752
|
+
{ name: "onSearch", type: "(query) => void", required: true, description: "Triggers remote search." },
|
|
6753
|
+
{ name: "onLoadMore", type: "() => void", description: "Loads more options when scrolling near the end." }
|
|
6754
|
+
],
|
|
6755
|
+
example: "<AsyncCombobox label='User' options={options} loading={loading} onSearch={searchUsers} />",
|
|
6756
|
+
related: ["Combobox", "SearchableSelect"]
|
|
6757
|
+
},
|
|
6758
|
+
{
|
|
6759
|
+
name: "Multiple selection",
|
|
6760
|
+
importName: "MultiSelect",
|
|
6761
|
+
purpose: "Choose many values from a searchable list.",
|
|
6762
|
+
useWhen: [
|
|
6763
|
+
"The user can pick multiple tags, users, or filters"
|
|
6764
|
+
],
|
|
6765
|
+
props: [
|
|
6766
|
+
{ name: "options", type: "MultiSelectOption[]", required: true, description: "List of selectable options." },
|
|
6767
|
+
{ name: "value", type: "Array<string | number>", description: "Selected values." },
|
|
6768
|
+
{ name: "onChange", type: "(values) => void", description: "Selected values callback." }
|
|
6769
|
+
],
|
|
6770
|
+
example: "<MultiSelect label='Tags' options={tagOptions} value={tags} onChange={setTags} />",
|
|
6771
|
+
related: ["Select", "SearchableSelect"]
|
|
6772
|
+
},
|
|
6773
|
+
{
|
|
6774
|
+
name: "Structured field wrapper",
|
|
6775
|
+
importName: "Field",
|
|
6776
|
+
purpose: "Add a shared label/helper/error wrapper around custom content.",
|
|
6777
|
+
useWhen: [
|
|
6778
|
+
"Wrapping a custom control that does not expose label props",
|
|
6779
|
+
"Grouping multiple controls under one heading"
|
|
6780
|
+
],
|
|
6781
|
+
avoidWhen: [
|
|
6782
|
+
"A built-in form control already renders its own label and helper text"
|
|
6783
|
+
],
|
|
6784
|
+
props: [
|
|
6785
|
+
{ name: "label", type: "ReactNode", description: "Field label rendered above children." },
|
|
6786
|
+
{ name: "helperText", type: "ReactNode", description: "Helper text below children." },
|
|
6787
|
+
{ name: "errorMessage", type: "ReactNode", description: "Error text below children." },
|
|
6788
|
+
{ name: "htmlFor", type: "string", description: "Associates the label with the wrapped control id." }
|
|
6789
|
+
],
|
|
6790
|
+
example: "<Field label='Schedule' htmlFor='range'><DateRangePicker id='range' /></Field>",
|
|
6791
|
+
related: ["Input", "Textarea", "Select"]
|
|
6792
|
+
},
|
|
6793
|
+
{
|
|
6794
|
+
name: "Data table",
|
|
6795
|
+
importName: "DataTable",
|
|
6796
|
+
purpose: "Render sortable tabular data with optional selection, actions, loading, and pagination.",
|
|
6797
|
+
useWhen: [
|
|
6798
|
+
"Displaying records in rows and columns",
|
|
6799
|
+
"Sort, pagination, selection, or row actions are required"
|
|
6800
|
+
],
|
|
6801
|
+
props: [
|
|
6802
|
+
{ name: "columns", type: "DataTableColumn<T>[]", required: true, description: "Column definitions." },
|
|
6803
|
+
{ name: "data", type: "T[]", required: true, description: "Rows to render." },
|
|
6804
|
+
{ name: "rowKey", type: "keyof T | ((record: T) => string | number)", required: true, description: "Stable key for each row." },
|
|
6805
|
+
{ name: "pagination", type: "DataTablePagination", description: "Enables pagination controls." },
|
|
6806
|
+
{ name: "sortMode", type: "'client' | 'server'", description: "Choose client or backend sorting." }
|
|
6807
|
+
],
|
|
6808
|
+
example: "<DataTable rowKey='id' columns={columns} data={rows} pagination={pagination} />",
|
|
6809
|
+
related: ["Card", "Pagination", "Loading"]
|
|
6810
|
+
},
|
|
6811
|
+
{
|
|
6812
|
+
name: "Status message",
|
|
6813
|
+
importName: "Alert",
|
|
6814
|
+
purpose: "Communicate neutral, success, warning, or danger states.",
|
|
6815
|
+
useWhen: [
|
|
6816
|
+
"You need a visible status or validation banner"
|
|
6817
|
+
],
|
|
6818
|
+
props: [
|
|
6819
|
+
{ name: "tone", type: "'neutral' | 'info' | 'success' | 'warning' | 'danger'", description: "Semantic tone mapping." },
|
|
6820
|
+
{ name: "title", type: "ReactNode", description: "Alert headline." },
|
|
6821
|
+
{ name: "description", type: "ReactNode", description: "Supporting message." }
|
|
6822
|
+
],
|
|
6823
|
+
example: "<Alert tone='warning' title='Missing information' description='Please fill all required fields.' />",
|
|
6824
|
+
related: ["Loading", "Card"]
|
|
6825
|
+
},
|
|
6826
|
+
{
|
|
6827
|
+
name: "Layout section",
|
|
6828
|
+
importName: "Card",
|
|
6829
|
+
purpose: "Wrap a coherent chunk of UI in a bordered surface.",
|
|
6830
|
+
useWhen: [
|
|
6831
|
+
"Building forms, dashboards, settings sections, or summary panels"
|
|
6832
|
+
],
|
|
6833
|
+
props: [
|
|
6834
|
+
{ name: "className", type: "string", description: "Adds layout-specific spacing or width." }
|
|
6835
|
+
],
|
|
6836
|
+
example: "<Card><CardHeader><CardTitle>Account</CardTitle></CardHeader><CardContent>...</CardContent></Card>",
|
|
6837
|
+
related: ["CardHeader", "CardTitle", "CardContent", "CardFooter"]
|
|
6838
|
+
}
|
|
6839
|
+
]
|
|
6840
|
+
};
|
|
6841
|
+
|
|
6842
|
+
// src/theme-contract.ts
|
|
6843
|
+
var uiThemeContract = {
|
|
6844
|
+
packageName: "@lucasvu/scope-ui",
|
|
6845
|
+
importOrder: [
|
|
6846
|
+
"import '@lucasvu/scope-ui/styles.css'",
|
|
6847
|
+
"import './styles/ui-theme.css'"
|
|
6848
|
+
],
|
|
6849
|
+
selectors: {
|
|
6850
|
+
light: [":root", "[data-ui-theme='light']"],
|
|
6851
|
+
dark: [".dark", "[data-ui-theme='dark']"]
|
|
6852
|
+
},
|
|
6853
|
+
overrideFile: "src/styles/ui-theme.css",
|
|
6854
|
+
rules: [
|
|
6855
|
+
"Import the package stylesheet before your project theme override stylesheet.",
|
|
6856
|
+
"Override tokens in one shared theme file instead of scattering colors across components.",
|
|
6857
|
+
"Use :root for the default light theme and .dark or [data-ui-theme='dark'] for dark theme.",
|
|
6858
|
+
"Only override tokens declared here. Do not patch component internals unless you are extending the library.",
|
|
6859
|
+
"For AI-generated screens, choose one theme source of truth and keep component code token-driven."
|
|
6860
|
+
],
|
|
6861
|
+
tokens: [
|
|
6862
|
+
{
|
|
6863
|
+
name: "--tw-background",
|
|
6864
|
+
description: "Base app background in HSL triplet format.",
|
|
6865
|
+
defaultLight: "0 0% 100%",
|
|
6866
|
+
defaultDark: "222.2 84% 4.9%",
|
|
6867
|
+
usedBy: ["Input", "Select", "Card", "Tabs", "global surface"]
|
|
6868
|
+
},
|
|
6869
|
+
{
|
|
6870
|
+
name: "--tw-foreground",
|
|
6871
|
+
description: "Primary text color in HSL triplet format.",
|
|
6872
|
+
defaultLight: "222.2 47.4% 11.2%",
|
|
6873
|
+
defaultDark: "210 40% 98%",
|
|
6874
|
+
usedBy: ["Button", "Input", "Card", "Typography"]
|
|
6875
|
+
},
|
|
6876
|
+
{
|
|
6877
|
+
name: "--tw-primary",
|
|
6878
|
+
description: "Primary brand color in HSL triplet format.",
|
|
6879
|
+
defaultLight: "221.2 83.2% 53.3%",
|
|
6880
|
+
defaultDark: "217.2 91.2% 59.8%",
|
|
6881
|
+
usedBy: ["Button", "focus rings", "brand emphasis"]
|
|
6882
|
+
},
|
|
6883
|
+
{
|
|
6884
|
+
name: "--tw-accent",
|
|
6885
|
+
description: "Accent color in HSL triplet format.",
|
|
6886
|
+
defaultLight: "199 89% 48%",
|
|
6887
|
+
defaultDark: "199 89% 48%",
|
|
6888
|
+
usedBy: ["Tabs", "checkbox accent", "highlight states"]
|
|
6889
|
+
},
|
|
6890
|
+
{
|
|
6891
|
+
name: "--tw-success",
|
|
6892
|
+
description: "Success state color in HSL triplet format.",
|
|
6893
|
+
defaultLight: "142.1 76.2% 36.3%",
|
|
6894
|
+
defaultDark: "142.1 70.6% 45.3%",
|
|
6895
|
+
usedBy: ["Alert", "confirm buttons", "status text"]
|
|
6896
|
+
},
|
|
6897
|
+
{
|
|
6898
|
+
name: "--tw-destructive",
|
|
6899
|
+
description: "Danger state color in HSL triplet format.",
|
|
6900
|
+
defaultLight: "0 84.2% 60.2%",
|
|
6901
|
+
defaultDark: "0 62.8% 30.6%",
|
|
6902
|
+
usedBy: ["Alert", "destructive buttons", "error states"]
|
|
6903
|
+
},
|
|
6904
|
+
{
|
|
6905
|
+
name: "--tw-border",
|
|
6906
|
+
description: "Border color in HSL triplet format.",
|
|
6907
|
+
defaultLight: "214.3 31.8% 91.4%",
|
|
6908
|
+
defaultDark: "217.2 32.6% 17.5%",
|
|
6909
|
+
usedBy: ["Input", "Select", "Card", "DataTable"]
|
|
6910
|
+
},
|
|
6911
|
+
{
|
|
6912
|
+
name: "--surface",
|
|
6913
|
+
description: "Default surface background as a CSS color.",
|
|
6914
|
+
defaultLight: "rgba(255, 255, 255, 0.92)",
|
|
6915
|
+
defaultDark: "rgba(15, 23, 42, 0.78)",
|
|
6916
|
+
usedBy: ["Tabs", "Pagination", "glass surfaces"]
|
|
6917
|
+
},
|
|
6918
|
+
{
|
|
6919
|
+
name: "--surface-strong",
|
|
6920
|
+
description: "Stronger elevated surface as a CSS color.",
|
|
6921
|
+
defaultLight: "rgba(241, 245, 249, 0.96)",
|
|
6922
|
+
defaultDark: "rgba(30, 41, 59, 0.92)",
|
|
6923
|
+
usedBy: ["Pagination hover", "raised surfaces"]
|
|
6924
|
+
},
|
|
6925
|
+
{
|
|
6926
|
+
name: "--grey",
|
|
6927
|
+
description: "Neutral table/header background as a CSS color.",
|
|
6928
|
+
defaultLight: "rgba(248, 250, 252, 0.96)",
|
|
6929
|
+
defaultDark: "rgba(30, 41, 59, 0.88)",
|
|
6930
|
+
usedBy: ["DataTable header"]
|
|
6931
|
+
},
|
|
6932
|
+
{
|
|
6933
|
+
name: "--grey-strong",
|
|
6934
|
+
description: "Hover background for neutral rows as a CSS color.",
|
|
6935
|
+
defaultLight: "rgba(241, 245, 249, 0.98)",
|
|
6936
|
+
defaultDark: "rgba(51, 65, 85, 0.92)",
|
|
6937
|
+
usedBy: ["DataTable row hover"]
|
|
6938
|
+
},
|
|
6939
|
+
{
|
|
6940
|
+
name: "--text",
|
|
6941
|
+
description: "Generic text color alias as a CSS color.",
|
|
6942
|
+
defaultLight: "hsl(var(--tw-foreground))",
|
|
6943
|
+
defaultDark: "hsl(var(--tw-foreground))",
|
|
6944
|
+
usedBy: ["Pagination", "legacy utility surfaces"]
|
|
6945
|
+
},
|
|
6946
|
+
{
|
|
6947
|
+
name: "--muted",
|
|
6948
|
+
description: "Generic muted text color alias as a CSS color.",
|
|
6949
|
+
defaultLight: "hsl(var(--tw-muted-foreground))",
|
|
6950
|
+
defaultDark: "hsl(var(--tw-muted-foreground))",
|
|
6951
|
+
usedBy: ["Pagination", "DataTable sort icon", "helper text"]
|
|
6952
|
+
},
|
|
6953
|
+
{
|
|
6954
|
+
name: "--accent",
|
|
6955
|
+
description: "Generic accent color alias as a CSS color.",
|
|
6956
|
+
defaultLight: "hsl(var(--tw-accent))",
|
|
6957
|
+
defaultDark: "hsl(var(--tw-accent))",
|
|
6958
|
+
usedBy: ["DataTable checkbox accent", "focus accents"]
|
|
6959
|
+
},
|
|
6960
|
+
{
|
|
6961
|
+
name: "--primary-grad-from",
|
|
6962
|
+
description: "Gradient start color in HSL triplet format.",
|
|
6963
|
+
defaultLight: "199 89% 48%",
|
|
6964
|
+
defaultDark: "198.6 88.7% 48.4%",
|
|
6965
|
+
usedBy: ["Button variant create"]
|
|
6966
|
+
},
|
|
6967
|
+
{
|
|
6968
|
+
name: "--primary-grad-to",
|
|
6969
|
+
description: "Gradient end color in HSL triplet format.",
|
|
6970
|
+
defaultLight: "221.2 83.2% 53.3%",
|
|
6971
|
+
defaultDark: "221.2 83.2% 53.3%",
|
|
6972
|
+
usedBy: ["Button variant create"]
|
|
6973
|
+
}
|
|
6974
|
+
],
|
|
6975
|
+
exampleCss: `:root {
|
|
6976
|
+
--tw-primary: 12 88% 56%;
|
|
6977
|
+
--tw-accent: 24 95% 52%;
|
|
6978
|
+
--primary-grad-from: 24 95% 52%;
|
|
6979
|
+
--primary-grad-to: 12 88% 56%;
|
|
6980
|
+
--surface: rgba(255, 248, 240, 0.92);
|
|
6981
|
+
}
|
|
6982
|
+
|
|
6983
|
+
.dark {
|
|
6984
|
+
--tw-primary: 18 100% 62%;
|
|
6985
|
+
--tw-accent: 35 100% 58%;
|
|
6986
|
+
--surface: rgba(24, 24, 27, 0.82);
|
|
6987
|
+
}`
|
|
6988
|
+
};
|
|
6989
|
+
var uiProjectAiRules = [
|
|
6990
|
+
"Use @lucasvu/scope-ui as the default UI library.",
|
|
6991
|
+
"Import @lucasvu/scope-ui/styles.css once at the app entry.",
|
|
6992
|
+
"Import the project override theme file after the package stylesheet.",
|
|
6993
|
+
"Read uiAiManifest to choose the correct component by intent before coding.",
|
|
6994
|
+
"Read uiThemeContract before changing colors, shadows, or theme behavior.",
|
|
6995
|
+
"Prefer root exports and avoid MainFe unless the task explicitly targets a legacy screen."
|
|
6996
|
+
];
|
|
6997
|
+
|
|
6998
|
+
export { Alert, ArgonSidebar, AsyncCombobox, Badge, Breadcrumb, Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Combobox, FormControl as Control, DataTable, FormDescription as Description, FormField as Field, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Input, FormItem as Item, FormLabel as Label, LineClampTooltip, Loading, main_fe_exports as MainFe, FormMessage as Message, MultiSelect, NumericInput, OverflowTooltip, PageTitle, Pagination, SearchableSelect, SectionTitle, Select, Sidebar, SidebarItem, Stat, Table, TableBody, TableCell, TableHeader, TableRow, Tabs, Textarea, Tooltip, TruncatedText, cn, defaultPermissionChecker, filterSidebarItems, hasActiveDescendant, uiAiManifest, uiProjectAiRules, uiThemeContract };
|