@anvilkit/plugin-asset-manager 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +159 -0
  3. package/dist/adapters/data-url.cjs +78 -0
  4. package/dist/adapters/data-url.d.cts +6 -0
  5. package/dist/adapters/data-url.d.cts.map +1 -0
  6. package/dist/adapters/data-url.d.ts +6 -0
  7. package/dist/adapters/data-url.d.ts.map +1 -0
  8. package/dist/adapters/data-url.js +44 -0
  9. package/dist/adapters/extract-image-dimensions.cjs +69 -0
  10. package/dist/adapters/extract-image-dimensions.d.cts +21 -0
  11. package/dist/adapters/extract-image-dimensions.d.cts.map +1 -0
  12. package/dist/adapters/extract-image-dimensions.d.ts +21 -0
  13. package/dist/adapters/extract-image-dimensions.d.ts.map +1 -0
  14. package/dist/adapters/extract-image-dimensions.js +35 -0
  15. package/dist/adapters/in-memory.cjs +62 -0
  16. package/dist/adapters/in-memory.d.cts +3 -0
  17. package/dist/adapters/in-memory.d.cts.map +1 -0
  18. package/dist/adapters/in-memory.d.ts +3 -0
  19. package/dist/adapters/in-memory.d.ts.map +1 -0
  20. package/dist/adapters/in-memory.js +28 -0
  21. package/dist/adapters/s3-presigned.cjs +166 -0
  22. package/dist/adapters/s3-presigned.d.cts +59 -0
  23. package/dist/adapters/s3-presigned.d.cts.map +1 -0
  24. package/dist/adapters/s3-presigned.d.ts +59 -0
  25. package/dist/adapters/s3-presigned.d.ts.map +1 -0
  26. package/dist/adapters/s3-presigned.js +129 -0
  27. package/dist/asset-reference.cjs +38 -0
  28. package/dist/asset-reference.d.cts +10 -0
  29. package/dist/asset-reference.d.cts.map +1 -0
  30. package/dist/asset-reference.d.ts +10 -0
  31. package/dist/asset-reference.d.ts.map +1 -0
  32. package/dist/asset-reference.js +4 -0
  33. package/dist/csp.cjs +83 -0
  34. package/dist/csp.d.cts +45 -0
  35. package/dist/csp.d.cts.map +1 -0
  36. package/dist/csp.d.ts +45 -0
  37. package/dist/csp.d.ts.map +1 -0
  38. package/dist/csp.js +49 -0
  39. package/dist/errors.cjs +58 -0
  40. package/dist/errors.d.cts +15 -0
  41. package/dist/errors.d.cts.map +1 -0
  42. package/dist/errors.d.ts +15 -0
  43. package/dist/errors.d.ts.map +1 -0
  44. package/dist/errors.js +21 -0
  45. package/dist/header-action.cjs +44 -0
  46. package/dist/header-action.d.cts +3 -0
  47. package/dist/header-action.d.cts.map +1 -0
  48. package/dist/header-action.d.ts +3 -0
  49. package/dist/header-action.d.ts.map +1 -0
  50. package/dist/header-action.js +10 -0
  51. package/dist/index.cjs +90 -0
  52. package/dist/index.d.cts +18 -0
  53. package/dist/index.d.cts.map +1 -0
  54. package/dist/index.d.ts +18 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +10 -0
  57. package/dist/infer-kind.cjs +45 -0
  58. package/dist/infer-kind.d.cts +8 -0
  59. package/dist/infer-kind.d.cts.map +1 -0
  60. package/dist/infer-kind.d.ts +8 -0
  61. package/dist/infer-kind.d.ts.map +1 -0
  62. package/dist/infer-kind.js +11 -0
  63. package/dist/plugin.cjs +258 -0
  64. package/dist/plugin.d.cts +9 -0
  65. package/dist/plugin.d.cts.map +1 -0
  66. package/dist/plugin.d.ts +9 -0
  67. package/dist/plugin.d.ts.map +1 -0
  68. package/dist/plugin.js +212 -0
  69. package/dist/registry.cjs +198 -0
  70. package/dist/registry.d.cts +3 -0
  71. package/dist/registry.d.cts.map +1 -0
  72. package/dist/registry.d.ts +3 -0
  73. package/dist/registry.d.ts.map +1 -0
  74. package/dist/registry.js +164 -0
  75. package/dist/resolver.cjs +185 -0
  76. package/dist/resolver.d.cts +10 -0
  77. package/dist/resolver.d.cts.map +1 -0
  78. package/dist/resolver.d.ts +10 -0
  79. package/dist/resolver.d.ts.map +1 -0
  80. package/dist/resolver.js +148 -0
  81. package/dist/retry.cjs +123 -0
  82. package/dist/retry.d.cts +71 -0
  83. package/dist/retry.d.cts.map +1 -0
  84. package/dist/retry.d.ts +71 -0
  85. package/dist/retry.d.ts.map +1 -0
  86. package/dist/retry.js +86 -0
  87. package/dist/studio-asset-source.cjs +211 -0
  88. package/dist/studio-asset-source.d.cts +52 -0
  89. package/dist/studio-asset-source.d.cts.map +1 -0
  90. package/dist/studio-asset-source.d.ts +52 -0
  91. package/dist/studio-asset-source.d.ts.map +1 -0
  92. package/dist/studio-asset-source.js +171 -0
  93. package/dist/testing/index.cjs +66 -0
  94. package/dist/testing/index.d.cts +24 -0
  95. package/dist/testing/index.d.cts.map +1 -0
  96. package/dist/testing/index.d.ts +24 -0
  97. package/dist/testing/index.d.ts.map +1 -0
  98. package/dist/testing/index.js +29 -0
  99. package/dist/types.cjs +18 -0
  100. package/dist/types.d.cts +132 -0
  101. package/dist/types.d.cts.map +1 -0
  102. package/dist/types.d.ts +132 -0
  103. package/dist/types.d.ts.map +1 -0
  104. package/dist/types.js +0 -0
  105. package/dist/ui/AssetBrowser.cjs +271 -0
  106. package/dist/ui/AssetBrowser.d.cts +45 -0
  107. package/dist/ui/AssetBrowser.d.cts.map +1 -0
  108. package/dist/ui/AssetBrowser.d.ts +45 -0
  109. package/dist/ui/AssetBrowser.d.ts.map +1 -0
  110. package/dist/ui/AssetBrowser.js +237 -0
  111. package/dist/ui/AssetCommandPalette.cjs +135 -0
  112. package/dist/ui/AssetCommandPalette.d.cts +21 -0
  113. package/dist/ui/AssetCommandPalette.d.cts.map +1 -0
  114. package/dist/ui/AssetCommandPalette.d.ts +21 -0
  115. package/dist/ui/AssetCommandPalette.d.ts.map +1 -0
  116. package/dist/ui/AssetCommandPalette.js +101 -0
  117. package/dist/ui/AssetManagerUI.cjs +169 -0
  118. package/dist/ui/AssetManagerUI.d.cts +15 -0
  119. package/dist/ui/AssetManagerUI.d.cts.map +1 -0
  120. package/dist/ui/AssetManagerUI.d.ts +15 -0
  121. package/dist/ui/AssetManagerUI.d.ts.map +1 -0
  122. package/dist/ui/AssetManagerUI.js +135 -0
  123. package/dist/ui/DeleteAssetDialog.cjs +70 -0
  124. package/dist/ui/DeleteAssetDialog.d.cts +22 -0
  125. package/dist/ui/DeleteAssetDialog.d.cts.map +1 -0
  126. package/dist/ui/DeleteAssetDialog.d.ts +22 -0
  127. package/dist/ui/DeleteAssetDialog.d.ts.map +1 -0
  128. package/dist/ui/DeleteAssetDialog.js +36 -0
  129. package/dist/ui/MetadataPanel.cjs +147 -0
  130. package/dist/ui/MetadataPanel.d.cts +21 -0
  131. package/dist/ui/MetadataPanel.d.cts.map +1 -0
  132. package/dist/ui/MetadataPanel.d.ts +21 -0
  133. package/dist/ui/MetadataPanel.d.ts.map +1 -0
  134. package/dist/ui/MetadataPanel.js +113 -0
  135. package/dist/ui/ReplaceAssetDialog.cjs +125 -0
  136. package/dist/ui/ReplaceAssetDialog.d.cts +14 -0
  137. package/dist/ui/ReplaceAssetDialog.d.cts.map +1 -0
  138. package/dist/ui/ReplaceAssetDialog.d.ts +14 -0
  139. package/dist/ui/ReplaceAssetDialog.d.ts.map +1 -0
  140. package/dist/ui/ReplaceAssetDialog.js +91 -0
  141. package/dist/ui/UploadButton.cjs +189 -0
  142. package/dist/ui/UploadButton.d.cts +17 -0
  143. package/dist/ui/UploadButton.d.cts.map +1 -0
  144. package/dist/ui/UploadButton.d.ts +17 -0
  145. package/dist/ui/UploadButton.d.ts.map +1 -0
  146. package/dist/ui/UploadButton.js +155 -0
  147. package/dist/ui/index.cjs +60 -0
  148. package/dist/ui/index.d.cts +15 -0
  149. package/dist/ui/index.d.cts.map +1 -0
  150. package/dist/ui/index.d.ts +15 -0
  151. package/dist/ui/index.d.ts.map +1 -0
  152. package/dist/ui/index.js +7 -0
  153. package/dist/validate-upload-result.cjs +149 -0
  154. package/dist/validate-upload-result.d.cts +9 -0
  155. package/dist/validate-upload-result.d.cts.map +1 -0
  156. package/dist/validate-upload-result.d.ts +9 -0
  157. package/dist/validate-upload-result.d.ts.map +1 -0
  158. package/dist/validate-upload-result.js +115 -0
  159. package/package.json +131 -0
@@ -0,0 +1,237 @@
1
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@anvilkit/ui/card";
2
+ import { Input } from "@anvilkit/ui/input";
3
+ import { inferAssetKind } from "../infer-kind.js";
4
+ import * as __rspack_external_react from "react";
5
+ const KIND_FILTERS = [
6
+ "image",
7
+ "video",
8
+ "audio",
9
+ "font",
10
+ "document"
11
+ ];
12
+ const DEFAULT_VIRTUALIZE_THRESHOLD = 50;
13
+ const DEFAULT_ITEM_HEIGHT = 56;
14
+ const DEFAULT_MAX_HEIGHT = 400;
15
+ const DEFAULT_PAGE_SIZE = 100;
16
+ const OVERSCAN = 4;
17
+ function AssetBrowser({ assets, onInsert, onDelete, onReplace, onEdit, searchEnabled = false, pageSize = DEFAULT_PAGE_SIZE, virtualizeThreshold = DEFAULT_VIRTUALIZE_THRESHOLD, itemHeight = DEFAULT_ITEM_HEIGHT, maxHeight = DEFAULT_MAX_HEIGHT }) {
18
+ const [activeIndex, setActiveIndex] = __rspack_external_react.useState(assets.length > 0 ? 0 : -1);
19
+ const [scrollTop, setScrollTop] = __rspack_external_react.useState(0);
20
+ const [query, setQuery] = __rspack_external_react.useState("");
21
+ const [activeKinds, setActiveKinds] = __rspack_external_react.useState([]);
22
+ const [pageLimit, setPageLimit] = __rspack_external_react.useState(pageSize);
23
+ const buttonRefs = __rspack_external_react.useRef([]);
24
+ const scrollContainerRef = __rspack_external_react.useRef(null);
25
+ const filteredAssets = __rspack_external_react.useMemo(()=>{
26
+ if (!searchEnabled) return assets;
27
+ const lower = query.trim().toLowerCase();
28
+ return assets.filter((asset)=>{
29
+ if (activeKinds.length > 0) {
30
+ if (!activeKinds.includes(inferAssetKind(asset))) return false;
31
+ }
32
+ if ("" === lower) return true;
33
+ if (asset.id.toLowerCase().includes(lower)) return true;
34
+ if (asset.name?.toLowerCase().includes(lower)) return true;
35
+ if (asset.meta?.mimeType?.toLowerCase().includes(lower)) return true;
36
+ if (asset.tags?.some((tag)=>tag.toLowerCase().includes(lower))) return true;
37
+ return false;
38
+ });
39
+ }, [
40
+ assets,
41
+ activeKinds,
42
+ query,
43
+ searchEnabled
44
+ ]);
45
+ const visibleSlice = __rspack_external_react.useMemo(()=>searchEnabled ? filteredAssets.slice(0, pageLimit) : filteredAssets, [
46
+ filteredAssets,
47
+ pageLimit,
48
+ searchEnabled
49
+ ]);
50
+ const total = visibleSlice.length;
51
+ const isVirtualized = total > virtualizeThreshold;
52
+ const hasMore = searchEnabled && filteredAssets.length > visibleSlice.length;
53
+ __rspack_external_react.useEffect(()=>{
54
+ if (0 === total) return void setActiveIndex(-1);
55
+ setActiveIndex((currentIndex)=>currentIndex >= 0 && currentIndex < total ? currentIndex : 0);
56
+ }, [
57
+ total
58
+ ]);
59
+ function moveFocus(nextIndex) {
60
+ if (0 === total) return;
61
+ const clampedIndex = Math.max(0, Math.min(nextIndex, total - 1));
62
+ setActiveIndex(clampedIndex);
63
+ if (isVirtualized && scrollContainerRef.current) {
64
+ const targetTop = clampedIndex * itemHeight;
65
+ const targetBottom = targetTop + itemHeight;
66
+ const viewTop = scrollContainerRef.current.scrollTop;
67
+ const viewBottom = viewTop + maxHeight;
68
+ if (targetTop < viewTop) scrollContainerRef.current.scrollTop = targetTop;
69
+ else if (targetBottom > viewBottom) scrollContainerRef.current.scrollTop = targetBottom - maxHeight;
70
+ queueMicrotask(()=>{
71
+ buttonRefs.current[clampedIndex]?.focus();
72
+ });
73
+ return;
74
+ }
75
+ buttonRefs.current[clampedIndex]?.focus();
76
+ }
77
+ function toggleKind(kind) {
78
+ setActiveKinds((current)=>current.includes(kind) ? current.filter((entry)=>entry !== kind) : [
79
+ ...current,
80
+ kind
81
+ ]);
82
+ }
83
+ const firstVisible = isVirtualized ? Math.max(0, Math.floor(scrollTop / itemHeight) - OVERSCAN) : 0;
84
+ const lastVisible = isVirtualized ? Math.min(total - 1, Math.ceil((scrollTop + maxHeight) / itemHeight) + OVERSCAN) : total - 1;
85
+ const visibleAssets = 0 === total ? [] : isVirtualized ? visibleSlice.slice(firstVisible, lastVisible + 1) : visibleSlice;
86
+ function renderRow(asset, index) {
87
+ return /*#__PURE__*/ __rspack_external_react.createElement("li", {
88
+ "aria-posinset": index + 1,
89
+ "aria-setsize": total,
90
+ key: asset.id,
91
+ role: "listitem"
92
+ }, /*#__PURE__*/ __rspack_external_react.createElement("button", {
93
+ "aria-label": `Insert asset ${asset.id}`,
94
+ onClick: ()=>{
95
+ onInsert(asset);
96
+ },
97
+ onFocus: ()=>{
98
+ setActiveIndex(index);
99
+ },
100
+ onKeyDown: (event)=>{
101
+ if ("ArrowDown" === event.key) {
102
+ event.preventDefault();
103
+ moveFocus(index + 1);
104
+ return;
105
+ }
106
+ if ("ArrowUp" === event.key) {
107
+ event.preventDefault();
108
+ moveFocus(index - 1);
109
+ return;
110
+ }
111
+ if ("Home" === event.key) {
112
+ event.preventDefault();
113
+ moveFocus(0);
114
+ return;
115
+ }
116
+ if ("End" === event.key) {
117
+ event.preventDefault();
118
+ moveFocus(total - 1);
119
+ return;
120
+ }
121
+ if ("Enter" === event.key || " " === event.key) {
122
+ event.preventDefault();
123
+ onInsert(asset);
124
+ }
125
+ },
126
+ ref: (node)=>{
127
+ buttonRefs.current[index] = node;
128
+ },
129
+ tabIndex: activeIndex === index ? 0 : -1,
130
+ type: "button"
131
+ }, /*#__PURE__*/ __rspack_external_react.createElement("span", null, asset.id), /*#__PURE__*/ __rspack_external_react.createElement("span", null, asset.meta?.mimeType ?? "unknown type")), void 0 !== onEdit ? /*#__PURE__*/ __rspack_external_react.createElement("button", {
132
+ "aria-label": `Edit asset ${asset.id}`,
133
+ "data-asset-action": "edit",
134
+ onClick: ()=>{
135
+ onEdit(asset);
136
+ },
137
+ type: "button"
138
+ }, "Edit") : null, void 0 !== onReplace ? /*#__PURE__*/ __rspack_external_react.createElement("button", {
139
+ "aria-label": `Replace asset ${asset.id}`,
140
+ "data-asset-action": "replace",
141
+ onClick: ()=>{
142
+ onReplace(asset);
143
+ },
144
+ type: "button"
145
+ }, "Replace") : null, void 0 !== onDelete ? /*#__PURE__*/ __rspack_external_react.createElement("button", {
146
+ "aria-label": `Delete asset ${asset.id}`,
147
+ "data-asset-action": "delete",
148
+ onClick: ()=>{
149
+ onDelete(asset);
150
+ },
151
+ type: "button"
152
+ }, "Delete") : null);
153
+ }
154
+ const filterRow = searchEnabled ? /*#__PURE__*/ __rspack_external_react.createElement("div", {
155
+ "data-asset-manager-filters": true
156
+ }, /*#__PURE__*/ __rspack_external_react.createElement(Input, {
157
+ "aria-label": "Search assets",
158
+ onChange: (event)=>{
159
+ setQuery(event.target.value);
160
+ setPageLimit(pageSize);
161
+ },
162
+ placeholder: "Search by name, tag, or MIME",
163
+ value: query
164
+ }), /*#__PURE__*/ __rspack_external_react.createElement("div", {
165
+ "aria-label": "Asset kind filters",
166
+ role: "group"
167
+ }, KIND_FILTERS.map((kind)=>{
168
+ const active = activeKinds.includes(kind);
169
+ return /*#__PURE__*/ __rspack_external_react.createElement("button", {
170
+ "aria-label": `Filter ${kind} assets`,
171
+ "aria-pressed": active,
172
+ "data-asset-kind-filter": kind,
173
+ key: kind,
174
+ onClick: ()=>{
175
+ toggleKind(kind);
176
+ setPageLimit(pageSize);
177
+ },
178
+ type: "button"
179
+ }, kind);
180
+ }))) : null;
181
+ if (0 === total) {
182
+ const emptyLabel = searchEnabled && ("" !== query || activeKinds.length > 0) ? "No assets match the current filters." : "No assets uploaded yet.";
183
+ return /*#__PURE__*/ __rspack_external_react.createElement(Card, null, /*#__PURE__*/ __rspack_external_react.createElement(CardHeader, null, /*#__PURE__*/ __rspack_external_react.createElement(CardTitle, null, "Asset browser"), /*#__PURE__*/ __rspack_external_react.createElement(CardDescription, null, "Validated assets currently registered in memory.")), /*#__PURE__*/ __rspack_external_react.createElement(CardContent, null, filterRow, /*#__PURE__*/ __rspack_external_react.createElement("ul", {
184
+ "aria-label": "Assets",
185
+ role: "list"
186
+ }, /*#__PURE__*/ __rspack_external_react.createElement("li", {
187
+ role: "listitem"
188
+ }, emptyLabel))));
189
+ }
190
+ if (!isVirtualized) return /*#__PURE__*/ __rspack_external_react.createElement(Card, null, /*#__PURE__*/ __rspack_external_react.createElement(CardHeader, null, /*#__PURE__*/ __rspack_external_react.createElement(CardTitle, null, "Asset browser"), /*#__PURE__*/ __rspack_external_react.createElement(CardDescription, null, "Validated assets currently registered in memory.")), /*#__PURE__*/ __rspack_external_react.createElement(CardContent, null, filterRow, /*#__PURE__*/ __rspack_external_react.createElement("ul", {
191
+ "aria-label": "Assets",
192
+ role: "list"
193
+ }, visibleAssets.map((asset, offset)=>renderRow(asset, offset))), hasMore ? /*#__PURE__*/ __rspack_external_react.createElement("button", {
194
+ "data-asset-action": "load-more",
195
+ onClick: ()=>{
196
+ setPageLimit((current)=>current + pageSize);
197
+ },
198
+ type: "button"
199
+ }, "Load more") : null));
200
+ const totalHeight = total * itemHeight;
201
+ const offsetY = firstVisible * itemHeight;
202
+ return /*#__PURE__*/ __rspack_external_react.createElement(Card, null, /*#__PURE__*/ __rspack_external_react.createElement(CardHeader, null, /*#__PURE__*/ __rspack_external_react.createElement(CardTitle, null, "Asset browser"), /*#__PURE__*/ __rspack_external_react.createElement(CardDescription, null, "Validated assets currently registered in memory.")), /*#__PURE__*/ __rspack_external_react.createElement(CardContent, null, filterRow, /*#__PURE__*/ __rspack_external_react.createElement("div", {
203
+ "data-asset-manager-virtual": true,
204
+ onScroll: (event)=>{
205
+ setScrollTop(event.currentTarget.scrollTop);
206
+ },
207
+ ref: scrollContainerRef,
208
+ style: {
209
+ height: maxHeight,
210
+ overflowY: "auto",
211
+ position: "relative"
212
+ }
213
+ }, /*#__PURE__*/ __rspack_external_react.createElement("div", {
214
+ style: {
215
+ height: totalHeight,
216
+ position: "relative"
217
+ }
218
+ }, /*#__PURE__*/ __rspack_external_react.createElement("ul", {
219
+ "aria-label": "Assets",
220
+ role: "list",
221
+ style: {
222
+ margin: 0,
223
+ padding: 0,
224
+ position: "absolute",
225
+ top: offsetY,
226
+ left: 0,
227
+ right: 0
228
+ }
229
+ }, visibleAssets.map((asset, offset)=>renderRow(asset, firstVisible + offset))))), hasMore ? /*#__PURE__*/ __rspack_external_react.createElement("button", {
230
+ "data-asset-action": "load-more",
231
+ onClick: ()=>{
232
+ setPageLimit((current)=>current + pageSize);
233
+ },
234
+ type: "button"
235
+ }, "Load more") : null));
236
+ }
237
+ export { AssetBrowser };
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ AssetCommandPalette: ()=>AssetCommandPalette
28
+ });
29
+ const dialog_namespaceObject = require("@anvilkit/ui/dialog");
30
+ const input_namespaceObject = require("@anvilkit/ui/input");
31
+ const external_react_namespaceObject = require("react");
32
+ const external_infer_kind_cjs_namespaceObject = require("../infer-kind.cjs");
33
+ const DEFAULT_MAX_RESULTS = 20;
34
+ function AssetCommandPalette({ registry, open, onOpenChange, onSelect, maxResults = DEFAULT_MAX_RESULTS }) {
35
+ const [query, setQuery] = external_react_namespaceObject.useState("");
36
+ const [results, setResults] = external_react_namespaceObject.useState([]);
37
+ const [activeIndex, setActiveIndex] = external_react_namespaceObject.useState(0);
38
+ const inputRef = external_react_namespaceObject.useRef(null);
39
+ const listRef = external_react_namespaceObject.useRef(null);
40
+ const refresh = external_react_namespaceObject.useCallback((nextQuery)=>{
41
+ const page = registry.search({
42
+ query: nextQuery,
43
+ limit: maxResults
44
+ });
45
+ setResults(page.items);
46
+ setActiveIndex(0);
47
+ }, [
48
+ registry,
49
+ maxResults
50
+ ]);
51
+ external_react_namespaceObject.useEffect(()=>{
52
+ if (!open) return;
53
+ refresh(query);
54
+ const unsubscribe = registry.subscribe(()=>{
55
+ refresh(query);
56
+ });
57
+ return unsubscribe;
58
+ }, [
59
+ open,
60
+ query,
61
+ registry,
62
+ refresh
63
+ ]);
64
+ external_react_namespaceObject.useEffect(()=>{
65
+ if (!open) return;
66
+ setQuery("");
67
+ queueMicrotask(()=>{
68
+ inputRef.current?.focus();
69
+ });
70
+ }, [
71
+ open
72
+ ]);
73
+ function handleKeyDown(event) {
74
+ if ("ArrowDown" === event.key) {
75
+ event.preventDefault();
76
+ setActiveIndex((current)=>0 === results.length ? 0 : Math.min(current + 1, results.length - 1));
77
+ return;
78
+ }
79
+ if ("ArrowUp" === event.key) {
80
+ event.preventDefault();
81
+ setActiveIndex((current)=>Math.max(current - 1, 0));
82
+ return;
83
+ }
84
+ if ("Enter" === event.key) {
85
+ event.preventDefault();
86
+ const picked = results[activeIndex];
87
+ if (void 0 !== picked) {
88
+ onSelect(picked);
89
+ onOpenChange(false);
90
+ }
91
+ }
92
+ }
93
+ return /*#__PURE__*/ external_react_namespaceObject.createElement(dialog_namespaceObject.Dialog, {
94
+ open: open,
95
+ onOpenChange: onOpenChange
96
+ }, /*#__PURE__*/ external_react_namespaceObject.createElement(dialog_namespaceObject.DialogContent, null, /*#__PURE__*/ external_react_namespaceObject.createElement(dialog_namespaceObject.DialogHeader, null, /*#__PURE__*/ external_react_namespaceObject.createElement(dialog_namespaceObject.DialogTitle, null, "Find an asset"), /*#__PURE__*/ external_react_namespaceObject.createElement(dialog_namespaceObject.DialogDescription, null, "Search by name, id, MIME type, or tag.")), /*#__PURE__*/ external_react_namespaceObject.createElement(input_namespaceObject.Input, {
97
+ "aria-label": "Asset search query",
98
+ onChange: (event)=>{
99
+ setQuery(event.target.value);
100
+ },
101
+ onKeyDown: handleKeyDown,
102
+ placeholder: "Type to search…",
103
+ ref: inputRef,
104
+ value: query
105
+ }), /*#__PURE__*/ external_react_namespaceObject.createElement("ul", {
106
+ "aria-label": "Asset results",
107
+ "data-asset-manager-palette-results": true,
108
+ ref: listRef,
109
+ role: "listbox"
110
+ }, 0 === results.length ? /*#__PURE__*/ external_react_namespaceObject.createElement("li", {
111
+ role: "presentation"
112
+ }, "No matches.") : results.map((asset, index)=>/*#__PURE__*/ external_react_namespaceObject.createElement("li", {
113
+ "aria-selected": index === activeIndex,
114
+ key: asset.id,
115
+ role: "option"
116
+ }, /*#__PURE__*/ external_react_namespaceObject.createElement("button", {
117
+ "aria-label": `Insert asset ${asset.id}`,
118
+ "data-active": index === activeIndex ? "true" : void 0,
119
+ onClick: ()=>{
120
+ onSelect(asset);
121
+ onOpenChange(false);
122
+ },
123
+ onMouseEnter: ()=>{
124
+ setActiveIndex(index);
125
+ },
126
+ type: "button"
127
+ }, /*#__PURE__*/ external_react_namespaceObject.createElement("span", null, asset.name ?? asset.id), /*#__PURE__*/ external_react_namespaceObject.createElement("span", null, asset.meta?.mimeType ?? (0, external_infer_kind_cjs_namespaceObject.inferAssetKind)(asset))))))));
128
+ }
129
+ exports.AssetCommandPalette = __webpack_exports__.AssetCommandPalette;
130
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
131
+ "AssetCommandPalette"
132
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
133
+ Object.defineProperty(exports, '__esModule', {
134
+ value: true
135
+ });
@@ -0,0 +1,21 @@
1
+ import type { AssetRegistry, UploadResult } from "../types.js";
2
+ export interface AssetCommandPaletteProps {
3
+ /** Registry the palette searches against. */
4
+ readonly registry: AssetRegistry;
5
+ /** When `true`, the palette is shown. The host owns this state. */
6
+ readonly open: boolean;
7
+ /** Called when the palette is dismissed (Esc, click outside, after pick). */
8
+ readonly onOpenChange: (open: boolean) => void;
9
+ /**
10
+ * Invoked when a result is selected. The host typically calls
11
+ * `onAssetInserted` and closes the palette.
12
+ */
13
+ readonly onSelect: (asset: UploadResult) => void;
14
+ /**
15
+ * Maximum number of results rendered. Defaults to 20 — enough to
16
+ * surface relevant matches without ballooning the dialog height.
17
+ */
18
+ readonly maxResults?: number;
19
+ }
20
+ export declare function AssetCommandPalette({ registry, open, onOpenChange, onSelect, maxResults, }: AssetCommandPaletteProps): import("react/jsx-runtime").JSX.Element;
21
+ //# sourceMappingURL=AssetCommandPalette.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssetCommandPalette.d.cts","sourceRoot":"","sources":["../../src/ui/AssetCommandPalette.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,wBAAwB;IACxC,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID,wBAAgB,mBAAmB,CAAC,EACnC,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,UAAgC,GAChC,EAAE,wBAAwB,2CAmH1B"}
@@ -0,0 +1,21 @@
1
+ import type { AssetRegistry, UploadResult } from "../types.js";
2
+ export interface AssetCommandPaletteProps {
3
+ /** Registry the palette searches against. */
4
+ readonly registry: AssetRegistry;
5
+ /** When `true`, the palette is shown. The host owns this state. */
6
+ readonly open: boolean;
7
+ /** Called when the palette is dismissed (Esc, click outside, after pick). */
8
+ readonly onOpenChange: (open: boolean) => void;
9
+ /**
10
+ * Invoked when a result is selected. The host typically calls
11
+ * `onAssetInserted` and closes the palette.
12
+ */
13
+ readonly onSelect: (asset: UploadResult) => void;
14
+ /**
15
+ * Maximum number of results rendered. Defaults to 20 — enough to
16
+ * surface relevant matches without ballooning the dialog height.
17
+ */
18
+ readonly maxResults?: number;
19
+ }
20
+ export declare function AssetCommandPalette({ registry, open, onOpenChange, onSelect, maxResults, }: AssetCommandPaletteProps): import("react/jsx-runtime").JSX.Element;
21
+ //# sourceMappingURL=AssetCommandPalette.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssetCommandPalette.d.ts","sourceRoot":"","sources":["../../src/ui/AssetCommandPalette.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,wBAAwB;IACxC,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID,wBAAgB,mBAAmB,CAAC,EACnC,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,UAAgC,GAChC,EAAE,wBAAwB,2CAmH1B"}
@@ -0,0 +1,101 @@
1
+ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@anvilkit/ui/dialog";
2
+ import { Input } from "@anvilkit/ui/input";
3
+ import { inferAssetKind } from "../infer-kind.js";
4
+ import * as __rspack_external_react from "react";
5
+ const DEFAULT_MAX_RESULTS = 20;
6
+ function AssetCommandPalette({ registry, open, onOpenChange, onSelect, maxResults = DEFAULT_MAX_RESULTS }) {
7
+ const [query, setQuery] = __rspack_external_react.useState("");
8
+ const [results, setResults] = __rspack_external_react.useState([]);
9
+ const [activeIndex, setActiveIndex] = __rspack_external_react.useState(0);
10
+ const inputRef = __rspack_external_react.useRef(null);
11
+ const listRef = __rspack_external_react.useRef(null);
12
+ const refresh = __rspack_external_react.useCallback((nextQuery)=>{
13
+ const page = registry.search({
14
+ query: nextQuery,
15
+ limit: maxResults
16
+ });
17
+ setResults(page.items);
18
+ setActiveIndex(0);
19
+ }, [
20
+ registry,
21
+ maxResults
22
+ ]);
23
+ __rspack_external_react.useEffect(()=>{
24
+ if (!open) return;
25
+ refresh(query);
26
+ const unsubscribe = registry.subscribe(()=>{
27
+ refresh(query);
28
+ });
29
+ return unsubscribe;
30
+ }, [
31
+ open,
32
+ query,
33
+ registry,
34
+ refresh
35
+ ]);
36
+ __rspack_external_react.useEffect(()=>{
37
+ if (!open) return;
38
+ setQuery("");
39
+ queueMicrotask(()=>{
40
+ inputRef.current?.focus();
41
+ });
42
+ }, [
43
+ open
44
+ ]);
45
+ function handleKeyDown(event) {
46
+ if ("ArrowDown" === event.key) {
47
+ event.preventDefault();
48
+ setActiveIndex((current)=>0 === results.length ? 0 : Math.min(current + 1, results.length - 1));
49
+ return;
50
+ }
51
+ if ("ArrowUp" === event.key) {
52
+ event.preventDefault();
53
+ setActiveIndex((current)=>Math.max(current - 1, 0));
54
+ return;
55
+ }
56
+ if ("Enter" === event.key) {
57
+ event.preventDefault();
58
+ const picked = results[activeIndex];
59
+ if (void 0 !== picked) {
60
+ onSelect(picked);
61
+ onOpenChange(false);
62
+ }
63
+ }
64
+ }
65
+ return /*#__PURE__*/ __rspack_external_react.createElement(Dialog, {
66
+ open: open,
67
+ onOpenChange: onOpenChange
68
+ }, /*#__PURE__*/ __rspack_external_react.createElement(DialogContent, null, /*#__PURE__*/ __rspack_external_react.createElement(DialogHeader, null, /*#__PURE__*/ __rspack_external_react.createElement(DialogTitle, null, "Find an asset"), /*#__PURE__*/ __rspack_external_react.createElement(DialogDescription, null, "Search by name, id, MIME type, or tag.")), /*#__PURE__*/ __rspack_external_react.createElement(Input, {
69
+ "aria-label": "Asset search query",
70
+ onChange: (event)=>{
71
+ setQuery(event.target.value);
72
+ },
73
+ onKeyDown: handleKeyDown,
74
+ placeholder: "Type to search…",
75
+ ref: inputRef,
76
+ value: query
77
+ }), /*#__PURE__*/ __rspack_external_react.createElement("ul", {
78
+ "aria-label": "Asset results",
79
+ "data-asset-manager-palette-results": true,
80
+ ref: listRef,
81
+ role: "listbox"
82
+ }, 0 === results.length ? /*#__PURE__*/ __rspack_external_react.createElement("li", {
83
+ role: "presentation"
84
+ }, "No matches.") : results.map((asset, index)=>/*#__PURE__*/ __rspack_external_react.createElement("li", {
85
+ "aria-selected": index === activeIndex,
86
+ key: asset.id,
87
+ role: "option"
88
+ }, /*#__PURE__*/ __rspack_external_react.createElement("button", {
89
+ "aria-label": `Insert asset ${asset.id}`,
90
+ "data-active": index === activeIndex ? "true" : void 0,
91
+ onClick: ()=>{
92
+ onSelect(asset);
93
+ onOpenChange(false);
94
+ },
95
+ onMouseEnter: ()=>{
96
+ setActiveIndex(index);
97
+ },
98
+ type: "button"
99
+ }, /*#__PURE__*/ __rspack_external_react.createElement("span", null, asset.name ?? asset.id), /*#__PURE__*/ __rspack_external_react.createElement("span", null, asset.meta?.mimeType ?? inferAssetKind(asset))))))));
100
+ }
101
+ export { AssetCommandPalette };