@webdevarif/dashui 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -98,8 +98,6 @@ __export(index_exports, {
98
98
  LoadingSpinner: () => LoadingSpinner,
99
99
  LocalInput: () => LocalInput,
100
100
  MediaCard: () => MediaCard,
101
- MediaGrid: () => MediaGrid,
102
- MediaPickerDialog: () => MediaPickerDialog,
103
101
  NotificationBell: () => NotificationBell,
104
102
  Page: () => Page,
105
103
  PageSection: () => PageSection,
@@ -145,6 +143,8 @@ __export(index_exports, {
145
143
  TooltipProvider: () => TooltipProvider,
146
144
  TooltipTrigger: () => TooltipTrigger,
147
145
  TopBar: () => TopBar,
146
+ UploadProgressPanel: () => UploadProgressPanel,
147
+ UploadZone: () => UploadZone,
148
148
  badgeVariants: () => badgeVariants,
149
149
  buttonVariants: () => buttonVariants,
150
150
  cn: () => cn,
@@ -1909,436 +1909,150 @@ function StorageBar({
1909
1909
  ] });
1910
1910
  }
1911
1911
 
1912
- // src/components/media/media-card.tsx
1912
+ // src/components/media/upload-zone.tsx
1913
+ var import_react = require("react");
1914
+ var import_lucide_react10 = require("lucide-react");
1913
1915
  var import_jsx_runtime32 = require("react/jsx-runtime");
1914
- function stripExtension(name) {
1915
- return name.replace(/\.[^/.]+$/, "");
1916
- }
1917
- function getFileEmoji(mimeType) {
1918
- if (mimeType.startsWith("video/")) return "\u{1F3AC}";
1919
- if (mimeType.startsWith("audio/")) return "\u{1F3B5}";
1920
- if (mimeType === "application/pdf") return "\u{1F4C4}";
1921
- return "\u{1F4CE}";
1922
- }
1923
- function MediaCard({ file, selected = false, onClick, className }) {
1924
- const isImage = file.mimeType.startsWith("image/");
1925
- const displayName = stripExtension(file.name);
1916
+ var CloudUploadIcon = import_lucide_react10.Cloud;
1917
+ function UploadZone({
1918
+ onFiles,
1919
+ accept = "*",
1920
+ multiple = true,
1921
+ disabled = false
1922
+ }) {
1923
+ const [dragActive, setDragActive] = (0, import_react.useState)(false);
1924
+ const inputRef = (0, import_react.useRef)(null);
1925
+ const handleDrag = (e) => {
1926
+ e.preventDefault();
1927
+ e.stopPropagation();
1928
+ if (!disabled) {
1929
+ setDragActive(e.type === "dragenter" || e.type === "dragover");
1930
+ }
1931
+ };
1932
+ const handleDrop = (e) => {
1933
+ e.preventDefault();
1934
+ e.stopPropagation();
1935
+ setDragActive(false);
1936
+ if (!disabled && e.dataTransfer.files?.length) {
1937
+ onFiles(Array.from(e.dataTransfer.files));
1938
+ }
1939
+ };
1940
+ const handleChange = (e) => {
1941
+ if (e.target.files?.length) {
1942
+ onFiles(Array.from(e.target.files));
1943
+ }
1944
+ };
1926
1945
  return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
1927
1946
  "div",
1928
1947
  {
1929
- role: "button",
1930
- tabIndex: 0,
1931
- onClick,
1932
- onKeyDown: (e) => {
1933
- if (e.key === "Enter" || e.key === " ") onClick?.();
1934
- },
1935
- className: cn(
1936
- "group relative aspect-square cursor-pointer rounded-xl overflow-hidden border-2 transition-all duration-150",
1937
- selected ? "border-[color:var(--primary)] shadow-[0_0_0_2px_rgba(40,127,113,0.2)]" : "border-transparent hover:border-[color:var(--primary)]/40",
1938
- className
1939
- ),
1948
+ className: `relative rounded-lg border-2 border-dashed p-8 text-center transition-colors ${dragActive ? "border-blue-500 bg-blue-50/50" : "border-gray-300 bg-gray-50/50 hover:border-gray-400"} ${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`,
1949
+ onDragEnter: handleDrag,
1950
+ onDragLeave: handleDrag,
1951
+ onDragOver: handleDrag,
1952
+ onDrop: handleDrop,
1953
+ onClick: () => !disabled && inputRef.current?.click(),
1940
1954
  children: [
1941
- isImage ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1942
- "img",
1955
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1956
+ "input",
1943
1957
  {
1944
- src: file.url,
1945
- alt: file.name,
1946
- className: "h-full w-full object-cover transition-transform duration-150 group-hover:scale-[1.03]",
1947
- loading: "lazy"
1958
+ ref: inputRef,
1959
+ type: "file",
1960
+ accept,
1961
+ multiple,
1962
+ onChange: handleChange,
1963
+ disabled,
1964
+ className: "hidden"
1948
1965
  }
1949
- ) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex h-full w-full items-center justify-center bg-muted text-4xl", children: getFileEmoji(file.mimeType) }),
1950
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "absolute inset-0 flex items-end bg-gradient-to-t from-black/60 via-transparent to-transparent opacity-0 transition-opacity duration-150 group-hover:opacity-100", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "truncate px-2 pb-2 text-[11px] font-medium text-white", children: displayName }) }),
1951
- selected && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "absolute right-1.5 top-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-primary shadow", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1952
- "svg",
1953
- {
1954
- className: "h-3 w-3 text-white",
1955
- xmlns: "http://www.w3.org/2000/svg",
1956
- viewBox: "0 0 24 24",
1957
- fill: "none",
1958
- stroke: "currentColor",
1959
- strokeWidth: "3",
1960
- strokeLinecap: "round",
1961
- strokeLinejoin: "round",
1962
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("polyline", { points: "20 6 9 17 4 12" })
1963
- }
1964
- ) })
1966
+ ),
1967
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col items-center gap-2", children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(CloudUploadIcon, { className: "w-8 h-8 text-gray-400" }),
1969
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { children: [
1970
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-sm font-medium text-gray-700", children: dragActive ? "Drop files here" : "Drag & drop files, or click to browse" }),
1971
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-xs text-gray-500 mt-1", children: "Max file size: 100MB" })
1972
+ ] })
1973
+ ] })
1965
1974
  ]
1966
1975
  }
1967
1976
  );
1968
1977
  }
1969
1978
 
1970
- // src/components/media/media-grid.tsx
1979
+ // src/components/media/upload-progress-panel.tsx
1980
+ var import_lucide_react11 = require("lucide-react");
1971
1981
  var import_jsx_runtime33 = require("react/jsx-runtime");
1972
- var columnsClass = {
1973
- 4: "grid-cols-2 sm:grid-cols-3 md:grid-cols-4",
1974
- 5: "grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5",
1975
- 6: "grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6"
1976
- };
1977
- function MediaGrid({
1978
- files,
1979
- selected,
1980
- onSelect,
1981
- loading = false,
1982
- columns = 5,
1983
- emptyMessage = "No files yet.",
1984
- onUploadClick,
1985
- className
1982
+ var CheckmarkCircle01Icon = import_lucide_react11.CheckCircle;
1983
+ var Cancel01Icon = import_lucide_react11.X;
1984
+ function UploadProgressPanel({
1985
+ items,
1986
+ onRetry,
1987
+ onCancel,
1988
+ onCancelAll
1986
1989
  }) {
1987
- if (loading) {
1988
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: cn("grid gap-2", columnsClass[columns] ?? columnsClass[5], className), children: Array.from({ length: columns * 2 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1990
+ if (items.length === 0) return null;
1991
+ const activeCount = items.filter((it) => it.status === "uploading").length;
1992
+ const doneCount = items.filter((it) => it.status === "done").length;
1993
+ const failedCount = items.filter((it) => it.status === "failed").length;
1994
+ const avgProgress = items.length > 0 ? Math.round(items.reduce((s, it) => s + it.progress, 0) / items.length) : 0;
1995
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "rounded-lg border border-gray-200 bg-white p-4 shadow-sm", children: [
1996
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [
1997
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h3", { className: "text-sm font-semibold text-gray-900", children: activeCount > 0 ? `Uploading ${activeCount} file${activeCount > 1 ? "s" : ""}` : "Upload complete" }),
1998
+ onCancelAll && items.some((it) => it.status === "uploading" || it.status === "pending") && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1999
+ "button",
2000
+ {
2001
+ onClick: onCancelAll,
2002
+ className: "text-xs text-red-600 hover:text-red-700 font-medium",
2003
+ children: "Cancel all"
2004
+ }
2005
+ )
2006
+ ] }),
2007
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "w-full h-2 bg-gray-200 rounded-full overflow-hidden mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1989
2008
  "div",
1990
2009
  {
1991
- className: "aspect-square animate-pulse rounded-xl bg-muted"
1992
- },
1993
- i
1994
- )) });
1995
- }
1996
- if (files.length === 0) {
1997
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: cn("flex flex-col items-center justify-center gap-3 py-16 text-center", className), children: [
1998
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex h-14 w-14 items-center justify-center rounded-full bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1999
- "svg",
2010
+ className: "h-full bg-blue-500 transition-all duration-300",
2011
+ style: { width: `${avgProgress}%` }
2012
+ }
2013
+ ) }),
2014
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { className: "text-xs text-gray-600 mb-4", children: [
2015
+ doneCount,
2016
+ " done",
2017
+ failedCount > 0 && ` \xB7 ${failedCount} failed`,
2018
+ activeCount > 0 && ` \xB7 ${activeCount} uploading`
2019
+ ] }),
2020
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "space-y-2 max-h-48 overflow-y-auto", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-3 p-2 rounded-md bg-gray-50 hover:bg-gray-100 transition-colors", children: [
2021
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "shrink-0", children: [
2022
+ item.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CheckmarkCircle01Icon, { className: "w-4 h-4 text-green-600" }),
2023
+ item.status === "failed" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Cancel01Icon, { className: "w-4 h-4 text-red-600" }),
2024
+ (item.status === "uploading" || item.status === "pending") && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "w-4 h-4 rounded-full border-2 border-blue-300 border-t-blue-600 animate-spin" })
2025
+ ] }),
2026
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex-1 min-w-0", children: [
2027
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-xs font-medium text-gray-900 truncate", children: item.name }),
2028
+ item.error && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-xs text-red-600", children: item.error }),
2029
+ item.status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { className: "text-xs text-gray-500", children: [
2030
+ item.progress,
2031
+ "%"
2032
+ ] })
2033
+ ] }),
2034
+ item.status === "failed" && onRetry && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2035
+ "button",
2000
2036
  {
2001
- className: "h-7 w-7 text-muted-foreground",
2002
- xmlns: "http://www.w3.org/2000/svg",
2003
- viewBox: "0 0 24 24",
2004
- fill: "none",
2005
- stroke: "currentColor",
2006
- strokeWidth: "1.5",
2007
- strokeLinecap: "round",
2008
- strokeLinejoin: "round",
2009
- children: [
2010
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
2011
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("circle", { cx: "9", cy: "9", r: "2" }),
2012
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
2013
- ]
2037
+ onClick: () => onRetry(item.id),
2038
+ className: "text-xs text-blue-600 hover:text-blue-700 font-medium shrink-0",
2039
+ children: "Retry"
2014
2040
  }
2015
- ) }),
2016
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-sm text-muted-foreground", children: emptyMessage }),
2017
- onUploadClick && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2041
+ ),
2042
+ (item.status === "uploading" || item.status === "pending") && onCancel && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2018
2043
  "button",
2019
2044
  {
2020
- onClick: onUploadClick,
2021
- className: "rounded-md bg-primary px-4 py-1.5 text-sm font-medium text-primary-foreground transition-opacity hover:opacity-90",
2022
- children: "Upload files"
2045
+ onClick: () => onCancel(item.id),
2046
+ className: "text-xs text-gray-600 hover:text-gray-700 shrink-0",
2047
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Cancel01Icon, { className: "w-4 h-4" })
2023
2048
  }
2024
2049
  )
2025
- ] });
2026
- }
2027
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: cn("grid gap-2", columnsClass[columns] ?? columnsClass[5], className), children: files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2028
- MediaCard,
2029
- {
2030
- file,
2031
- selected: selected?.has(file.id),
2032
- onClick: () => onSelect?.(file)
2033
- },
2034
- file.id
2035
- )) });
2036
- }
2037
-
2038
- // src/components/media/media-picker-dialog.tsx
2039
- var React20 = __toESM(require("react"));
2040
- var import_jsx_runtime34 = require("react/jsx-runtime");
2041
- var ALL_TYPE_OPTIONS = [
2042
- { value: "all", label: "All" },
2043
- { value: "images", label: "Images" },
2044
- { value: "videos", label: "Videos" },
2045
- { value: "documents", label: "Docs" }
2046
- ];
2047
- function MediaPickerDialog({
2048
- open,
2049
- onOpenChange,
2050
- title = "Media Library",
2051
- files,
2052
- folders = [],
2053
- loading = false,
2054
- total,
2055
- typeFilter = "all",
2056
- onTypeChange,
2057
- search = "",
2058
- onSearch,
2059
- activeFolderId,
2060
- onFolderChange,
2061
- onUpload,
2062
- onLoadMore,
2063
- hasMore = false,
2064
- multiple = false,
2065
- initialSelected = [],
2066
- accept = "all",
2067
- onSelect
2068
- }) {
2069
- const [localSelected, setLocalSelected] = React20.useState(new Set(initialSelected));
2070
- const [isDragging, setIsDragging] = React20.useState(false);
2071
- const fileInputRef = React20.useRef(null);
2072
- const dragCounterRef = React20.useRef(0);
2073
- React20.useEffect(() => {
2074
- if (!open) {
2075
- setLocalSelected(/* @__PURE__ */ new Set());
2076
- setIsDragging(false);
2077
- dragCounterRef.current = 0;
2078
- }
2079
- }, [open]);
2080
- const prevInitialRef = React20.useRef("");
2081
- React20.useEffect(() => {
2082
- if (!open) return;
2083
- const key = initialSelected.join(",");
2084
- if (key === prevInitialRef.current) return;
2085
- prevInitialRef.current = key;
2086
- if (initialSelected.length > 0) {
2087
- setLocalSelected(new Set(initialSelected));
2088
- }
2089
- }, [open, initialSelected]);
2090
- const handleDragEnter = (e) => {
2091
- e.preventDefault();
2092
- e.stopPropagation();
2093
- if (e.dataTransfer.types.includes("Files")) {
2094
- dragCounterRef.current++;
2095
- setIsDragging(true);
2096
- }
2097
- };
2098
- const handleDragLeave = (e) => {
2099
- e.preventDefault();
2100
- e.stopPropagation();
2101
- dragCounterRef.current--;
2102
- if (dragCounterRef.current <= 0) {
2103
- dragCounterRef.current = 0;
2104
- setIsDragging(false);
2105
- }
2106
- };
2107
- const handleDragOver = (e) => {
2108
- e.preventDefault();
2109
- e.stopPropagation();
2110
- };
2111
- const handleDrop = (e) => {
2112
- e.preventDefault();
2113
- e.stopPropagation();
2114
- dragCounterRef.current = 0;
2115
- setIsDragging(false);
2116
- if (!onUpload || !e.dataTransfer.files.length) return;
2117
- const accepted = accept === "images" ? Array.from(e.dataTransfer.files).filter((f) => f.type.startsWith("image/")) : Array.from(e.dataTransfer.files);
2118
- if (!accepted.length) return;
2119
- const dt = new DataTransfer();
2120
- accepted.forEach((f) => dt.items.add(f));
2121
- onUpload(dt.files);
2122
- };
2123
- const typeOptions = ALL_TYPE_OPTIONS.filter(
2124
- (o) => accept === "images" ? o.value === "all" || o.value === "images" : true
2125
- );
2126
- const handleFileSelect = (file) => {
2127
- setLocalSelected((prev) => {
2128
- if (multiple) {
2129
- const next = new Set(prev);
2130
- next.has(file.id) ? next.delete(file.id) : next.add(file.id);
2131
- return next;
2132
- }
2133
- return prev.has(file.id) ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([file.id]);
2134
- });
2135
- };
2136
- const handleConfirm = () => {
2137
- const selected = files.filter((f) => localSelected.has(f.id));
2138
- onSelect(selected);
2139
- onOpenChange(false);
2140
- };
2141
- const handleUploadChange = (e) => {
2142
- if (e.target.files && onUpload) {
2143
- onUpload(e.target.files);
2144
- }
2145
- };
2146
- const firstSelected = files.find((f) => localSelected.has(f.id));
2147
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Dialog, { open, onOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2148
- DialogContent,
2149
- {
2150
- "aria-describedby": void 0,
2151
- className: "flex h-[85vh] max-h-[700px] max-w-4xl flex-col gap-0 p-0",
2152
- children: [
2153
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(DialogHeader, { className: "flex-row items-center gap-3 border-b px-5 py-3 pr-12 space-y-0 shrink-0", children: [
2154
- onUpload && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
2155
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2156
- "input",
2157
- {
2158
- ref: fileInputRef,
2159
- type: "file",
2160
- className: "hidden",
2161
- multiple: true,
2162
- accept: accept === "images" ? "image/*" : void 0,
2163
- onChange: handleUploadChange
2164
- }
2165
- ),
2166
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2167
- "button",
2168
- {
2169
- onClick: () => fileInputRef.current?.click(),
2170
- className: "flex shrink-0 items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground transition-opacity hover:opacity-90",
2171
- children: [
2172
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("svg", { className: "h-3.5 w-3.5", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2173
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
2174
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("polyline", { points: "17 8 12 3 7 8" }),
2175
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
2176
- ] }),
2177
- "Upload"
2178
- ]
2179
- }
2180
- )
2181
- ] }),
2182
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(DialogTitle, { className: "text-sm font-semibold flex-1", children: title }),
2183
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "text-xs text-muted-foreground shrink-0", children: [
2184
- total,
2185
- " files"
2186
- ] })
2187
- ] }),
2188
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex flex-1 overflow-hidden min-h-0", children: [
2189
- folders.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("aside", { className: "flex w-44 shrink-0 flex-col gap-0.5 overflow-y-auto border-r p-2", children: [
2190
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2191
- "button",
2192
- {
2193
- onClick: () => onFolderChange?.(""),
2194
- className: cn(
2195
- "flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-sm transition-colors",
2196
- !activeFolderId ? "bg-primary/10 font-medium text-primary" : "text-muted-foreground hover:bg-accent"
2197
- ),
2198
- children: [
2199
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("svg", { className: "h-3.5 w-3.5 shrink-0", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("path", { d: "M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" }) }),
2200
- "All files"
2201
- ]
2202
- }
2203
- ),
2204
- folders.map((folder) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2205
- "button",
2206
- {
2207
- onClick: () => onFolderChange?.(folder.id),
2208
- className: cn(
2209
- "flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-sm transition-colors",
2210
- activeFolderId === folder.id ? "bg-primary/10 font-medium text-primary" : "text-muted-foreground hover:bg-accent"
2211
- ),
2212
- children: [
2213
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "text-base leading-none", children: folder.icon ?? "\u{1F4C1}" }),
2214
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "truncate", children: folder.name })
2215
- ]
2216
- },
2217
- folder.id
2218
- ))
2219
- ] }),
2220
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex flex-1 flex-col overflow-hidden min-w-0", children: [
2221
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-2 border-b px-4 py-2 shrink-0", children: [
2222
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex items-center gap-0.5 rounded-lg border border-input bg-muted/50 p-0.5", children: typeOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2223
- "button",
2224
- {
2225
- onClick: () => onTypeChange?.(opt.value),
2226
- className: cn(
2227
- "rounded-md px-2.5 py-1 text-xs font-medium transition-colors",
2228
- typeFilter === opt.value ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
2229
- ),
2230
- children: opt.label
2231
- },
2232
- opt.value
2233
- )) }),
2234
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "relative flex-1 max-w-xs ml-auto", children: [
2235
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("svg", { className: "pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2236
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("circle", { cx: "11", cy: "11", r: "8" }),
2237
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("path", { d: "m21 21-4.35-4.35" })
2238
- ] }),
2239
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2240
- "input",
2241
- {
2242
- type: "search",
2243
- value: search,
2244
- onChange: (e) => onSearch?.(e.target.value),
2245
- placeholder: "Search\u2026",
2246
- className: "h-8 w-full rounded-lg border border-input bg-background pl-8 pr-3 text-xs outline-none focus:border-primary focus:ring-1 focus:ring-primary/30"
2247
- }
2248
- )
2249
- ] })
2250
- ] }),
2251
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2252
- "div",
2253
- {
2254
- className: "relative flex-1 overflow-y-auto p-4",
2255
- onDragEnter: handleDragEnter,
2256
- onDragLeave: handleDragLeave,
2257
- onDragOver: handleDragOver,
2258
- onDrop: handleDrop,
2259
- children: [
2260
- isDragging && onUpload && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "pointer-events-none absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed border-primary bg-primary/5 backdrop-blur-[1px]", children: [
2261
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("svg", { className: "h-10 w-10 text-primary opacity-70", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2262
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
2263
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("polyline", { points: "17 8 12 3 7 8" }),
2264
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
2265
- ] }),
2266
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("p", { className: "text-sm font-medium text-primary", children: [
2267
- "Drop ",
2268
- accept === "images" ? "images" : "files",
2269
- " to upload"
2270
- ] })
2271
- ] }),
2272
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2273
- MediaGrid,
2274
- {
2275
- files,
2276
- selected: localSelected,
2277
- onSelect: handleFileSelect,
2278
- loading,
2279
- columns: 5,
2280
- onUploadClick: onUpload ? () => fileInputRef.current?.click() : void 0
2281
- }
2282
- ),
2283
- hasMore && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "mt-4 flex justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2284
- "button",
2285
- {
2286
- onClick: onLoadMore,
2287
- className: "rounded-lg border px-4 py-2 text-xs text-muted-foreground transition-colors hover:bg-accent",
2288
- children: "Load more"
2289
- }
2290
- ) })
2291
- ]
2292
- }
2293
- )
2294
- ] })
2295
- ] }),
2296
- localSelected.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center justify-between gap-4 border-t px-5 py-3 shrink-0 bg-muted/20", children: [
2297
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "text-sm text-muted-foreground", children: [
2298
- localSelected.size,
2299
- " file",
2300
- localSelected.size !== 1 ? "s" : "",
2301
- " selected"
2302
- ] }),
2303
- firstSelected && firstSelected.mimeType.startsWith("image/") && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2304
- "img",
2305
- {
2306
- src: firstSelected.url,
2307
- alt: firstSelected.name,
2308
- className: "h-9 w-9 rounded-lg border border-border object-cover shadow-sm"
2309
- }
2310
- ),
2311
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-2", children: [
2312
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2313
- "button",
2314
- {
2315
- onClick: () => setLocalSelected(/* @__PURE__ */ new Set()),
2316
- className: "rounded-lg px-3 py-1.5 text-sm text-muted-foreground transition-colors hover:bg-accent",
2317
- children: "Cancel"
2318
- }
2319
- ),
2320
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2321
- "button",
2322
- {
2323
- onClick: handleConfirm,
2324
- className: "rounded-lg bg-primary px-4 py-1.5 text-sm font-medium text-primary-foreground transition-opacity hover:opacity-90",
2325
- children: [
2326
- "Use ",
2327
- localSelected.size,
2328
- " file",
2329
- localSelected.size !== 1 ? "s" : ""
2330
- ]
2331
- }
2332
- )
2333
- ] })
2334
- ] })
2335
- ]
2336
- }
2337
- ) });
2050
+ ] }, item.id)) })
2051
+ ] });
2338
2052
  }
2339
2053
 
2340
2054
  // src/components/media/image-picker-field.tsx
2341
- var import_jsx_runtime35 = require("react/jsx-runtime");
2055
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2342
2056
  var sizeMap = {
2343
2057
  sm: { box: "h-12 w-12", icon: "h-4 w-4", text: "text-[9px]" },
2344
2058
  md: { box: "h-20 w-20", icon: "h-5 w-5", text: "text-[10px]" },
@@ -2354,8 +2068,8 @@ function ImagePickerField({
2354
2068
  className
2355
2069
  }) {
2356
2070
  const sz = sizeMap[size];
2357
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: cn("relative inline-flex shrink-0", className), children: [
2358
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2071
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: cn("relative inline-flex shrink-0", className), children: [
2072
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2359
2073
  "button",
2360
2074
  {
2361
2075
  type: "button",
@@ -2365,15 +2079,15 @@ function ImagePickerField({
2365
2079
  sz.box,
2366
2080
  value ? "border-border overflow-hidden" : "border-border hover:border-[color:var(--primary)] hover:bg-primary/5"
2367
2081
  ),
2368
- children: value ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2082
+ children: value ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2369
2083
  "img",
2370
2084
  {
2371
2085
  src: value,
2372
2086
  alt: filename ?? "Selected image",
2373
2087
  className: "h-full w-full rounded-md object-cover"
2374
2088
  }
2375
- ) : /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-col items-center gap-1", children: [
2376
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
2089
+ ) : /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex flex-col items-center gap-1", children: [
2090
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2377
2091
  "svg",
2378
2092
  {
2379
2093
  className: cn(sz.icon, "text-muted-foreground transition-colors group-hover:text-primary"),
@@ -2385,17 +2099,17 @@ function ImagePickerField({
2385
2099
  strokeLinecap: "round",
2386
2100
  strokeLinejoin: "round",
2387
2101
  children: [
2388
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
2389
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("circle", { cx: "9", cy: "9", r: "2" }),
2390
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
2102
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
2103
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("circle", { cx: "9", cy: "9", r: "2" }),
2104
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
2391
2105
  ]
2392
2106
  }
2393
2107
  ),
2394
- size !== "sm" && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: cn(sz.text, "text-muted-foreground text-center leading-tight transition-colors group-hover:text-primary"), children: emptyLabel })
2108
+ size !== "sm" && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: cn(sz.text, "text-muted-foreground text-center leading-tight transition-colors group-hover:text-primary"), children: emptyLabel })
2395
2109
  ] })
2396
2110
  }
2397
2111
  ),
2398
- value && onRemove && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2112
+ value && onRemove && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2399
2113
  "button",
2400
2114
  {
2401
2115
  type: "button",
@@ -2405,12 +2119,70 @@ function ImagePickerField({
2405
2119
  },
2406
2120
  className: "absolute -right-1.5 -top-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-destructive text-destructive-foreground shadow-sm transition-opacity hover:opacity-90",
2407
2121
  "aria-label": "Remove image",
2408
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("svg", { className: "h-2.5 w-2.5", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("path", { d: "M18 6 6 18M6 6l12 12" }) })
2122
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("svg", { className: "h-2.5 w-2.5", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("path", { d: "M18 6 6 18M6 6l12 12" }) })
2409
2123
  }
2410
2124
  )
2411
2125
  ] });
2412
2126
  }
2413
2127
 
2128
+ // src/components/media/media-card.tsx
2129
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2130
+ function stripExtension(name) {
2131
+ return name.replace(/\.[^/.]+$/, "");
2132
+ }
2133
+ function getFileEmoji(mimeType) {
2134
+ if (mimeType.startsWith("video/")) return "\u{1F3AC}";
2135
+ if (mimeType.startsWith("audio/")) return "\u{1F3B5}";
2136
+ if (mimeType === "application/pdf") return "\u{1F4C4}";
2137
+ return "\u{1F4CE}";
2138
+ }
2139
+ function MediaCard({ file, selected = false, onClick, className }) {
2140
+ const isImage = file.mimeType.startsWith("image/");
2141
+ const displayName = stripExtension(file.name);
2142
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
2143
+ "div",
2144
+ {
2145
+ role: "button",
2146
+ tabIndex: 0,
2147
+ onClick,
2148
+ onKeyDown: (e) => {
2149
+ if (e.key === "Enter" || e.key === " ") onClick?.();
2150
+ },
2151
+ className: cn(
2152
+ "group relative aspect-square cursor-pointer rounded-xl overflow-hidden border-2 transition-all duration-150",
2153
+ selected ? "border-[color:var(--primary)] shadow-[0_0_0_2px_rgba(40,127,113,0.2)]" : "border-transparent hover:border-[color:var(--primary)]/40",
2154
+ className
2155
+ ),
2156
+ children: [
2157
+ isImage ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2158
+ "img",
2159
+ {
2160
+ src: file.url,
2161
+ alt: file.name,
2162
+ className: "h-full w-full object-cover transition-transform duration-150 group-hover:scale-[1.03]",
2163
+ loading: "lazy"
2164
+ }
2165
+ ) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "flex h-full w-full items-center justify-center bg-muted text-4xl", children: getFileEmoji(file.mimeType) }),
2166
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "absolute inset-0 flex items-end bg-gradient-to-t from-black/60 via-transparent to-transparent opacity-0 transition-opacity duration-150 group-hover:opacity-100", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "truncate px-2 pb-2 text-[11px] font-medium text-white", children: displayName }) }),
2167
+ selected && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "absolute right-1.5 top-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-primary shadow", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2168
+ "svg",
2169
+ {
2170
+ className: "h-3 w-3 text-white",
2171
+ xmlns: "http://www.w3.org/2000/svg",
2172
+ viewBox: "0 0 24 24",
2173
+ fill: "none",
2174
+ stroke: "currentColor",
2175
+ strokeWidth: "3",
2176
+ strokeLinecap: "round",
2177
+ strokeLinejoin: "round",
2178
+ children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("polyline", { points: "20 6 9 17 4 12" })
2179
+ }
2180
+ ) })
2181
+ ]
2182
+ }
2183
+ );
2184
+ }
2185
+
2414
2186
  // src/components/form/form-field.tsx
2415
2187
  var import_jsx_runtime36 = require("react/jsx-runtime");
2416
2188
  function FormField({
@@ -2478,7 +2250,7 @@ function FormSection({
2478
2250
  }
2479
2251
 
2480
2252
  // src/components/form/local-input.tsx
2481
- var import_react = require("react");
2253
+ var import_react2 = require("react");
2482
2254
  var import_jsx_runtime39 = require("react/jsx-runtime");
2483
2255
  function LocalInput({
2484
2256
  value,
@@ -2486,11 +2258,11 @@ function LocalInput({
2486
2258
  commitOnBlur,
2487
2259
  ...rest
2488
2260
  }) {
2489
- const [local, setLocal] = (0, import_react.useState)(String(value ?? ""));
2490
- const ref = (0, import_react.useRef)(null);
2491
- const onChangeRef = (0, import_react.useRef)(onChange);
2261
+ const [local, setLocal] = (0, import_react2.useState)(String(value ?? ""));
2262
+ const ref = (0, import_react2.useRef)(null);
2263
+ const onChangeRef = (0, import_react2.useRef)(onChange);
2492
2264
  onChangeRef.current = onChange;
2493
- (0, import_react.useEffect)(() => {
2265
+ (0, import_react2.useEffect)(() => {
2494
2266
  if (document.activeElement !== ref.current) {
2495
2267
  setLocal(String(value ?? ""));
2496
2268
  }
@@ -2517,7 +2289,7 @@ function LocalInput({
2517
2289
  }
2518
2290
 
2519
2291
  // src/components/form/responsive-size-device-icon.tsx
2520
- var import_react2 = require("react");
2292
+ var import_react3 = require("react");
2521
2293
 
2522
2294
  // src/components/form/responsive-types.tsx
2523
2295
  var import_jsx_runtime40 = require("react/jsx-runtime");
@@ -2552,7 +2324,7 @@ function ResponsiveSizeDeviceIcon({
2552
2324
  activeDevice,
2553
2325
  setActiveDevice
2554
2326
  }) {
2555
- const [open, setOpen] = (0, import_react2.useState)(false);
2327
+ const [open, setOpen] = (0, import_react3.useState)(false);
2556
2328
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "relative", children: [
2557
2329
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2558
2330
  "button",
@@ -2602,7 +2374,7 @@ function ResponsiveSizeDeviceIcon({
2602
2374
  }
2603
2375
 
2604
2376
  // src/components/form/responsive-size-field.tsx
2605
- var import_react3 = require("react");
2377
+ var import_react4 = require("react");
2606
2378
  var import_jsx_runtime42 = require("react/jsx-runtime");
2607
2379
  function ResponsiveSizeField({
2608
2380
  value,
@@ -2613,7 +2385,7 @@ function ResponsiveSizeField({
2613
2385
  onUnitChange,
2614
2386
  onCustomChange
2615
2387
  }) {
2616
- const [unitOpen, setUnitOpen] = (0, import_react3.useState)(false);
2388
+ const [unitOpen, setUnitOpen] = (0, import_react4.useState)(false);
2617
2389
  const currentVal = value[activeDevice] ?? value.desktop ?? 0;
2618
2390
  const isCustom = unit === "custom";
2619
2391
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "relative flex items-center border border-white/10 rounded-md bg-white/5 w-full", style: { height: 32 }, children: [
@@ -2696,23 +2468,23 @@ function ResponsiveSizeField({
2696
2468
  }
2697
2469
 
2698
2470
  // src/components/feedback/alert.tsx
2699
- var import_lucide_react10 = require("lucide-react");
2471
+ var import_lucide_react12 = require("lucide-react");
2700
2472
  var import_jsx_runtime43 = require("react/jsx-runtime");
2701
2473
  var variantConfig = {
2702
2474
  info: {
2703
- icon: import_lucide_react10.Info,
2475
+ icon: import_lucide_react12.Info,
2704
2476
  classes: "border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-100"
2705
2477
  },
2706
2478
  success: {
2707
- icon: import_lucide_react10.CheckCircle2,
2479
+ icon: import_lucide_react12.CheckCircle2,
2708
2480
  classes: "border-green-200 bg-green-50 text-green-900 dark:border-green-800 dark:bg-green-950 dark:text-green-100"
2709
2481
  },
2710
2482
  warning: {
2711
- icon: import_lucide_react10.AlertTriangle,
2483
+ icon: import_lucide_react12.AlertTriangle,
2712
2484
  classes: "border-yellow-200 bg-yellow-50 text-yellow-900 dark:border-yellow-800 dark:bg-yellow-950 dark:text-yellow-100"
2713
2485
  },
2714
2486
  error: {
2715
- icon: import_lucide_react10.AlertCircle,
2487
+ icon: import_lucide_react12.AlertCircle,
2716
2488
  classes: "border-red-200 bg-red-50 text-red-900 dark:border-red-800 dark:bg-red-950 dark:text-red-100"
2717
2489
  }
2718
2490
  };
@@ -2746,7 +2518,7 @@ function Alert({
2746
2518
  {
2747
2519
  onClick: onDismiss,
2748
2520
  className: "absolute right-3 top-3 rounded-md p-1 opacity-70 hover:opacity-100",
2749
- children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react10.X, { className: "h-4 w-4" })
2521
+ children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react12.X, { className: "h-4 w-4" })
2750
2522
  }
2751
2523
  )
2752
2524
  ]
@@ -2755,7 +2527,7 @@ function Alert({
2755
2527
  }
2756
2528
 
2757
2529
  // src/components/feedback/loading-spinner.tsx
2758
- var import_lucide_react11 = require("lucide-react");
2530
+ var import_lucide_react13 = require("lucide-react");
2759
2531
  var import_jsx_runtime44 = require("react/jsx-runtime");
2760
2532
  var sizeMap2 = {
2761
2533
  sm: "h-4 w-4",
@@ -2764,7 +2536,7 @@ var sizeMap2 = {
2764
2536
  };
2765
2537
  function LoadingSpinner({ size = "md", className }) {
2766
2538
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2767
- import_lucide_react11.Loader2,
2539
+ import_lucide_react13.Loader2,
2768
2540
  {
2769
2541
  className: cn("animate-spin text-muted-foreground", sizeMap2[size], className)
2770
2542
  }
@@ -2875,7 +2647,7 @@ function PostStatusBadge({
2875
2647
  }
2876
2648
 
2877
2649
  // src/components/content/post-list-table.tsx
2878
- var React21 = __toESM(require("react"));
2650
+ var React20 = __toESM(require("react"));
2879
2651
 
2880
2652
  // src/components/Skeleton.tsx
2881
2653
  var import_jsx_runtime47 = require("react/jsx-runtime");
@@ -2965,10 +2737,10 @@ function getInitials(name) {
2965
2737
  return name.split(" ").slice(0, 2).map((w) => w[0]?.toUpperCase() ?? "").join("");
2966
2738
  }
2967
2739
  function RowActions({ post, onEdit, onDelete, onDuplicate, onStatusChange }) {
2968
- const [open, setOpen] = React21.useState(false);
2969
- const [statusOpen, setStatusOpen] = React21.useState(false);
2970
- const ref = React21.useRef(null);
2971
- React21.useEffect(() => {
2740
+ const [open, setOpen] = React20.useState(false);
2741
+ const [statusOpen, setStatusOpen] = React20.useState(false);
2742
+ const ref = React20.useRef(null);
2743
+ React20.useEffect(() => {
2972
2744
  if (!open) return;
2973
2745
  function handleClick(e) {
2974
2746
  if (ref.current && !ref.current.contains(e.target)) {
@@ -3480,7 +3252,7 @@ function SlugInput({
3480
3252
  }
3481
3253
 
3482
3254
  // src/components/content/post-sidebar-section.tsx
3483
- var React22 = __toESM(require("react"));
3255
+ var React21 = __toESM(require("react"));
3484
3256
  var import_jsx_runtime52 = require("react/jsx-runtime");
3485
3257
  function IconChevronDown() {
3486
3258
  return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
@@ -3504,7 +3276,7 @@ function PostSidebarSection({
3504
3276
  defaultOpen = true,
3505
3277
  className
3506
3278
  }) {
3507
- const [open, setOpen] = React22.useState(defaultOpen);
3279
+ const [open, setOpen] = React21.useState(defaultOpen);
3508
3280
  return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: cn("border-b border-border", className), children: [
3509
3281
  /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
3510
3282
  "button",
@@ -3540,18 +3312,18 @@ function PostSidebarSection({
3540
3312
  }
3541
3313
 
3542
3314
  // src/components/ui/hsl-color-input.tsx
3543
- var import_react5 = require("react");
3315
+ var import_react6 = require("react");
3544
3316
  var import_color2 = __toESM(require("color"));
3545
3317
  var Popover2 = __toESM(require("@radix-ui/react-popover"));
3546
3318
 
3547
3319
  // src/components/ui/color-picker.tsx
3548
3320
  var import_color = __toESM(require("color"));
3549
- var import_react4 = require("react");
3321
+ var import_react5 = require("react");
3550
3322
  var Slider = __toESM(require("@radix-ui/react-slider"));
3551
3323
  var import_jsx_runtime53 = require("react/jsx-runtime");
3552
- var ColorPickerContext = (0, import_react4.createContext)(void 0);
3324
+ var ColorPickerContext = (0, import_react5.createContext)(void 0);
3553
3325
  var useColorPicker = () => {
3554
- const ctx = (0, import_react4.useContext)(ColorPickerContext);
3326
+ const ctx = (0, import_react5.useContext)(ColorPickerContext);
3555
3327
  if (!ctx) throw new Error("useColorPicker must be used within ColorPicker");
3556
3328
  return ctx;
3557
3329
  };
@@ -3569,51 +3341,51 @@ var ColorPicker = ({
3569
3341
  return (0, import_color.default)("#000000");
3570
3342
  }
3571
3343
  })();
3572
- const [hue, setHueState] = (0, import_react4.useState)(initial.hue());
3573
- const [saturation, setSaturationState] = (0, import_react4.useState)(initial.saturationl());
3574
- const [lightness, setLightnessState] = (0, import_react4.useState)(initial.lightness());
3575
- const [alpha, setAlphaState] = (0, import_react4.useState)(initial.alpha() * 100);
3576
- const [mode, setMode] = (0, import_react4.useState)("HEX");
3577
- const notifyRef = (0, import_react4.useRef)(onChange);
3578
- (0, import_react4.useEffect)(() => {
3344
+ const [hue, setHueState] = (0, import_react5.useState)(initial.hue());
3345
+ const [saturation, setSaturationState] = (0, import_react5.useState)(initial.saturationl());
3346
+ const [lightness, setLightnessState] = (0, import_react5.useState)(initial.lightness());
3347
+ const [alpha, setAlphaState] = (0, import_react5.useState)(initial.alpha() * 100);
3348
+ const [mode, setMode] = (0, import_react5.useState)("HEX");
3349
+ const notifyRef = (0, import_react5.useRef)(onChange);
3350
+ (0, import_react5.useEffect)(() => {
3579
3351
  notifyRef.current = onChange;
3580
3352
  }, [onChange]);
3581
- const notify = (0, import_react4.useCallback)((h, s, l, a) => {
3353
+ const notify = (0, import_react5.useCallback)((h, s, l, a) => {
3582
3354
  notifyRef.current?.(import_color.default.hsl(h, s, l).alpha(a / 100).hexa());
3583
3355
  }, []);
3584
- const setHue = (0, import_react4.useCallback)((h) => {
3356
+ const setHue = (0, import_react5.useCallback)((h) => {
3585
3357
  setHueState(h);
3586
3358
  notify(h, saturation, lightness, alpha);
3587
3359
  }, [saturation, lightness, alpha, notify]);
3588
- const setSaturation = (0, import_react4.useCallback)((s) => {
3360
+ const setSaturation = (0, import_react5.useCallback)((s) => {
3589
3361
  setSaturationState(s);
3590
3362
  notify(hue, s, lightness, alpha);
3591
3363
  }, [hue, lightness, alpha, notify]);
3592
- const setLightness = (0, import_react4.useCallback)((l) => {
3364
+ const setLightness = (0, import_react5.useCallback)((l) => {
3593
3365
  setLightnessState(l);
3594
3366
  notify(hue, saturation, l, alpha);
3595
3367
  }, [hue, saturation, alpha, notify]);
3596
- const setAlpha = (0, import_react4.useCallback)((a) => {
3368
+ const setAlpha = (0, import_react5.useCallback)((a) => {
3597
3369
  setAlphaState(a);
3598
3370
  notify(hue, saturation, lightness, a);
3599
3371
  }, [hue, saturation, lightness, notify]);
3600
3372
  return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ColorPickerContext.Provider, { value: { hue, saturation, lightness, alpha, mode, setHue, setSaturation, setLightness, setAlpha, setMode }, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: cn("flex flex-col gap-3", className), ...props, children }) });
3601
3373
  };
3602
- var ColorPickerSelection = (0, import_react4.memo)(({ className, ...props }) => {
3603
- const containerRef = (0, import_react4.useRef)(null);
3604
- const [isDragging, setIsDragging] = (0, import_react4.useState)(false);
3374
+ var ColorPickerSelection = (0, import_react5.memo)(({ className, ...props }) => {
3375
+ const containerRef = (0, import_react5.useRef)(null);
3376
+ const [isDragging, setIsDragging] = (0, import_react5.useState)(false);
3605
3377
  const { hue, saturation, lightness, setSaturation, setLightness } = useColorPicker();
3606
- const [posX, setPosX] = (0, import_react4.useState)(() => saturation / 100);
3607
- const [posY, setPosY] = (0, import_react4.useState)(() => {
3378
+ const [posX, setPosX] = (0, import_react5.useState)(() => saturation / 100);
3379
+ const [posY, setPosY] = (0, import_react5.useState)(() => {
3608
3380
  const x = saturation / 100;
3609
3381
  const topL = x < 0.01 ? 100 : 50 + 50 * (1 - x);
3610
3382
  return topL > 0 ? Math.max(0, Math.min(1, 1 - lightness / topL)) : 0;
3611
3383
  });
3612
- const bg = (0, import_react4.useMemo)(
3384
+ const bg = (0, import_react5.useMemo)(
3613
3385
  () => `linear-gradient(0deg,rgba(0,0,0,1),rgba(0,0,0,0)),linear-gradient(90deg,rgba(255,255,255,1),rgba(255,255,255,0)),hsl(${hue},100%,50%)`,
3614
3386
  [hue]
3615
3387
  );
3616
- const handleMove = (0, import_react4.useCallback)((e) => {
3388
+ const handleMove = (0, import_react5.useCallback)((e) => {
3617
3389
  if (!isDragging || !containerRef.current) return;
3618
3390
  const rect = containerRef.current.getBoundingClientRect();
3619
3391
  const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
@@ -3623,7 +3395,7 @@ var ColorPickerSelection = (0, import_react4.memo)(({ className, ...props }) =>
3623
3395
  setSaturation(x * 100);
3624
3396
  setLightness((x < 0.01 ? 100 : 50 + 50 * (1 - x)) * (1 - y));
3625
3397
  }, [isDragging, setSaturation, setLightness]);
3626
- (0, import_react4.useEffect)(() => {
3398
+ (0, import_react5.useEffect)(() => {
3627
3399
  if (!isDragging) return;
3628
3400
  const up = () => setIsDragging(false);
3629
3401
  window.addEventListener("pointermove", handleMove);
@@ -3732,8 +3504,8 @@ var ColorPickerOutput = ({ className, ...props }) => {
3732
3504
  };
3733
3505
  var ColorPickerFormat = ({ className, ...props }) => {
3734
3506
  const { hue, saturation, lightness, alpha, mode, setHue, setSaturation, setLightness, setAlpha } = useColorPicker();
3735
- const [focused, setFocused] = (0, import_react4.useState)(false);
3736
- const [localVal, setLocalVal] = (0, import_react4.useState)("");
3507
+ const [focused, setFocused] = (0, import_react5.useState)(false);
3508
+ const [localVal, setLocalVal] = (0, import_react5.useState)("");
3737
3509
  const computedVal = (() => {
3738
3510
  try {
3739
3511
  const c = import_color.default.hsl(hue, saturation, lightness).alpha(alpha / 100);
@@ -3747,7 +3519,7 @@ var ColorPickerFormat = ({ className, ...props }) => {
3747
3519
  }
3748
3520
  return "";
3749
3521
  })();
3750
- (0, import_react4.useEffect)(() => {
3522
+ (0, import_react5.useEffect)(() => {
3751
3523
  if (!focused) setLocalVal(computedVal);
3752
3524
  }, [computedVal, focused]);
3753
3525
  const tryApply = (raw) => {
@@ -3845,7 +3617,7 @@ function hexToHsl(hex) {
3845
3617
  }
3846
3618
  function ColorReader({ onHexChange }) {
3847
3619
  const { hue, saturation, lightness, alpha } = useColorPicker();
3848
- (0, import_react5.useEffect)(() => {
3620
+ (0, import_react6.useEffect)(() => {
3849
3621
  try {
3850
3622
  const hex = import_color2.default.hsl(hue, saturation, lightness).alpha(alpha / 100).hex();
3851
3623
  onHexChange(hex);
@@ -3855,21 +3627,21 @@ function ColorReader({ onHexChange }) {
3855
3627
  return null;
3856
3628
  }
3857
3629
  function HslColorInput({ value, onChange, className, inputClassName, disabled }) {
3858
- const [open, setOpen] = (0, import_react5.useState)(false);
3630
+ const [open, setOpen] = (0, import_react6.useState)(false);
3859
3631
  const hexValue = hslToHex(value);
3860
3632
  const cssColor = value ? `hsl(${value})` : "transparent";
3861
- const pendingHexRef = (0, import_react5.useRef)(hexValue);
3862
- const onChangeRef = (0, import_react5.useRef)(onChange);
3863
- (0, import_react5.useEffect)(() => {
3633
+ const pendingHexRef = (0, import_react6.useRef)(hexValue);
3634
+ const onChangeRef = (0, import_react6.useRef)(onChange);
3635
+ (0, import_react6.useEffect)(() => {
3864
3636
  onChangeRef.current = onChange;
3865
3637
  }, [onChange]);
3866
- (0, import_react5.useEffect)(() => {
3638
+ (0, import_react6.useEffect)(() => {
3867
3639
  if (open) pendingHexRef.current = hexValue;
3868
3640
  }, [open, hexValue]);
3869
- const handleHexChange = (0, import_react5.useCallback)((hex) => {
3641
+ const handleHexChange = (0, import_react6.useCallback)((hex) => {
3870
3642
  pendingHexRef.current = hex;
3871
3643
  }, []);
3872
- const handleOpenChange = (0, import_react5.useCallback)((newOpen) => {
3644
+ const handleOpenChange = (0, import_react6.useCallback)((newOpen) => {
3873
3645
  if (!newOpen && open) {
3874
3646
  const pending = pendingHexRef.current;
3875
3647
  if (pending && pending !== hexValue) {
@@ -3937,9 +3709,9 @@ function HslColorInput({ value, onChange, className, inputClassName, disabled })
3937
3709
  }
3938
3710
 
3939
3711
  // src/hooks/index.ts
3940
- var import_react6 = require("react");
3712
+ var import_react7 = require("react");
3941
3713
  function useDisclosure(initial = false) {
3942
- const [isOpen, setIsOpen] = (0, import_react6.useState)(initial);
3714
+ const [isOpen, setIsOpen] = (0, import_react7.useState)(initial);
3943
3715
  return {
3944
3716
  isOpen,
3945
3717
  open: () => setIsOpen(true),
@@ -3949,7 +3721,7 @@ function useDisclosure(initial = false) {
3949
3721
  };
3950
3722
  }
3951
3723
  function usePagination(total, pageSize = 20) {
3952
- const [page, setPage] = (0, import_react6.useState)(1);
3724
+ const [page, setPage] = (0, import_react7.useState)(1);
3953
3725
  const totalPages = Math.ceil(total / pageSize);
3954
3726
  return { page, setPage, pageSize, total, totalPages };
3955
3727
  }
@@ -4346,8 +4118,6 @@ var import_next_themes2 = require("next-themes");
4346
4118
  LoadingSpinner,
4347
4119
  LocalInput,
4348
4120
  MediaCard,
4349
- MediaGrid,
4350
- MediaPickerDialog,
4351
4121
  NotificationBell,
4352
4122
  Page,
4353
4123
  PageSection,
@@ -4393,6 +4163,8 @@ var import_next_themes2 = require("next-themes");
4393
4163
  TooltipProvider,
4394
4164
  TooltipTrigger,
4395
4165
  TopBar,
4166
+ UploadProgressPanel,
4167
+ UploadZone,
4396
4168
  badgeVariants,
4397
4169
  buttonVariants,
4398
4170
  cn,