@alpaca-editor/core 1.0.3896 → 1.0.3897

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 (99) hide show
  1. package/dist/components/ActionButton.js +2 -2
  2. package/dist/components/ActionButton.js.map +1 -1
  3. package/dist/components/ui/button.js +2 -2
  4. package/dist/components/ui/button.js.map +1 -1
  5. package/dist/config/config.js +37 -22
  6. package/dist/config/config.js.map +1 -1
  7. package/dist/editor/FieldListField.js +1 -1
  8. package/dist/editor/FieldListField.js.map +1 -1
  9. package/dist/editor/Titlebar.js +2 -1
  10. package/dist/editor/Titlebar.js.map +1 -1
  11. package/dist/editor/client/EditorClient.d.ts +18 -2
  12. package/dist/editor/client/EditorClient.js +117 -1
  13. package/dist/editor/client/EditorClient.js.map +1 -1
  14. package/dist/editor/client/editContext.d.ts +4 -1
  15. package/dist/editor/client/editContext.js.map +1 -1
  16. package/dist/editor/client/itemsRepository.js +1 -1
  17. package/dist/editor/client/itemsRepository.js.map +1 -1
  18. package/dist/editor/client/operations.js +1 -1
  19. package/dist/editor/client/operations.js.map +1 -1
  20. package/dist/editor/control-center/About.d.ts +1 -0
  21. package/dist/editor/control-center/About.js +8 -0
  22. package/dist/editor/control-center/About.js.map +1 -0
  23. package/dist/editor/control-center/ControlCenterMenu.js +3 -0
  24. package/dist/editor/control-center/ControlCenterMenu.js.map +1 -1
  25. package/dist/editor/control-center/Info.d.ts +1 -0
  26. package/dist/editor/control-center/Info.js +10 -0
  27. package/dist/editor/control-center/Info.js.map +1 -0
  28. package/dist/editor/control-center/QuotaInfo.d.ts +1 -0
  29. package/dist/editor/control-center/QuotaInfo.js +52 -0
  30. package/dist/editor/control-center/QuotaInfo.js.map +1 -0
  31. package/dist/editor/control-center/Status.js +69 -2
  32. package/dist/editor/control-center/Status.js.map +1 -1
  33. package/dist/editor/page-editor-chrome/FieldActionIndicator.js +7 -6
  34. package/dist/editor/page-editor-chrome/FieldActionIndicator.js.map +1 -1
  35. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  36. package/dist/editor/services/aiService.d.ts +6 -1
  37. package/dist/editor/services/aiService.js +4 -0
  38. package/dist/editor/services/aiService.js.map +1 -1
  39. package/dist/editor/sidebar/ComponentTree.js +1 -1
  40. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  41. package/dist/editor/sidebar/ViewSelector.js +9 -4
  42. package/dist/editor/sidebar/ViewSelector.js.map +1 -1
  43. package/dist/editor/ui/Icons.d.ts +17 -1
  44. package/dist/editor/ui/Icons.js +23 -5
  45. package/dist/editor/ui/Icons.js.map +1 -1
  46. package/dist/editor/ui/SimpleMenu.js +1 -1
  47. package/dist/editor/ui/SimpleMenu.js.map +1 -1
  48. package/dist/images/wizard-bg.png +0 -0
  49. package/dist/page-wizard/WizardBox.d.ts +8 -0
  50. package/dist/page-wizard/WizardBox.js +6 -0
  51. package/dist/page-wizard/WizardBox.js.map +1 -0
  52. package/dist/page-wizard/WizardBoxConnector.d.ts +3 -0
  53. package/dist/page-wizard/WizardBoxConnector.js +6 -0
  54. package/dist/page-wizard/WizardBoxConnector.js.map +1 -0
  55. package/dist/page-wizard/WizardSteps.js +63 -16
  56. package/dist/page-wizard/WizardSteps.js.map +1 -1
  57. package/dist/page-wizard/steps/CollectStep.js +16 -21
  58. package/dist/page-wizard/steps/CollectStep.js.map +1 -1
  59. package/dist/page-wizard/steps/ComponentTypesSelector.js +50 -45
  60. package/dist/page-wizard/steps/ComponentTypesSelector.js.map +1 -1
  61. package/dist/page-wizard/steps/CreatePageAndLayoutStep.js +21 -28
  62. package/dist/page-wizard/steps/CreatePageAndLayoutStep.js.map +1 -1
  63. package/dist/page-wizard/steps/usePageCreator.js +41 -12
  64. package/dist/page-wizard/steps/usePageCreator.js.map +1 -1
  65. package/dist/revision.d.ts +2 -2
  66. package/dist/revision.js +2 -2
  67. package/dist/styles.css +144 -49
  68. package/images/wizard-bg.png +0 -0
  69. package/package.json +1 -1
  70. package/src/components/ActionButton.tsx +6 -8
  71. package/src/components/ui/button.tsx +2 -2
  72. package/src/config/config.tsx +47 -22
  73. package/src/editor/FieldListField.tsx +2 -2
  74. package/src/editor/Titlebar.tsx +2 -1
  75. package/src/editor/client/EditorClient.tsx +153 -8
  76. package/src/editor/client/editContext.ts +4 -2
  77. package/src/editor/client/itemsRepository.ts +1 -1
  78. package/src/editor/client/operations.ts +1 -1
  79. package/src/editor/control-center/About.tsx +342 -0
  80. package/src/editor/control-center/ControlCenterMenu.tsx +5 -0
  81. package/src/editor/control-center/Info.tsx +104 -0
  82. package/src/editor/control-center/QuotaInfo.tsx +171 -0
  83. package/src/editor/control-center/Status.tsx +108 -2
  84. package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +20 -5
  85. package/src/editor/page-viewer/PageViewer.tsx +1 -1
  86. package/src/editor/services/aiService.ts +13 -1
  87. package/src/editor/sidebar/ComponentTree.tsx +1 -1
  88. package/src/editor/sidebar/ViewSelector.tsx +10 -11
  89. package/src/editor/ui/Icons.tsx +147 -26
  90. package/src/editor/ui/SimpleMenu.tsx +1 -1
  91. package/src/page-wizard/WizardBox.tsx +40 -0
  92. package/src/page-wizard/WizardBoxConnector.tsx +21 -0
  93. package/src/page-wizard/WizardSteps.tsx +134 -84
  94. package/src/page-wizard/steps/CollectStep.tsx +129 -67
  95. package/src/page-wizard/steps/ComponentTypesSelector.tsx +32 -11
  96. package/src/page-wizard/steps/CreatePageAndLayoutStep.tsx +47 -30
  97. package/src/page-wizard/steps/usePageCreator.ts +40 -14
  98. package/src/revision.ts +2 -2
  99. package/styles.css +11 -8
@@ -170,9 +170,9 @@ export default function FieldListField({
170
170
  ></SimpleIconButton>
171
171
  )}
172
172
  {executingAction && executingAction.state === "running" && (
173
- <div className="ml-auto flex items-center gap-3 text-xs">
173
+ <div className="ml-auto flex items-center gap-1.5 text-xs">
174
174
  <i className="pi pi-spin pi-spinner flex items-center text-xs" />{" "}
175
- {executingAction?.actionButton.label}
175
+ {executingAction?.label}
176
176
  </div>
177
177
  )}
178
178
  <OverlayPanel ref={generatorsOverlay} className="p-1">
@@ -2,6 +2,7 @@ import { MenuIcon } from "lucide-react";
2
2
  import { useEditContext } from "./client/editContext";
3
3
  import { useEffect, useState, useRef } from "react";
4
4
  import { createPortal } from "react-dom";
5
+ import { Logo } from "./ui/Icons";
5
6
 
6
7
  export function Titlebar() {
7
8
  const editContext = useEditContext();
@@ -43,7 +44,7 @@ export function Titlebar() {
43
44
  href="/sitecore/shell/sitecore/client/Applications/Launchpad"
44
45
  className="glow-text flex items-center"
45
46
  >
46
- <i className="pi pi-sparkles text-pink-400" />
47
+ <Logo />
47
48
  <span className="ml-2 font-mono">workbench</span>
48
49
  </a>
49
50
  </div>
@@ -121,20 +121,26 @@ import { usePageWizard } from "../../page-wizard/usePageWizard";
121
121
 
122
122
  export type FieldAction = {
123
123
  field: FieldDescriptor;
124
- actionButton: FieldButton;
125
124
  message?: string;
126
125
  state: "running" | "success" | "error";
126
+ label?: string;
127
127
  };
128
128
 
129
- export type WindowSize = {
130
- width: number;
131
- height: number;
132
- };
133
-
129
+ export type WindowSize = { width: number; height: number };
134
130
  export type InsertingState = {
135
131
  positionElement: Element;
136
132
  positionAnchor: "left" | "right" | "top" | "bottom";
137
133
  };
134
+ export type QuotaUsage = { tokens: number; images: number };
135
+ export type QuotaLimits = {
136
+ absoluteTokens: number;
137
+ dailyTokens: number;
138
+ monthlyTokens: number;
139
+ absoluteImages: number;
140
+ dailyImages: number;
141
+ monthlyImages: number;
142
+ };
143
+ export type QuotaInfo = { usage: QuotaUsage; limits: QuotaLimits };
138
144
 
139
145
  export function EditorClient({
140
146
  configuration,
@@ -284,7 +290,7 @@ export function EditorClient({
284
290
  const [focusFieldComponentId, setFocusFieldComponentId] = useState<string>();
285
291
 
286
292
  const [enableCompletions, setEnableCompletions] = useState(false);
287
-
293
+ const [quotaInfo, setQuotaInfo] = useState<QuotaInfo | null>(null);
288
294
  const pageWizard = usePageWizard();
289
295
 
290
296
  useEffect(() => {
@@ -390,6 +396,8 @@ export function EditorClient({
390
396
  if (!event.data.startsWith("{")) return;
391
397
  const message = JSON.parse(event.data);
392
398
 
399
+ console.log("message", message);
400
+
393
401
  if (message.type === "active-sessions") {
394
402
  setActiveSessions(() => {
395
403
  return message.payload;
@@ -459,6 +467,69 @@ export function EditorClient({
459
467
  });
460
468
  }
461
469
 
470
+ if (message.type === "executing-field-action") {
471
+ setActiveFieldActions((x) => {
472
+ const payload = message.payload;
473
+ const fieldId = payload.fieldId;
474
+ const item = payload.item;
475
+ const status = payload.status;
476
+ const msg = payload.message;
477
+ const label = payload.label;
478
+
479
+ // Map backend status to FieldAction state
480
+ let state: "running" | "success" | "error";
481
+ switch (status?.toLowerCase()) {
482
+ case "completed":
483
+ case "success":
484
+ state = "success";
485
+ break;
486
+ case "failed":
487
+ case "error":
488
+ state = "error";
489
+ break;
490
+ default:
491
+ state = "running";
492
+ break;
493
+ }
494
+
495
+ // Check if action already exists
496
+ const existingActionIndex = x.findIndex(
497
+ (action) =>
498
+ action.field.fieldId === fieldId &&
499
+ action.field.item.id === item.id &&
500
+ action.field.item.language === item.language &&
501
+ action.field.item.version === item.version,
502
+ );
503
+
504
+ if (existingActionIndex !== -1) {
505
+ // Update existing action
506
+ const newActions = [...x];
507
+ newActions[existingActionIndex]!.state = state;
508
+ newActions[existingActionIndex]!.message = msg;
509
+ return newActions;
510
+ } else {
511
+ // Insert new action
512
+ const fieldDescriptor: FieldDescriptor = {
513
+ fieldId: fieldId,
514
+ item: item,
515
+ };
516
+
517
+ const newAction: FieldAction = {
518
+ field: fieldDescriptor,
519
+ state,
520
+ message: msg,
521
+ label: label,
522
+ };
523
+ console.log(newAction);
524
+ return [...x, newAction];
525
+ }
526
+ });
527
+ }
528
+
529
+ if (message.type === "update-quota") {
530
+ setQuotaInfo(message.payload);
531
+ }
532
+
462
533
  if (message.type === "edit-operation") {
463
534
  const op = message.payload as EditOperation;
464
535
 
@@ -1432,6 +1503,73 @@ export function EditorClient({
1432
1503
  router.push(url.toString(), { scroll: false });
1433
1504
  }, []);
1434
1505
 
1506
+ // Quota checking functions
1507
+ const isQuotaExceeded = useCallback(() => {
1508
+ if (!quotaInfo) return false;
1509
+
1510
+ const { usage, limits } = quotaInfo;
1511
+
1512
+ // Check absolute limits
1513
+ if (limits.absoluteTokens > 0 && usage.tokens >= limits.absoluteTokens)
1514
+ return true;
1515
+ if (limits.absoluteImages > 0 && usage.images >= limits.absoluteImages)
1516
+ return true;
1517
+
1518
+ // For now, we're only checking absolute limits as daily/monthly would require server-side logic
1519
+ // You can extend this to check daily/monthly limits if the server provides that information
1520
+
1521
+ return false;
1522
+ }, [quotaInfo]);
1523
+
1524
+ const getQuotaWarningMessage = useCallback(() => {
1525
+ if (!quotaInfo) return null;
1526
+
1527
+ const { usage, limits } = quotaInfo;
1528
+ const warnings: string[] = [];
1529
+
1530
+ // Check tokens
1531
+ if (limits.absoluteTokens > 0) {
1532
+ const tokenPercentage = (usage.tokens / limits.absoluteTokens) * 100;
1533
+ if (tokenPercentage >= 100) {
1534
+ warnings.push(
1535
+ `Token limit exceeded (${usage.tokens}/${limits.absoluteTokens})`,
1536
+ );
1537
+ } else if (tokenPercentage >= 90) {
1538
+ warnings.push(
1539
+ `Token usage high: ${Math.round(tokenPercentage)}% (${usage.tokens}/${limits.absoluteTokens})`,
1540
+ );
1541
+ }
1542
+ }
1543
+
1544
+ // Check images
1545
+ if (limits.absoluteImages > 0) {
1546
+ const imagePercentage = (usage.images / limits.absoluteImages) * 100;
1547
+ if (imagePercentage >= 100) {
1548
+ warnings.push(
1549
+ `Image limit exceeded (${usage.images}/${limits.absoluteImages})`,
1550
+ );
1551
+ } else if (imagePercentage >= 90) {
1552
+ warnings.push(
1553
+ `Image usage high: ${Math.round(imagePercentage)}% (${usage.images}/${limits.absoluteImages})`,
1554
+ );
1555
+ }
1556
+ }
1557
+
1558
+ return warnings.length > 0 ? warnings.join(", ") : null;
1559
+ }, [quotaInfo]);
1560
+
1561
+ // Show warning when quota is exceeded
1562
+ useEffect(() => {
1563
+ const warningMessage = getQuotaWarningMessage();
1564
+ if (warningMessage) {
1565
+ const isExceeded = isQuotaExceeded();
1566
+ showErrorToast({
1567
+ summary: isExceeded ? "AI Quota Exceeded" : "AI Quota Warning",
1568
+ details: warningMessage,
1569
+ });
1570
+ }
1571
+ }, [quotaInfo, getQuotaWarningMessage, isQuotaExceeded, showErrorToast]);
1572
+
1435
1573
  const editContext = useMemo(() => {
1436
1574
  return {
1437
1575
  operations: operationsContext.ops,
@@ -1600,7 +1738,8 @@ export function EditorClient({
1600
1738
 
1601
1739
  const op: FieldAction = {
1602
1740
  field: fieldDescriptor,
1603
- actionButton,
1741
+ // actionButton,
1742
+ label: actionButton.label,
1604
1743
  state: "running",
1605
1744
  };
1606
1745
  const fieldItem = fieldDescriptor.item;
@@ -1807,6 +1946,9 @@ export function EditorClient({
1807
1946
  setShowSuggestedEditsDiff,
1808
1947
  enableCompletions,
1809
1948
  setEnableCompletions,
1949
+ quotaInfo,
1950
+ isQuotaExceeded: isQuotaExceeded(),
1951
+ getQuotaWarningMessage,
1810
1952
  isMobile,
1811
1953
  openDialog,
1812
1954
  pageWizard,
@@ -1883,6 +2025,9 @@ export function EditorClient({
1883
2025
  setShowSuggestedEdits,
1884
2026
  showSuggestedEditsDiff,
1885
2027
  setShowSuggestedEditsDiff,
2028
+ quotaInfo,
2029
+ isQuotaExceeded,
2030
+ getQuotaWarningMessage,
1886
2031
  isMobile,
1887
2032
  openDialog,
1888
2033
  pageWizard,
@@ -10,7 +10,7 @@ import React, {
10
10
  import { ToastMessage } from "primereact/toast";
11
11
 
12
12
  import { EditorConfiguration, EditorView } from "../../config/types";
13
- import { FieldAction, InsertingState } from "./EditorClient";
13
+ import { FieldAction, InsertingState, QuotaInfo } from "./EditorClient";
14
14
  import { MenuItem } from "primereact/menuitem";
15
15
  import { Command, CommandData } from "../commands/commands";
16
16
  import { ComponentDetails } from "../services/componentDesignerService";
@@ -303,7 +303,9 @@ export type EditContextType = {
303
303
 
304
304
  enableCompletions: boolean;
305
305
  setEnableCompletions: React.Dispatch<React.SetStateAction<boolean>>;
306
-
306
+ quotaInfo: QuotaInfo | null;
307
+ isQuotaExceeded: boolean;
308
+ getQuotaWarningMessage: () => string | null;
307
309
  isMobile: boolean;
308
310
 
309
311
  openDialog: OpenDialog;
@@ -66,7 +66,7 @@ export function useItemsRepository(
66
66
  itemsToLoad.forEach((item) => {
67
67
  const key = generateKey(item);
68
68
  if (!requestGroups.has(key)) {
69
- requestGroups.set(key, item);
69
+ requestGroups.set(key, getItemDescriptor(item));
70
70
  }
71
71
  });
72
72
 
@@ -387,7 +387,7 @@ export function getOperationsContext(
387
387
  value,
388
388
  rawValue,
389
389
  );
390
- editFieldImmediate({ field, value, rawValue, refresh });
390
+ return editFieldImmediate({ field, value, rawValue, refresh });
391
391
  },
392
392
  state.configuration.debounceFieldEditsInterval * 2,
393
393
  { trailing: true },
@@ -0,0 +1,342 @@
1
+ import React from "react";
2
+ import { useEditContext } from "../client/editContext";
3
+ import { version, buildDate } from "../../revision";
4
+
5
+ export function About() {
6
+ const editContext = useEditContext();
7
+
8
+ return (
9
+ <div className="max-h-full space-y-6 p-4">
10
+ <div className="space-y-4">
11
+ <h2 className="text-xl font-semibold text-gray-800">
12
+ About Alpaca Editor
13
+ </h2>
14
+ {/* Application Information */}
15
+ <div className="rounded-lg border border-gray-200 bg-white p-4">
16
+ <h3 className="mb-3 text-lg font-medium text-gray-700">
17
+ Application Information
18
+ </h3>
19
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
20
+ <div>
21
+ <label className="text-sm font-medium text-gray-500">
22
+ Product Name
23
+ </label>
24
+ <p className="text-gray-900">Alpaca Editor</p>
25
+ </div>
26
+ <div>
27
+ <label className="text-sm font-medium text-gray-500">
28
+ Version
29
+ </label>
30
+ <p className="text-gray-900">{version}</p>
31
+ </div>
32
+
33
+ <div>
34
+ <label className="text-sm font-medium text-gray-500">
35
+ Build Date
36
+ </label>
37
+ <p className="text-gray-900">{buildDate}</p>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ {/* License Information */}{" "}
42
+ <div className="rounded-lg border border-gray-200 bg-white p-4">
43
+ {" "}
44
+ <h3 className="mb-3 text-lg font-medium text-gray-700">
45
+ {" "}
46
+ License Information{" "}
47
+ </h3>{" "}
48
+ <div className="space-y-4">
49
+ {" "}
50
+ <div>
51
+ {" "}
52
+ <label className="text-sm font-medium text-gray-500">
53
+ {" "}
54
+ Copyright{" "}
55
+ </label>{" "}
56
+ <p className="text-gray-900">© 2025 Canvas Reply GmbH</p>{" "}
57
+ </div>{" "}
58
+ <div>
59
+ {" "}
60
+ <label className="text-sm font-medium text-gray-500">
61
+ {" "}
62
+ License{" "}
63
+ </label>{" "}
64
+ <p className="text-gray-900">
65
+ {" "}
66
+ All rights reserved. Please contact Canvas Reply GmbH for
67
+ licensing information.{" "}
68
+ </p>{" "}
69
+ </div>{" "}
70
+ </div>{" "}
71
+ </div>
72
+ {/* Third-Party Licenses */}{" "}
73
+ <div className="rounded-lg border border-gray-200 bg-white p-4">
74
+ {" "}
75
+ <h3 className="mb-3 text-lg font-medium text-gray-700">
76
+ {" "}
77
+ Third-Party Components{" "}
78
+ </h3>{" "}
79
+ <div className="max-h-96 space-y-3 overflow-y-auto">
80
+ {" "}
81
+ <div className="rounded-lg bg-gray-50 p-3">
82
+ {" "}
83
+ <div className="mb-2 flex items-center justify-between">
84
+ {" "}
85
+ <h4 className="text-sm font-medium text-gray-700">
86
+ React
87
+ </h4>{" "}
88
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
89
+ </div>{" "}
90
+ <p className="text-xs text-gray-600">
91
+ {" "}
92
+ A JavaScript library for building user interfaces{" "}
93
+ </p>{" "}
94
+ </div>{" "}
95
+ <div className="rounded-lg bg-gray-50 p-3">
96
+ {" "}
97
+ <div className="mb-2 flex items-center justify-between">
98
+ {" "}
99
+ <h4 className="text-sm font-medium text-gray-700">
100
+ Next.js
101
+ </h4>{" "}
102
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
103
+ </div>{" "}
104
+ <p className="text-xs text-gray-600">
105
+ {" "}
106
+ The React Framework for Production{" "}
107
+ </p>{" "}
108
+ </div>{" "}
109
+ <div className="rounded-lg bg-gray-50 p-3">
110
+ {" "}
111
+ <div className="mb-2 flex items-center justify-between">
112
+ {" "}
113
+ <h4 className="text-sm font-medium text-gray-700">
114
+ TypeScript
115
+ </h4>{" "}
116
+ <span className="text-xs text-gray-500">
117
+ Apache 2.0 License
118
+ </span>{" "}
119
+ </div>{" "}
120
+ <p className="text-xs text-gray-600">
121
+ {" "}
122
+ TypeScript is a language for application scale JavaScript
123
+ development{" "}
124
+ </p>{" "}
125
+ </div>{" "}
126
+ <div className="rounded-lg bg-gray-50 p-3">
127
+ {" "}
128
+ <div className="mb-2 flex items-center justify-between">
129
+ {" "}
130
+ <h4 className="text-sm font-medium text-gray-700">
131
+ Tailwind CSS
132
+ </h4>{" "}
133
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
134
+ </div>{" "}
135
+ <p className="text-xs text-gray-600">
136
+ {" "}
137
+ A utility-first CSS framework for rapidly building custom
138
+ designs{" "}
139
+ </p>{" "}
140
+ </div>{" "}
141
+ <div className="rounded-lg bg-gray-50 p-3">
142
+ {" "}
143
+ <div className="mb-2 flex items-center justify-between">
144
+ {" "}
145
+ <h4 className="text-sm font-medium text-gray-700">
146
+ PrimeReact
147
+ </h4>{" "}
148
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
149
+ </div>{" "}
150
+ <p className="text-xs text-gray-600">
151
+ {" "}
152
+ React UI Component Library with rich set of components{" "}
153
+ </p>{" "}
154
+ </div>{" "}
155
+ <div className="rounded-lg bg-gray-50 p-3">
156
+ {" "}
157
+ <div className="mb-2 flex items-center justify-between">
158
+ {" "}
159
+ <h4 className="text-sm font-medium text-gray-700">
160
+ Lucide React
161
+ </h4>{" "}
162
+ <span className="text-xs text-gray-500">ISC License</span>{" "}
163
+ </div>{" "}
164
+ <p className="text-xs text-gray-600">
165
+ {" "}
166
+ Beautiful & consistent icon toolkit made by the community{" "}
167
+ </p>{" "}
168
+ </div>{" "}
169
+ <div className="rounded-lg bg-gray-50 p-3">
170
+ {" "}
171
+ <div className="mb-2 flex items-center justify-between">
172
+ {" "}
173
+ <h4 className="text-sm font-medium text-gray-700">
174
+ Radix UI
175
+ </h4>{" "}
176
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
177
+ </div>{" "}
178
+ <p className="text-xs text-gray-600">
179
+ {" "}
180
+ Low-level UI primitives for React with accessibility
181
+ built-in{" "}
182
+ </p>{" "}
183
+ </div>{" "}
184
+ <div className="rounded-lg bg-gray-50 p-3">
185
+ {" "}
186
+ <div className="mb-2 flex items-center justify-between">
187
+ {" "}
188
+ <h4 className="text-sm font-medium text-gray-700">
189
+ Axios
190
+ </h4>{" "}
191
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
192
+ </div>{" "}
193
+ <p className="text-xs text-gray-600">
194
+ {" "}
195
+ Promise based HTTP client for the browser and node.js{" "}
196
+ </p>{" "}
197
+ </div>{" "}
198
+ <div className="rounded-lg bg-gray-50 p-3">
199
+ {" "}
200
+ <div className="mb-2 flex items-center justify-between">
201
+ {" "}
202
+ <h4 className="text-sm font-medium text-gray-700">
203
+ Quill
204
+ </h4>{" "}
205
+ <span className="text-xs text-gray-500">
206
+ BSD 3-Clause License
207
+ </span>{" "}
208
+ </div>{" "}
209
+ <p className="text-xs text-gray-600">
210
+ {" "}
211
+ Modern WYSIWYG editor built for compatibility and
212
+ extensibility{" "}
213
+ </p>{" "}
214
+ </div>{" "}
215
+ <div className="rounded-lg bg-gray-50 p-3">
216
+ {" "}
217
+ <div className="mb-2 flex items-center justify-between">
218
+ {" "}
219
+ <h4 className="text-sm font-medium text-gray-700">
220
+ React Inspector
221
+ </h4>{" "}
222
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
223
+ </div>{" "}
224
+ <p className="text-xs text-gray-600">
225
+ {" "}
226
+ Power of Browser DevTools inspectors right inside your React
227
+ app{" "}
228
+ </p>{" "}
229
+ </div>{" "}
230
+ <div className="rounded-lg bg-gray-50 p-3">
231
+ {" "}
232
+ <div className="mb-2 flex items-center justify-between">
233
+ {" "}
234
+ <h4 className="text-sm font-medium text-gray-700">
235
+ Allotment
236
+ </h4>{" "}
237
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
238
+ </div>{" "}
239
+ <p className="text-xs text-gray-600">
240
+ {" "}
241
+ React split-pane component with support for multiple
242
+ layouts{" "}
243
+ </p>{" "}
244
+ </div>{" "}
245
+ <div className="rounded-lg bg-gray-50 p-3">
246
+ {" "}
247
+ <div className="mb-2 flex items-center justify-between">
248
+ {" "}
249
+ <h4 className="text-sm font-medium text-gray-700">
250
+ Lodash
251
+ </h4>{" "}
252
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
253
+ </div>{" "}
254
+ <p className="text-xs text-gray-600">
255
+ {" "}
256
+ A modern JavaScript utility library delivering modularity,
257
+ performance & extras{" "}
258
+ </p>{" "}
259
+ </div>{" "}
260
+ <div className="rounded-lg bg-gray-50 p-3">
261
+ {" "}
262
+ <div className="mb-2 flex items-center justify-between">
263
+ {" "}
264
+ <h4 className="text-sm font-medium text-gray-700">clsx</h4>{" "}
265
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
266
+ </div>{" "}
267
+ <p className="text-xs text-gray-600">
268
+ {" "}
269
+ A tiny utility for constructing className strings
270
+ conditionally{" "}
271
+ </p>{" "}
272
+ </div>{" "}
273
+ <div className="rounded-lg bg-gray-50 p-3">
274
+ {" "}
275
+ <div className="mb-2 flex items-center justify-between">
276
+ {" "}
277
+ <h4 className="text-sm font-medium text-gray-700">
278
+ PostCSS
279
+ </h4>{" "}
280
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
281
+ </div>{" "}
282
+ <p className="text-xs text-gray-600">
283
+ {" "}
284
+ Tool for transforming CSS with JavaScript{" "}
285
+ </p>{" "}
286
+ </div>{" "}
287
+ <div className="rounded-lg bg-gray-50 p-3">
288
+ {" "}
289
+ <div className="mb-2 flex items-center justify-between">
290
+ {" "}
291
+ <h4 className="text-sm font-medium text-gray-700">
292
+ Universal Cookie
293
+ </h4>{" "}
294
+ <span className="text-xs text-gray-500">MIT License</span>{" "}
295
+ </div>{" "}
296
+ <p className="text-xs text-gray-600">
297
+ {" "}
298
+ Universal cookies for JavaScript{" "}
299
+ </p>{" "}
300
+ </div>{" "}
301
+ </div>{" "}
302
+ </div>
303
+ {/* Contact Information */}
304
+ <div className="rounded-lg border border-gray-200 bg-white p-4">
305
+ <h3 className="mb-3 text-lg font-medium text-gray-700">
306
+ Contact & Support
307
+ </h3>
308
+ <div className="space-y-3">
309
+ <div>
310
+ <label className="text-sm font-medium text-gray-500">
311
+ Documentation
312
+ </label>
313
+ <p className="text-gray-900">
314
+ <a
315
+ href="#"
316
+ className="text-blue-600 underline hover:text-blue-800"
317
+ onClick={(e) => e.preventDefault()}
318
+ >
319
+ View Documentation
320
+ </a>
321
+ </p>
322
+ </div>
323
+ <div>
324
+ <label className="text-sm font-medium text-gray-500">
325
+ Support
326
+ </label>
327
+ <p className="text-gray-900">
328
+ <a
329
+ href="#"
330
+ className="text-blue-600 underline hover:text-blue-800"
331
+ onClick={(e) => e.preventDefault()}
332
+ >
333
+ Contact Support
334
+ </a>
335
+ </p>
336
+ </div>
337
+ </div>
338
+ </div>
339
+ </div>
340
+ </div>
341
+ );
342
+ }
@@ -50,6 +50,11 @@ export function ControlCenterMenu() {
50
50
  const item = groups
51
51
  ?.flatMap((x) => x.panels)
52
52
  ?.find((item) => item.id === activeItemKey);
53
+
54
+ if (!item) {
55
+ setActiveItemKey(groups?.[0]?.panels?.[0]?.id || null);
56
+ }
57
+
53
58
  if (item) {
54
59
  editContext?.setCenterPanelView(item.content);
55
60
  }