konpeki 0.1.1 → 0.2.0
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/.agents/skills/authoring-visuals/SKILL.md +12 -1
- package/README.md +4 -4
- package/composition/README.md +26 -49
- package/composition/compile.ts +1 -1
- package/composition/document.ts +2 -111
- package/composition/schema.json +3 -3
- package/composition/schema.ts +1 -1
- package/composition/types.ts +1 -20
- package/composition/validate.ts +3 -461
- package/composition/visualizations.ts +0 -16
- package/docs/development.md +4 -0
- package/docs/workflow.md +49 -7
- package/package.json +3 -1
- package/runtime/konpeki.mjs +182 -460
- package/slides/introducing-konpeki/composition.json +1740 -1735
- package/src/app/App.tsx +92 -18
- package/src/components/BuildOrb.tsx +40 -0
- package/src/components/Canvas.tsx +17 -0
- package/src/components/InspectorPanel.tsx +37 -3
- package/src/components/LeftPanel.tsx +16 -3
- package/src/components/RevisionNotes.tsx +56 -0
- package/src/components/WorkspaceChrome.tsx +6 -6
- package/src/lib/examples/react-page-migration.json +1 -1
- package/src/lib/export-png.ts +4 -1
- package/src/lib/file-session.ts +14 -1
- package/src/lib/review.ts +26 -0
- package/src/lib/use-file-session.ts +49 -13
- package/src/styles/base.css +16 -0
- package/src/styles/canvas.css +43 -12
- package/src/styles/chrome.css +78 -28
- package/src/styles/feedback.css +19 -1
- package/src/styles/left-panel.css +48 -7
- package/src/styles/right-panel.css +62 -20
- package/src/styles/shell.css +41 -26
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type ReviewTarget = { slideId: string; componentId?: string; elementId?: string };
|
|
2
|
+
export type RevisionNote = ReviewTarget & {
|
|
3
|
+
id: string;
|
|
4
|
+
text: string;
|
|
5
|
+
resolved: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type BuildRequest = ReviewTarget & {
|
|
8
|
+
schema: "konpeki-build-request/v2";
|
|
9
|
+
id: string;
|
|
10
|
+
compositionPath: string;
|
|
11
|
+
revision: string;
|
|
12
|
+
instruction: string;
|
|
13
|
+
notes: RevisionNote[];
|
|
14
|
+
status: "submitted" | "working" | "done" | "needs-clarification" | "failed";
|
|
15
|
+
message?: string;
|
|
16
|
+
resultRevision?: string;
|
|
17
|
+
};
|
|
18
|
+
export type ReviewState = {
|
|
19
|
+
schema: "konpeki-review/v1";
|
|
20
|
+
version: number;
|
|
21
|
+
notes: RevisionNote[];
|
|
22
|
+
request?: BuildRequest;
|
|
23
|
+
};
|
|
24
|
+
export const emptyReview = (): ReviewState => ({ schema: "konpeki-review/v1", version: 0, notes: [] });
|
|
25
|
+
export const activeRequest = (request?: BuildRequest) =>
|
|
26
|
+
request?.status === "submitted" || request?.status === "working";
|
|
@@ -2,7 +2,11 @@ import { useEffect, useRef, useState } from "react";
|
|
|
2
2
|
import { canonicalJSON } from "../../composition/compile.ts";
|
|
3
3
|
import { validateComposition } from "../../composition/validate.ts";
|
|
4
4
|
import type { Draft } from "./model.ts";
|
|
5
|
+
import { activeRequest, emptyReview, type ReviewState, type ReviewTarget } from "./review.ts";
|
|
5
6
|
import {
|
|
7
|
+
addNote,
|
|
8
|
+
removeNote,
|
|
9
|
+
cancelRequest,
|
|
6
10
|
FileSessionError,
|
|
7
11
|
fileSessionToken,
|
|
8
12
|
loadFileSession,
|
|
@@ -15,7 +19,7 @@ export type FileStatus = "loading" | "saved" | "saving" | "conflict" | "error";
|
|
|
15
19
|
type Callbacks = {
|
|
16
20
|
onOpen: (draft: Draft, name: string) => void;
|
|
17
21
|
onExternalChange: (draft: Draft) => void;
|
|
18
|
-
onNotice: (message: string) => void;
|
|
22
|
+
onNotice: (message: string, tone?: "neutral" | "error") => void;
|
|
19
23
|
onError: (message: string) => void;
|
|
20
24
|
};
|
|
21
25
|
|
|
@@ -27,6 +31,8 @@ export function useFileSession(
|
|
|
27
31
|
const token = fileSessionToken();
|
|
28
32
|
const [status, setStatus] = useState<FileStatus>(enabled ? "loading" : "saved");
|
|
29
33
|
const [building, setBuilding] = useState(false);
|
|
34
|
+
const [review, setReview] = useState<ReviewState>(emptyReview);
|
|
35
|
+
const reviewRef = useRef(review);
|
|
30
36
|
const buildPending = useRef(false);
|
|
31
37
|
const submittingBuild = useRef(false);
|
|
32
38
|
const callbacksRef = useRef(callbacks);
|
|
@@ -52,6 +58,7 @@ export function useFileSession(
|
|
|
52
58
|
savedJSON: canonicalJSON(session.document),
|
|
53
59
|
};
|
|
54
60
|
setStatus("saved");
|
|
61
|
+
receiveReview(session.review);
|
|
55
62
|
callbacksRef.current.onOpen(session.document, session.name);
|
|
56
63
|
}).catch((error) => {
|
|
57
64
|
if (cancelled) return;
|
|
@@ -78,12 +85,15 @@ export function useFileSession(
|
|
|
78
85
|
if (!state.current.ready || state.current.drain || submittingBuild.current) return;
|
|
79
86
|
const revision = state.current.revision;
|
|
80
87
|
void loadFileSession(token).then((remote) => {
|
|
81
|
-
if (cancelled || state.current.drain || submittingBuild.current || revision !== state.current.revision
|
|
88
|
+
if (cancelled || state.current.drain || submittingBuild.current || revision !== state.current.revision) return;
|
|
89
|
+
receiveReview(remote.review);
|
|
90
|
+
setStatus(current => current === "error" ? "saved" : current);
|
|
91
|
+
if (remote.revision === revision) return;
|
|
82
92
|
if (canonicalJSON(draftRef.current) !== state.current.savedJSON) {
|
|
83
|
-
finishBuild();
|
|
84
93
|
setStatus("conflict");
|
|
85
94
|
callbacksRef.current.onNotice(
|
|
86
95
|
"The file changed elsewhere. Neither version was overwritten.",
|
|
96
|
+
"error",
|
|
87
97
|
);
|
|
88
98
|
return;
|
|
89
99
|
}
|
|
@@ -94,13 +104,11 @@ export function useFileSession(
|
|
|
94
104
|
};
|
|
95
105
|
setStatus("saved");
|
|
96
106
|
callbacksRef.current.onExternalChange(remote.document);
|
|
97
|
-
finishBuild();
|
|
98
107
|
}).catch(() => {
|
|
99
108
|
if (cancelled || submittingBuild.current || revision !== state.current.revision) return;
|
|
100
109
|
setStatus("error");
|
|
101
110
|
if (buildPending.current) {
|
|
102
|
-
|
|
103
|
-
callbacksRef.current.onNotice("Could not load the agent result. Check the local file service.");
|
|
111
|
+
callbacksRef.current.onNotice("Could not load the agent result. Check the local file service.", "error");
|
|
104
112
|
}
|
|
105
113
|
});
|
|
106
114
|
}, 1000);
|
|
@@ -115,8 +123,25 @@ export function useFileSession(
|
|
|
115
123
|
setBuilding(false);
|
|
116
124
|
}
|
|
117
125
|
|
|
126
|
+
function receiveReview(next: ReviewState) {
|
|
127
|
+
if (next.version < reviewRef.current.version) return;
|
|
128
|
+
reviewRef.current = next;
|
|
129
|
+
setReview(next);
|
|
130
|
+
buildPending.current = activeRequest(next.request);
|
|
131
|
+
setBuilding(buildPending.current);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function noteAction(action: () => Promise<ReviewState>, saveFirst = false) {
|
|
135
|
+
if (submittingBuild.current) throw new Error("Wait for the current save before submitting.");
|
|
136
|
+
submittingBuild.current = true;
|
|
137
|
+
try {
|
|
138
|
+
if (saveFirst && !await save(draftRef.current)) throw new Error("Save the composition before adding a note.");
|
|
139
|
+
receiveReview(await action());
|
|
140
|
+
} finally { submittingBuild.current = false; }
|
|
141
|
+
}
|
|
142
|
+
|
|
118
143
|
function save(next: Draft): Promise<string | undefined> {
|
|
119
|
-
if (!token || !state.current.ready) return Promise.resolve(undefined);
|
|
144
|
+
if (!token || !state.current.ready || activeRequest(reviewRef.current.request)) return Promise.resolve(undefined);
|
|
120
145
|
const serialized = canonicalJSON(next);
|
|
121
146
|
if (serialized === state.current.savedJSON && !state.current.drain)
|
|
122
147
|
return Promise.resolve(state.current.revision);
|
|
@@ -149,9 +174,12 @@ export function useFileSession(
|
|
|
149
174
|
state.current.pending = undefined;
|
|
150
175
|
const conflict = error instanceof FileSessionError && error.status === 409;
|
|
151
176
|
setStatus(conflict ? "conflict" : "error");
|
|
152
|
-
callbacksRef.current.onNotice(
|
|
153
|
-
|
|
154
|
-
|
|
177
|
+
callbacksRef.current.onNotice(
|
|
178
|
+
conflict
|
|
179
|
+
? "The composition changed outside this browser. Your edits were not overwritten."
|
|
180
|
+
: error instanceof Error ? error.message : "Could not save the composition file.",
|
|
181
|
+
"error",
|
|
182
|
+
);
|
|
155
183
|
return;
|
|
156
184
|
}
|
|
157
185
|
}
|
|
@@ -161,8 +189,10 @@ export function useFileSession(
|
|
|
161
189
|
instruction: string,
|
|
162
190
|
slideId: string,
|
|
163
191
|
componentId?: string,
|
|
192
|
+
elementId?: string,
|
|
164
193
|
) {
|
|
165
194
|
if (!token || buildPending.current) return false;
|
|
195
|
+
if (submittingBuild.current) throw new Error("Wait for your note to finish saving before building.");
|
|
166
196
|
buildPending.current = true;
|
|
167
197
|
submittingBuild.current = true;
|
|
168
198
|
setBuilding(true);
|
|
@@ -172,12 +202,13 @@ export function useFileSession(
|
|
|
172
202
|
finishBuild();
|
|
173
203
|
return false;
|
|
174
204
|
}
|
|
175
|
-
await sendBuildRequest(token, {
|
|
205
|
+
receiveReview(await sendBuildRequest(token, {
|
|
176
206
|
revision,
|
|
177
207
|
instruction,
|
|
178
208
|
slideId,
|
|
179
209
|
...(componentId ? { componentId } : {}),
|
|
180
|
-
|
|
210
|
+
...(elementId ? { elementId } : {}),
|
|
211
|
+
}));
|
|
181
212
|
return true;
|
|
182
213
|
} catch (error) {
|
|
183
214
|
finishBuild();
|
|
@@ -187,5 +218,10 @@ export function useFileSession(
|
|
|
187
218
|
}
|
|
188
219
|
}
|
|
189
220
|
|
|
190
|
-
return {
|
|
221
|
+
return {
|
|
222
|
+
status, build, building, review,
|
|
223
|
+
addNote: (target: ReviewTarget, text: string) => noteAction(() => addNote(token!, target, text), true),
|
|
224
|
+
removeNote: (id: string) => noteAction(() => removeNote(token!, id)),
|
|
225
|
+
cancel: () => noteAction(() => cancelRequest(token!, reviewRef.current.request!.id)),
|
|
226
|
+
};
|
|
191
227
|
}
|
package/src/styles/base.css
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
color: #202020;
|
|
4
4
|
background: #f4f4f4;
|
|
5
5
|
font-synthesis: none;
|
|
6
|
+
-webkit-font-smoothing: antialiased;
|
|
7
|
+
-moz-osx-font-smoothing: grayscale;
|
|
6
8
|
--accent: #007bbb;
|
|
7
9
|
--muted: #626262;
|
|
8
10
|
--ui-bg: #f4f4f4;
|
|
@@ -39,6 +41,8 @@ button {
|
|
|
39
41
|
color: inherit;
|
|
40
42
|
background: #fff;
|
|
41
43
|
padding: 7px 10px;
|
|
44
|
+
transition: background-color 120ms ease-out, border-color 120ms ease-out,
|
|
45
|
+
color 120ms ease-out, box-shadow 120ms ease-out, transform 120ms ease-out;
|
|
42
46
|
}
|
|
43
47
|
button:hover:not(:disabled) {
|
|
44
48
|
background: #f1f1f1;
|
|
@@ -62,6 +66,8 @@ select {
|
|
|
62
66
|
background: #fff;
|
|
63
67
|
color: var(--ui-text);
|
|
64
68
|
padding: 7px 9px;
|
|
69
|
+
transition: background-color 120ms ease-out, border-color 120ms ease-out,
|
|
70
|
+
box-shadow 120ms ease-out;
|
|
65
71
|
}
|
|
66
72
|
input:hover,
|
|
67
73
|
textarea:hover,
|
|
@@ -92,6 +98,16 @@ h2 {
|
|
|
92
98
|
background: #383838;
|
|
93
99
|
border-color: #383838;
|
|
94
100
|
}
|
|
101
|
+
.panel-toggle:active:not(:disabled),
|
|
102
|
+
.action-island button:active:not(:disabled),
|
|
103
|
+
.component-dock button:active:not(:disabled),
|
|
104
|
+
.segmented-options button:active:not(:disabled),
|
|
105
|
+
.theme-options button:active:not(:disabled),
|
|
106
|
+
.appearance-options button:active:not(:disabled),
|
|
107
|
+
.revision-note-submit button:active:not(:disabled),
|
|
108
|
+
.revision-note-remove:active:not(:disabled) {
|
|
109
|
+
transform: scale(0.96);
|
|
110
|
+
}
|
|
95
111
|
.sr-only {
|
|
96
112
|
position: absolute;
|
|
97
113
|
width: 1px;
|
package/src/styles/canvas.css
CHANGED
|
@@ -192,8 +192,8 @@
|
|
|
192
192
|
}
|
|
193
193
|
.resize-handle {
|
|
194
194
|
position: absolute;
|
|
195
|
-
width:
|
|
196
|
-
height:
|
|
195
|
+
width: 40px;
|
|
196
|
+
height: 40px;
|
|
197
197
|
padding: 0;
|
|
198
198
|
border: 0;
|
|
199
199
|
background: transparent;
|
|
@@ -202,8 +202,8 @@
|
|
|
202
202
|
}
|
|
203
203
|
.resize-handle::after {
|
|
204
204
|
position: absolute;
|
|
205
|
-
top:
|
|
206
|
-
left:
|
|
205
|
+
top: 17px;
|
|
206
|
+
left: 17px;
|
|
207
207
|
width: 6px;
|
|
208
208
|
height: 6px;
|
|
209
209
|
border: 1px solid var(--accent);
|
|
@@ -215,23 +215,23 @@
|
|
|
215
215
|
background: transparent;
|
|
216
216
|
}
|
|
217
217
|
.resize-nw {
|
|
218
|
-
top: -
|
|
219
|
-
left: -
|
|
218
|
+
top: -20px;
|
|
219
|
+
left: -20px;
|
|
220
220
|
cursor: nwse-resize;
|
|
221
221
|
}
|
|
222
222
|
.resize-ne {
|
|
223
|
-
top: -
|
|
224
|
-
right: -
|
|
223
|
+
top: -20px;
|
|
224
|
+
right: -20px;
|
|
225
225
|
cursor: nesw-resize;
|
|
226
226
|
}
|
|
227
227
|
.resize-sw {
|
|
228
|
-
bottom: -
|
|
229
|
-
left: -
|
|
228
|
+
bottom: -20px;
|
|
229
|
+
left: -20px;
|
|
230
230
|
cursor: nesw-resize;
|
|
231
231
|
}
|
|
232
232
|
.resize-se {
|
|
233
|
-
right: -
|
|
234
|
-
bottom: -
|
|
233
|
+
right: -20px;
|
|
234
|
+
bottom: -20px;
|
|
235
235
|
cursor: nwse-resize;
|
|
236
236
|
}
|
|
237
237
|
.guide {
|
|
@@ -266,3 +266,34 @@
|
|
|
266
266
|
min-height: 100%;
|
|
267
267
|
height: 100%;
|
|
268
268
|
}
|
|
269
|
+
.revision-pin {
|
|
270
|
+
position: absolute;
|
|
271
|
+
display: grid;
|
|
272
|
+
place-items: center;
|
|
273
|
+
width: 40px;
|
|
274
|
+
height: 40px;
|
|
275
|
+
min-height: 0;
|
|
276
|
+
padding: 0;
|
|
277
|
+
border: 0;
|
|
278
|
+
border-radius: 50%;
|
|
279
|
+
background: radial-gradient(
|
|
280
|
+
circle,
|
|
281
|
+
var(--accent) 0 10px,
|
|
282
|
+
var(--ui-surface) 10.5px 11.5px,
|
|
283
|
+
transparent 12px
|
|
284
|
+
);
|
|
285
|
+
color: white;
|
|
286
|
+
font: 500 11px "IBM Plex Sans", sans-serif;
|
|
287
|
+
box-shadow: none;
|
|
288
|
+
filter: drop-shadow(0 1px 2px #0003);
|
|
289
|
+
}
|
|
290
|
+
.revision-pin:hover:not(:disabled),
|
|
291
|
+
.revision-pin:active:not(:disabled) {
|
|
292
|
+
border-color: transparent;
|
|
293
|
+
background: radial-gradient(
|
|
294
|
+
circle,
|
|
295
|
+
#006aa1 0 10px,
|
|
296
|
+
var(--ui-surface) 10.5px 11.5px,
|
|
297
|
+
transparent 12px
|
|
298
|
+
);
|
|
299
|
+
}
|
package/src/styles/chrome.css
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
top: 20px;
|
|
15
15
|
left: 20px;
|
|
16
16
|
width: 252px;
|
|
17
|
-
height:
|
|
17
|
+
height: 60px;
|
|
18
18
|
gap: 10px;
|
|
19
19
|
padding: 8px 10px;
|
|
20
20
|
border-radius: 14px;
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
.document-title input {
|
|
45
|
-
height:
|
|
45
|
+
height: 40px;
|
|
46
46
|
border-color: transparent;
|
|
47
47
|
background: transparent;
|
|
48
48
|
font-size: 13px;
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
display: inline-flex;
|
|
126
126
|
align-items: center;
|
|
127
127
|
gap: 6px;
|
|
128
|
-
height:
|
|
128
|
+
height: 40px;
|
|
129
129
|
padding: 0 12px;
|
|
130
130
|
border-radius: 7px;
|
|
131
131
|
border-color: transparent;
|
|
@@ -141,8 +141,9 @@
|
|
|
141
141
|
|
|
142
142
|
.action-island button.icon-only {
|
|
143
143
|
flex: none;
|
|
144
|
-
width:
|
|
145
|
-
padding: 0
|
|
144
|
+
width: 40px;
|
|
145
|
+
padding: 0;
|
|
146
|
+
justify-content: center;
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
.action-island button.primary {
|
|
@@ -163,46 +164,78 @@
|
|
|
163
164
|
position: relative;
|
|
164
165
|
overflow: hidden;
|
|
165
166
|
justify-content: center;
|
|
166
|
-
flex:
|
|
167
|
+
flex: none;
|
|
168
|
+
padding-inline: 9px;
|
|
167
169
|
white-space: nowrap;
|
|
168
170
|
}
|
|
169
171
|
|
|
172
|
+
.build-button .build-label {
|
|
173
|
+
min-width: 11ch;
|
|
174
|
+
text-align: left;
|
|
175
|
+
}
|
|
176
|
+
|
|
170
177
|
.action-island button.build-button.building {
|
|
171
|
-
background:
|
|
172
|
-
|
|
173
|
-
color: white;
|
|
178
|
+
background: white;
|
|
179
|
+
color: #2449aa;
|
|
174
180
|
opacity: 1;
|
|
175
181
|
cursor: wait;
|
|
176
|
-
animation: build-colors 4s linear infinite;
|
|
177
182
|
}
|
|
178
183
|
|
|
179
|
-
.build-button.building
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
184
|
+
.build-button.building .build-label {
|
|
185
|
+
background: linear-gradient(110deg, #155e75 10%, #2449aa 30%, #a82d62 45%, #87bdeb 50%, #7535a5 55%, #155e75 80%);
|
|
186
|
+
background-size: 250% 100%;
|
|
187
|
+
background-clip: text;
|
|
188
|
+
color: transparent;
|
|
189
|
+
animation: build-colors 3s linear infinite;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.build-orb circle {
|
|
193
|
+
fill: currentColor;
|
|
194
|
+
stroke: none;
|
|
195
|
+
transform: translate(var(--x), var(--y));
|
|
196
|
+
}
|
|
197
|
+
.build-orb.active circle {
|
|
198
|
+
animation: orb-shape 8s ease-in-out infinite;
|
|
187
199
|
}
|
|
200
|
+
.build-orb.nebula {
|
|
201
|
+
width: 100%;
|
|
202
|
+
height: 100%;
|
|
203
|
+
color: #4265ad;
|
|
204
|
+
transform: rotate(-18deg);
|
|
205
|
+
}
|
|
206
|
+
.build-orb.nebula circle {
|
|
207
|
+
animation: orb-sphere 12s ease-in-out infinite;
|
|
208
|
+
animation-delay: var(--phase);
|
|
209
|
+
}
|
|
210
|
+
.build-orb.nebula circle:nth-child(3n) { fill: #9772bb; }
|
|
211
|
+
.build-orb.nebula circle:nth-child(3n + 1) { fill: #198faa; }
|
|
188
212
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
213
|
+
@keyframes orb-shape {
|
|
214
|
+
0%, 10%, 100% { transform: translate(var(--x), var(--y)); }
|
|
215
|
+
25%, 35% { transform: translate(var(--tx), var(--ty)); }
|
|
216
|
+
50%, 60% { transform: translate(var(--sx), var(--sy)); }
|
|
217
|
+
75%, 85% { transform: translate(var(--hx), var(--hy)); }
|
|
218
|
+
}
|
|
219
|
+
@keyframes orb-sphere {
|
|
220
|
+
0%, 100% { transform: translate(var(--radius), var(--y)); opacity: 0.5; }
|
|
221
|
+
25% { transform: translate(0, var(--y)); opacity: 1; }
|
|
222
|
+
50% { transform: translate(calc(-1 * var(--radius)), var(--y)); opacity: 0.5; }
|
|
223
|
+
75% { transform: translate(0, var(--y)); opacity: 0.15; }
|
|
224
|
+
}
|
|
225
|
+
@keyframes orb-turn {
|
|
226
|
+
to { transform: rotate(360deg); }
|
|
192
227
|
}
|
|
193
228
|
|
|
194
229
|
@keyframes build-colors {
|
|
195
230
|
to { background-position: 150% 0; }
|
|
196
231
|
}
|
|
197
232
|
|
|
198
|
-
@keyframes build-sweep {
|
|
199
|
-
to { transform: translateX(100%); }
|
|
200
|
-
}
|
|
201
|
-
|
|
202
233
|
@media (prefers-reduced-motion: reduce) {
|
|
203
|
-
.
|
|
204
|
-
.build-
|
|
205
|
-
.build-
|
|
234
|
+
.build-button.building .build-label,
|
|
235
|
+
.build-orb.active circle,
|
|
236
|
+
.build-orb.nebula circle,
|
|
237
|
+
.build-orb.nebula,
|
|
238
|
+
.build-nebula::before {
|
|
206
239
|
animation: none;
|
|
207
240
|
}
|
|
208
241
|
}
|
|
@@ -218,6 +251,11 @@
|
|
|
218
251
|
stroke-width: 1.7;
|
|
219
252
|
}
|
|
220
253
|
|
|
254
|
+
.action-island .build-button .build-orb {
|
|
255
|
+
width: 20px;
|
|
256
|
+
height: 20px;
|
|
257
|
+
}
|
|
258
|
+
|
|
221
259
|
.component-dock {
|
|
222
260
|
bottom: 20px;
|
|
223
261
|
left: 50%;
|
|
@@ -236,6 +274,7 @@
|
|
|
236
274
|
gap: 1px;
|
|
237
275
|
padding: 4px 7px;
|
|
238
276
|
border-color: transparent;
|
|
277
|
+
border-radius: 10px;
|
|
239
278
|
color: var(--ui-text);
|
|
240
279
|
font-size: 11px;
|
|
241
280
|
}
|
|
@@ -260,3 +299,14 @@
|
|
|
260
299
|
min-width: 55px;
|
|
261
300
|
}
|
|
262
301
|
}
|
|
302
|
+
@media (max-width: 600px) {
|
|
303
|
+
.component-dock {
|
|
304
|
+
width: calc(100% - 24px);
|
|
305
|
+
}
|
|
306
|
+
.component-dock button {
|
|
307
|
+
flex: 1;
|
|
308
|
+
min-width: 0;
|
|
309
|
+
padding-inline: 3px;
|
|
310
|
+
font-size: 10px;
|
|
311
|
+
}
|
|
312
|
+
}
|
package/src/styles/feedback.css
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
max-width: min(520px, max(240px, calc(100vw - 744px)), calc(100vw - 40px));
|
|
21
21
|
z-index: 50;
|
|
22
22
|
}
|
|
23
|
-
.toast >
|
|
23
|
+
.toast > span {
|
|
24
24
|
min-width: 0;
|
|
25
25
|
overflow-wrap: anywhere;
|
|
26
26
|
}
|
|
@@ -32,6 +32,24 @@
|
|
|
32
32
|
pointer-events: none;
|
|
33
33
|
animation: toast-exit 180ms ease-in both;
|
|
34
34
|
}
|
|
35
|
+
.toast.error {
|
|
36
|
+
border-color: #e4b5b5;
|
|
37
|
+
background: #fff7f7;
|
|
38
|
+
color: #8d1717;
|
|
39
|
+
box-shadow: 0 0 0 1px #b13d3d14, 0 4px 16px #5f11110d;
|
|
40
|
+
}
|
|
41
|
+
.toast.error::before {
|
|
42
|
+
display: grid;
|
|
43
|
+
place-items: center;
|
|
44
|
+
flex: none;
|
|
45
|
+
width: 20px;
|
|
46
|
+
height: 20px;
|
|
47
|
+
border-radius: 50%;
|
|
48
|
+
background: #b13d3d;
|
|
49
|
+
color: white;
|
|
50
|
+
content: "!";
|
|
51
|
+
font-weight: 600;
|
|
52
|
+
}
|
|
35
53
|
@keyframes toast-enter {
|
|
36
54
|
from {
|
|
37
55
|
opacity: 0;
|
|
@@ -1,3 +1,44 @@
|
|
|
1
|
+
.left-notes {
|
|
2
|
+
flex: 1;
|
|
3
|
+
min-height: 0;
|
|
4
|
+
overflow-y: auto;
|
|
5
|
+
}
|
|
6
|
+
.revision-notes { padding: 18px 16px; }
|
|
7
|
+
.revision-note-submit {
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: space-between;
|
|
11
|
+
gap: 12px;
|
|
12
|
+
}
|
|
13
|
+
.revision-note-count,
|
|
14
|
+
.revision-note-scope {
|
|
15
|
+
color: var(--ui-muted);
|
|
16
|
+
font-size: 11px;
|
|
17
|
+
line-height: 1.5;
|
|
18
|
+
}
|
|
19
|
+
.revision-note-count { margin: 20px 0 8px; }
|
|
20
|
+
.revision-notes label { font-size: 12px; }
|
|
21
|
+
.revision-note-scope {
|
|
22
|
+
margin: 6px 0 8px;
|
|
23
|
+
padding: 6px 8px;
|
|
24
|
+
border-radius: 6px;
|
|
25
|
+
background: var(--ui-selected);
|
|
26
|
+
color: #31596d;
|
|
27
|
+
overflow-wrap: anywhere;
|
|
28
|
+
}
|
|
29
|
+
.revision-notes textarea { width: 100%; resize: vertical; min-height: 72px; font-size: 12px; line-height: 1.5; }
|
|
30
|
+
.revision-notes textarea { border-color: transparent; border-radius: 6px; background: #f4f4f5; }
|
|
31
|
+
.revision-notes textarea:hover { border-color: #d4d4d8; }
|
|
32
|
+
.revision-note-submit { margin-top: 8px; justify-content: flex-end; }
|
|
33
|
+
.revision-note-submit button { min-height: 40px; white-space: nowrap; font-size: 12px; }
|
|
34
|
+
.revision-note-list { list-style: none; margin: 0; padding: 0; }
|
|
35
|
+
.revision-note-list li { padding: 12px 0; border-top: 1px solid var(--ui-border); }
|
|
36
|
+
.revision-note-target { display: flex; align-items: baseline; gap: 8px; width: 100%; min-height: 40px; padding: 6px 0; border: 0; background: none; text-align: left; font-size: 11px; overflow-wrap: anywhere; }
|
|
37
|
+
.revision-note-target > span { color: var(--ui-muted); }
|
|
38
|
+
.revision-note-list p { font-size: 12px; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
39
|
+
.revision-note-remove { display: inline-flex; align-items: center; min-height: 40px; border: 0; padding: 6px 0; background: none; color: var(--ui-muted); font-size: 11px; }
|
|
40
|
+
.revision-note-error { color: #a02020; font-size: 12px; }
|
|
41
|
+
|
|
1
42
|
.slide-list {
|
|
2
43
|
flex: 1;
|
|
3
44
|
min-height: 0;
|
|
@@ -24,8 +65,8 @@
|
|
|
24
65
|
|
|
25
66
|
.slide-thumbnail.selected,
|
|
26
67
|
.slide-thumbnail.selected:hover {
|
|
27
|
-
border-color:
|
|
28
|
-
background:
|
|
68
|
+
border-color: color-mix(in srgb, var(--accent) 45%, white);
|
|
69
|
+
background: var(--ui-selected);
|
|
29
70
|
}
|
|
30
71
|
|
|
31
72
|
.slide-thumbnail:focus-visible {
|
|
@@ -42,10 +83,10 @@
|
|
|
42
83
|
|
|
43
84
|
.remove-slide {
|
|
44
85
|
position: absolute;
|
|
45
|
-
right:
|
|
46
|
-
bottom:
|
|
47
|
-
width:
|
|
48
|
-
height:
|
|
86
|
+
right: 2px;
|
|
87
|
+
bottom: 0;
|
|
88
|
+
width: 40px;
|
|
89
|
+
height: 40px;
|
|
49
90
|
padding: 0;
|
|
50
91
|
border-color: transparent;
|
|
51
92
|
border-radius: 5px;
|
|
@@ -64,7 +105,7 @@
|
|
|
64
105
|
align-items: center;
|
|
65
106
|
justify-content: center;
|
|
66
107
|
width: 100%;
|
|
67
|
-
min-height:
|
|
108
|
+
min-height: 40px;
|
|
68
109
|
margin-top: 8px;
|
|
69
110
|
border-style: dashed;
|
|
70
111
|
color: var(--ui-muted);
|