@alpaca-editor/core 1.0.3959 → 1.0.3962

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 (82) hide show
  1. package/dist/components/FilterInput.d.ts +21 -0
  2. package/dist/components/FilterInput.js +33 -0
  3. package/dist/components/FilterInput.js.map +1 -0
  4. package/dist/components/index.d.ts +2 -0
  5. package/dist/components/index.js +2 -0
  6. package/dist/components/index.js.map +1 -0
  7. package/dist/config/config.js +4 -2
  8. package/dist/config/config.js.map +1 -1
  9. package/dist/config/types.d.ts +1 -1
  10. package/dist/editor/ContextMenu.js +1 -1
  11. package/dist/editor/ContextMenu.js.map +1 -1
  12. package/dist/editor/FieldList.js +2 -3
  13. package/dist/editor/FieldList.js.map +1 -1
  14. package/dist/editor/ImageEditButton.d.ts +26 -0
  15. package/dist/editor/ImageEditButton.js +15 -0
  16. package/dist/editor/ImageEditButton.js.map +1 -0
  17. package/dist/editor/ImageEditor.js +25 -21
  18. package/dist/editor/ImageEditor.js.map +1 -1
  19. package/dist/editor/ItemInfo.js +1 -1
  20. package/dist/editor/ItemInfo.js.map +1 -1
  21. package/dist/editor/MainLayout.js +1 -1
  22. package/dist/editor/MainLayout.js.map +1 -1
  23. package/dist/editor/MobileLayout.js +1 -1
  24. package/dist/editor/MobileLayout.js.map +1 -1
  25. package/dist/editor/PictureEditor.js +60 -36
  26. package/dist/editor/PictureEditor.js.map +1 -1
  27. package/dist/editor/client/EditorClient.js +7 -8
  28. package/dist/editor/client/EditorClient.js.map +1 -1
  29. package/dist/editor/context-menu/InsertMenu.js +3 -5
  30. package/dist/editor/context-menu/InsertMenu.js.map +1 -1
  31. package/dist/editor/field-types/DropListEditor.js +1 -1
  32. package/dist/editor/field-types/DropListEditor.js.map +1 -1
  33. package/dist/editor/menubar/VersionSelector.js +8 -8
  34. package/dist/editor/menubar/VersionSelector.js.map +1 -1
  35. package/dist/editor/page-editor-chrome/FrameMenu.js +1 -1
  36. package/dist/editor/page-viewer/PageViewer.js +7 -2
  37. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  38. package/dist/editor/page-viewer/PageViewerFrame.js +1 -1
  39. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  40. package/dist/editor/sidebar/ComponentPalette.js +3 -5
  41. package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
  42. package/dist/editor/sidebar/Insert.js +1 -1
  43. package/dist/editor/sidebar/Insert.js.map +1 -1
  44. package/dist/editor/sidebar/SidebarView.js +1 -1
  45. package/dist/editor/sidebar/SidebarView.js.map +1 -1
  46. package/dist/editor/ui/Icons.d.ts +3 -0
  47. package/dist/editor/ui/Icons.js +4 -1
  48. package/dist/editor/ui/Icons.js.map +1 -1
  49. package/dist/editor/ui/ItemSearch.js +3 -4
  50. package/dist/editor/ui/ItemSearch.js.map +1 -1
  51. package/dist/editor/ui/SimpleTabs.js +2 -2
  52. package/dist/editor/ui/SimpleTabs.js.map +1 -1
  53. package/dist/revision.d.ts +2 -2
  54. package/dist/revision.js +2 -2
  55. package/dist/styles.css +25 -18
  56. package/package.json +1 -1
  57. package/src/components/FilterInput.tsx +102 -0
  58. package/src/components/index.ts +2 -0
  59. package/src/config/config.tsx +4 -2
  60. package/src/config/types.ts +2 -1
  61. package/src/editor/ContextMenu.tsx +1 -3
  62. package/src/editor/FieldList.tsx +6 -15
  63. package/src/editor/ImageEditButton.tsx +78 -0
  64. package/src/editor/ImageEditor.tsx +43 -91
  65. package/src/editor/ItemInfo.tsx +1 -1
  66. package/src/editor/MainLayout.tsx +2 -2
  67. package/src/editor/MobileLayout.tsx +1 -1
  68. package/src/editor/PictureEditor.tsx +60 -134
  69. package/src/editor/client/EditorClient.tsx +8 -8
  70. package/src/editor/context-menu/InsertMenu.tsx +5 -14
  71. package/src/editor/field-types/DropListEditor.tsx +1 -1
  72. package/src/editor/menubar/VersionSelector.tsx +28 -28
  73. package/src/editor/page-editor-chrome/FrameMenu.tsx +3 -3
  74. package/src/editor/page-viewer/PageViewer.tsx +8 -4
  75. package/src/editor/page-viewer/PageViewerFrame.tsx +1 -1
  76. package/src/editor/sidebar/ComponentPalette.tsx +9 -32
  77. package/src/editor/sidebar/Insert.tsx +1 -1
  78. package/src/editor/sidebar/SidebarView.tsx +2 -2
  79. package/src/editor/ui/Icons.tsx +23 -0
  80. package/src/editor/ui/ItemSearch.tsx +10 -23
  81. package/src/editor/ui/SimpleTabs.tsx +3 -3
  82. package/src/revision.ts +2 -2
@@ -1,10 +1,4 @@
1
- import {
2
- MouseEventHandler,
3
- useCallback,
4
- useEffect,
5
- useRef,
6
- useState,
7
- } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
8
2
  import { useEditContext } from "./client/editContext";
9
3
  import { PictureCropper } from "./PictureCropper";
10
4
  import { PictureField, PictureRawValue } from "./fieldTypes";
@@ -15,12 +9,7 @@ import {
15
9
  FieldActionsOverlay,
16
10
  FieldActionsOverlayRef,
17
11
  } from "./FieldActionsOverlay";
18
- import {
19
- DropdownMenu,
20
- DropdownMenuContent,
21
- DropdownMenuItem,
22
- DropdownMenuTrigger,
23
- } from "../components/ui/dropdown-menu";
12
+ import { ImageEditButton, MenuAction } from "./ImageEditButton";
24
13
 
25
14
  export function PictureEditor({
26
15
  field,
@@ -35,7 +24,6 @@ export function PictureEditor({
35
24
  forwardScrollevents?: boolean;
36
25
  isPageEditor?: boolean;
37
26
  }) {
38
- const [showMenu, setShowMenu] = useState(false);
39
27
  const [showCropper, setShowCropper] = useState(false);
40
28
  const [fieldButtons, setFieldButtons] = useState<FieldButton[]>([]);
41
29
  const fieldActionsOverlay = useRef<FieldActionsOverlayRef>(null);
@@ -159,6 +147,63 @@ export function PictureEditor({
159
147
 
160
148
  const notEmpty = variant?.mediaId;
161
149
 
150
+ // Build actions for the dropdown menu
151
+ const menuActions: MenuAction[] = [
152
+ {
153
+ id: "select",
154
+ label: "Select",
155
+ icon: "pi pi-image",
156
+ onClick: () => selectMedia("images"),
157
+ },
158
+ ...(field.value?.allowVideos
159
+ ? [
160
+ {
161
+ id: "video",
162
+ label: "Video",
163
+ icon: "pi pi-video",
164
+ onClick: () => selectVideo(),
165
+ },
166
+ ]
167
+ : []),
168
+ ...(notEmpty
169
+ ? [
170
+ {
171
+ id: "crop",
172
+ label: "Crop",
173
+ icon: "pi pi-expand",
174
+ onClick: () => setShowCropper(true),
175
+ },
176
+ ]
177
+ : []),
178
+ ...(rawVariant
179
+ ? [
180
+ {
181
+ id: "reset",
182
+ label: "Reset",
183
+ icon: "pi pi-times",
184
+ onClick: () => reset(),
185
+ },
186
+ ]
187
+ : []),
188
+ ...fieldButtons.map((button) => ({
189
+ id: button.id,
190
+ label: button.label,
191
+ icon: button.isGenerator
192
+ ? "pi pi-cog"
193
+ : button.icon && button.icon.trim() !== ""
194
+ ? button.icon
195
+ : "pi pi-bolt",
196
+ onClick: (e?: React.MouseEvent) => {
197
+ if (button.parameters && button.parameters.length > 0) {
198
+ fieldActionsOverlay.current?.show(e, button);
199
+ } else {
200
+ handleActionClick(button);
201
+ }
202
+ },
203
+ parameters: button.parameters,
204
+ })),
205
+ ];
206
+
162
207
  return (
163
208
  <div
164
209
  className="absolute inset-0 flex items-center justify-center"
@@ -187,92 +232,7 @@ export function PictureEditor({
187
232
  style={style}
188
233
  data-testid="select-media"
189
234
  >
190
- {/* Dropdown menu with button in bottom right corner */}
191
- <DropdownMenu open={showMenu} onOpenChange={setShowMenu}>
192
- <DropdownMenuTrigger asChild>
193
- <button
194
- className="absolute right-2 bottom-2 flex h-8 w-8 items-center justify-center rounded-full border bg-gray-500 text-white opacity-70 shadow-lg transition-opacity hover:opacity-85"
195
- onClick={(e) => {
196
- e.stopPropagation();
197
- }}
198
- data-testid="menu-toggle-button"
199
- >
200
- <i className="pi pi-ellipsis-v text-xs" />
201
- </button>
202
- </DropdownMenuTrigger>
203
- <DropdownMenuContent align="end" side="top" sideOffset={8}>
204
- <DropdownMenuItem
205
- onClick={() => {
206
- selectMedia("images");
207
- setShowMenu(false);
208
- }}
209
- >
210
- <i className="pi pi-image" />
211
- Select
212
- </DropdownMenuItem>
213
-
214
- {field.value?.allowVideos && (
215
- <DropdownMenuItem
216
- onClick={() => {
217
- selectVideo();
218
- setShowMenu(false);
219
- }}
220
- >
221
- <i className="pi pi-video" />
222
- Video
223
- </DropdownMenuItem>
224
- )}
225
-
226
- {notEmpty && (
227
- <DropdownMenuItem
228
- onClick={() => {
229
- setShowCropper(true);
230
- setShowMenu(false);
231
- }}
232
- >
233
- <i className="pi pi-expand" />
234
- Crop
235
- </DropdownMenuItem>
236
- )}
237
-
238
- {rawVariant && (
239
- <DropdownMenuItem
240
- onClick={() => {
241
- reset();
242
- setShowMenu(false);
243
- }}
244
- >
245
- <i className="pi pi-times" />
246
- Reset
247
- </DropdownMenuItem>
248
- )}
249
-
250
- {fieldButtons.map((button) => (
251
- <DropdownMenuItem
252
- key={button.id}
253
- onClick={(e) => {
254
- if (button.parameters && button.parameters.length > 0) {
255
- fieldActionsOverlay.current?.show(e, button);
256
- } else {
257
- handleActionClick(button);
258
- }
259
- setShowMenu(false);
260
- }}
261
- >
262
- <i
263
- className={
264
- button.isGenerator
265
- ? "pi pi-cog"
266
- : button.icon && button.icon.trim() !== ""
267
- ? button.icon
268
- : "pi pi-bolt"
269
- }
270
- />
271
- {button.label}
272
- </DropdownMenuItem>
273
- ))}
274
- </DropdownMenuContent>
275
- </DropdownMenu>
235
+ <ImageEditButton actions={menuActions} compact={true} />
276
236
 
277
237
  {showCropper && (
278
238
  <PictureCropper
@@ -294,37 +254,3 @@ export function PictureEditor({
294
254
  </div>
295
255
  );
296
256
  }
297
-
298
- function Btn({
299
- label,
300
- icon,
301
- onClick,
302
- testId,
303
- className,
304
- }: {
305
- label: string;
306
- icon: string;
307
- onClick: MouseEventHandler<HTMLButtonElement>;
308
- testId?: string;
309
- className?: string;
310
- }) {
311
- const isImageUrl =
312
- icon.startsWith("http://") ||
313
- icon.startsWith("https://") ||
314
- icon.startsWith("/");
315
-
316
- return (
317
- <button
318
- className={`btn flex cursor-pointer items-center gap-1.5 rounded-lg border bg-gray-500 p-1.5 text-white opacity-70 hover:opacity-85 ${className} text-2xs`}
319
- onClick={onClick}
320
- data-testid={testId}
321
- >
322
- {isImageUrl ? (
323
- <img src={icon} alt="" className="h-3 w-3" />
324
- ) : (
325
- <i className={icon} />
326
- )}{" "}
327
- {label}
328
- </button>
329
- );
330
- }
@@ -408,8 +408,6 @@ export function EditorClient({
408
408
  }, [currentView]);
409
409
 
410
410
  const sendClientInfo = async () => {
411
- const socket = (globalThis as any).editorSocket;
412
-
413
411
  const clientInfoMessage = {
414
412
  type: "client-info",
415
413
  sessionId: sessionId,
@@ -423,12 +421,14 @@ export function EditorClient({
423
421
  : null,
424
422
  };
425
423
 
426
- if (socket.readyState !== WebSocket.OPEN) {
427
- const url = "/alpaca/editor/client";
428
- await post(url, clientInfoMessage);
429
- } else {
430
- socket.send(JSON.stringify(clientInfoMessage));
431
- }
424
+ // const socket = (globalThis as any).editorSocket;
425
+
426
+ //if (socket.readyState !== WebSocket.OPEN) {
427
+ const url = "/alpaca/editor/client";
428
+ await post(url, clientInfoMessage);
429
+ //} else {
430
+ // socket.send(JSON.stringify(clientInfoMessage));
431
+ //}
432
432
  };
433
433
 
434
434
  const startTour = () => {
@@ -3,7 +3,7 @@ import { ItemTreeNodeData } from "../services/contentService";
3
3
  import { useEditContext } from "../../client-components";
4
4
  import { useRef, useState, useEffect } from "react";
5
5
  import { FullItem } from "../pageModel";
6
- import { InputText } from "primereact/inputtext";
6
+ import { FilterInput, highlightMatch } from "../../components/FilterInput";
7
7
  import { ScrollingContentTree } from "../ScrollingContentTree";
8
8
  import { SimpleTabs, Tab } from "../ui/SimpleTabs";
9
9
  import ItemSearch, { ResultItem } from "../ui/ItemSearch";
@@ -127,11 +127,12 @@ export const InsertMenuTemplate = ({
127
127
  label: "Options",
128
128
  content: (
129
129
  <div className="flex h-full flex-col gap-2">
130
- <InputText
130
+ <FilterInput
131
131
  ref={filterRef}
132
132
  className="w-full text-sm"
133
133
  placeholder="Filter"
134
- onChange={(e) => setFilter(e.target.value)}
134
+ value={filter}
135
+ onChange={setFilter}
135
136
  />
136
137
  <div className="relative flex-1">
137
138
  <div className="absolute inset-0 overflow-auto">
@@ -152,17 +153,7 @@ export const InsertMenuTemplate = ({
152
153
  >
153
154
  {option.icon}
154
155
  <span className="flex-1">
155
- {option.label
156
- .split(new RegExp(`(${filter})`, "i"))
157
- .map((part: string, i: number) =>
158
- part.toLowerCase() === filter.toLowerCase() ? (
159
- <u className="font-bold" key={i}>
160
- {part}
161
- </u>
162
- ) : (
163
- part
164
- ),
165
- )}
156
+ {highlightMatch(option.label, filter)}
166
157
  </span>
167
158
  </div>
168
159
  ))}
@@ -60,7 +60,7 @@ export function DropListEditor({
60
60
  optionLabel="name"
61
61
  optionValue="name"
62
62
  placeholder="Select"
63
- className="w-full md:w-14rem"
63
+ className="md:w-14rem bg-gray-5 w-full"
64
64
  virtualScrollerOptions={{
65
65
  lazy: true,
66
66
  onLazyLoad: onLazyLoad,
@@ -92,7 +92,7 @@ export function VersionSelector({
92
92
  </PopoverTrigger>
93
93
  <PopoverContent className="w-64 p-0" align="start">
94
94
  <Command>
95
- <CommandList className="max-h-[50vh]">
95
+ <CommandList className="max-h-[30vh]">
96
96
  <CommandEmpty>No versions available</CommandEmpty>
97
97
  <CommandGroup>
98
98
  {versions.length > 0 && (
@@ -132,34 +132,34 @@ export function VersionSelector({
132
132
  </CommandItem>
133
133
  ))}
134
134
  </CommandGroup>
135
- {!readOnly && (
136
- <>
137
- <CommandSeparator />
138
- <CommandGroup>
139
- <CommandItem
140
- onSelect={handleCreateVersion}
141
- className="flex items-center gap-2 px-2 py-2"
142
- >
143
- <Plus className="h-4 w-4" />
144
- <span>Add Version</span>
145
- </CommandItem>
146
- <CommandItem
147
- onSelect={handleDeleteVersion}
148
- disabled={versions.length === 0}
149
- className={cn(
150
- "flex items-center gap-2 px-2 py-2",
151
- versions.length === 0
152
- ? "cursor-not-allowed opacity-50"
153
- : "text-red-600 hover:text-red-700",
154
- )}
155
- >
156
- <X className="h-4 w-4" />
157
- <span>Delete Version</span>
158
- </CommandItem>
159
- </CommandGroup>
160
- </>
161
- )}
162
135
  </CommandList>
136
+ {!readOnly && (
137
+ <>
138
+ <div className="border-t border-gray-200" />
139
+ <div className="p-1">
140
+ <CommandItem
141
+ onSelect={handleCreateVersion}
142
+ className="flex items-center gap-2 px-2 py-2"
143
+ >
144
+ <Plus className="h-4 w-4" />
145
+ <span>Add Version</span>
146
+ </CommandItem>
147
+ <CommandItem
148
+ onSelect={handleDeleteVersion}
149
+ disabled={versions.length === 0}
150
+ className={cn(
151
+ "flex items-center gap-2 px-2 py-2",
152
+ versions.length === 0
153
+ ? "cursor-not-allowed opacity-50"
154
+ : "text-red-600 hover:text-red-700",
155
+ )}
156
+ >
157
+ <X className="h-4 w-4" />
158
+ <span>Delete Version</span>
159
+ </CommandItem>
160
+ </div>
161
+ </>
162
+ )}
163
163
  </Command>
164
164
  </PopoverContent>
165
165
  </Popover>
@@ -252,12 +252,12 @@ export function FrameMenu({
252
252
  <>
253
253
  <div
254
254
  className={cn(
255
- "pointer-events-none absolute inset-0 rounded-b-sm border-2",
255
+ "pointer-events-none absolute inset-0 rounded-b-xs border-2",
256
256
  colorVariants[color],
257
257
  "tour-frame-menu opacity-70 hover:opacity-100",
258
258
  !isMultiSelected && isHeaderWiderThanComponent && "border-t-0",
259
- !isMultiSelected && !isHeaderWiderThanComponent && "rounded-tl-sm",
260
- isMultiSelected && "rounded-t-sm",
259
+ !isMultiSelected && !isHeaderWiderThanComponent && "rounded-tl-xs",
260
+ isMultiSelected && "rounded-t-xs",
261
261
  )}
262
262
  style={{
263
263
  left: componentRect.x,
@@ -2,10 +2,8 @@ import { PageViewContext } from "./pageViewContext";
2
2
  import { EditorForm } from "./EditorForm";
3
3
  import { PageViewerFrame } from "./PageViewerFrame";
4
4
  import { useEffect, useState } from "react";
5
- import { useRef } from "react";
6
5
  import { SimpleIconButton } from "../ui/SimpleIconButton";
7
6
  import { useEditContext } from "../client/editContext";
8
- import { useDebouncedCallback } from "use-debounce";
9
7
  import { Ellipsis, PanelLeftClose, PanelLeftOpen } from "lucide-react";
10
8
  import { cn } from "../../lib/utils";
11
9
  import { Splitter, SplitterPanel } from "../ui/Splitter";
@@ -32,6 +30,12 @@ export function PageViewer({
32
30
 
33
31
  if (!pageViewContext) return null;
34
32
 
33
+ useEffect(() => {
34
+ if (editContext?.insertMode) {
35
+ setFormEditorCollapsed(false);
36
+ }
37
+ }, [editContext?.insertMode]);
38
+
35
39
  useEffect(() => {
36
40
  if (
37
41
  followEdits &&
@@ -79,7 +83,7 @@ export function PageViewer({
79
83
  defaultSize: 300,
80
84
  collapsible: true,
81
85
  content: (
82
- <div className="h-full w-full p-2 pl-1">
86
+ <div className="h-full w-full py-2 pl-2">
83
87
  <div className="border-gray-3 flex h-full w-full flex-col rounded-2xl border bg-white">
84
88
  <div className="flex-1">
85
89
  <EditorForm
@@ -134,7 +138,7 @@ export function PageViewer({
134
138
  <div className={cn("h-full w-full", className)}>
135
139
  <Splitter
136
140
  panels={panels}
137
- splitterClassName="bg-transparent -ml-1"
141
+ splitterClassName="bg-transparent -mr-2 md:w-[8px]"
138
142
  direction={editContext?.isMobile ? "vertical" : "horizontal"}
139
143
  localStorageKey={`editor.pageViewer.${name}`}
140
144
  />
@@ -803,7 +803,7 @@ export function PageViewerFrame({
803
803
  : pageViewContext.deviceHeight || 640;
804
804
 
805
805
  return (
806
- <div className="relative mt-2 flex h-full w-full flex-col items-center select-none">
806
+ <div className="relative mt-2 mr-2 ml-2 flex h-full w-full flex-col items-center select-none">
807
807
  {!editContext.pageView.fullscreen && (
808
808
  <EditorWarnings item={pageViewContext.page?.item} />
809
809
  )}
@@ -6,7 +6,7 @@ import { classNames } from "primereact/utils";
6
6
  import { InsertOption } from "../../types";
7
7
 
8
8
  import { useEditContext } from "../client/editContext";
9
- import { InputText } from "primereact/inputtext";
9
+ import { FilterInput, highlightMatch } from "../../components/FilterInput";
10
10
  import { useState, useRef } from "react";
11
11
 
12
12
  export function ComponentPalette({}: {}) {
@@ -31,25 +31,13 @@ export function ComponentPalette({}: {}) {
31
31
  return (
32
32
  <div className="tour-component-palette relative flex h-full flex-col">
33
33
  <div className="p-4">
34
- <div className="relative flex items-center">
35
- <input
36
- ref={filterRef}
37
- type="text"
38
- value={filter}
39
- onChange={(e) => setFilter(e.target.value)}
40
- className="w-full rounded border border-gray-300 px-2 py-1 pr-6 text-xs focus:border-blue-400 focus:ring-1 focus:ring-blue-400 focus:outline-none"
41
- placeholder="Filter..."
42
- />
43
- {!filter && (
44
- <i className="pi pi-search absolute right-2 text-xs text-gray-400" />
45
- )}
46
- {filter && (
47
- <i
48
- className="pi pi-times absolute right-2 cursor-pointer text-xs text-gray-400 hover:text-gray-600"
49
- onClick={() => setFilter("")}
50
- />
51
- )}
52
- </div>
34
+ <FilterInput
35
+ ref={filterRef}
36
+ value={filter}
37
+ onChange={setFilter}
38
+ className="border-gray-300"
39
+ placeholder="Filter..."
40
+ />
53
41
  </div>
54
42
  <div className="relative flex-1">
55
43
  <div className="absolute inset-0 overflow-y-auto">
@@ -127,18 +115,7 @@ export function ComponentPalette({}: {}) {
127
115
  )}
128
116
 
129
117
  <div className="text-xs">
130
- {option.name
131
- .split(new RegExp(`(${filter})`, "i"))
132
- .map((part: string, i: number) =>
133
- part.toLowerCase() === filter.toLowerCase() &&
134
- part ? (
135
- <u className="font-bold" key={i}>
136
- {part}
137
- </u>
138
- ) : (
139
- part && <span key={i}>{part}</span>
140
- ),
141
- )}
118
+ {highlightMatch(option.name, filter)}
142
119
  </div>
143
120
 
144
121
  {option.isInvalid && (
@@ -27,7 +27,7 @@ export function Insert() {
27
27
  <SimpleTabs
28
28
  key="insert-tabs"
29
29
  tabs={tabs}
30
- className="flex items-center justify-center gap-4 border-b border-gray-200 py-3 text-sm"
30
+ className="border-gray-3 flex items-center justify-center gap-4 border-b pt-3 text-xs"
31
31
  activeTab={activeTab}
32
32
  setActiveTab={(index) => setActiveTab(index)}
33
33
  />
@@ -60,11 +60,11 @@ export function SidebarView({
60
60
  if (!panel) return <div className="hidden h-full" />;
61
61
 
62
62
  return (
63
- <div className={cn("h-full", detached ? "border-gray-3 border p-2" : "")}>
63
+ <div className={cn("h-full", detached ? "p-2" : "")}>
64
64
  <div
65
65
  className={cn(
66
66
  "flex h-full flex-col bg-white",
67
- detached ? "rounded-xl" : "",
67
+ detached ? "border-gray-3 rounded-xl border" : "",
68
68
  )}
69
69
  >
70
70
  {getHeader(panel, 0)}
@@ -715,7 +715,29 @@ export function Logo({ className }: { className?: string }) {
715
715
  </svg>
716
716
  );
717
717
  }
718
+
718
719
  export function MagicEditIcon() {
720
+ return (
721
+ <svg
722
+ width="21"
723
+ height="17"
724
+ viewBox="0 0 21 17"
725
+ fill="none"
726
+ xmlns="http://www.w3.org/2000/svg"
727
+ >
728
+ <path
729
+ d="M20.4141 5.70117C20.7921 5.32317 21.0001 4.82117 21.0001 4.28717C21.0001 3.75317 20.7921 3.25117 20.4141 2.87317L18.8281 1.28717C18.4501 0.909172 17.9481 0.701172 17.4141 0.701172C16.8801 0.701172 16.3781 0.909172 16.0011 1.28617L5.36914 11.8852V16.3002H9.78214L20.4141 5.70117ZM7.36914 14.3002V12.7152L14.4091 5.69717L15.9951 7.28317L8.95614 14.3002H7.36914Z"
730
+ fill="white"
731
+ />
732
+ <path
733
+ d="M0.376146 4.6906C0.63534 4.62716 0.873518 4.56373 1.1117 4.50029C1.39891 4.42276 1.69313 4.35932 1.97334 4.26064C3.05916 3.85183 3.74567 3.0765 4.03989 1.9558C4.14497 1.54699 4.25005 1.13818 4.35513 0.736414C4.35513 0.722317 4.35513 0.715269 4.37615 0.701172C4.39716 0.771656 4.41117 0.842141 4.43219 0.912625C4.54427 1.34258 4.63534 1.77254 4.77545 2.19544C5.15373 3.3232 5.93131 4.04919 7.07317 4.36637C7.47947 4.47915 7.89278 4.57782 8.29909 4.6906C8.3201 4.6906 8.33411 4.69765 8.36914 4.71174C8.21502 4.75403 8.07492 4.78928 7.92781 4.82452C7.54953 4.93025 7.16424 5.00778 6.79296 5.13465C5.71415 5.52232 5.02063 6.2906 4.7124 7.40426C4.59331 7.83421 4.48823 8.26417 4.36214 8.70117C4.36214 8.68003 4.34813 8.65888 4.34112 8.63774C4.22904 8.20073 4.13096 7.76373 3.99786 7.33377C3.64059 6.14258 2.85601 5.3743 1.65811 5.04302C1.2518 4.93025 0.845498 4.83157 0.439193 4.72584C0.418178 4.72584 0.404167 4.71879 0.369141 4.7047L0.376146 4.6906Z"
734
+ fill="white"
735
+ />
736
+ </svg>
737
+ );
738
+ }
739
+
740
+ export function MagicEditImageIcon({ className }: { className?: string }) {
719
741
  return (
720
742
  <svg
721
743
  width="15"
@@ -724,6 +746,7 @@ export function MagicEditIcon() {
724
746
  fill="none"
725
747
  strokeWidth={1}
726
748
  xmlns="http://www.w3.org/2000/svg"
749
+ className={className}
727
750
  >
728
751
  <path
729
752
  d="M9.49999 13.915H14M5 13.915H5.83727C6.08186 13.915 6.20416 13.915 6.31925 13.886C6.42128 13.8602 6.51883 13.8178 6.6083 13.7601C6.70922 13.6951 6.79569 13.6042 6.96865 13.4224L13.25 6.81862C13.6642 6.38315 13.6642 5.67711 13.25 5.24164C12.8358 4.80617 12.1642 4.80617 11.75 5.24164L5.46863 11.8454C5.29568 12.0272 5.2092 12.1181 5.14736 12.2242C5.09253 12.3183 5.05213 12.4208 5.02763 12.5281C5 12.6491 5 12.7777 5 13.0348V13.915Z"
@@ -1,4 +1,3 @@
1
- import { InputText } from "primereact/inputtext";
2
1
  import { useEffect, useRef, useState } from "react";
3
2
  import { useEditContext } from "../client/editContext";
4
3
  import { executeSearch } from "../services/aiService";
@@ -7,6 +6,7 @@ import { ProgressSpinner } from "primereact/progressspinner";
7
6
  import { ItemDescriptor } from "../pageModel";
8
7
  import { getItemDescriptor } from "../utils";
9
8
  import { ItemList } from "./ItemList";
9
+ import { FilterInput } from "../../components/FilterInput";
10
10
 
11
11
  export type ResultItem = {
12
12
  id: string;
@@ -117,28 +117,15 @@ export const ItemSearch: React.FC<SearchProps> = ({
117
117
  return (
118
118
  <>
119
119
  <div className="relative">
120
- <div className="flex items-center">
121
- <InputText
122
- className="w-full rounded border border-gray-300 px-2 py-1 pr-6 text-xs focus:border-blue-400 focus:ring-1 focus:ring-blue-400 focus:outline-none"
123
- onChange={(e) => setQuery(e.target.value)}
124
- value={query}
125
- ref={inputRef}
126
- />
127
- {!loading && !query && (
128
- <i className="pi pi-search px-3 text-gray-500" />
129
- )}
130
- {!loading && query && (
131
- <i
132
- className="pi pi-times cursor-pointer px-3 text-gray-500"
133
- onClick={() => setQuery("")}
134
- />
135
- )}
136
- {loading && (
137
- <ProgressSpinner
138
- style={{ width: "24px", height: "24px", margin: "0 12px" }}
139
- />
140
- )}
141
- </div>
120
+ <FilterInput
121
+ className="focus:border-blue-400 focus:ring-blue-400"
122
+ onChange={setQuery}
123
+ value={query}
124
+ ref={inputRef}
125
+ autoFocus={autoFocus}
126
+ placeholder="Search..."
127
+ loading={loading}
128
+ />
142
129
  </div>
143
130
  {results.length > 0 && (
144
131
  <ItemList
@@ -31,12 +31,12 @@ export function SimpleTabs({
31
31
 
32
32
  return (
33
33
  <>
34
- <div className={twMerge("flex gap-4", className)}>
34
+ <div className={twMerge("flex flex-wrap gap-2", className)}>
35
35
  {tabs.map((tab, index) => (
36
36
  <button
37
37
  id={tab.id}
38
38
  className={twMerge(
39
- "flex cursor-pointer items-center gap-2 px-6 pb-2",
39
+ "flex min-w-0 flex-shrink cursor-pointer items-center gap-2 px-3 pb-2 whitespace-nowrap",
40
40
  activeTab === index ? "active-tab" : "",
41
41
  )}
42
42
  key={tab.id}
@@ -50,7 +50,7 @@ export function SimpleTabs({
50
50
  }}
51
51
  >
52
52
  {tab.icon && <i className={tab.icon} />}
53
- {tab.label}
53
+ <span className="truncate">{tab.label}</span>
54
54
  </button>
55
55
  ))}
56
56
  </div>
package/src/revision.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const version = "1.0.3959";
2
- export const buildDate = "2025-06-22 17:51:55";
1
+ export const version = "1.0.3962";
2
+ export const buildDate = "2025-06-23 12:43:13";