@docubook/create 1.16.1 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/dist/app/docs/[[...slug]]/page.tsx +22 -11
- package/src/dist/app/layout.tsx +3 -0
- package/src/dist/app/page.tsx +6 -6
- package/src/dist/components/SearchModal.tsx +41 -37
- package/src/dist/components/context-popover.tsx +3 -2
- package/src/dist/components/contexts/theme-provider.tsx +1 -2
- package/src/dist/components/leftbar.tsx +1 -1
- package/src/dist/components/markdown/AccordionMdx.tsx +6 -6
- package/src/dist/components/markdown/CardGroupMdx.tsx +10 -2
- package/src/dist/components/markdown/CopyMdx.tsx +1 -1
- package/src/dist/components/markdown/FileTreeMdx.tsx +2 -2
- package/src/dist/components/markdown/ImageMdx.tsx +122 -18
- package/src/dist/components/markdown/NoteMdx.tsx +46 -29
- package/src/dist/components/markdown/PreMdx.tsx +1 -1
- package/src/dist/components/mob-toc.tsx +1 -1
- package/src/dist/components/scroll-to-top.tsx +1 -0
- package/src/dist/components/sublink.tsx +2 -1
- package/src/dist/components/theme-toggle.tsx +38 -37
- package/src/dist/components/ui/tabs.tsx +1 -1
- package/src/dist/eslint.config.mjs +35 -0
- package/src/dist/hooks/useScrollPosition.ts +6 -5
- package/src/dist/package.json +44 -39
- package/src/dist/postcss.config.js +1 -1
- package/src/dist/styles/globals.css +230 -111
- package/src/dist/tailwind.config.ts +107 -105
- package/src/dist/tsconfig.json +21 -6
- package/src/dist/components/ui/icon-cloud.tsx +0 -324
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import React, { useEffect, useRef, useState } from "react";
|
|
4
|
-
import { renderToString } from "react-dom/server";
|
|
5
|
-
|
|
6
|
-
interface Icon {
|
|
7
|
-
x: number;
|
|
8
|
-
y: number;
|
|
9
|
-
z: number;
|
|
10
|
-
scale: number;
|
|
11
|
-
opacity: number;
|
|
12
|
-
id: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface IconCloudProps {
|
|
16
|
-
icons?: React.ReactNode[];
|
|
17
|
-
images?: string[];
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function easeOutCubic(t: number): number {
|
|
21
|
-
return 1 - Math.pow(1 - t, 3);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function IconCloud({ icons, images }: IconCloudProps) {
|
|
25
|
-
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
26
|
-
const [iconPositions, setIconPositions] = useState<Icon[]>([]);
|
|
27
|
-
const [rotation] = useState({ x: 0, y: 0 });
|
|
28
|
-
const [isDragging, setIsDragging] = useState(false);
|
|
29
|
-
const [lastMousePos, setLastMousePos] = useState({ x: 0, y: 0 });
|
|
30
|
-
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
|
|
31
|
-
const [targetRotation, setTargetRotation] = useState<{
|
|
32
|
-
x: number;
|
|
33
|
-
y: number;
|
|
34
|
-
startX: number;
|
|
35
|
-
startY: number;
|
|
36
|
-
distance: number;
|
|
37
|
-
startTime: number;
|
|
38
|
-
duration: number;
|
|
39
|
-
} | null>(null);
|
|
40
|
-
const animationFrameRef = useRef<number>();
|
|
41
|
-
const rotationRef = useRef(rotation);
|
|
42
|
-
const iconCanvasesRef = useRef<HTMLCanvasElement[]>([]);
|
|
43
|
-
const imagesLoadedRef = useRef<boolean[]>([]);
|
|
44
|
-
|
|
45
|
-
// Create icon canvases once when icons/images change
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (!icons && !images) return;
|
|
48
|
-
|
|
49
|
-
const items = icons || images || [];
|
|
50
|
-
imagesLoadedRef.current = new Array(items.length).fill(false);
|
|
51
|
-
|
|
52
|
-
const newIconCanvases = items.map((item, index) => {
|
|
53
|
-
const offscreen = document.createElement("canvas");
|
|
54
|
-
offscreen.width = 40;
|
|
55
|
-
offscreen.height = 40;
|
|
56
|
-
const offCtx = offscreen.getContext("2d");
|
|
57
|
-
|
|
58
|
-
if (offCtx) {
|
|
59
|
-
if (images) {
|
|
60
|
-
// Handle image URLs directly
|
|
61
|
-
const img = new Image();
|
|
62
|
-
img.crossOrigin = "anonymous";
|
|
63
|
-
img.src = items[index] as string;
|
|
64
|
-
img.onload = () => {
|
|
65
|
-
offCtx.clearRect(0, 0, offscreen.width, offscreen.height);
|
|
66
|
-
|
|
67
|
-
// Create circular clipping path
|
|
68
|
-
offCtx.beginPath();
|
|
69
|
-
offCtx.arc(20, 20, 20, 0, Math.PI * 2);
|
|
70
|
-
offCtx.closePath();
|
|
71
|
-
offCtx.clip();
|
|
72
|
-
|
|
73
|
-
// Draw the image
|
|
74
|
-
offCtx.drawImage(img, 0, 0, 40, 40);
|
|
75
|
-
|
|
76
|
-
imagesLoadedRef.current[index] = true;
|
|
77
|
-
};
|
|
78
|
-
} else {
|
|
79
|
-
// Handle SVG icons
|
|
80
|
-
offCtx.scale(0.4, 0.4);
|
|
81
|
-
const svgString = renderToString(item as React.ReactElement);
|
|
82
|
-
const img = new Image();
|
|
83
|
-
img.src = "data:image/svg+xml;base64," + btoa(svgString);
|
|
84
|
-
img.onload = () => {
|
|
85
|
-
offCtx.clearRect(0, 0, offscreen.width, offscreen.height);
|
|
86
|
-
offCtx.drawImage(img, 0, 0);
|
|
87
|
-
imagesLoadedRef.current[index] = true;
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return offscreen;
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
iconCanvasesRef.current = newIconCanvases;
|
|
95
|
-
}, [icons, images]);
|
|
96
|
-
|
|
97
|
-
// Generate initial icon positions on a sphere
|
|
98
|
-
useEffect(() => {
|
|
99
|
-
const items = icons || images || [];
|
|
100
|
-
const newIcons: Icon[] = [];
|
|
101
|
-
const numIcons = items.length || 20;
|
|
102
|
-
|
|
103
|
-
// Fibonacci sphere parameters
|
|
104
|
-
const offset = 2 / numIcons;
|
|
105
|
-
const increment = Math.PI * (3 - Math.sqrt(5));
|
|
106
|
-
|
|
107
|
-
for (let i = 0; i < numIcons; i++) {
|
|
108
|
-
const y = i * offset - 1 + offset / 2;
|
|
109
|
-
const r = Math.sqrt(1 - y * y);
|
|
110
|
-
const phi = i * increment;
|
|
111
|
-
|
|
112
|
-
const x = Math.cos(phi) * r;
|
|
113
|
-
const z = Math.sin(phi) * r;
|
|
114
|
-
|
|
115
|
-
newIcons.push({
|
|
116
|
-
x: x * 100,
|
|
117
|
-
y: y * 100,
|
|
118
|
-
z: z * 100,
|
|
119
|
-
scale: 1,
|
|
120
|
-
opacity: 1,
|
|
121
|
-
id: i,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
setIconPositions(newIcons);
|
|
125
|
-
}, [icons, images]);
|
|
126
|
-
|
|
127
|
-
// Handle mouse events
|
|
128
|
-
const handleMouseDown = (e: React.MouseEvent<HTMLCanvasElement>) => {
|
|
129
|
-
const rect = canvasRef.current?.getBoundingClientRect();
|
|
130
|
-
if (!rect || !canvasRef.current) return;
|
|
131
|
-
|
|
132
|
-
const x = e.clientX - rect.left;
|
|
133
|
-
const y = e.clientY - rect.top;
|
|
134
|
-
|
|
135
|
-
const ctx = canvasRef.current.getContext("2d");
|
|
136
|
-
if (!ctx) return;
|
|
137
|
-
|
|
138
|
-
iconPositions.forEach((icon) => {
|
|
139
|
-
const cosX = Math.cos(rotationRef.current.x);
|
|
140
|
-
const sinX = Math.sin(rotationRef.current.x);
|
|
141
|
-
const cosY = Math.cos(rotationRef.current.y);
|
|
142
|
-
const sinY = Math.sin(rotationRef.current.y);
|
|
143
|
-
|
|
144
|
-
const rotatedX = icon.x * cosY - icon.z * sinY;
|
|
145
|
-
const rotatedZ = icon.x * sinY + icon.z * cosY;
|
|
146
|
-
const rotatedY = icon.y * cosX + rotatedZ * sinX;
|
|
147
|
-
|
|
148
|
-
const screenX = canvasRef.current!.width / 2 + rotatedX;
|
|
149
|
-
const screenY = canvasRef.current!.height / 2 + rotatedY;
|
|
150
|
-
|
|
151
|
-
const scale = (rotatedZ + 200) / 300;
|
|
152
|
-
const radius = 20 * scale;
|
|
153
|
-
const dx = x - screenX;
|
|
154
|
-
const dy = y - screenY;
|
|
155
|
-
|
|
156
|
-
if (dx * dx + dy * dy < radius * radius) {
|
|
157
|
-
const targetX = -Math.atan2(
|
|
158
|
-
icon.y,
|
|
159
|
-
Math.sqrt(icon.x * icon.x + icon.z * icon.z),
|
|
160
|
-
);
|
|
161
|
-
const targetY = Math.atan2(icon.x, icon.z);
|
|
162
|
-
|
|
163
|
-
const currentX = rotationRef.current.x;
|
|
164
|
-
const currentY = rotationRef.current.y;
|
|
165
|
-
const distance = Math.sqrt(
|
|
166
|
-
Math.pow(targetX - currentX, 2) + Math.pow(targetY - currentY, 2),
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
const duration = Math.min(2000, Math.max(800, distance * 1000));
|
|
170
|
-
|
|
171
|
-
setTargetRotation({
|
|
172
|
-
x: targetX,
|
|
173
|
-
y: targetY,
|
|
174
|
-
startX: currentX,
|
|
175
|
-
startY: currentY,
|
|
176
|
-
distance,
|
|
177
|
-
startTime: performance.now(),
|
|
178
|
-
duration,
|
|
179
|
-
});
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
setIsDragging(true);
|
|
185
|
-
setLastMousePos({ x: e.clientX, y: e.clientY });
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const handleMouseMove = (e: React.MouseEvent<HTMLCanvasElement>) => {
|
|
189
|
-
const rect = canvasRef.current?.getBoundingClientRect();
|
|
190
|
-
if (rect) {
|
|
191
|
-
const x = e.clientX - rect.left;
|
|
192
|
-
const y = e.clientY - rect.top;
|
|
193
|
-
setMousePos({ x, y });
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (isDragging) {
|
|
197
|
-
const deltaX = e.clientX - lastMousePos.x;
|
|
198
|
-
const deltaY = e.clientY - lastMousePos.y;
|
|
199
|
-
|
|
200
|
-
rotationRef.current = {
|
|
201
|
-
x: rotationRef.current.x + deltaY * 0.002,
|
|
202
|
-
y: rotationRef.current.y + deltaX * 0.002,
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
setLastMousePos({ x: e.clientX, y: e.clientY });
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
const handleMouseUp = () => {
|
|
210
|
-
setIsDragging(false);
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
// Animation and rendering
|
|
214
|
-
useEffect(() => {
|
|
215
|
-
const canvas = canvasRef.current;
|
|
216
|
-
const ctx = canvas?.getContext("2d");
|
|
217
|
-
if (!canvas || !ctx) return;
|
|
218
|
-
|
|
219
|
-
const animate = () => {
|
|
220
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
221
|
-
|
|
222
|
-
const centerX = canvas.width / 2;
|
|
223
|
-
const centerY = canvas.height / 2;
|
|
224
|
-
const maxDistance = Math.sqrt(centerX * centerX + centerY * centerY);
|
|
225
|
-
const dx = mousePos.x - centerX;
|
|
226
|
-
const dy = mousePos.y - centerY;
|
|
227
|
-
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
228
|
-
const speed = 0.003 + (distance / maxDistance) * 0.01;
|
|
229
|
-
|
|
230
|
-
if (targetRotation) {
|
|
231
|
-
const elapsed = performance.now() - targetRotation.startTime;
|
|
232
|
-
const progress = Math.min(1, elapsed / targetRotation.duration);
|
|
233
|
-
const easedProgress = easeOutCubic(progress);
|
|
234
|
-
|
|
235
|
-
rotationRef.current = {
|
|
236
|
-
x:
|
|
237
|
-
targetRotation.startX +
|
|
238
|
-
(targetRotation.x - targetRotation.startX) * easedProgress,
|
|
239
|
-
y:
|
|
240
|
-
targetRotation.startY +
|
|
241
|
-
(targetRotation.y - targetRotation.startY) * easedProgress,
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
if (progress >= 1) {
|
|
245
|
-
setTargetRotation(null);
|
|
246
|
-
}
|
|
247
|
-
} else if (!isDragging) {
|
|
248
|
-
rotationRef.current = {
|
|
249
|
-
x: rotationRef.current.x + (dy / canvas.height) * speed,
|
|
250
|
-
y: rotationRef.current.y + (dx / canvas.width) * speed,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
iconPositions.forEach((icon, index) => {
|
|
255
|
-
const cosX = Math.cos(rotationRef.current.x);
|
|
256
|
-
const sinX = Math.sin(rotationRef.current.x);
|
|
257
|
-
const cosY = Math.cos(rotationRef.current.y);
|
|
258
|
-
const sinY = Math.sin(rotationRef.current.y);
|
|
259
|
-
|
|
260
|
-
const rotatedX = icon.x * cosY - icon.z * sinY;
|
|
261
|
-
const rotatedZ = icon.x * sinY + icon.z * cosY;
|
|
262
|
-
const rotatedY = icon.y * cosX + rotatedZ * sinX;
|
|
263
|
-
|
|
264
|
-
const scale = (rotatedZ + 200) / 300;
|
|
265
|
-
const opacity = Math.max(0.2, Math.min(1, (rotatedZ + 150) / 200));
|
|
266
|
-
|
|
267
|
-
ctx.save();
|
|
268
|
-
ctx.translate(
|
|
269
|
-
canvas.width / 2 + rotatedX,
|
|
270
|
-
canvas.height / 2 + rotatedY,
|
|
271
|
-
);
|
|
272
|
-
ctx.scale(scale, scale);
|
|
273
|
-
ctx.globalAlpha = opacity;
|
|
274
|
-
|
|
275
|
-
if (icons || images) {
|
|
276
|
-
// Only try to render icons/images if they exist
|
|
277
|
-
if (
|
|
278
|
-
iconCanvasesRef.current[index] &&
|
|
279
|
-
imagesLoadedRef.current[index]
|
|
280
|
-
) {
|
|
281
|
-
ctx.drawImage(iconCanvasesRef.current[index], -20, -20, 40, 40);
|
|
282
|
-
}
|
|
283
|
-
} else {
|
|
284
|
-
// Show numbered circles if no icons/images are provided
|
|
285
|
-
ctx.beginPath();
|
|
286
|
-
ctx.arc(0, 0, 20, 0, Math.PI * 2);
|
|
287
|
-
ctx.fillStyle = "#4444ff";
|
|
288
|
-
ctx.fill();
|
|
289
|
-
ctx.fillStyle = "white";
|
|
290
|
-
ctx.textAlign = "center";
|
|
291
|
-
ctx.textBaseline = "middle";
|
|
292
|
-
ctx.font = "16px Arial";
|
|
293
|
-
ctx.fillText(`${icon.id + 1}`, 0, 0);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
ctx.restore();
|
|
297
|
-
});
|
|
298
|
-
animationFrameRef.current = requestAnimationFrame(animate);
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
animate();
|
|
302
|
-
|
|
303
|
-
return () => {
|
|
304
|
-
if (animationFrameRef.current) {
|
|
305
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
}, [icons, images, iconPositions, isDragging, mousePos, targetRotation]);
|
|
309
|
-
|
|
310
|
-
return (
|
|
311
|
-
<canvas
|
|
312
|
-
ref={canvasRef}
|
|
313
|
-
width={400}
|
|
314
|
-
height={400}
|
|
315
|
-
onMouseDown={handleMouseDown}
|
|
316
|
-
onMouseMove={handleMouseMove}
|
|
317
|
-
onMouseUp={handleMouseUp}
|
|
318
|
-
onMouseLeave={handleMouseUp}
|
|
319
|
-
className="rounded-full"
|
|
320
|
-
aria-label="Interactive 3D Icon Cloud"
|
|
321
|
-
role="img"
|
|
322
|
-
/>
|
|
323
|
-
);
|
|
324
|
-
}
|