Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/www/css/graph.css

14.0 KiB, 1 run

created by r2519314175:1101, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1/* graph.css — the Graph pane. Rules for #panel-graph and its SVG picture only.
2 *
3 * Every colour comes from the palette variables, never from a literal, so the
4 * picture holds up in the dark, light and candy themes without a rule per
5 * theme. There are two exceptions and both are colours somebody chose rather
6 * than colours a theme owns: the hue of a relation chip, which is hashed from
7 * the relation's own name so one word is one colour everywhere, and a Diamond's
8 * own fill, handed in as `--node-bg` from the store the rest of the app keeps
9 * it in. Both arrive as custom properties with the theme's colour as the
10 * fallback, so a Diamond nobody has coloured is drawn exactly as before.
11 *
12 * A box is a flattened hexagon rather than a rectangle: horizontal top and
13 * bottom, and the two sides kinked out to a point at the middle. Those points
14 * are where the lines meet it, and graph.js computes them from the same two
15 * numbers this file rounds the joins with.
16 *
17 * The pane is now edited as well as read, so there are three more things to
18 * style than there were: the strip of controls above the picture, the menu the
19 * right button opens, and the small form a link is edited in. All three float
20 * INSIDE the panel card, which clips, so graph.js clamps them to it rather than
21 * letting one hang off an edge and disappear.
22 */
23
24/* The panel is a stage card like the AI, the Web page and a message, but it is
25 not in app.css's list of them -- so it is given the same card here. */
26.graph {
27 min-width: 0;
28 background: var(--bg-secondary);
29 border: 1px solid var(--border);
30 border-radius: var(--radius-lg);
31 display: flex;
32 flex-direction: column;
33 overflow: hidden;
34 /* The menu and the link form are positioned against this card. */
35 position: relative;
36}
37
38/* The picture is panned by scrolling, and scaled only when asked: an instrument
39 that quietly rescaled itself would make two stores of different sizes look
40 alike, so "All" is a button and never a reflex. */
41.graph-body {
42 flex: 1 1 auto;
43 min-height: 0;
44 overflow: auto;
45 padding: 4px 4px 0;
46}
47
48.graph-svg {
49 display: block;
50 /* SVG text inherits nothing from the page by default. */
51 font-family: var(--font);
52 /* A press and a sweep is a drag here, not a text selection. The mousedown
53 handler refuses the selection as well; this is the belt, so a gesture that
54 starts somewhere the handler does not run still cannot leave the picture
55 striped in highlight. The stats line below is left selectable. */
56 user-select: none;
57 -webkit-user-select: none;
58}
59
60/* ── Nodes ──────────────────────────────────────────────────── */
61
62.graph-node { cursor: pointer; }
63
64/* The box. `--node-bg` is the colour this Diamond was given elsewhere in the
65 app; every rule below keeps the theme's own colour as its fallback, so the
66 two kinds of Diamond differ in one property and not in a ruleset each.
67 `stroke-linejoin: round` is what keeps the six corners from drawing spikes
68 where the sides kink. */
69.graph-node-box {
70 fill: var(--node-bg, var(--bg-tertiary));
71 stroke: var(--border-2);
72 stroke-width: 1;
73 stroke-linejoin: round;
74}
75/* Hover moves the outline rather than the fill when a Diamond has a colour of
76 its own -- a chosen colour that changed under the pointer would read as a
77 different Diamond. */
78.graph-node:hover .graph-node-box {
79 fill: var(--node-bg, var(--bg-hover));
80 stroke: var(--accent);
81}
82
83/* An unlinked Diamond is stated, not hidden: the same box, drawn quieter. */
84.graph-node.isolate .graph-node-box {
85 fill: var(--node-bg, var(--bg-primary));
86 stroke: var(--border);
87 stroke-dasharray: 3 3;
88}
89
90/* A Diamond on a cycle is worth noticing without being alarming, so the box is
91 outlined in the warning colour rather than filled with it. */
92.graph-node.cycled .graph-node-box {
93 stroke: var(--warn);
94 stroke-width: 1.5;
95}
96
97/* Being dragged. No transition on the transform: the box has to be under the
98 pointer, and a box easing towards where the pointer was is a box that is not
99 where the user is holding it. */
100.graph-node.dragging { cursor: grabbing; }
101.graph-node.dragging .graph-node-box {
102 stroke: var(--accent);
103 stroke-width: 1.5;
104}
105
106/* The Diamond a link is being drawn FROM, while it is being drawn. */
107.graph-node.link-source .graph-node-box {
108 stroke: var(--accent);
109 stroke-width: 2;
110}
111
112.graph-node-name {
113 fill: var(--node-fg, var(--text-primary));
114 font-size: var(--fs-sm);
115 dominant-baseline: alphabetic;
116}
117.graph-node.isolate .graph-node-name { fill: var(--node-fg, var(--text-secondary)); }
118
119/* The artefact count: files, pages and chats this Diamond is linked to, which
120 are counted rather than drawn so they cannot crowd out the structure. */
121.graph-badge-text {
122 fill: var(--text-muted);
123 font-size: var(--fs-3xs);
124 text-anchor: end;
125}
126.graph-cycle-text {
127 fill: var(--warn);
128 font-size: var(--fs-2xs);
129 text-anchor: end;
130}
131
132/* ── Edges ──────────────────────────────────────────────────── */
133
134.graph-edge-line {
135 fill: none;
136 stroke: var(--text-muted);
137 stroke-width: 1.4;
138}
139.graph-edge:hover .graph-edge-line { stroke: var(--accent); }
140
141/* The wide invisible twin that takes the clicks. 1.4 pixels of stroke is not
142 something a pointer can be asked to hit, and widening the drawn line to make
143 it hittable would be letting the interaction decide how the picture looks. */
144.graph-edge-hit {
145 fill: none;
146 stroke: transparent;
147 stroke-width: 16;
148 cursor: pointer;
149}
150
151/* A closing edge -- the one that makes a cycle a cycle. Dashed and in the
152 warning colour, because the instrument's job is to make it visible, not to
153 pretend the graph is a tree. */
154.graph-edge.back .graph-edge-line {
155 stroke: var(--warn);
156 stroke-dasharray: 6 4;
157}
158
159.graph-arrow { fill: var(--text-muted); }
160.graph-arrow.back { fill: var(--warn); }
161.graph-arrow.live { fill: var(--accent); }
162
163/* ── The relation chips ─────────────────────────────────────── */
164/* A link's relations ride its own line as pills, one per relation, stacked when
165 there are several. They are the Diamonds' tag chips in another medium: the
166 same hue per word, the same shape. The halo the bare word used to be painted
167 with is gone with it -- a pill is opaque, so the line runs up to its edge and
168 stops there, which is the join the words never had.
169
170 The type is pinned at 11px rather than taken from `--fs-2xs`. The pill's
171 width is arithmetic in graph.js, done without asking the page how wide the
172 word came out, and type that floated against that arithmetic would sit in a
173 pill of the wrong size. */
174.graph-chip-box {
175 fill: hsl(var(--tag-h, 0) 30% 20%);
176 stroke: hsl(var(--tag-h, 0) 28% 34%);
177 stroke-width: 1;
178}
179.graph-edge-label {
180 fill: hsl(var(--tag-h, 0) 55% 78%);
181 font-size: 11px;
182 text-anchor: middle;
183 dominant-baseline: central;
184 pointer-events: none;
185}
186/* The light themes take the reverse, exactly as `.tag-chip` does in app.css:
187 pale pill, dark lettering, and neither side has to know which relations
188 exist. */
189:root[data-ink="dark"] .graph-chip-box {
190 fill: hsl(var(--tag-h, 0) 70% 94%);
191 stroke: hsl(var(--tag-h, 0) 45% 80%);
192}
193:root[data-ink="dark"] .graph-edge-label { fill: hsl(var(--tag-h, 0) 55% 30%); }
194
195/* ── The live arrow ─────────────────────────────────────────── */
196/* The only moving thing in the pane. It follows the pointer while a link is
197 being drawn and is thrown away when the gesture ends; nothing about it is
198 ever stored, which is why the picture is still a function of the store. */
199.graph-live-line {
200 fill: none;
201 stroke: var(--accent);
202 stroke-width: 1.6;
203 stroke-dasharray: 5 4;
204 pointer-events: none;
205}
206.graph-live { pointer-events: none; }
207
208/* While a link is being aimed, the pointer says what a click will do. */
209.graph-body.linking { cursor: crosshair; }
210.graph-body.panning { cursor: grabbing; }
211
212/* ── Furniture ──────────────────────────────────────────────── */
213
214.graph-band {
215 fill: var(--text-muted);
216 font-size: var(--fs-2xs);
217 letter-spacing: 0.06em;
218 text-transform: uppercase;
219}
220
221.graph-empty {
222 margin: 0;
223 padding: 14px 16px;
224 color: var(--text-secondary);
225 font-size: var(--fs-sm);
226 line-height: 1.5;
227}
228
229.graph-stats {
230 flex: none;
231 padding: 6px 14px 8px;
232 border-top: 1px solid var(--border);
233 color: var(--text-muted);
234 font-size: var(--fs-2xs);
235 font-family: var(--font-mono);
236}
237/* The stats line is the panel's foot, not part of what scrolls. */
238.graph-body > .graph-stats { position: sticky; bottom: 0; background: var(--bg-secondary); }
239
240/* ── The control strip ──────────────────────────────────────── */
241/* Above the picture and outside the scroller, so it neither scrolls away nor
242 competes with the picture for the panel's own head. */
243.graph-bar {
244 flex: none;
245 display: flex;
246 align-items: center;
247 gap: 6px;
248 padding: 5px 10px 6px;
249 border-bottom: 1px solid var(--border);
250 min-width: 0;
251}
252
253.graph-btn {
254 flex: none;
255 font: inherit;
256 font-size: var(--fs-2xs);
257 line-height: 1.4;
258 padding: 3px 10px;
259 border-radius: var(--radius-sm);
260 border: 1px solid var(--border-2);
261 background: var(--bg-tertiary);
262 color: var(--text-secondary);
263 cursor: pointer;
264}
265.graph-btn:hover {
266 background: var(--bg-hover);
267 color: var(--text-primary);
268 border-color: var(--border-strong);
269}
270/* Armed. The pressed state has to read as pressed without the label changing,
271 or the button says one thing and means another. */
272.graph-btn.on {
273 background: var(--accent-soft);
274 border-color: var(--accent);
275 color: var(--accent-text);
276}
277.graph-btn.primary {
278 background: var(--accent);
279 border-color: var(--accent);
280 color: var(--on-fill);
281}
282.graph-btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
283.graph-btn.danger { color: var(--danger); }
284.graph-btn.danger:hover { color: var(--danger); border-color: var(--danger); }
285
286/* What the mode is waiting for, said where the control that armed it is. */
287.graph-say {
288 min-width: 0;
289 overflow: hidden;
290 text-overflow: ellipsis;
291 white-space: nowrap;
292 font-size: var(--fs-2xs);
293 color: var(--text-muted);
294}
295.graph-say.warn { color: var(--warn); }
296
297/* ── The right-click menu ───────────────────────────────────── */
298
299.graph-menu {
300 position: absolute;
301 z-index: 30;
302 min-width: 170px;
303 padding: 4px;
304 border: 1px solid var(--border-2);
305 border-radius: var(--radius-sm);
306 background: var(--bg-tertiary);
307 box-shadow: 0 6px 22px rgba(0, 0, 0, 0.28);
308}
309
310.graph-menu-item {
311 display: block;
312 width: 100%;
313 text-align: left;
314 font: inherit;
315 font-size: var(--fs-xs);
316 padding: 5px 9px;
317 border: 0;
318 border-radius: var(--radius-sm);
319 background: transparent;
320 color: var(--text-primary);
321 cursor: pointer;
322}
323.graph-menu-item:hover { background: var(--bg-hover); }
324.graph-menu-item.danger { color: var(--danger); }
325.graph-menu-sep {
326 height: 1px;
327 margin: 4px 2px;
328 background: var(--border);
329}
330
331/* ── The link form ──────────────────────────────────────────── */
332
333.graph-edit {
334 position: absolute;
335 z-index: 31;
336 width: 260px;
337 padding: 10px 12px 11px;
338 border: 1px solid var(--border-2);
339 border-radius: var(--radius);
340 background: var(--bg-tertiary);
341 box-shadow: 0 8px 26px rgba(0, 0, 0, 0.32);
342}
343
344.graph-edit-head {
345 font-size: var(--fs-xs);
346 color: var(--text-primary);
347 margin-bottom: 8px;
348 overflow: hidden;
349 text-overflow: ellipsis;
350 white-space: nowrap;
351}
352
353.graph-edit-lab {
354 display: block;
355 font-size: var(--fs-3xs);
356 letter-spacing: 0.06em;
357 text-transform: uppercase;
358 color: var(--text-muted);
359 margin: 6px 0 3px;
360}
361
362.graph-edit-input,
363.graph-edit-note {
364 display: block;
365 width: 100%;
366 box-sizing: border-box;
367 font: inherit;
368 font-size: var(--fs-xs);
369 padding: 4px 7px;
370 border: 1px solid var(--border-2);
371 border-radius: var(--radius-sm);
372 background: var(--bg-primary);
373 color: var(--text-primary);
374}
375.graph-edit-note { resize: vertical; min-height: 40px; }
376.graph-edit-input:focus,
377.graph-edit-note:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
378
379/* ── Relations, in the form ─────────────────────────────────── */
380/* The chips on the link, and under the box the ones the store already holds.
381 Both rows scroll rather than grow: the form floats inside the panel card,
382 which clips, and a link given six relations must not push its own buttons off
383 the bottom of it. */
384.graph-rel-row {
385 display: flex;
386 flex-wrap: wrap;
387 gap: 4px;
388 margin-top: 3px;
389 max-height: 62px;
390 overflow-y: auto;
391}
392.graph-rel-add {
393 display: flex;
394 align-items: center;
395 gap: 6px;
396 margin-top: 6px;
397}
398.graph-rel-add .graph-edit-input { flex: 1 1 auto; min-width: 0; }
399.graph-rel-add .graph-btn { flex: none; padding: 3px 9px; }
400
401.graph-rel-none {
402 font-size: var(--fs-3xs);
403 color: var(--text-muted);
404}
405.graph-rel-pool-lab {
406 margin-top: 8px;
407 font-size: var(--fs-3xs);
408 letter-spacing: 0.06em;
409 text-transform: uppercase;
410 color: var(--text-muted);
411}
412
413/* Said when a relation will not fit, beside the box it was refused from. In the
414 warning colour and not the muted one: it is the reason a save did not happen,
415 which is not a footnote. */
416.graph-rel-hint {
417 margin-top: 7px;
418 font-size: var(--fs-3xs);
419 line-height: 1.45;
420 color: var(--warn);
421}
422
423.graph-edit-row {
424 display: flex;
425 gap: 6px;
426 margin-top: 10px;
427}
428
429/* The link editor's title row: the pair being linked, and the way out. The head
430 ellipsises rather than pushing the cross off the end of a 260px box, which is
431 what `min-width: 0` on a flex child is for. The row carries the space the head
432 used to carry itself. */
433.graph-edit-top { margin-bottom: 8px; }
434.graph-edit-top .graph-edit-head { margin-bottom: 0; min-width: 0; }