finch-markdown-editor 0.1.9 → 0.2.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/dist/codemirror.js +36 -34
- package/dist/index.js +356 -199
- package/dist/panel.css +207 -0
- package/dist/panel.html +33 -2615
- package/dist/panel.js +43 -0
- package/i18n/en-US.json +3 -0
- package/i18n/zh-CN.json +3 -0
- package/package.json +12 -3
package/dist/panel.css
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
:root{
|
|
2
|
+
--bg:var(--finch-bg-root,#f7f7f8);
|
|
3
|
+
--card:var(--finch-bg-elevated,#fff);
|
|
4
|
+
--text:var(--finch-text-primary,#1d1d20);
|
|
5
|
+
--muted:var(--finch-text-secondary,#777);
|
|
6
|
+
--border:var(--finch-border,#ddd);
|
|
7
|
+
--accent:var(--finch-accent,#3f6b56);
|
|
8
|
+
}
|
|
9
|
+
@media (prefers-color-scheme: dark) {
|
|
10
|
+
:root{
|
|
11
|
+
--bg:var(--finch-bg-root,#17171a);
|
|
12
|
+
--card:var(--finch-bg-elevated,#242429);
|
|
13
|
+
--text:var(--finch-text-primary,#eee);
|
|
14
|
+
--muted:var(--finch-text-secondary,#aaa);
|
|
15
|
+
--border:var(--finch-border,#3c3c43);
|
|
16
|
+
--accent:var(--finch-accent,#70a889);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
*{box-sizing:border-box}
|
|
20
|
+
html,body{height:100%}
|
|
21
|
+
body{margin:0;background:var(--bg);color:var(--text);font:13px var(--finch-font-body,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif)}
|
|
22
|
+
.app{width:100%;height:100vh;display:flex;flex-direction:column}
|
|
23
|
+
.stage{position:relative;flex:1;min-height:0}
|
|
24
|
+
.pane{position:absolute;inset:0;display:none}
|
|
25
|
+
.pane.active{display:block}
|
|
26
|
+
.editor-wrap{position:relative;width:100%;height:100%;overflow:hidden;background:var(--card)}
|
|
27
|
+
#editor{width:100%;height:100%;overflow:hidden}
|
|
28
|
+
.fake-sel{position:fixed;z-index:15;border-radius:2px;background:rgba(63,107,86,.35);background:color-mix(in srgb,var(--accent) 34%,transparent);pointer-events:none}
|
|
29
|
+
.preview-shell{position:absolute;inset:0;padding:32px 20px 20px;background:var(--bg);overflow:hidden}
|
|
30
|
+
#article{display:block;width:100%;height:100%;border:0;border-radius:10px;background:var(--card)}
|
|
31
|
+
/* Home / empty state: a launch page rather than a placeholder — headline plus
|
|
32
|
+
the Markdown files this editor recently touched in the current working
|
|
33
|
+
directory, newest-modified first, so a session can be resumed in one click. */
|
|
34
|
+
.empty{position:absolute;z-index:5;inset:0;background:var(--card);color:var(--muted);display:block;overflow:auto;padding:30px 26px 34px;font-size:14px;line-height:1.7}
|
|
35
|
+
.empty[hidden]{display:none}
|
|
36
|
+
.home{max-width:860px;margin:0 auto}
|
|
37
|
+
.home-head{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;margin-bottom:22px}
|
|
38
|
+
.home-head h1{margin:0 0 3px;font-size:19px;font-weight:650;line-height:1.3;color:var(--text);letter-spacing:-.01em}
|
|
39
|
+
.home-head p{margin:0;font-size:12.5px;line-height:1.5;color:var(--muted)}
|
|
40
|
+
.home-cta{display:flex;gap:8px;flex:none}
|
|
41
|
+
.home-btn{appearance:none;display:inline-flex;align-items:center;gap:5px;border:1px solid var(--accent);border-radius:8px;background:var(--accent);color:#fff;padding:7px 12px;font:inherit;font-size:13px;font-weight:600;cursor:pointer;white-space:nowrap}
|
|
42
|
+
.home-btn:hover{filter:brightness(1.06)}
|
|
43
|
+
.home-btn.ghost{background:transparent;border-color:var(--border);color:var(--text);font-weight:500}
|
|
44
|
+
.home-btn.ghost:hover{filter:none;background:var(--bg)}
|
|
45
|
+
.home-btn svg{width:14px;height:14px;flex:none}
|
|
46
|
+
.home-section{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin:0 0 10px;font-size:12px;color:var(--muted)}
|
|
47
|
+
.home-section b{font-size:12.5px;font-weight:600;color:var(--text);flex:none}
|
|
48
|
+
/* flex:0 1 auto (not flex:1) so the button hugs its own text — the blank
|
|
49
|
+
space next to a short path stays unclickable. Front-truncation (the "…"
|
|
50
|
+
prefix) happens in JS, not via a direction:rtl CSS trick: mixing a plain
|
|
51
|
+
"~" with rtl reordered the whole string visually ("finch-tools/~" instead
|
|
52
|
+
of "~/finch-tools"), so this box now renders left-to-right like normal. */
|
|
53
|
+
.home-section .home-cwd{flex:0 1 auto;max-width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:.75;cursor:pointer;background:none;border:0;padding:0;font:inherit;color:inherit}
|
|
54
|
+
.home-section .home-cwd:hover{opacity:1;color:var(--accent);text-decoration:underline}
|
|
55
|
+
.home-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(196px,1fr));gap:12px}
|
|
56
|
+
.doc-card{position:relative;display:flex;flex-direction:column;height:138px;padding:0;text-align:left;border:1px solid var(--border);border-radius:11px;background:var(--bg);color:var(--text);font:inherit;cursor:pointer;overflow:hidden;transition:border-color .15s ease,box-shadow .15s ease,transform .1s ease}
|
|
57
|
+
.doc-card:hover{border-color:var(--accent);box-shadow:0 5px 16px rgba(0,0,0,.1)}
|
|
58
|
+
.doc-card:active{transform:scale(.99)}
|
|
59
|
+
.doc-preview{position:relative;flex:1;min-height:0;padding:11px 12px 0;font-size:11.5px;line-height:1.6;color:var(--muted);overflow:hidden;word-break:break-word}
|
|
60
|
+
.doc-preview::after{content:'';position:absolute;left:0;right:0;bottom:0;height:34px;background:linear-gradient(to bottom,transparent,var(--bg))}
|
|
61
|
+
.doc-foot{flex:none;padding:8px 12px 10px;border-top:1px solid var(--border);background:var(--card)}
|
|
62
|
+
.doc-title{font-size:13px;font-weight:600;line-height:1.35;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
63
|
+
.doc-sub{margin-top:2px;display:flex;gap:6px;font-size:11px;line-height:1.4;color:var(--muted)}
|
|
64
|
+
/* Front-truncated in JS (see elideFrontAll) rather than via CSS
|
|
65
|
+
direction:rtl trick: that trick reorders per Unicode bidi rules on a
|
|
66
|
+
per-run basis, and a plain LTR path (Latin text + weakly-directional
|
|
67
|
+
`/` separators) doesn't reliably get its ellipsis pinned to the front —
|
|
68
|
+
it was observed still cutting off the filename at the end instead.
|
|
69
|
+
Doing it in JS with real measured width is reliable regardless of
|
|
70
|
+
directionality. */
|
|
71
|
+
.doc-sub .doc-path{flex:1;min-width:0;overflow:hidden;white-space:nowrap}
|
|
72
|
+
.doc-sub .doc-time{flex:none;opacity:.8}
|
|
73
|
+
.doc-reveal{position:absolute;top:6px;right:6px;z-index:1;display:flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;border-radius:7px;background:color-mix(in srgb, var(--bg) 72%, transparent);color:var(--muted);cursor:pointer;opacity:0;transition:opacity .12s ease,background .12s ease,color .12s ease}
|
|
74
|
+
.doc-reveal svg{width:14px;height:14px}
|
|
75
|
+
.doc-card:hover .doc-reveal,.doc-reveal:focus-visible{opacity:1}
|
|
76
|
+
.doc-reveal:hover{background:var(--card);color:var(--text)}
|
|
77
|
+
.home-hint{margin:0;padding:22px 0;text-align:center;font-size:12.5px;color:var(--muted)}
|
|
78
|
+
.home-hint[hidden],.home-recent[hidden]{display:none}
|
|
79
|
+
.status{font-size:12px;color:var(--muted);padding:5px 10px;text-align:center;border-top:1px solid var(--border);background:var(--bg)}
|
|
80
|
+
.status a{color:var(--accent);text-decoration:underline;text-underline-offset:2px;cursor:pointer}
|
|
81
|
+
.status.error{color:#c54232}
|
|
82
|
+
.status:empty{padding:0;border-top:0}
|
|
83
|
+
/* Changed glyphs briefly take the accent color, then settle back to normal
|
|
84
|
+
text. Nested syntax spans receive the same animation; no background flash. */
|
|
85
|
+
.cm-line.cm-ai-changed,.cm-line.cm-ai-changed *{animation:ai-edit-text-flash 1.9s cubic-bezier(.33,0,.2,1) both}
|
|
86
|
+
@keyframes ai-edit-text-flash{0%,26%{color:var(--accent)}100%{color:var(--text)}}
|
|
87
|
+
@media (prefers-reduced-motion:reduce){.cm-line.cm-ai-changed,.cm-line.cm-ai-changed *{animation:none}}
|
|
88
|
+
.popup{position:fixed;z-index:20;display:flex;align-items:center;gap:6px;background:var(--card);border:1px solid var(--border);border-radius:9px;box-shadow:0 8px 22px rgba(0,0,0,.22);padding:5px;transform-origin:center bottom;animation:popup-in .18s cubic-bezier(.2,.8,.2,1)}
|
|
89
|
+
.popup.hidden{display:none}
|
|
90
|
+
@keyframes popup-in{from{opacity:0;transform:translateY(5px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}
|
|
91
|
+
@media (prefers-reduced-motion:reduce){.popup{animation:none}}
|
|
92
|
+
.popup input{width:147px;border:0;outline:0;background:transparent;color:var(--text);font:inherit;padding:4px 6px}
|
|
93
|
+
.popup button{font:inherit;border:0;background:var(--accent);color:#fff;border-radius:6px;padding:2px 6px;cursor:pointer;white-space:nowrap}
|
|
94
|
+
.popup button:hover{opacity:.9}
|
|
95
|
+
/* The slot stays sized by the shortcut even after focus: hiding with
|
|
96
|
+
visibility (rather than display:none) preserves both the popup's width
|
|
97
|
+
and height. Send is absolutely centered over that reserved space, so it
|
|
98
|
+
never coexists visually with the hint. */
|
|
99
|
+
.popup-action-slot{position:relative;display:flex;align-items:center;justify-content:center;min-height:28px}
|
|
100
|
+
.popup-shortcut{display:flex;align-items:center;gap:3px;padding:0 6px;color:var(--muted);white-space:nowrap;cursor:default;font-size:11px}
|
|
101
|
+
.popup-shortcut kbd{font:inherit;font-size:11px}
|
|
102
|
+
.popup.focused .popup-shortcut{visibility:hidden}
|
|
103
|
+
.popup:not(.focused) .popup-send{visibility:hidden;pointer-events:none}
|
|
104
|
+
/* Round send button, icon-only, centered inside the permanently reserved
|
|
105
|
+
shortcut slot. */
|
|
106
|
+
.popup-send{position:absolute;inset:0;margin:auto;margin-right:1px;width:28px;height:28px;padding:0;border-radius:999px;display:flex;align-items:center;justify-content:center}
|
|
107
|
+
.popup-send svg{width:18px;height:18px}
|
|
108
|
+
/* Inline prompt bar: the blank line the writer pressed space on turns into
|
|
109
|
+
a full-width input. Width comes from JS (the editor's text column), so the
|
|
110
|
+
input just needs to fill whatever it is given. */
|
|
111
|
+
.popup.popup-bar{border-radius:8px;padding:5px 6px;box-shadow:0 4px 16px rgba(0,0,0,.18)}
|
|
112
|
+
.popup.popup-bar input{flex:1;width:auto;white-space:nowrap;text-overflow:ellipsis}
|
|
113
|
+
/* Unsaved-changes confirm, deliberately a plain in-page overlay rather than
|
|
114
|
+
`window.finch.ui.confirm()`: the only entry point that can trigger it is
|
|
115
|
+
the toolbar's "home" button, which reaches this page as a `finch:menu`
|
|
116
|
+
postMessage — not a real user gesture — so the same Bridge action that
|
|
117
|
+
requires one (see `.actions` comment above) could not be trusted to fire
|
|
118
|
+
reliably from there. A plain DOM overlay has no such gesture requirement. */
|
|
119
|
+
.confirm-overlay{position:fixed;inset:0;z-index:30;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.32);animation:confirm-fade .12s ease}
|
|
120
|
+
.confirm-overlay[hidden]{display:none}
|
|
121
|
+
@keyframes confirm-fade{from{opacity:0}to{opacity:1}}
|
|
122
|
+
.confirm-card{width:300px;max-width:calc(100% - 40px);background:var(--card);border:1px solid var(--border);border-radius:12px;box-shadow:0 16px 40px rgba(0,0,0,.3);padding:16px;animation:confirm-in .16s cubic-bezier(.2,.8,.2,1)}
|
|
123
|
+
@keyframes confirm-in{from{opacity:0;transform:translateY(6px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}
|
|
124
|
+
@media (prefers-reduced-motion:reduce){.confirm-overlay,.confirm-card{animation:none}}
|
|
125
|
+
.confirm-card p{margin:0 0 16px;font-size:13px;line-height:1.6;color:var(--text)}
|
|
126
|
+
.confirm-actions{display:flex;justify-content:flex-end;gap:8px}
|
|
127
|
+
.confirm-actions button{font:inherit;font-size:13px;border:1px solid var(--border);background:transparent;color:var(--text);border-radius:7px;padding:6px 12px;cursor:pointer}
|
|
128
|
+
.confirm-actions button:hover{background:var(--bg)}
|
|
129
|
+
.confirm-actions button.primary{border-color:var(--accent);background:var(--accent);color:#fff}
|
|
130
|
+
.confirm-actions button.primary:hover{filter:brightness(1.06)}
|
|
131
|
+
.confirm-actions button.ghost{border-color:transparent}
|
|
132
|
+
/* Floating copy/export bar, deliberately living INSIDE this page rather than
|
|
133
|
+
in the host toolbar: a host toolbar click arrives here as a postMessage,
|
|
134
|
+
which is not a user activation for this document, so the async Clipboard
|
|
135
|
+
API and execCommand('copy') both refuse to run until the user happens to
|
|
136
|
+
click the page first. A real in-page click is a genuine user gesture, so
|
|
137
|
+
every copy/export below works on the first try. */
|
|
138
|
+
.actions{position:absolute;right:32px;bottom:32px;z-index:8;display:flex;align-items:center;gap:3px;padding:5px;border-radius:11px;background:var(--card);border:1px solid var(--border);box-shadow:0 6px 20px rgba(0,0,0,.18);transition:gap .15s ease}
|
|
139
|
+
.actions[hidden]{display:none}
|
|
140
|
+
.actions button{position:relative;display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:0;border-radius:7px;background:transparent;color:var(--text);font:inherit;cursor:pointer;white-space:nowrap}
|
|
141
|
+
.actions button:hover:not(:disabled){background:var(--finch-bg-hover,var(--bg))}
|
|
142
|
+
.actions button[data-tooltip]::after{content:attr(data-tooltip);position:absolute;left:50%;bottom:calc(100% + 8px);z-index:20;padding:6px 8px;border:1px solid var(--border);border-radius:7px;background:linear-gradient(135deg,color-mix(in srgb,var(--card) 96%,#fff),var(--bg));box-shadow:0 5px 16px rgba(0,0,0,.2);color:var(--text);font-size:12px;line-height:1;white-space:nowrap;pointer-events:none;opacity:0;transform:translate(-50%,3px);transition:opacity .16s ease,transform .16s ease}
|
|
143
|
+
.actions button[data-tooltip]:hover::after{opacity:1;transform:translate(-50%,0)}
|
|
144
|
+
/* Center tooltips over ordinary buttons; at either end of the floating bar,
|
|
145
|
+
grow them inward instead of letting a long label escape the panel. */
|
|
146
|
+
.actions button:first-child[data-tooltip]::after{left:0;transform:translateY(3px)}
|
|
147
|
+
.actions button:first-child[data-tooltip]:hover::after{transform:translateY(0)}
|
|
148
|
+
.actions button:last-child[data-tooltip]::after{left:auto;right:0;transform:translateY(3px)}
|
|
149
|
+
.actions button:last-child[data-tooltip]:hover::after{transform:translateY(0)}
|
|
150
|
+
.actions button:disabled{opacity:.45;cursor:default}
|
|
151
|
+
.actions .sep{width:1px;height:18px;background:var(--border);margin:0 3px}
|
|
152
|
+
.actions svg{width:16px;height:16px;flex:none}
|
|
153
|
+
.actions .act-toggle{padding:0 6px;color:var(--muted)}
|
|
154
|
+
.actions .act-toggle svg{transition:transform .15s ease;transform:rotate(180deg)}
|
|
155
|
+
/* Collapsed = only the WeChat-copy action (icon only, label hidden) plus the
|
|
156
|
+
toggle remain visible; every other button/separator is a one-off export
|
|
157
|
+
action that most sessions never touch, so hiding them by default keeps
|
|
158
|
+
the floating bar from covering preview content. */
|
|
159
|
+
.actions.collapsed [data-collapsible]{display:none}
|
|
160
|
+
.actions.collapsed #actCopyWx{padding:0 7px}
|
|
161
|
+
.actions.collapsed #actCopyWx .act-label{display:none}
|
|
162
|
+
.actions.collapsed .act-toggle svg{transform:rotate(0deg)}
|
|
163
|
+
|
|
164
|
+
/* Full-screen App View shell. The same document remains a compact, host-toolbar-
|
|
165
|
+
driven Panel App until finch:env explicitly identifies the appView surface. */
|
|
166
|
+
.app-toolbar{height:48px;flex:none;align-items:center;gap:12px;padding:7px 12px;border-bottom:1px solid var(--border);background:var(--card)}
|
|
167
|
+
body.app-view .app-toolbar{display:flex!important}
|
|
168
|
+
.app-toolbar-group{display:flex;align-items:center;gap:6px;flex:none}
|
|
169
|
+
.app-toolbar-end{margin-left:auto}
|
|
170
|
+
.app-tool{height:32px;border:1px solid transparent;border-radius:8px;background:transparent;color:var(--text);font:12px var(--finch-font-body,system-ui);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;min-width:32px;padding:0 8px;gap:6px}
|
|
171
|
+
.app-tool svg{width:19px;height:19px;stroke-width:1.8}.app-tool:hover:not(:disabled){background:var(--finch-bg-hover,var(--bg))}
|
|
172
|
+
.app-tool:disabled{opacity:.4;cursor:default}.app-tool.checked{color:var(--text);background:var(--finch-bg-active,color-mix(in srgb,var(--text) 10%,transparent))}.app-tool.dirty{color:var(--accent);background:transparent}
|
|
173
|
+
.app-tool-label{min-width:auto;padding:0 9px}.app-menu-wrap{position:relative}.app-menu{position:absolute;z-index:32;top:calc(100% + 7px);right:0;min-width:174px;padding:5px;border:1px solid var(--border);border-radius:9px;background:var(--card);box-shadow:0 2px 8px rgba(0,0,0,.12)}
|
|
174
|
+
.app-menu button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:12px;padding:7px 9px;border:0;border-radius:6px;background:transparent;color:var(--text);font:12px var(--finch-font-body,system-ui);text-align:left;cursor:pointer}.app-menu button:hover{background:var(--finch-bg-hover,var(--bg))}.app-menu button.checked{color:var(--text);background:var(--finch-bg-active,color-mix(in srgb,var(--text) 10%,transparent));font-weight:600}.app-menu hr{margin:5px 3px;border:0;border-top:1px solid var(--border)}
|
|
175
|
+
.app-document-meta{min-width:0;display:flex;flex-direction:column;gap:1px}.app-document-meta strong,.app-document-meta span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.app-document-meta strong{font-size:13px}.app-document-meta span{max-width:34vw;color:var(--muted);font-size:11px}
|
|
176
|
+
body.app-view .stage{display:grid;grid-template-columns:minmax(0,1fr) 6px var(--preview-width,480px);background:var(--bg)}
|
|
177
|
+
body.app-view .pane{position:relative;inset:auto;min-width:0;min-height:0}
|
|
178
|
+
body.app-view .pane.active{display:block}
|
|
179
|
+
body.app-view.has-document #editPane,body.app-view.has-document #previewPane{display:block}
|
|
180
|
+
body.app-view #editPane{border-right:1px solid var(--border)}
|
|
181
|
+
body.app-view .preview-shell{padding:20px;display:flex;justify-content:center;align-items:stretch}
|
|
182
|
+
body.app-view #article{width:100%;border:1px solid var(--border)}
|
|
183
|
+
body.app-view.preview-hidden .stage{grid-template-columns:minmax(0,1fr)}body.app-view.preview-hidden #previewPane,body.app-view.preview-hidden .preview-resizer{display:none!important}
|
|
184
|
+
@keyframes cm-ai-spin{to{transform:rotate(360deg)}}
|
|
185
|
+
.preview-resizer{position:relative;z-index:10;width:6px;cursor:col-resize;background:var(--border);opacity:0;transition:opacity .15s ease}.preview-resizer:hover,.preview-resizer.active{opacity:1}
|
|
186
|
+
.app-tool{position:relative}.app-tool[data-tooltip]::after{content:attr(data-tooltip);position:absolute;left:50%;top:calc(100% + 8px);z-index:40;padding:6px 8px;border:1px solid var(--border);border-radius:7px;background:var(--card);box-shadow:0 5px 16px rgba(0,0,0,.2);color:var(--text);font-size:12px;line-height:1;white-space:nowrap;pointer-events:none;opacity:0;transform:translate(-50%,-3px);transition:opacity .16s ease,transform .16s ease}.app-tool[data-tooltip]:hover::after{opacity:1;transform:translate(-50%,0)}
|
|
187
|
+
body.app-view .empty{grid-column:1/-1;background:var(--bg);padding:54px 38px}
|
|
188
|
+
body.app-view .home{max-width:1040px}body.app-view #emptyNew{display:none}
|
|
189
|
+
body.app-view .actions{right:28px;bottom:28px}
|
|
190
|
+
.library-backdrop{position:fixed;z-index:39;inset:0;background:rgba(0,0,0,.24)}
|
|
191
|
+
.library-drawer{position:fixed;z-index:40;top:0;bottom:0;left:0;width:min(390px,88vw);padding:0;background:var(--card);border-right:1px solid var(--border);transform:translateX(-102%);transition:transform .18s ease;display:flex;flex-direction:column}
|
|
192
|
+
.library-drawer.open{transform:translateX(0)}.library-drawer>header{height:58px;display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-bottom:1px solid var(--border)}
|
|
193
|
+
.library-drawer>header div{display:flex;flex-direction:column}.library-drawer>header span{font-size:11px;color:var(--muted)}.library-drawer>header button{border:0;background:transparent;color:var(--muted);font-size:24px;cursor:pointer}
|
|
194
|
+
/* Directory-like library: every source group is a native collapsible
|
|
195
|
+
details/summary, while rows separate a readable title from the quieter
|
|
196
|
+
path + time metadata instead of cramming all three into one text run. */
|
|
197
|
+
.library-groups{flex:1;overflow:auto;padding:10px 12px 20px}
|
|
198
|
+
.library-group{margin:0 0 8px;border:0;border-radius:9px}.library-group[open]{background:color-mix(in srgb,var(--text) 2%,transparent)}
|
|
199
|
+
.library-group summary{display:flex;align-items:center;gap:7px;min-height:32px;padding:0 9px;list-style:none;color:var(--muted);font-size:12px;font-weight:600;cursor:pointer;user-select:none;touch-action:none}.library-group summary::-webkit-details-marker{display:none}.library-group summary::before{content:'›';color:var(--muted);font-size:17px;font-weight:400;line-height:1;transform:rotate(0deg);transition:transform .14s ease}.library-group[open] summary::before{transform:rotate(90deg)}.library-group summary:hover{background:var(--finch-bg-hover,var(--bg));border-radius:8px}.library-group[data-draggable="true"] summary{cursor:grab}.library-group.dragging{opacity:.32}
|
|
200
|
+
.library-group-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--muted)}.library-group-count{margin-left:auto;color:var(--muted);font-size:11px;font-weight:400}.library-items{padding:0 4px 4px}
|
|
201
|
+
/* Custom drag ghost: a floating clone that follows the pointer, replacing
|
|
202
|
+
the browser's native (unstylable) HTML5 drag screenshot. Collapsed
|
|
203
|
+
(.library-items hidden) since drags always start from a collapsed row. */
|
|
204
|
+
.library-drag-ghost{position:fixed;left:0;top:0;z-index:9999;pointer-events:none;margin:0;border-radius:10px;background:var(--card,var(--bg));box-shadow:0 10px 28px color-mix(in srgb,#000 22%,transparent),0 2px 6px color-mix(in srgb,#000 12%,transparent);transform:scale(1.02) rotate(-1deg);opacity:.96;overflow:hidden}
|
|
205
|
+
.library-drag-ghost .library-items{display:none}.library-drag-ghost summary{cursor:grabbing}
|
|
206
|
+
.library-row{display:block;width:100%;padding:9px 9px 8px;border:0;border-radius:7px;background:transparent;color:var(--text);text-align:left;cursor:pointer}.library-row:hover{background:var(--finch-bg-hover,var(--bg))}.library-row-main,.library-row strong,.library-row-meta,.library-row-path{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.library-row strong{font-size:13px;line-height:1.35;font-weight:600}.library-row-meta{display:flex;align-items:center;gap:7px;margin-top:3px;color:var(--muted);font-size:11px;line-height:1.3}.library-row-path,.library-row time{color:var(--muted)}.library-row-path{flex:1}.library-row time{flex:none;opacity:.86}
|
|
207
|
+
@media(max-width:850px){body.app-view .stage{grid-template-columns:minmax(0,1fr) 6px min(var(--preview-width,480px),40vw)}.app-document-meta span{display:none}.app-toolbar{gap:6px}.app-tool-label{padding:0 8px}}
|