sol-components 2.1.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.
Files changed (150) hide show
  1. package/README.md +7 -0
  2. package/core/activate.js +27 -0
  3. package/core/adopt.js +71 -0
  4. package/core/auth-core.js +73 -0
  5. package/core/auth-fetch.js +154 -0
  6. package/core/component-mount.js +110 -0
  7. package/core/defaults.js +48 -0
  8. package/core/define.js +15 -0
  9. package/core/display-target.js +166 -0
  10. package/core/edit-placements.js +28 -0
  11. package/core/editor-self.js +127 -0
  12. package/core/editor.js +162 -0
  13. package/core/events.js +27 -0
  14. package/core/extension-points.js +189 -0
  15. package/core/form-utils.js +210 -0
  16. package/core/from-query.js +138 -0
  17. package/core/from-rdf.js +52 -0
  18. package/core/here.js +33 -0
  19. package/core/include-core.js +73 -0
  20. package/core/inrupt-global.js +18 -0
  21. package/core/menu-consumer.js +41 -0
  22. package/core/menu-rdf.js +154 -0
  23. package/core/pod-ops.js +392 -0
  24. package/core/pod-registry.js +82 -0
  25. package/core/popup-proxy.js +255 -0
  26. package/core/rdf-core.js +280 -0
  27. package/core/rdf-render.js +136 -0
  28. package/core/rdf-utils.js +411 -0
  29. package/core/rdf.js +154 -0
  30. package/core/services.js +106 -0
  31. package/core/shape-to-form.js +741 -0
  32. package/core/sparql-safety.js +20 -0
  33. package/core/utils.js +196 -0
  34. package/dist/importmap-cdn.json +49 -0
  35. package/dist/importmap-local.json +49 -0
  36. package/dist/sol-loader.manifest.json +140 -0
  37. package/dist/vendor/@comunica-query-sparql.js +137851 -0
  38. package/dist/vendor/@inrupt-solid-client-authn-browser.js +7503 -0
  39. package/dist/vendor/dompurify.js +1476 -0
  40. package/dist/vendor/ical.js.js +9739 -0
  41. package/dist/vendor/marked.js +85 -0
  42. package/dist/vendor/n3.js +14670 -0
  43. package/dist/vendor/rdf-validate-shacl.js +6970 -0
  44. package/dist/vendor/rdflib.js +35172 -0
  45. package/dist/vendor/solid-logic.js +6819 -0
  46. package/dist/vendor/solid-ui.js +21945 -0
  47. package/node/sol-form.js +133 -0
  48. package/node/sol-include.js +55 -0
  49. package/node/sol-login.js +632 -0
  50. package/node/sol-menu.js +639 -0
  51. package/node/sol-query.js +116 -0
  52. package/package.json +133 -0
  53. package/web/menu-from-rdf.js +23 -0
  54. package/web/scripts/prefs.js +25 -0
  55. package/web/sol-accordion.js +114 -0
  56. package/web/sol-basic.js +50 -0
  57. package/web/sol-breadcrumb.js +131 -0
  58. package/web/sol-button.js +244 -0
  59. package/web/sol-calendar.js +465 -0
  60. package/web/sol-default.js +118 -0
  61. package/web/sol-dropdown-button.js +222 -0
  62. package/web/sol-feed.js +1336 -0
  63. package/web/sol-form.js +949 -0
  64. package/web/sol-full.js +43 -0
  65. package/web/sol-gallery.js +303 -0
  66. package/web/sol-include.js +246 -0
  67. package/web/sol-live-edit.js +415 -0
  68. package/web/sol-login.js +856 -0
  69. package/web/sol-menu.js +593 -0
  70. package/web/sol-modal.js +377 -0
  71. package/web/sol-pod-extras.js +17 -0
  72. package/web/sol-pod-ops.js +680 -0
  73. package/web/sol-pod.js +1039 -0
  74. package/web/sol-query.js +546 -0
  75. package/web/sol-rolodex.js +95 -0
  76. package/web/sol-search.js +402 -0
  77. package/web/sol-settings.js +199 -0
  78. package/web/sol-solidos.js +93 -0
  79. package/web/sol-tabs.js +445 -0
  80. package/web/sol-time.js +194 -0
  81. package/web/sol-tree-edit.js +492 -0
  82. package/web/sol-wac.js +456 -0
  83. package/web/sol-weather.js +337 -0
  84. package/web/sol-window.js +142 -0
  85. package/web/styles/buttons-css.js +108 -0
  86. package/web/styles/help.css +242 -0
  87. package/web/styles/root.css +112 -0
  88. package/web/styles/sol-accordion-css.js +97 -0
  89. package/web/styles/sol-calendar-css.js +154 -0
  90. package/web/styles/sol-feed-css.js +475 -0
  91. package/web/styles/sol-form-css.js +471 -0
  92. package/web/styles/sol-gallery-css.js +181 -0
  93. package/web/styles/sol-include-css.js +95 -0
  94. package/web/styles/sol-live-edit-css.js +84 -0
  95. package/web/styles/sol-live-edit.css +101 -0
  96. package/web/styles/sol-login-css.js +116 -0
  97. package/web/styles/sol-menu-css.js +145 -0
  98. package/web/styles/sol-modal-css.js +134 -0
  99. package/web/styles/sol-pod-css.js +187 -0
  100. package/web/styles/sol-pod-modal-css.js +203 -0
  101. package/web/styles/sol-query-css.js +140 -0
  102. package/web/styles/sol-query-help.css +267 -0
  103. package/web/styles/sol-query-one-pager.css +67 -0
  104. package/web/styles/sol-search-css.js +157 -0
  105. package/web/styles/sol-solidos-css.js +7 -0
  106. package/web/styles/sol-tabs-css.js +114 -0
  107. package/web/styles/sol-time-css.js +30 -0
  108. package/web/styles/sol-wac-css.js +73 -0
  109. package/web/styles/sol-weather-css.js +59 -0
  110. package/web/styles/solid-logo.svg +9 -0
  111. package/web/styles/view-accordion-css.js +66 -0
  112. package/web/styles/view-anchorlist-css.js +22 -0
  113. package/web/styles/view-autocomplete-css.js +59 -0
  114. package/web/styles/view-rolodex-css.js +102 -0
  115. package/web/styles/view-select-css.js +21 -0
  116. package/web/utils/calendar-fetch.js +388 -0
  117. package/web/utils/code-mirror-editor.js +82 -0
  118. package/web/utils/commons-fetch.js +108 -0
  119. package/web/utils/feed-edit.js +159 -0
  120. package/web/utils/feed-edit.smoke.mjs +74 -0
  121. package/web/utils/feed-fetch.js +573 -0
  122. package/web/utils/live-edit-help/csv.js +64 -0
  123. package/web/utils/live-edit-help/graphviz.js +41 -0
  124. package/web/utils/live-edit-help/jsonld.js +55 -0
  125. package/web/utils/live-edit-help/markdown.js +52 -0
  126. package/web/utils/live-edit-help/mermaid.js +48 -0
  127. package/web/utils/live-edit-help/turtle.js +85 -0
  128. package/web/utils/rdf-config.js +125 -0
  129. package/web/utils/renderers/csv.js +124 -0
  130. package/web/utils/renderers/d3-force.js +82 -0
  131. package/web/utils/renderers/graphviz.js +13 -0
  132. package/web/utils/renderers/html.js +10 -0
  133. package/web/utils/renderers/jsonld.js +63 -0
  134. package/web/utils/renderers/markdown.js +19 -0
  135. package/web/utils/renderers/mermaid.js +54 -0
  136. package/web/utils/renderers/turtle.js +51 -0
  137. package/web/utils/sol-query-triple-patterns.js +151 -0
  138. package/web/utils/sol-query-ui.js +250 -0
  139. package/web/utils/sol-query-views.js +32 -0
  140. package/web/views/_helpers.js +34 -0
  141. package/web/views/accordion.js +133 -0
  142. package/web/views/anchorlist.js +59 -0
  143. package/web/views/auto-complete.js +183 -0
  144. package/web/views/dl.js +38 -0
  145. package/web/views/list.js +19 -0
  146. package/web/views/menu.js +56 -0
  147. package/web/views/rolodex.js +126 -0
  148. package/web/views/select.js +79 -0
  149. package/web/views/table.js +73 -0
  150. package/web/views/tabs.js +57 -0
@@ -0,0 +1,475 @@
1
+ // Styles for <sol-feed>'s shadow root. Exports the raw `CSS` string plus a
2
+ // constructable `sheet` (null in non-DOM envs) — the same shape as the
3
+ // other web/styles/*-css.js modules. All colours and metrics reference the
4
+ // shared design tokens so the component themes with the rest of the suite.
5
+ import { sheetFrom } from '../../core/adopt.js';
6
+
7
+ export const CSS = `
8
+ :host {
9
+ display: flex;
10
+ flex-direction: column;
11
+ /* Respect whatever height the container gives us; with no container
12
+ height, fall back to this viewport cap. Either way the article list
13
+ scrolls inside the component and never overflows its container. */
14
+ height: 100%;
15
+ max-height: 100vh;
16
+ font-family: var(--font-ui, system-ui, -apple-system, sans-serif);
17
+ font-size: var(--font-size, 20px);
18
+ color: var(--text, #212121);
19
+ }
20
+ * { box-sizing: border-box; }
21
+
22
+ /* The component owns its own scrolling: each view puts the scrollbar on
23
+ its own list/grid, so the status line and news picker stay pinned. */
24
+ .sol-feed { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
25
+
26
+ /* ── status / loading / empty ───────────────────────────────────────── */
27
+ .sol-feed-status {
28
+ flex: 0 0 auto;
29
+ padding: .5rem .75rem;
30
+ color: var(--text-muted, #7f8c8d);
31
+ font-size: .85em;
32
+ }
33
+ .sol-feed-status[data-error] { color: var(--error, #e74c3c); }
34
+ .sol-feed-empty {
35
+ padding: 1rem .75rem;
36
+ color: var(--text-muted, #7f8c8d);
37
+ font-style: italic;
38
+ }
39
+
40
+ /* ── feed + topic link lists ─────────────────────────────────────────── */
41
+ /* Both feed and topic stack vertically and fill the host. Topic puts a
42
+ fixed-height source pane on top (~5 entries, scrolling for more) and
43
+ the article list below. */
44
+ .sol-feed-list {
45
+ display: flex;
46
+ flex-direction: column;
47
+ flex: 1 1 auto;
48
+ min-height: 0;
49
+ }
50
+ .sol-feed-list.feed { gap: 0; }
51
+ .sol-feed-list.topic { gap: .9rem; }
52
+
53
+ .feed-sources,
54
+ .feed-items {
55
+ margin: 0;
56
+ border: 1px solid var(--border, #d0d0d0);
57
+ border-radius: 6px;
58
+ background: var(--surface, #fff);
59
+ }
60
+ /* Topic view: a touch darker border so the two floating panels stand
61
+ apart from the page; the slightly taller .9rem gap above gives them
62
+ breathing room. */
63
+ .sol-feed-list.topic .feed-sources,
64
+ .sol-feed-list.topic .feed-items { border-color: #6e6e6e; }
65
+ /* topic: the sources pane shows ~5 entries; the rest scroll inside it */
66
+ .feed-sources { flex: 0 0 11rem; overflow: auto; }
67
+ /* the articles list fills whatever height is left in the column */
68
+ .feed-items {
69
+ list-style: none;
70
+ padding: 0;
71
+ flex: 1 1 auto;
72
+ min-height: 0;
73
+ overflow: auto;
74
+ }
75
+
76
+ .feed-source-list { list-style: none; margin: 0; padding: 0; }
77
+ .feed-source-list li + li,
78
+ .feed-items li + li { border-top: 1px solid var(--border, #eee); }
79
+
80
+ .feed-link {
81
+ display: block;
82
+ padding: .45rem .7rem;
83
+ /* Article and source links use the theme's link colour (themed
84
+ in root.css for light + dark), with --accent as the legacy
85
+ fallback for pages that load sol-feed without root.css. */
86
+ color: var(--link, var(--accent, #2980b9));
87
+ text-decoration: none;
88
+ line-height: 1.35;
89
+ }
90
+ .feed-link:hover { background: var(--hover, #eaf2fb); text-decoration: underline; }
91
+ .feed-link.selected {
92
+ background: var(--focus-bg, #ebf5fb);
93
+ /* Text on the selection fill. Defaults to the normal link colour so
94
+ existing consumers are unchanged; hosts that tint --focus-bg with
95
+ a strong colour can set --selected-fg for a readable contrast. */
96
+ color: var(--selected-fg, var(--link, var(--accent, #2980b9)));
97
+ font-weight: 600;
98
+ }
99
+ .feed-link .feed-link-meta {
100
+ display: block;
101
+ font-size: .72em;
102
+ color: var(--text-muted, #7f8c8d);
103
+ font-weight: 400;
104
+ }
105
+
106
+ /* ── topics view ─────────────────────────────────────────────────────── */
107
+ /* A "newsstand": one column per topic across the top (each listing its
108
+ sources), with the shared .feed-articles card grid below. The columns
109
+ band reuses the all-view's darker top-bar tint; together with the
110
+ lighter articles strip they read as one two-tone panel. */
111
+ .sol-feed-list.topics { gap: 0; }
112
+ .feed-topic-columns {
113
+ flex: 0 0 auto;
114
+ display: flex;
115
+ flex-wrap: wrap;
116
+ align-items: flex-start;
117
+ gap: .6rem;
118
+ padding: .8rem .9rem;
119
+ background: var(--feed-top-bar-bg,
120
+ color-mix(in srgb, var(--bg, #f5f5f5) 75%, #000));
121
+ border-radius: var(--radius-md, 6px) var(--radius-md, 6px) 0 0;
122
+ }
123
+ .feed-topic-column {
124
+ flex: 1 1 12rem;
125
+ min-width: 0;
126
+ border: 1px solid var(--border, #d0d0d0);
127
+ border-radius: 6px;
128
+ background: var(--surface, #fff);
129
+ display: flex;
130
+ flex-direction: column;
131
+ overflow: hidden;
132
+ }
133
+ .feed-topic-head {
134
+ margin: 0;
135
+ padding: .4rem .7rem;
136
+ font-size: .74em;
137
+ font-weight: 700;
138
+ text-transform: uppercase;
139
+ letter-spacing: .04em;
140
+ color: var(--text-muted, #7f8c8d);
141
+ border-bottom: 1px solid var(--border, #d0d0d0);
142
+ }
143
+ /* Caps each column ~6 sources tall; the rest scroll within the column. */
144
+ .feed-topic-col-list { max-height: 14rem; overflow: auto; }
145
+
146
+ /* News cards: shorter boxes; the title tracks the host font size
147
+ (1em = --font-size, set by the text-size setter) rather than the
148
+ all-view's smaller .88em. Scoped to topics so other views are
149
+ unchanged. */
150
+ .sol-feed-list.topics .feed-card { aspect-ratio: 9 / 4; }
151
+ .sol-feed-list.topics .feed-card-title {
152
+ font-size: 1em;
153
+ -webkit-line-clamp: 3;
154
+ }
155
+
156
+ /* ── all view ───────────────────────────────────────────────────────── */
157
+ /* Two-tone defaults: a darker strip behind the top-bar (controls)
158
+ and a lighter strip behind the articles grid, both relative to
159
+ the page --bg via color-mix. Override via
160
+ --feed-top-bar-bg / --feed-articles-bg (or by re-styling the
161
+ "top-bar" / "articles" shadow parts from outside) when the
162
+ defaults don't suit. The two strips sit flush so they read as
163
+ one continuous two-tone panel, rounded at the outer corners. */
164
+ .feed-top-bar {
165
+ flex: 0 0 auto;
166
+ display: flex;
167
+ align-items: center;
168
+ gap: .6rem;
169
+ margin: 0;
170
+ padding: .8rem .9rem;
171
+ background: var(--feed-top-bar-bg,
172
+ color-mix(in srgb, var(--bg, #f5f5f5) 75%, #000));
173
+ border-radius: var(--radius-md, 6px) var(--radius-md, 6px) 0 0;
174
+ }
175
+ .feed-source-buttons {
176
+ flex: 1 1 auto;
177
+ display: flex;
178
+ flex-wrap: wrap;
179
+ gap: .35rem;
180
+ min-width: 0;
181
+ }
182
+ .feed-source-btn {
183
+ font: inherit;
184
+ font-size: .85em;
185
+ padding: .3rem .9rem;
186
+ border: 1px solid var(--border, #d0d0d0);
187
+ border-radius: 999px;
188
+ background: var(--surface, #fff);
189
+ color: var(--text, #212121);
190
+ cursor: pointer;
191
+ white-space: nowrap;
192
+ }
193
+ .feed-source-btn:hover { background: var(--hover, #eaf2fb); }
194
+ .feed-source-btn.selected {
195
+ background: var(--accent, #3498db);
196
+ color: #fff;
197
+ border-color: var(--accent, #3498db);
198
+ }
199
+ .feed-source-btn:focus-visible {
200
+ outline: 2px solid var(--accent, #3498db);
201
+ outline-offset: 2px;
202
+ }
203
+
204
+ .feed-picker-toggle {
205
+ font: inherit;
206
+ font-size: 1.2em;
207
+ line-height: 1;
208
+ padding: .25rem .5rem;
209
+ border: 1px solid var(--border, #d0d0d0);
210
+ border-radius: 6px;
211
+ background: var(--surface, #fff);
212
+ color: var(--text, #212121);
213
+ cursor: pointer;
214
+ }
215
+ .feed-picker-toggle:hover { background: var(--hover, #eaf2fb); }
216
+ .feed-picker-toggle:focus-visible {
217
+ outline: 2px solid var(--accent, #3498db);
218
+ outline-offset: 2px;
219
+ }
220
+
221
+ /* Two-column picker: left = instruction + topic fieldsets; right = the
222
+ "add topic / add source" forms. */
223
+ .feed-picker {
224
+ flex: 0 0 auto;
225
+ align-self: center;
226
+ width: 100%;
227
+ max-width: 1280px;
228
+ display: grid;
229
+ /* left column carries the topic fieldsets — give it room so each
230
+ fieldset can fit several checkboxes side-by-side. */
231
+ grid-template-columns: 2fr 1fr;
232
+ gap: 1rem;
233
+ margin: 2rem 0 1rem;
234
+ }
235
+ .feed-picker[hidden] { display: none; }
236
+ .feed-picker-left,
237
+ .feed-picker-right { display: flex; flex-direction: column; gap: .55rem; min-width: 0; }
238
+ .feed-picker-instruct {
239
+ margin: 0;
240
+ font-style: italic;
241
+ color: var(--text-muted, #7f8c8d);
242
+ font-size: .9em;
243
+ }
244
+ .feed-picker-note {
245
+ margin: 0;
246
+ font-size: .75em;
247
+ color: var(--text-muted, #7f8c8d);
248
+ }
249
+ .feed-picker-note[data-error] { color: var(--error, #e74c3c); }
250
+
251
+ /* Add-topic / add-feed forms are <form>s wrapping a <fieldset>; the
252
+ fieldset carries the visible chrome so its legend sits on the top
253
+ border, matching the .feed-topic source-picker boxes. */
254
+ .feed-add-wrap { margin: 0; }
255
+ .feed-add-form {
256
+ border: 1px solid var(--border, #d0d0d0);
257
+ border-radius: 6px;
258
+ background: var(--surface, #fff);
259
+ margin: 0;
260
+ padding: .2rem .8rem .55rem; /* match .feed-topic */
261
+ display: flex;
262
+ flex-direction: column;
263
+ gap: .35rem;
264
+ }
265
+ .feed-add-form legend {
266
+ font-size: .74em;
267
+ font-weight: 700;
268
+ text-transform: uppercase;
269
+ letter-spacing: .04em;
270
+ color: var(--text-muted, #7f8c8d);
271
+ padding: 0 .3rem;
272
+ }
273
+ .feed-add-form label {
274
+ display: flex;
275
+ flex-direction: column;
276
+ gap: .15rem;
277
+ font-size: .8em;
278
+ color: var(--text, #212121);
279
+ }
280
+ .feed-add-form input,
281
+ .feed-add-form select {
282
+ font: inherit;
283
+ font-size: .9em;
284
+ padding: .2rem .35rem;
285
+ border: 1px solid var(--border, #d0d0d0);
286
+ border-radius: 4px;
287
+ background: var(--surface, #fff);
288
+ color: var(--text, #212121);
289
+ }
290
+ .feed-add-form button[type="submit"] {
291
+ align-self: flex-end;
292
+ font: inherit;
293
+ font-size: .8em;
294
+ padding: .25rem .8rem;
295
+ border: 1px solid var(--accent, #3498db);
296
+ border-radius: 6px;
297
+ background: var(--accent, #3498db);
298
+ color: #fff;
299
+ cursor: pointer;
300
+ margin-top: .15rem;
301
+ }
302
+ .feed-add-form button[type="submit"]:hover { filter: brightness(.94); }
303
+ .feed-topic {
304
+ border: 1px solid var(--border, #d0d0d0);
305
+ border-radius: 6px;
306
+ background: var(--surface, #fff);
307
+ margin: 0;
308
+ padding: .2rem .8rem .5rem;
309
+ display: flex;
310
+ flex-wrap: wrap;
311
+ gap: .1rem 1.15rem;
312
+ }
313
+ .feed-topic legend {
314
+ font-size: .74em;
315
+ font-weight: 700;
316
+ text-transform: uppercase;
317
+ letter-spacing: .04em;
318
+ color: var(--text-muted, #7f8c8d);
319
+ padding: 0 .3rem;
320
+ }
321
+ .feed-topic label {
322
+ display: inline-flex;
323
+ align-items: center;
324
+ gap: .35rem;
325
+ font-size: .82em;
326
+ cursor: pointer;
327
+ padding: .1rem 0;
328
+ }
329
+ .feed-topic input { cursor: pointer; }
330
+
331
+ /* Articles container — a grid of horizontal cards for the active
332
+ feed. Sits flush against the top-bar above; together they form a
333
+ two-tone panel (darker strip / lighter strip) framed by rounded
334
+ outer corners. Override --feed-articles-bg to retint, or
335
+ re-style the "articles" shadow part from outside. */
336
+ .feed-articles {
337
+ flex: 1 1 auto;
338
+ min-height: 0;
339
+ overflow: auto;
340
+ display: grid;
341
+ grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
342
+ grid-auto-rows: min-content;
343
+ gap: 1rem;
344
+ padding: 1.4rem 1rem 1rem;
345
+ background: var(--feed-articles-bg,
346
+ color-mix(in srgb, var(--bg, #f5f5f5) 88%, #000));
347
+ border-radius: 0 0 var(--radius-md, 6px) var(--radius-md, 6px);
348
+ }
349
+
350
+ /* Horizontal card: image on the left, title on the right. The outer
351
+ dimensions are fixed so image-less cards keep the same footprint —
352
+ a coloured placeholder block stands in for the missing image. */
353
+ .feed-card {
354
+ display: flex;
355
+ flex-direction: row;
356
+ width: 17rem;
357
+ aspect-ratio: 3 / 2;
358
+ border-radius: 8px;
359
+ overflow: hidden;
360
+ background: var(--surface, #fff);
361
+ border: 1px solid var(--border, #d0d0d0);
362
+ box-shadow: 0 1px 4px var(--shadow, rgba(0,0,0,0.08));
363
+ text-decoration: none;
364
+ color: var(--text, #212121);
365
+ }
366
+ .feed-card-img {
367
+ flex: 0 0 6rem;
368
+ width: 6rem;
369
+ height: 100%;
370
+ object-fit: cover;
371
+ display: block;
372
+ }
373
+ /* Stand-in for cards without an image, same size as .feed-card-img. */
374
+ .feed-card.no-image::before {
375
+ content: '';
376
+ flex: 0 0 6rem;
377
+ background: linear-gradient(135deg, var(--accent, #3498db), var(--accent-dark, #2980b9));
378
+ }
379
+
380
+ .feed-card-title {
381
+ flex: 1 1 auto;
382
+ margin: 0;
383
+ padding: .55rem .7rem;
384
+ font-family: var(--font-ui, system-ui, sans-serif);
385
+ font-size: .88em;
386
+ font-weight: 400;
387
+ line-height: 1.3;
388
+ /* Article titles are link text — use the theme's link colour
389
+ (themed in root.css for light + dark) so they read as clickable
390
+ even though the whole card is the click target. */
391
+ color: var(--link, var(--accent, #2980b9));
392
+ overflow: hidden;
393
+ display: -webkit-box;
394
+ -webkit-line-clamp: 5;
395
+ -webkit-box-orient: vertical;
396
+ }
397
+
398
+ /* ── focus visibility ───────────────────────────────────────────────── */
399
+ .feed-link:focus-visible,
400
+ .feed-card:focus-visible,
401
+ .feed-topic input:focus-visible {
402
+ outline: 2px solid var(--accent, #3498db);
403
+ outline-offset: -2px;
404
+ }
405
+
406
+ /* ── editing affordances (view="topics" + [editable]) ────────────────── */
407
+ .feed-topic-headwrap { display: flex; align-items: center; gap: .25rem; }
408
+ .feed-topic-headwrap .feed-topic-head { flex: 1 1 auto; min-width: 0; }
409
+ .feed-topic-head.editable { cursor: text; border-radius: 4px; }
410
+ .feed-topic-head.editable:hover { background: var(--hover, rgba(0,0,0,.06)); }
411
+ .feed-add-source {
412
+ flex: 0 0 auto; width: 1.4em; height: 1.4em; line-height: 1; padding: 0;
413
+ border: 1px solid var(--border, #d0d0d0); border-radius: 5px; cursor: pointer;
414
+ background: var(--surface, #fff); color: var(--text-muted, #7f8c8d); font-size: .9em;
415
+ }
416
+ .feed-add-source:hover { background: var(--accent, #3498db); color: #fff; border-color: transparent; }
417
+ .feed-topic-rename {
418
+ width: 100%; font: inherit; font-weight: 700; padding: .15rem .3rem;
419
+ border: 1px solid var(--accent, #3498db); border-radius: 4px;
420
+ background: var(--bg, #fff); color: var(--text, #111);
421
+ }
422
+ .feed-add-form { display: flex; flex-direction: column; gap: .3rem; padding: .35rem .4rem; }
423
+ .feed-add-input {
424
+ font: inherit; font-size: .82em; padding: .3rem .4rem; border: 1px solid var(--border, #c0c0c0);
425
+ border-radius: 5px; background: var(--bg, #fff); color: var(--text, #111);
426
+ }
427
+ .feed-add-row { display: flex; gap: .3rem; }
428
+ .feed-add-row button {
429
+ font: inherit; font-size: .8em; padding: .25rem .6rem; border-radius: 5px; cursor: pointer;
430
+ border: 1px solid var(--border, #c0c0c0); background: var(--surface, #fff); color: inherit;
431
+ }
432
+ .feed-add-row button.primary { background: var(--accent, #3498db); color: #fff; border-color: transparent; }
433
+
434
+ .feed-source-list .editable-row { display: flex; align-items: center; gap: .15rem; }
435
+ .editable-row .feed-link { flex: 1 1 auto; min-width: 0; cursor: grab; }
436
+ .editable-row.dragging { opacity: .45; }
437
+ .feed-del {
438
+ flex: 0 0 auto; background: transparent; border: none; cursor: pointer; padding: 0 .25rem;
439
+ color: var(--text-muted, #9aa0a6); font-size: .85em; line-height: 1;
440
+ }
441
+ .feed-del:hover { color: var(--error, #e74c3c); }
442
+ .feed-topic-column.drop-target { outline: 2px dashed var(--accent, #3498db); outline-offset: -3px; border-radius: 6px; }
443
+
444
+ /* delete confirm (inline, replaces the row) */
445
+ .feed-del-confirm { display: flex; align-items: center; flex-wrap: wrap; gap: .3rem; padding: .15rem 0; width: 100%; }
446
+ .feed-del-q { flex: 1 1 100%; font-size: .8em; }
447
+ .feed-del-confirm button { font: inherit; font-size: .76em; padding: .15rem .55rem; border-radius: 5px; cursor: pointer; border: 1px solid var(--border, #c0c0c0); background: var(--surface, #fff); color: inherit; }
448
+ .feed-del-yes { background: var(--error, #e74c3c) !important; color: #fff !important; border-color: transparent !important; }
449
+
450
+ /* reorder insertion indicator (drop before/after a row) */
451
+ .editable-row.drop-before { box-shadow: inset 0 2px 0 0 var(--accent, #3498db); }
452
+ .editable-row.drop-after { box-shadow: inset 0 -2px 0 0 var(--accent, #3498db); }
453
+
454
+ /* deleted-bin view */
455
+ .feed-bin-bar { display: flex; align-items: center; gap: .8rem; padding: .5rem .7rem; border-bottom: 1px solid var(--border, #d0d0d0); }
456
+ .feed-bin-back { font: inherit; font-size: .85em; cursor: pointer; background: none; border: none; color: var(--link, #2980b9); padding: 0; }
457
+ .feed-bin-title { font-weight: 700; }
458
+ .feed-bin-list { padding: .5rem .7rem; }
459
+ .feed-bin-row { display: flex; align-items: center; gap: .5rem; padding: .25rem 0; }
460
+ .feed-bin-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
461
+ .feed-bin-restore-to { font: inherit; font-size: .82em; }
462
+ .feed-bin-restore {
463
+ font: inherit; font-size: .8em; padding: .2rem .6rem; border-radius: 5px; cursor: pointer;
464
+ border: 1px solid var(--border, #c0c0c0); background: var(--surface, #fff); color: inherit;
465
+ }
466
+ .feed-bin-restore:hover { background: var(--accent, #3498db); color: #fff; border-color: transparent; }
467
+ .feed-bin-purge {
468
+ font: inherit; font-size: .8em; padding: .2rem .6rem; border-radius: 5px; cursor: pointer;
469
+ border: 1px solid var(--error, #e74c3c); background: transparent; color: var(--error, #e74c3c);
470
+ }
471
+ .feed-bin-purge:hover { background: var(--error, #e74c3c); color: #fff; }
472
+
473
+ `;
474
+
475
+ export const sheet = sheetFrom(CSS);