@nextcloud/image-editor 1.0.0-beta.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.
- package/LICENSE +661 -0
- package/README.md +156 -0
- package/dist/assets/index.css +509 -0
- package/dist/index.cjs +4690 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +233 -0
- package/dist/index.mjs +4683 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +105 -0
package/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
- SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
|
3
|
+
- SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
-->
|
|
5
|
+
# @nextcloud/image-editor
|
|
6
|
+
|
|
7
|
+
Vue 3 image editor component for Nextcloud apps. Replacement for the
|
|
8
|
+
unmaintained Filerobot editor.
|
|
9
|
+
|
|
10
|
+
- 📖 [API documentation](https://nextcloud-libraries.github.io/nextcloud-image-editor/)
|
|
11
|
+
- 🎨 [Live demo](https://nextcloud-libraries.github.io/nextcloud-image-editor/demo/)
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Crop with rule-of-thirds guides, aspect presets (free, original,
|
|
16
|
+
1:1, 4:3, 16:9), 90° rotation, flips, fine rotation (±45°) and
|
|
17
|
+
scale; the frame stays stable thanks to cover scaling
|
|
18
|
+
- Brightness, contrast and saturation adjustments; sixteen filter
|
|
19
|
+
presets with live preview chips, from photographic grades (pop,
|
|
20
|
+
golden, coast, cinema, berry, mist, warm, cool, fade) over
|
|
21
|
+
monochromes (grayscale, noir, luna, sepia) to effects (invert,
|
|
22
|
+
solarize, posterize)
|
|
23
|
+
- Annotations: freehand drawing, rectangles, ellipses, arrows, text and
|
|
24
|
+
emoji stickers (the user's frequently used Nextcloud emojis plus the
|
|
25
|
+
full picker): movable, resizable, rotatable, recolorable,
|
|
26
|
+
duplicatable and deletable
|
|
27
|
+
- Redaction that destroys pixels (block averaging or strong blur),
|
|
28
|
+
never just an overlay
|
|
29
|
+
- Full undo/redo with Ctrl+Z/Y, a named history list to jump back to any
|
|
30
|
+
step, revert-all behind a confirmation, arrow-key nudging,
|
|
31
|
+
cursor-anchored wheel zoom, drag panning, pinch zoom on touch and a
|
|
32
|
+
click-to-reset zoom readout
|
|
33
|
+
- Ambient glass UI tinted by the image itself, responsive down to
|
|
34
|
+
phone-sized containers (container queries, not viewport media queries)
|
|
35
|
+
- Keyboard accessible, pointer-event based canvas (mouse and touch),
|
|
36
|
+
`prefers-reduced-motion` respected
|
|
37
|
+
- Exports a `Blob` at natural resolution in PNG, JPEG or WebP, optionally
|
|
38
|
+
bounded by `maxSize`; an unedited image is handed back untouched
|
|
39
|
+
|
|
40
|
+
## Design principles
|
|
41
|
+
|
|
42
|
+
- **Maintainability first.** Konva is the only canvas dependency, pinned to a
|
|
43
|
+
minor version and only bumped after a changelog review. No wrapped
|
|
44
|
+
third-party editor, no framework interop layers.
|
|
45
|
+
- **One declarative state.** Every edit lives in a single `EditorState`;
|
|
46
|
+
the Konva scene is a pure render of it, and the export runs through the
|
|
47
|
+
same code path as the interactive view: what you save is what you saw.
|
|
48
|
+
- **The library never persists.** It accepts an image (`Blob`, `File` or URL)
|
|
49
|
+
and emits an edited `Blob`. WebDAV, versioning and file naming belong to the
|
|
50
|
+
consuming app.
|
|
51
|
+
- **Every behavior is covered by tests**: unit tests for math and state,
|
|
52
|
+
Playwright tests in a real browser for everything touching canvas.
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
Install the peer dependencies alongside it:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
npm install @nextcloud/image-editor @nextcloud/vue @nextcloud/dialogs
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The component's styles come with it: the built module imports its own
|
|
63
|
+
stylesheet, so any bundler that handles CSS imports from dependencies
|
|
64
|
+
picks them up. Where yours does not, load them yourself from the
|
|
65
|
+
`@nextcloud/image-editor/style` export.
|
|
66
|
+
|
|
67
|
+
```vue
|
|
68
|
+
<script setup lang="ts">
|
|
69
|
+
import type { ExportResult } from '@nextcloud/image-editor'
|
|
70
|
+
import { ImageEditor } from '@nextcloud/image-editor'
|
|
71
|
+
|
|
72
|
+
function onSave({ blob, mimeType }: ExportResult) {
|
|
73
|
+
// persist the blob, e.g. via @nextcloud/upload or WebDAV PUT
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<template>
|
|
78
|
+
<ImageEditor :src="file" @save="onSave" @cancel="close" @error="showError" />
|
|
79
|
+
</template>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## API
|
|
83
|
+
|
|
84
|
+
### `<ImageEditor>`
|
|
85
|
+
|
|
86
|
+
| Prop | Type | Description |
|
|
87
|
+
|------|------|-------------|
|
|
88
|
+
| `src` | `Blob \| string` | Image to edit (Blob, File or URL). Required. |
|
|
89
|
+
| `label` | `string` | Accessible label of the canvas area. |
|
|
90
|
+
| `exportOptions` | `ExportOptions` | `format`, `quality` and `maxSize` for the save button. Defaults to PNG at natural resolution. |
|
|
91
|
+
| `initialState` | `EditorState` | State to open with, as emitted by `change`, for resuming an unfinished edit. Read when the source loads. |
|
|
92
|
+
|
|
93
|
+
| Event | Payload | Description |
|
|
94
|
+
|-------|---------|-------------|
|
|
95
|
+
| `save` | `ExportResult` | Edited image rendered at natural resolution. |
|
|
96
|
+
| `cancel` | – | User dismissed the editor. |
|
|
97
|
+
| `error` | `Error` | Loading or export failed. |
|
|
98
|
+
| `change` | `EditorState` | Fired when an edit is committed, e.g. for dirty tracking. A slider being dragged previews without emitting; releasing it emits once. |
|
|
99
|
+
|
|
100
|
+
Exposed methods:
|
|
101
|
+
|
|
102
|
+
- `exportImage(options?: ExportOptions): Promise<ExportResult>` with
|
|
103
|
+
`format`, `quality` and `maxSize` (longest edge bound) options.
|
|
104
|
+
- `reset(state?: EditorState)` to start over, optionally from a given
|
|
105
|
+
state.
|
|
106
|
+
|
|
107
|
+
Pair `change` with `initialState` to resume an edit across a reload: store
|
|
108
|
+
what `change` reports, hand it back as `initialState`, and the editor opens
|
|
109
|
+
where the user left off.
|
|
110
|
+
|
|
111
|
+
Saving an image that was not edited hands back the source bytes
|
|
112
|
+
untouched, rather than re-encoding them. That keeps the file's quality
|
|
113
|
+
and its metadata, and it only applies when `src` was given as a `Blob`
|
|
114
|
+
or `File`, nothing was asked of the encoder, and the state is pristine.
|
|
115
|
+
`isPristine(state)` is exported for the same check, e.g. to disable a
|
|
116
|
+
save button.
|
|
117
|
+
|
|
118
|
+
### `useHistory<T>(capacity?)`
|
|
119
|
+
|
|
120
|
+
Linear undo/redo history of immutable snapshots, used by the editor and
|
|
121
|
+
exported for standalone use. Each step carries an optional label, and
|
|
122
|
+
`entries`, `index` and `jumpTo()` let a consumer render a history list
|
|
123
|
+
and move to any step in it. Snapshots are held by reference: whatever
|
|
124
|
+
is pushed must not be mutated afterwards.
|
|
125
|
+
|
|
126
|
+
## Development
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
npm ci
|
|
130
|
+
npm run test # unit tests (vitest)
|
|
131
|
+
npm run test:e2e # Playwright tests (real browser, canvas)
|
|
132
|
+
npm run playground # dev playground at http://localhost:5173
|
|
133
|
+
npm run lint
|
|
134
|
+
npm run typecheck # vue-tsc over lib/ and __tests__/
|
|
135
|
+
npm run build
|
|
136
|
+
npm run build:doc # typedoc API documentation
|
|
137
|
+
npm run build:demo # static demo page build
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Testing policy
|
|
141
|
+
|
|
142
|
+
Regressions are the primary risk for a long-lived canvas library:
|
|
143
|
+
|
|
144
|
+
- Coordinate math (fit, crop, rotation/flip remapping) is implemented as
|
|
145
|
+
pure functions and unit-tested exhaustively.
|
|
146
|
+
- Editor state (history, tool state, annotations) is unit-tested without
|
|
147
|
+
a canvas.
|
|
148
|
+
- Rendering and interaction run as Playwright tests in a real browser
|
|
149
|
+
(chromium and firefox) against the playground app, asserting exported
|
|
150
|
+
pixels, not just DOM state; jsdom has no real canvas and is never used
|
|
151
|
+
to test Konva code.
|
|
152
|
+
- New tools ship with their tests in the same pull request, no exceptions.
|
|
153
|
+
- Current spread: 90+ unit tests over the pure state, geometry,
|
|
154
|
+
filter and interaction math; over 100 browser scenarios (twice,
|
|
155
|
+
chromium and firefox) asserting exported pixels and end-to-end
|
|
156
|
+
behavior.
|
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
.glass-surface[data-v-b60e81d9] {
|
|
2
|
+
background: var(--editor-glass, rgba(20, 20, 26, 0.6));
|
|
3
|
+
backdrop-filter: blur(24px) saturate(1.4);
|
|
4
|
+
border: 1px solid rgba(255, 255, 255, 0.09);
|
|
5
|
+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
|
|
6
|
+
}
|
|
7
|
+
.glass-surface--card[data-v-b60e81d9] {
|
|
8
|
+
border-radius: 20px;
|
|
9
|
+
}
|
|
10
|
+
.glass-surface--strip[data-v-b60e81d9] {
|
|
11
|
+
border-radius: 16px;
|
|
12
|
+
}
|
|
13
|
+
.glass-surface--pill[data-v-b60e81d9] {
|
|
14
|
+
border-radius: var(--border-radius-pill, 100px);
|
|
15
|
+
}.editor-slider[data-v-6562ea60] {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: calc(var(--default-grid-baseline) * 3);
|
|
19
|
+
width: 100%;
|
|
20
|
+
}
|
|
21
|
+
.editor-slider input[type=range][data-v-6562ea60] {
|
|
22
|
+
appearance: none;
|
|
23
|
+
flex: 1;
|
|
24
|
+
height: 20px;
|
|
25
|
+
margin: 0;
|
|
26
|
+
background: linear-gradient(rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.25)) center/100% 2px no-repeat;
|
|
27
|
+
cursor: ew-resize;
|
|
28
|
+
}
|
|
29
|
+
.editor-slider input[type=range][data-v-6562ea60]::-webkit-slider-thumb {
|
|
30
|
+
appearance: none;
|
|
31
|
+
width: 14px;
|
|
32
|
+
height: 14px;
|
|
33
|
+
border-radius: 50%;
|
|
34
|
+
background: #fff;
|
|
35
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
|
36
|
+
}
|
|
37
|
+
.editor-slider input[type=range][data-v-6562ea60]::-moz-range-thumb {
|
|
38
|
+
width: 14px;
|
|
39
|
+
height: 14px;
|
|
40
|
+
border: none;
|
|
41
|
+
border-radius: 50%;
|
|
42
|
+
background: #fff;
|
|
43
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
|
44
|
+
}
|
|
45
|
+
.editor-slider input[type=range][data-v-6562ea60]:focus-visible {
|
|
46
|
+
outline: 2px solid var(--color-primary-element);
|
|
47
|
+
outline-offset: 2px;
|
|
48
|
+
}
|
|
49
|
+
.editor-slider output[data-v-6562ea60] {
|
|
50
|
+
display: flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
justify-content: flex-end;
|
|
53
|
+
min-width: 44px;
|
|
54
|
+
min-height: 44px;
|
|
55
|
+
text-align: end;
|
|
56
|
+
font-size: 13px;
|
|
57
|
+
font-variant-numeric: tabular-nums;
|
|
58
|
+
}.icon-tab[data-v-95889662] {
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
align-items: center;
|
|
62
|
+
gap: 4px;
|
|
63
|
+
min-width: max(64px, var(--default-clickable-area, 44px));
|
|
64
|
+
min-height: var(--default-clickable-area, 44px);
|
|
65
|
+
padding: calc(var(--default-grid-baseline) * 2) var(--default-grid-baseline);
|
|
66
|
+
border: none;
|
|
67
|
+
border-radius: var(--border-radius-large, 12px);
|
|
68
|
+
background: transparent;
|
|
69
|
+
color: rgba(242, 242, 247, 0.75);
|
|
70
|
+
font-size: 11px;
|
|
71
|
+
letter-spacing: 0.01em;
|
|
72
|
+
cursor: pointer;
|
|
73
|
+
transition: background-color 0.12s ease, color 0.12s ease, transform 0.12s ease;
|
|
74
|
+
}
|
|
75
|
+
.icon-tab[data-v-95889662]:active:not(:disabled) {
|
|
76
|
+
transform: scale(0.96);
|
|
77
|
+
}
|
|
78
|
+
.icon-tab[data-v-95889662]:hover:not(:disabled) {
|
|
79
|
+
background-color: rgba(255, 255, 255, 0.07);
|
|
80
|
+
color: var(--color-main-text);
|
|
81
|
+
}
|
|
82
|
+
.icon-tab[data-v-95889662]:focus-visible {
|
|
83
|
+
outline: 2px solid var(--color-primary-element);
|
|
84
|
+
outline-offset: 2px;
|
|
85
|
+
}
|
|
86
|
+
.icon-tab--active[data-v-95889662] {
|
|
87
|
+
background: rgba(var(--editor-ambient, 88, 86, 112), 0.3);
|
|
88
|
+
color: var(--color-main-text);
|
|
89
|
+
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12), 0 4px 16px rgba(0, 0, 0, 0.3);
|
|
90
|
+
backdrop-filter: blur(12px);
|
|
91
|
+
}
|
|
92
|
+
.icon-tab[data-v-95889662]:disabled {
|
|
93
|
+
opacity: 0.5;
|
|
94
|
+
cursor: default;
|
|
95
|
+
}
|
|
96
|
+
@container editor (max-width: 600px) {
|
|
97
|
+
.icon-tab[data-v-95889662] {
|
|
98
|
+
min-width: var(--default-clickable-area, 44px);
|
|
99
|
+
padding: var(--default-grid-baseline) 2px;
|
|
100
|
+
font-size: 10px;
|
|
101
|
+
}
|
|
102
|
+
}.adjust-panel[data-v-d7f848cf] {
|
|
103
|
+
display: flex;
|
|
104
|
+
flex-direction: column;
|
|
105
|
+
align-items: center;
|
|
106
|
+
gap: calc(var(--default-grid-baseline) * 2);
|
|
107
|
+
width: 100%;
|
|
108
|
+
}
|
|
109
|
+
.adjust-panel__tabs[data-v-d7f848cf] {
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
justify-content: center;
|
|
113
|
+
gap: calc(var(--default-grid-baseline) * 2);
|
|
114
|
+
}.annotate-panel[data-v-1bf135e4] {
|
|
115
|
+
display: flex;
|
|
116
|
+
flex-direction: column;
|
|
117
|
+
align-items: center;
|
|
118
|
+
gap: calc(var(--default-grid-baseline) * 2);
|
|
119
|
+
width: 100%;
|
|
120
|
+
}
|
|
121
|
+
.annotate-panel__row[data-v-1bf135e4] {
|
|
122
|
+
display: flex;
|
|
123
|
+
align-items: center;
|
|
124
|
+
justify-content: center;
|
|
125
|
+
flex-wrap: wrap;
|
|
126
|
+
gap: calc(var(--default-grid-baseline) * 2);
|
|
127
|
+
}
|
|
128
|
+
.annotate-panel__divider[data-v-1bf135e4] {
|
|
129
|
+
width: 1px;
|
|
130
|
+
height: 24px;
|
|
131
|
+
background-color: rgba(255, 255, 255, 0.12);
|
|
132
|
+
}
|
|
133
|
+
.annotate-panel__option[data-v-1bf135e4] {
|
|
134
|
+
display: flex;
|
|
135
|
+
align-items: center;
|
|
136
|
+
gap: var(--default-grid-baseline);
|
|
137
|
+
font-size: 12px;
|
|
138
|
+
opacity: 0.9;
|
|
139
|
+
}
|
|
140
|
+
.annotate-panel__dot[data-v-1bf135e4] {
|
|
141
|
+
display: block;
|
|
142
|
+
border-radius: 50%;
|
|
143
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.5);
|
|
144
|
+
}
|
|
145
|
+
.annotate-panel__glyph[data-v-1bf135e4] {
|
|
146
|
+
font-family: Helvetica, Arial, sans-serif;
|
|
147
|
+
line-height: 1;
|
|
148
|
+
text-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
|
|
149
|
+
}.crop-panel[data-v-30fdb9f8] {
|
|
150
|
+
display: flex;
|
|
151
|
+
flex-direction: column;
|
|
152
|
+
align-items: center;
|
|
153
|
+
gap: calc(var(--default-grid-baseline) * 2);
|
|
154
|
+
width: 100%;
|
|
155
|
+
}
|
|
156
|
+
.crop-panel__row[data-v-30fdb9f8] {
|
|
157
|
+
display: flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
justify-content: center;
|
|
160
|
+
flex-wrap: wrap;
|
|
161
|
+
gap: calc(var(--default-grid-baseline) * 2);
|
|
162
|
+
}
|
|
163
|
+
.crop-panel__divider[data-v-30fdb9f8] {
|
|
164
|
+
width: 1px;
|
|
165
|
+
height: 24px;
|
|
166
|
+
background-color: rgba(255, 255, 255, 0.12);
|
|
167
|
+
}.preset-chip[data-v-b38cdab1] {
|
|
168
|
+
display: flex;
|
|
169
|
+
flex-direction: column;
|
|
170
|
+
align-items: center;
|
|
171
|
+
gap: 2px;
|
|
172
|
+
padding: 0;
|
|
173
|
+
border: none;
|
|
174
|
+
background: transparent;
|
|
175
|
+
color: var(--color-main-text);
|
|
176
|
+
font-size: 10px;
|
|
177
|
+
cursor: pointer;
|
|
178
|
+
}
|
|
179
|
+
.preset-chip img[data-v-b38cdab1] {
|
|
180
|
+
width: 64px;
|
|
181
|
+
aspect-ratio: 4/3;
|
|
182
|
+
object-fit: cover;
|
|
183
|
+
border-radius: 10px;
|
|
184
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
185
|
+
transition: box-shadow 0.12s ease, transform 0.12s ease;
|
|
186
|
+
}
|
|
187
|
+
.preset-chip:hover img[data-v-b38cdab1] {
|
|
188
|
+
transform: scale(1.03);
|
|
189
|
+
}
|
|
190
|
+
.preset-chip--active img[data-v-b38cdab1], .preset-chip:focus-visible img[data-v-b38cdab1] {
|
|
191
|
+
box-shadow: 0 0 0 2px var(--color-primary-element);
|
|
192
|
+
}
|
|
193
|
+
.preset-chip[data-v-b38cdab1]:focus-visible {
|
|
194
|
+
outline: none;
|
|
195
|
+
}
|
|
196
|
+
.preset-chip[data-v-b38cdab1]:disabled {
|
|
197
|
+
opacity: 0.5;
|
|
198
|
+
cursor: default;
|
|
199
|
+
}.filter-strip[data-v-44007efa] {
|
|
200
|
+
display: flex;
|
|
201
|
+
flex-direction: column;
|
|
202
|
+
gap: calc(var(--default-grid-baseline) * 2);
|
|
203
|
+
padding: calc(var(--default-grid-baseline) * 2);
|
|
204
|
+
overflow-y: auto;
|
|
205
|
+
max-height: 100%;
|
|
206
|
+
scrollbar-width: thin;
|
|
207
|
+
scrollbar-color: rgba(255, 255, 255, 0.25) transparent;
|
|
208
|
+
}
|
|
209
|
+
.filter-strip[data-v-44007efa]::-webkit-scrollbar {
|
|
210
|
+
width: 6px;
|
|
211
|
+
height: 6px;
|
|
212
|
+
}
|
|
213
|
+
.filter-strip[data-v-44007efa]::-webkit-scrollbar-thumb {
|
|
214
|
+
background: rgba(255, 255, 255, 0.22);
|
|
215
|
+
border-radius: 3px;
|
|
216
|
+
}
|
|
217
|
+
.filter-strip[data-v-44007efa]::-webkit-scrollbar-track {
|
|
218
|
+
background: transparent;
|
|
219
|
+
}.redact-panel[data-v-ac701267] {
|
|
220
|
+
display: flex;
|
|
221
|
+
align-items: center;
|
|
222
|
+
justify-content: center;
|
|
223
|
+
gap: calc(var(--default-grid-baseline) * 2);
|
|
224
|
+
}.select-panel[data-v-2ca9996e] {
|
|
225
|
+
display: flex;
|
|
226
|
+
align-items: center;
|
|
227
|
+
justify-content: center;
|
|
228
|
+
}
|
|
229
|
+
.select-panel__option[data-v-2ca9996e] {
|
|
230
|
+
display: flex;
|
|
231
|
+
align-items: center;
|
|
232
|
+
gap: var(--default-grid-baseline);
|
|
233
|
+
font-size: 12px;
|
|
234
|
+
opacity: 0.9;
|
|
235
|
+
}
|
|
236
|
+
.select-panel__hint[data-v-2ca9996e] {
|
|
237
|
+
font-size: 12px;
|
|
238
|
+
opacity: 0.65;
|
|
239
|
+
}.sticker-panel[data-v-6307599b] {
|
|
240
|
+
display: flex;
|
|
241
|
+
align-items: center;
|
|
242
|
+
justify-content: center;
|
|
243
|
+
flex-wrap: wrap;
|
|
244
|
+
gap: calc(var(--default-grid-baseline) * 2);
|
|
245
|
+
}
|
|
246
|
+
.sticker-panel[data-v-6307599b] .sticker-panel__emoji {
|
|
247
|
+
min-height: var(--default-clickable-area, 44px);
|
|
248
|
+
font-size: 22px;
|
|
249
|
+
line-height: 1;
|
|
250
|
+
}.editor-card[data-v-3fb85d8f] {
|
|
251
|
+
display: flex;
|
|
252
|
+
flex-direction: column;
|
|
253
|
+
align-items: center;
|
|
254
|
+
gap: calc(var(--default-grid-baseline) * 2);
|
|
255
|
+
min-width: min(420px, 92cqw);
|
|
256
|
+
max-width: min(680px, 94cqw);
|
|
257
|
+
padding: calc(var(--default-grid-baseline) * 3) calc(var(--default-grid-baseline) * 5);
|
|
258
|
+
}
|
|
259
|
+
@container editor (max-width: 600px) {
|
|
260
|
+
.editor-card[data-v-3fb85d8f] {
|
|
261
|
+
min-width: 0;
|
|
262
|
+
width: 100%;
|
|
263
|
+
max-width: none;
|
|
264
|
+
padding-inline: calc(var(--default-grid-baseline) * 3);
|
|
265
|
+
}
|
|
266
|
+
}.editor-sidebar[data-v-a4223674] {
|
|
267
|
+
display: flex;
|
|
268
|
+
flex-direction: column;
|
|
269
|
+
align-items: center;
|
|
270
|
+
gap: calc(var(--default-grid-baseline) * 2);
|
|
271
|
+
padding: var(--default-grid-baseline);
|
|
272
|
+
}.editor-topbar[data-v-9ba9bd23] {
|
|
273
|
+
display: flex;
|
|
274
|
+
align-items: center;
|
|
275
|
+
justify-content: space-between;
|
|
276
|
+
padding: calc(var(--default-grid-baseline) * 2) calc(var(--default-grid-baseline) * 6);
|
|
277
|
+
}
|
|
278
|
+
.editor-topbar__history[data-v-9ba9bd23] {
|
|
279
|
+
display: flex;
|
|
280
|
+
align-items: center;
|
|
281
|
+
gap: 2px;
|
|
282
|
+
padding: 2px;
|
|
283
|
+
border-radius: var(--border-radius-pill, 100px);
|
|
284
|
+
background: var(--editor-glass, rgba(22, 22, 26, 0.6));
|
|
285
|
+
backdrop-filter: blur(24px) saturate(1.4);
|
|
286
|
+
border: 1px solid rgba(255, 255, 255, 0.09);
|
|
287
|
+
}
|
|
288
|
+
.editor-topbar__separator[data-v-9ba9bd23] {
|
|
289
|
+
width: 1px;
|
|
290
|
+
height: 20px;
|
|
291
|
+
background-color: var(--color-border);
|
|
292
|
+
}
|
|
293
|
+
.editor-topbar__zoom[data-v-9ba9bd23] {
|
|
294
|
+
min-width: 48px;
|
|
295
|
+
border: none;
|
|
296
|
+
background: transparent;
|
|
297
|
+
color: var(--color-main-text);
|
|
298
|
+
font-size: 12px;
|
|
299
|
+
font-variant-numeric: tabular-nums;
|
|
300
|
+
opacity: 0.8;
|
|
301
|
+
cursor: pointer;
|
|
302
|
+
padding: 0 4px;
|
|
303
|
+
}
|
|
304
|
+
.editor-topbar__zoom[data-v-9ba9bd23]:hover:not(:disabled) {
|
|
305
|
+
opacity: 1;
|
|
306
|
+
}
|
|
307
|
+
.editor-topbar__zoom[data-v-9ba9bd23]:focus-visible {
|
|
308
|
+
outline: 2px solid var(--color-primary-element);
|
|
309
|
+
outline-offset: 2px;
|
|
310
|
+
}
|
|
311
|
+
.editor-topbar__zoom[data-v-9ba9bd23]:disabled {
|
|
312
|
+
cursor: default;
|
|
313
|
+
opacity: 0.4;
|
|
314
|
+
}
|
|
315
|
+
.editor-topbar__spacer[data-v-9ba9bd23], .editor-topbar__actions[data-v-9ba9bd23] {
|
|
316
|
+
flex: 1;
|
|
317
|
+
display: flex;
|
|
318
|
+
align-items: center;
|
|
319
|
+
gap: var(--default-grid-baseline);
|
|
320
|
+
}
|
|
321
|
+
.editor-topbar__actions[data-v-9ba9bd23] {
|
|
322
|
+
justify-content: flex-end;
|
|
323
|
+
}
|
|
324
|
+
.editor-topbar__cancel-icon[data-v-9ba9bd23] {
|
|
325
|
+
display: none !important;
|
|
326
|
+
}
|
|
327
|
+
@container editor (max-width: 600px) {
|
|
328
|
+
.editor-topbar[data-v-9ba9bd23] {
|
|
329
|
+
padding-inline: calc(var(--default-grid-baseline) * 2);
|
|
330
|
+
}
|
|
331
|
+
.editor-topbar__cancel-text[data-v-9ba9bd23] {
|
|
332
|
+
display: none !important;
|
|
333
|
+
}
|
|
334
|
+
.editor-topbar__cancel-icon[data-v-9ba9bd23] {
|
|
335
|
+
display: inline-flex !important;
|
|
336
|
+
}
|
|
337
|
+
.editor-topbar__zoom[data-v-9ba9bd23] {
|
|
338
|
+
min-width: 40px;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
.selection-toolbar[data-v-09c1d16e] {
|
|
342
|
+
position: absolute;
|
|
343
|
+
display: flex;
|
|
344
|
+
gap: 2px;
|
|
345
|
+
padding: 2px;
|
|
346
|
+
transform: translateX(-50%);
|
|
347
|
+
z-index: 1;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.text-overlay[data-v-bbba034b] {
|
|
351
|
+
position: absolute;
|
|
352
|
+
/* Kept in sync with the Konva text nodes for WYSIWYG editing */
|
|
353
|
+
font-family: Helvetica, Arial, sans-serif;
|
|
354
|
+
line-height: 1;
|
|
355
|
+
padding: 0;
|
|
356
|
+
margin: 0;
|
|
357
|
+
border: none;
|
|
358
|
+
border-radius: 2px;
|
|
359
|
+
background: transparent;
|
|
360
|
+
outline: 1.5px solid var(--color-primary-element, #5b5cf0);
|
|
361
|
+
outline-offset: 4px;
|
|
362
|
+
box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15);
|
|
363
|
+
caret-color: var(--color-primary-element, #5b5cf0);
|
|
364
|
+
resize: none;
|
|
365
|
+
overflow: hidden;
|
|
366
|
+
white-space: pre;
|
|
367
|
+
z-index: 1;
|
|
368
|
+
}
|
|
369
|
+
.image-editor[data-v-e3346ccd] {
|
|
370
|
+
--color-main-text: #f2f2f7;
|
|
371
|
+
--color-main-background: #141416;
|
|
372
|
+
--color-background-hover: rgba(255, 255, 255, 0.08);
|
|
373
|
+
--color-background-dark: rgba(255, 255, 255, 0.14);
|
|
374
|
+
--color-border: rgba(255, 255, 255, 0.09);
|
|
375
|
+
--color-element-hover: rgba(255, 255, 255, 0.08);
|
|
376
|
+
--editor-glass: rgba(20, 20, 26, 0.6);
|
|
377
|
+
--default-clickable-area: 44px;
|
|
378
|
+
--default-grid-baseline: 4px;
|
|
379
|
+
font-size: 13px;
|
|
380
|
+
position: relative;
|
|
381
|
+
height: 100%;
|
|
382
|
+
width: 100%;
|
|
383
|
+
overflow: hidden;
|
|
384
|
+
color: var(--color-main-text);
|
|
385
|
+
background-color: var(--color-main-background);
|
|
386
|
+
container: editor/size;
|
|
387
|
+
}
|
|
388
|
+
.image-editor[data-v-e3346ccd], .image-editor[data-v-e3346ccd] *, .image-editor[data-v-e3346ccd] *::before, .image-editor[data-v-e3346ccd] *::after {
|
|
389
|
+
box-sizing: border-box;
|
|
390
|
+
}
|
|
391
|
+
.image-editor__shell[data-v-e3346ccd] {
|
|
392
|
+
position: relative;
|
|
393
|
+
height: 100%;
|
|
394
|
+
width: 100%;
|
|
395
|
+
}
|
|
396
|
+
.image-editor[data-v-e3346ccd]::before {
|
|
397
|
+
content: "";
|
|
398
|
+
position: absolute;
|
|
399
|
+
inset: -10%;
|
|
400
|
+
background-image: var(--editor-backdrop);
|
|
401
|
+
background-size: cover;
|
|
402
|
+
background-position: center;
|
|
403
|
+
filter: blur(64px) saturate(1.3) brightness(0.55);
|
|
404
|
+
transform: scale(1.15);
|
|
405
|
+
}
|
|
406
|
+
.image-editor__frame[data-v-e3346ccd] {
|
|
407
|
+
position: relative;
|
|
408
|
+
height: 100%;
|
|
409
|
+
width: 100%;
|
|
410
|
+
overflow: hidden;
|
|
411
|
+
background: rgba(14, 14, 18, 0.55);
|
|
412
|
+
backdrop-filter: blur(40px);
|
|
413
|
+
}
|
|
414
|
+
.image-editor__viewport[data-v-e3346ccd] {
|
|
415
|
+
position: absolute;
|
|
416
|
+
inset: 0;
|
|
417
|
+
padding: 56px 16px 16px;
|
|
418
|
+
}
|
|
419
|
+
@container editor (max-width: 600px) {
|
|
420
|
+
.image-editor__viewport[data-v-e3346ccd] {
|
|
421
|
+
padding: 56px 8px 8px;
|
|
422
|
+
}
|
|
423
|
+
.image-editor .image-editor__controls[data-v-e3346ccd] {
|
|
424
|
+
inset-inline: 8px;
|
|
425
|
+
transform: none;
|
|
426
|
+
min-width: 0;
|
|
427
|
+
max-width: none;
|
|
428
|
+
width: auto;
|
|
429
|
+
}
|
|
430
|
+
.image-editor .image-editor__strip[data-v-e3346ccd] {
|
|
431
|
+
flex-direction: row;
|
|
432
|
+
inset-inline: 8px;
|
|
433
|
+
inset-block-start: auto;
|
|
434
|
+
inset-block-end: calc(var(--default-grid-baseline) * 4);
|
|
435
|
+
transform: none;
|
|
436
|
+
max-height: none;
|
|
437
|
+
overflow-x: auto;
|
|
438
|
+
overflow-y: hidden;
|
|
439
|
+
}
|
|
440
|
+
.image-editor .image-editor__rail[data-v-e3346ccd] {
|
|
441
|
+
inset-inline-start: var(--default-grid-baseline);
|
|
442
|
+
inset-block-start: 64px;
|
|
443
|
+
transform: none;
|
|
444
|
+
max-height: calc(100% - 240px);
|
|
445
|
+
overflow-y: auto;
|
|
446
|
+
padding: var(--default-grid-baseline);
|
|
447
|
+
background: var(--editor-glass);
|
|
448
|
+
backdrop-filter: blur(24px) saturate(1.4);
|
|
449
|
+
border: 1px solid rgba(255, 255, 255, 0.09);
|
|
450
|
+
border-radius: var(--border-radius-large, 12px);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
.image-editor__canvas[data-v-e3346ccd] {
|
|
454
|
+
height: 100%;
|
|
455
|
+
width: 100%;
|
|
456
|
+
touch-action: none;
|
|
457
|
+
}
|
|
458
|
+
.image-editor__topbar[data-v-e3346ccd] {
|
|
459
|
+
position: absolute;
|
|
460
|
+
inset-block-start: 0;
|
|
461
|
+
inset-inline: 0;
|
|
462
|
+
background: linear-gradient(rgba(8, 8, 12, 0.5), transparent);
|
|
463
|
+
}
|
|
464
|
+
.image-editor__rail[data-v-e3346ccd] {
|
|
465
|
+
position: absolute;
|
|
466
|
+
inset-inline-start: calc(var(--default-grid-baseline) * 4);
|
|
467
|
+
inset-block-start: 50%;
|
|
468
|
+
transform: translateY(-50%);
|
|
469
|
+
}
|
|
470
|
+
.image-editor__controls[data-v-e3346ccd] {
|
|
471
|
+
position: absolute;
|
|
472
|
+
inset-block-end: calc(var(--default-grid-baseline) * 5);
|
|
473
|
+
inset-inline-start: 50%;
|
|
474
|
+
transform: translateX(-50%);
|
|
475
|
+
}
|
|
476
|
+
.image-editor__strip[data-v-e3346ccd] {
|
|
477
|
+
position: absolute;
|
|
478
|
+
inset-inline-end: calc(var(--default-grid-baseline) * 4);
|
|
479
|
+
inset-block-start: 50%;
|
|
480
|
+
transform: translateY(-50%);
|
|
481
|
+
max-height: calc(100% - 160px);
|
|
482
|
+
}
|
|
483
|
+
.image-editor .hidden-visually[data-v-e3346ccd] {
|
|
484
|
+
position: absolute;
|
|
485
|
+
width: 1px;
|
|
486
|
+
height: 1px;
|
|
487
|
+
overflow: hidden;
|
|
488
|
+
clip-path: inset(50%);
|
|
489
|
+
}
|
|
490
|
+
.image-editor__loading[data-v-e3346ccd] {
|
|
491
|
+
position: absolute;
|
|
492
|
+
inset: 0;
|
|
493
|
+
margin: auto;
|
|
494
|
+
}
|
|
495
|
+
.image-editor__error[data-v-e3346ccd] {
|
|
496
|
+
position: absolute;
|
|
497
|
+
inset: 0;
|
|
498
|
+
display: flex;
|
|
499
|
+
flex-direction: column;
|
|
500
|
+
align-items: center;
|
|
501
|
+
justify-content: center;
|
|
502
|
+
gap: calc(var(--default-grid-baseline) * 3);
|
|
503
|
+
text-align: center;
|
|
504
|
+
padding: calc(var(--default-grid-baseline) * 4);
|
|
505
|
+
}
|
|
506
|
+
.image-editor__error p[data-v-e3346ccd] {
|
|
507
|
+
margin: 0;
|
|
508
|
+
opacity: 0.85;
|
|
509
|
+
}
|