mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
minimum editor
This commit is contained in:
+2
-2
@@ -24,9 +24,9 @@
|
||||
"@ai-sdk/svelte": "^1.1.24",
|
||||
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@957a779",
|
||||
"@appwrite.io/pink-legacy": "^1.0.3",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@957a779",
|
||||
"@faker-js/faker": "^9.9.0",
|
||||
"@neodrag/svelte": "^2.3.3",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
|
||||
Generated
+10
-10
@@ -18,14 +18,14 @@ importers:
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
'@appwrite.io/pink-icons-svelte':
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc(svelte@5.25.3)
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@957a779
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@957a779(svelte@5.25.3)
|
||||
'@appwrite.io/pink-legacy':
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3
|
||||
'@appwrite.io/pink-svelte':
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc(svelte@5.25.3)
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@957a779
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@957a779(svelte@5.25.3)
|
||||
'@faker-js/faker':
|
||||
specifier: ^9.9.0
|
||||
version: 9.9.0
|
||||
@@ -284,8 +284,8 @@ packages:
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc}
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@957a779':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@957a779}
|
||||
version: 2.0.0-RC.1
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -299,8 +299,8 @@ packages:
|
||||
'@appwrite.io/pink-legacy@1.0.3':
|
||||
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc}
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@957a779':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@957a779}
|
||||
version: 2.0.0-RC.2
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -3837,7 +3837,7 @@ snapshots:
|
||||
dependencies:
|
||||
svelte: 5.25.3
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@957a779(svelte@5.25.3)':
|
||||
dependencies:
|
||||
svelte: 5.25.3
|
||||
|
||||
@@ -3850,7 +3850,7 @@ snapshots:
|
||||
'@appwrite.io/pink-icons': 1.0.0
|
||||
the-new-css-reset: 1.11.3
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@957a779(svelte@5.25.3)':
|
||||
dependencies:
|
||||
'@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3)
|
||||
'@floating-ui/dom': 1.6.13
|
||||
|
||||
+188
-199
@@ -3,7 +3,7 @@
|
||||
import { Container } from '$lib/layout';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { ImageFormat, Query, type Models } from '@appwrite.io/console';
|
||||
import { Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { Layout, Typography, Canvas } from '@appwrite.io/pink-svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { CopyInput } from '$lib/components';
|
||||
import ImageGrid from './components/imageGrid.svelte';
|
||||
@@ -14,6 +14,7 @@
|
||||
generateTransformationParams,
|
||||
type TransformationState
|
||||
} from '$lib/helpers/imageTransformations';
|
||||
import type { CanvasObjectUnion } from '@appwrite.io/pink-svelte/dist/canvas/index.js'; // Import type explicitly if needed or infer
|
||||
|
||||
// UI State
|
||||
let activeTab = $state<'design' | 'code'>('design');
|
||||
@@ -52,15 +53,95 @@
|
||||
let appliedPresets = $state<Record<string, string>>({}); // fileId -> presetId
|
||||
|
||||
// Canvas state
|
||||
let canvasContainer = $state<HTMLDivElement>();
|
||||
let imageElement = $state<HTMLImageElement>();
|
||||
let isResizing = $state(false);
|
||||
let resizeHandle = $state<string | null>(null);
|
||||
let startX = $state(0);
|
||||
let startY = $state(0);
|
||||
let startWidth = $state(0);
|
||||
let startHeight = $state(0);
|
||||
let focalPointOverlay = $state<string | null>(null);
|
||||
let canvasCore = $state<any>(null);
|
||||
let currentZoom = $state(1);
|
||||
|
||||
let canvasObjects = $state<CanvasObjectUnion[]>([]);
|
||||
|
||||
// Force selection of the main image and ensure handles are visible
|
||||
$effect(() => {
|
||||
if (canvasCore && selectedFile && canvasObjects.length > 0) {
|
||||
// Use a small timeout to ensure the canvas has initialized properly
|
||||
setTimeout(() => {
|
||||
canvasCore.selectObject('main-image');
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (!canvasCore) return;
|
||||
const unsubscribe = canvasCore.zoom.subscribe((z: number) => {
|
||||
currentZoom = z;
|
||||
zoom = Math.round(z * 100);
|
||||
});
|
||||
return unsubscribe;
|
||||
});
|
||||
// Sync init
|
||||
$effect(() => {
|
||||
if (selectedFile) {
|
||||
const existing = canvasObjects.find((o) => o.id === 'main-image');
|
||||
if (!existing) {
|
||||
// Initialize
|
||||
canvasObjects = [
|
||||
{
|
||||
id: 'main-image',
|
||||
type: 'image',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: transformationState.width || 700,
|
||||
height: transformationState.height || 438,
|
||||
rotation: transformationState.rotation || 0,
|
||||
src: previewUrl, // Use current preview URL
|
||||
alt: selectedFile.name,
|
||||
maintainAspectRatio: transformationState.aspectRatioLocked,
|
||||
selected: true,
|
||||
// Ensure optional props are set
|
||||
cropX: 0,
|
||||
cropY: 0
|
||||
}
|
||||
];
|
||||
// Center the view on init
|
||||
setTimeout(() => {
|
||||
canvasCore?.panTo(0, 0);
|
||||
canvasCore?.selectObject('main-image');
|
||||
}, 100);
|
||||
} else {
|
||||
// Only update if dimensions drastically mismatch initial load (prevent loops)
|
||||
// or if rotation changes from external source (like input)
|
||||
// But wait, if we bind, we should let Canvas update objects.
|
||||
// We should only push to objects if the user manually types in the inputs.
|
||||
// The issue is distinguishing input change vs drag change.
|
||||
// We can check document.activeElement?
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Update transformationState when canvasObjects change (drag/resize)
|
||||
$effect(() => {
|
||||
const obj = canvasObjects.find((o) => o.id === 'main-image');
|
||||
if (obj) {
|
||||
// Only update if not currently editing inputs?
|
||||
// Actually, the binding should handle it if we are consistent.
|
||||
transformationState.width = Math.round(obj.width);
|
||||
transformationState.height = Math.round(obj.height);
|
||||
if (obj.rotation !== undefined) transformationState.rotation = obj.rotation;
|
||||
}
|
||||
});
|
||||
|
||||
// When transformationState changes (e.g. user input), update canvas object
|
||||
// We need to avoid loops.
|
||||
$effect(() => {
|
||||
const obj = canvasObjects.find((o) => o.id === 'main-image');
|
||||
if (obj) {
|
||||
if (obj.width !== transformationState.width) obj.width = transformationState.width;
|
||||
if (obj.height !== transformationState.height) obj.height = transformationState.height;
|
||||
if (obj.rotation !== transformationState.rotation)
|
||||
obj.rotation = transformationState.rotation;
|
||||
if (obj.maintainAspectRatio !== transformationState.aspectRatioLocked)
|
||||
obj.maintainAspectRatio = transformationState.aspectRatioLocked;
|
||||
}
|
||||
});
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
@@ -104,9 +185,17 @@
|
||||
.toString() + '&mode=admin';
|
||||
}
|
||||
|
||||
function getPreviewUrl(): string {
|
||||
function getPreviewUrl(forCanvas = false): string {
|
||||
if (!selectedFile) return '';
|
||||
const params = generateTransformationParams(transformationState);
|
||||
|
||||
// For canvas preview, we want to omit width/height so the browser handles scaling
|
||||
// This prevents "zooming"/flashing artifacts during resize drag
|
||||
if (forCanvas) {
|
||||
delete params.width;
|
||||
delete params.height;
|
||||
}
|
||||
|
||||
const previewParams: any = {
|
||||
bucketId: selectedFile.bucketId,
|
||||
fileId: selectedFile.$id,
|
||||
@@ -120,7 +209,8 @@
|
||||
);
|
||||
}
|
||||
|
||||
let previewUrl = $derived(getPreviewUrl());
|
||||
let previewUrl = $derived(getPreviewUrl(true)); // For canvas
|
||||
let downloadUrl = $derived(getPreviewUrl(false)); // For final output/code
|
||||
|
||||
// Watch for file selection changes and apply presets
|
||||
let lastSelectedFileId = $state<string | null>(null);
|
||||
@@ -141,8 +231,15 @@
|
||||
});
|
||||
|
||||
function handleFocalPointClick(event: MouseEvent) {
|
||||
if (!canvasContainer || !selectedFile || isResizing) return;
|
||||
const rect = canvasContainer.getBoundingClientRect();
|
||||
if (!selectedFile) return;
|
||||
|
||||
const target = event.target as HTMLElement;
|
||||
if (target.closest('.resize-handle')) return;
|
||||
|
||||
const objectEl = target.closest('.canvas-object');
|
||||
if (!objectEl) return;
|
||||
|
||||
const rect = objectEl.getBoundingClientRect();
|
||||
const x = event.clientX - rect.left;
|
||||
const y = event.clientY - rect.top;
|
||||
const width = rect.width;
|
||||
@@ -167,55 +264,6 @@
|
||||
setTimeout(() => (focalPointOverlay = null), 1000);
|
||||
}
|
||||
|
||||
function handleResizeStart(event: MouseEvent, handle: string) {
|
||||
event.stopPropagation();
|
||||
isResizing = true;
|
||||
resizeHandle = handle;
|
||||
startX = event.clientX;
|
||||
startY = event.clientY;
|
||||
startWidth = transformationState.width || 0;
|
||||
startHeight = transformationState.height || 0;
|
||||
}
|
||||
|
||||
function handleMouseMove(event: MouseEvent) {
|
||||
if (!isResizing || !resizeHandle) return;
|
||||
const dx = event.clientX - startX;
|
||||
const dy = event.clientY - startY;
|
||||
const scale = zoom / 100;
|
||||
|
||||
let newWidth = startWidth;
|
||||
let newHeight = startHeight;
|
||||
|
||||
if (resizeHandle.includes('e')) {
|
||||
newWidth = Math.max(1, startWidth + dx / scale);
|
||||
}
|
||||
if (resizeHandle.includes('w')) {
|
||||
newWidth = Math.max(1, startWidth - dx / scale);
|
||||
}
|
||||
if (resizeHandle.includes('s')) {
|
||||
newHeight = Math.max(1, startHeight + dy / scale);
|
||||
}
|
||||
if (resizeHandle.includes('n')) {
|
||||
newHeight = Math.max(1, startHeight - dy / scale);
|
||||
}
|
||||
|
||||
if (transformationState.aspectRatioLocked && transformationState.originalAspectRatio) {
|
||||
if (resizeHandle.includes('e') || resizeHandle.includes('w')) {
|
||||
newHeight = Math.round(newWidth / transformationState.originalAspectRatio);
|
||||
} else {
|
||||
newWidth = Math.round(newHeight * transformationState.originalAspectRatio);
|
||||
}
|
||||
}
|
||||
|
||||
transformationState.width = newWidth;
|
||||
transformationState.height = newHeight;
|
||||
}
|
||||
|
||||
function handleMouseUp() {
|
||||
isResizing = false;
|
||||
resizeHandle = null;
|
||||
}
|
||||
|
||||
function handlePresetSelected(presetId: string | null) {
|
||||
selectedPresetId = presetId;
|
||||
if (presetId && selectedFile) {
|
||||
@@ -302,13 +350,13 @@
|
||||
<Layout.Stack gap="l">
|
||||
<!-- URL Input -->
|
||||
<div class="url-section">
|
||||
<CopyInput label="" value={previewUrl} />
|
||||
<CopyInput label="" value={downloadUrl} />
|
||||
</div>
|
||||
|
||||
<!-- Main Editor Layout -->
|
||||
<div class="editor-layout">
|
||||
<!-- Left Panel - Canvas -->
|
||||
<div class="preview-section">
|
||||
<div class="preview-section" style="--zoom: {currentZoom}">
|
||||
<!-- Focal Point Label -->
|
||||
<div class="focal-point-section">
|
||||
<Typography.Text class="focal-point-label">
|
||||
@@ -317,72 +365,61 @@
|
||||
</div>
|
||||
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="preview-container"
|
||||
bind:this={canvasContainer}
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
onclick={handleFocalPointClick}
|
||||
onkeydown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
handleFocalPointClick(e as any);
|
||||
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
|
||||
<Canvas.Root
|
||||
bind:this={canvasCore}
|
||||
settings={{
|
||||
canPan: true,
|
||||
canZoom: true,
|
||||
canSelect: true,
|
||||
grid: {
|
||||
dotColor: 'var(--color-neutral-20)',
|
||||
dotSize: 1,
|
||||
dotOpacity: 100
|
||||
}
|
||||
}}
|
||||
onmousemove={handleMouseMove}
|
||||
onmouseup={handleMouseUp}
|
||||
onmouseleave={handleMouseUp}
|
||||
style="cursor: crosshair;">
|
||||
<div class="preview-wrapper" style="transform: scale({zoom / 100});">
|
||||
<img
|
||||
bind:this={imageElement}
|
||||
src={previewUrl}
|
||||
alt={selectedFile.name}
|
||||
class="preview-image"
|
||||
draggable="false" />
|
||||
<!-- Grid overlay (rule of thirds) -->
|
||||
<div class="grid-overlay">
|
||||
<div class="grid-line grid-line-v" style="left: 33.33%;"></div>
|
||||
<div class="grid-line grid-line-v" style="left: 66.66%;"></div>
|
||||
<div class="grid-line grid-line-h" style="top: 33.33%;"></div>
|
||||
<div class="grid-line grid-line-h" style="top: 66.66%;"></div>
|
||||
</div>
|
||||
<!-- Focal point overlay -->
|
||||
{#if focalPointOverlay || transformationState.gravity}
|
||||
{@const pos = getFocalPointPosition()}
|
||||
<div
|
||||
class="focal-overlay"
|
||||
style="top: {pos.top}; left: {pos.left}; width: {pos.width}; height: {pos.height};">
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Resize handles -->
|
||||
<div class="resize-handles">
|
||||
<button
|
||||
type="button"
|
||||
class="handle handle-nw"
|
||||
onmousedown={(e) => handleResizeStart(e, 'nw')}
|
||||
aria-label="Resize from top-left"
|
||||
style="cursor: nwse-resize;"></button>
|
||||
<button
|
||||
type="button"
|
||||
class="handle handle-ne"
|
||||
onmousedown={(e) => handleResizeStart(e, 'ne')}
|
||||
aria-label="Resize from top-right"
|
||||
style="cursor: nesw-resize;"></button>
|
||||
<button
|
||||
type="button"
|
||||
class="handle handle-sw"
|
||||
onmousedown={(e) => handleResizeStart(e, 'sw')}
|
||||
aria-label="Resize from bottom-left"
|
||||
style="cursor: nesw-resize;"></button>
|
||||
<button
|
||||
type="button"
|
||||
class="handle handle-se"
|
||||
onmousedown={(e) => handleResizeStart(e, 'se')}
|
||||
aria-label="Resize from bottom-right"
|
||||
style="cursor: nwse-resize;"></button>
|
||||
</div>
|
||||
showGrid={true}
|
||||
width="100%"
|
||||
height="100%"
|
||||
bind:objects={canvasObjects}>
|
||||
<!-- Main Objects Loop -->
|
||||
<div style="display: contents" onclick={handleFocalPointClick}>
|
||||
{#each canvasObjects as obj (obj.id)}
|
||||
{#if obj.type === 'image'}
|
||||
<Canvas.Image object={obj} />
|
||||
{:else if obj.type === 'shape'}
|
||||
<Canvas.Shape object={obj} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- Focal Point Overlay (Manually positioned to match main-image) -->
|
||||
{#if focalPointOverlay || transformationState.gravity}
|
||||
{#each canvasObjects as obj (obj.id)}
|
||||
{#if obj.type === 'image' && obj.id === 'main-image'}
|
||||
{@const pos = getFocalPointPosition()}
|
||||
<div
|
||||
class="focal-point-layer"
|
||||
style="
|
||||
position: absolute;
|
||||
transform: translate3d({obj.x}px, {obj.y}px, 0);
|
||||
width: {obj.width}px;
|
||||
height: {obj.height}px;
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
">
|
||||
<div
|
||||
class="focal-overlay"
|
||||
style="top: {pos.top}; left: {pos.left}; width: {pos.width}; height: {pos.height};">
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</Canvas.Root>
|
||||
|
||||
<div class="overlay-controls">
|
||||
<Typography.Text variant="m-400" class="dimensions-text">
|
||||
{transformationState.width || 0} × {transformationState.height || 0}
|
||||
</Typography.Text>
|
||||
@@ -478,17 +515,6 @@
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
padding: 2rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.preview-wrapper {
|
||||
position: relative;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
@@ -498,37 +524,10 @@
|
||||
.preview-image {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
max-height: 70vh;
|
||||
border-radius: var(--border-radius-small);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.grid-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.grid-line {
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.grid-line-v {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.grid-line-h {
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.focal-overlay {
|
||||
position: absolute;
|
||||
background: rgba(59, 130, 246, 0.3);
|
||||
@@ -537,44 +536,39 @@
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.resize-handles {
|
||||
.overlay-controls {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
bottom: 1rem;
|
||||
left: 1rem;
|
||||
right: 1rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.handle {
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: var(--color-primary-100);
|
||||
border: 2px solid white;
|
||||
border-radius: 50%;
|
||||
pointer-events: all;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.handle-nw {
|
||||
top: -6px;
|
||||
left: -6px;
|
||||
/* Fallback styles for resize handles to ensure visibility */
|
||||
/* Fallback styles for resize handles to ensure visibility */
|
||||
:global(.resize-handle) {
|
||||
width: 10px !important;
|
||||
height: 10px !important;
|
||||
background-color: var(--color-primary-100, #00d9ff) !important;
|
||||
border: 1px solid blue !important;
|
||||
z-index: 20 !important;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) !important;
|
||||
/* Inverse scale to keep constant size */
|
||||
transform: scale(calc(1 / var(--zoom, 1))) !important;
|
||||
transform-origin: center !important;
|
||||
}
|
||||
|
||||
.handle-ne {
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
:global(.resize-handle:hover) {
|
||||
/* Scale up slightly from the base inverse scale */
|
||||
transform: scale(calc(1.2 / var(--zoom, 1))) !important;
|
||||
}
|
||||
|
||||
.handle-sw {
|
||||
bottom: -6px;
|
||||
left: -6px;
|
||||
}
|
||||
|
||||
.handle-se {
|
||||
bottom: -6px;
|
||||
right: -6px;
|
||||
.overlay-controls > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* svelte-ignore css_unused_selector */
|
||||
@@ -611,11 +605,6 @@
|
||||
color: var(--color-neutral-70);
|
||||
}
|
||||
|
||||
.handle {
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.controls-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
Reference in New Issue
Block a user