Jaspers Hub

jaspers / screener-mcp

pluginOfficialReviewed★ 0↓ 33

Screener MCP: Screens US SEC filers by fundamentals, holders and insider data, then checks criteria in words against filings via the Jaspers screener server.

10 KB
1/* The screener's own CSS. A plugin view is an iframe of its own, so there is no Tailwind here:
2 plain classes, the house style by hand. Colours are the app's theme variables, so the view
3 follows its light and dark, each with its light value as the fallback for an app from before the
4 theme; the screener's own colours get their dark values at the end. Square everything, one grey
5 line, muted secondary text, 13px in the table, and no accent colour but the sign of a number
6 and the verdict of a run, which reuse the same two. */
7
8.sc-root {
9 position: relative;
10 display: flex;
11 flex-direction: column;
12 height: 100%;
13 width: 100%;
14 background: var(--jaspers-background, #fff);
15 color: var(--jaspers-foreground, #171717);
16 font: 13px/1.45 var(--jaspers-font-sans, system-ui, -apple-system, sans-serif);
17}
18
19/* The two bars: filters and counts above, paging and status below. */
20
21.sc-bar {
22 flex: 0 0 auto;
23 display: flex;
24 align-items: center;
25 flex-wrap: wrap;
26 gap: 6px;
27 padding: 6px 8px;
28 border-bottom: 1px solid var(--jaspers-border, #e5e5e5);
29}
30
31.sc-foot {
32 border-bottom: 0;
33 border-top: 1px solid var(--jaspers-border, #e5e5e5);
34}
35
36/* The element's close button sits over the top-right corner. */
37.sc-top {
38 padding-right: 40px;
39}
40
41/* The funnel: the universe, what each filter passes on its own, what passes all of them. */
42
43.sc-funnel {
44 gap: 2px 8px;
45 color: var(--jaspers-muted-foreground, #737373);
46}
47
48.sc-funnel b {
49 color: var(--jaspers-foreground, #171717);
50 font-weight: 600;
51 font-variant-numeric: tabular-nums;
52}
53
54.sc-step {
55 white-space: nowrap;
56}
57
58.sc-arrow {
59 color: #a3a3a3;
60}
61
62.sc-btn {
63 border: 1px solid var(--jaspers-border, #e5e5e5);
64 background: var(--jaspers-background, #fff);
65 color: var(--jaspers-foreground, #171717);
66 padding: 2px 8px;
67 cursor: pointer;
68}
69
70.sc-btn:hover:not(:disabled) {
71 background: var(--jaspers-muted, #f5f5f5);
72}
73
74.sc-btn:disabled {
75 color: #a3a3a3;
76 cursor: default;
77}
78
79.sc-chip {
80 display: inline-flex;
81 align-items: center;
82 gap: 4px;
83 border: 1px solid var(--jaspers-border, #e5e5e5);
84 background: var(--jaspers-muted, #fafafa);
85 padding: 1px 3px 1px 6px;
86 white-space: nowrap;
87}
88
89.sc-chip-label {
90 border: 0;
91 background: none;
92 color: inherit;
93 font: inherit;
94 padding: 0;
95 cursor: pointer;
96}
97
98.sc-chip-x {
99 border: 0;
100 background: none;
101 color: var(--jaspers-muted-foreground, #737373);
102 cursor: pointer;
103 padding: 0 3px;
104 line-height: 1;
105}
106
107.sc-chip-x:hover:not(:disabled) {
108 color: var(--jaspers-foreground, #171717);
109}
110
111.sc-chip-x:disabled {
112 cursor: default;
113}
114
115.sc-input {
116 border: 1px solid var(--jaspers-border, #e5e5e5);
117 background: var(--jaspers-background, #fff);
118 color: var(--jaspers-foreground, #171717);
119 padding: 2px 6px;
120 outline: none;
121}
122
123.sc-input:focus {
124 border-color: var(--jaspers-foreground, #171717);
125}
126
127.sc-count {
128 margin-left: auto;
129 color: var(--jaspers-muted-foreground, #737373);
130 white-space: nowrap;
131}
132
133.sc-status {
134 color: var(--jaspers-muted-foreground, #737373);
135 white-space: nowrap;
136}
137
138.sc-error {
139 color: var(--jaspers-destructive, #dc2626);
140 white-space: normal;
141}
142
143/* The table fills what is left and scrolls both ways; nothing else on the page moves. */
144
145.sc-table-wrap {
146 flex: 1 1 auto;
147 overflow: auto;
148}
149
150.sc-table {
151 border-collapse: collapse;
152 font-size: 13px;
153 width: max-content;
154 min-width: 100%;
155}
156
157.sc-table th,
158.sc-table td {
159 border-bottom: 1px solid var(--jaspers-border, #e5e5e5);
160 padding: 3px 8px;
161 white-space: nowrap;
162}
163
164.sc-table th {
165 position: sticky;
166 top: 0;
167 z-index: 1;
168 background: var(--jaspers-background, #fff);
169 text-align: left;
170 font-weight: 500;
171 color: var(--jaspers-muted-foreground, #737373);
172 padding: 0;
173}
174
175.sc-sort {
176 width: 100%;
177 border: 0;
178 background: none;
179 color: inherit;
180 font: inherit;
181 text-align: right;
182 padding: 4px 8px;
183 cursor: pointer;
184}
185
186.sc-sort:hover {
187 color: var(--jaspers-foreground, #171717);
188}
189
190.sc-num {
191 text-align: right;
192 font-variant-numeric: tabular-nums;
193}
194
195.sc-up {
196 color: var(--jaspers-positive, #15803d);
197}
198
199.sc-down {
200 color: var(--jaspers-negative, #b91c1c);
201}
202
203/* The company column stays put while the numbers scroll past it. */
204
205.sc-company {
206 position: sticky;
207 left: 0;
208 background: var(--jaspers-background, #fff);
209 z-index: 2;
210 padding: 3px 8px;
211 text-align: left;
212 min-width: 190px;
213}
214
215th.sc-company {
216 z-index: 3;
217 padding: 4px 8px;
218}
219
220.sc-ticker {
221 font-weight: 600;
222}
223
224.sc-name {
225 display: block;
226 color: var(--jaspers-muted-foreground, #737373);
227 max-width: 210px;
228 overflow: hidden;
229 text-overflow: ellipsis;
230}
231
232.sc-table tbody tr:hover td {
233 background: var(--jaspers-muted, #f5f5f5);
234}
235
236.sc-empty {
237 padding: 12px;
238 color: var(--jaspers-muted-foreground, #737373);
239}
240
241/* The filter dialog: a panel over the view, not a portal and not a window. */
242
243.sc-dialog {
244 position: absolute;
245 inset: 0;
246 z-index: 10;
247 display: flex;
248 align-items: flex-start;
249 justify-content: center;
250 padding: 10px;
251 background: rgba(23, 23, 23, 0.15);
252}
253
254.sc-dialog-panel {
255 display: flex;
256 flex-direction: column;
257 width: 100%;
258 max-width: 760px;
259 max-height: 100%;
260 background: var(--jaspers-background, #fff);
261 border: 1px solid var(--jaspers-border, #e5e5e5);
262 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
263}
264
265.sc-dialog-head,
266.sc-dialog-foot {
267 flex: 0 0 auto;
268 display: flex;
269 align-items: center;
270 gap: 8px;
271 padding: 6px 10px;
272}
273
274.sc-dialog-head {
275 border-bottom: 1px solid var(--jaspers-border, #e5e5e5);
276 font-weight: 600;
277}
278
279.sc-dialog-foot {
280 border-top: 1px solid var(--jaspers-border, #e5e5e5);
281 justify-content: flex-end;
282}
283
284.sc-dialog-body {
285 flex: 1 1 auto;
286 overflow: auto;
287 padding: 10px;
288}
289
290.sc-section {
291 margin-bottom: 12px;
292}
293
294.sc-label {
295 display: block;
296 margin-bottom: 4px;
297 color: var(--jaspers-muted-foreground, #737373);
298 font-size: 11px;
299 text-transform: uppercase;
300 letter-spacing: 0.04em;
301}
302
303.sc-check {
304 display: inline-flex;
305 align-items: center;
306 gap: 5px;
307 cursor: pointer;
308 user-select: none;
309}
310
311.sc-check input {
312 accent-color: var(--jaspers-primary, #171717);
313}
314
315/* One filter to a row: the field, how it is compared, the value in the unit the user types. */
316
317.sc-filter {
318 margin-bottom: 6px;
319}
320
321.sc-filter-row {
322 display: flex;
323 align-items: center;
324 flex-wrap: wrap;
325 gap: 4px;
326}
327
328.sc-filter-field {
329 flex: 0 1 230px;
330 min-width: 0;
331}
332
333.sc-filter-value {
334 flex: 1 1 110px;
335 min-width: 0;
336}
337
338.sc-hint {
339 margin: 2px 0 0;
340 color: var(--jaspers-muted-foreground, #737373);
341 font-size: 12px;
342}
343
344.sc-filter .sc-error,
345.sc-dialog-body > .sc-error {
346 margin: 2px 0 0;
347}
348
349.sc-tickers {
350 width: 100%;
351 resize: vertical;
352 font: inherit;
353}
354
355.sc-columns {
356 display: grid;
357 grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
358 gap: 2px 16px;
359}
360
361.sc-unit {
362 color: #a3a3a3;
363 white-space: nowrap;
364}
365
366/* The question chip carries a sentence; its text truncates and its cross stays. */
367.sc-chip.sc-q {
368 display: inline-flex;
369 align-items: center;
370 max-width: 480px;
371 min-width: 0;
372}
373.sc-q-text {
374 min-width: 0;
375 overflow: hidden;
376 text-overflow: ellipsis;
377 white-space: nowrap;
378}
379
380/* The qualitative stage: the criteria and their input on the left, the run on the right. */
381
382.sc-q-input {
383 flex: 1 1 220px;
384 min-width: 0;
385}
386
387.sc-run,
388.sc-verdicts {
389 display: inline-flex;
390 align-items: center;
391 flex-wrap: wrap;
392 gap: 6px;
393}
394
395.sc-run {
396 margin-left: auto;
397}
398
399.sc-primary {
400 border-color: var(--jaspers-foreground, #171717);
401 font-weight: 600;
402}
403
404.sc-toggle.sc-on {
405 border-color: currentColor;
406 background: var(--jaspers-muted, #f5f5f5);
407}
408
409/* A verdict is the one other thing with a colour: the sign's two, an amber, and the muted grey. */
410
411.sc-v-pass {
412 color: var(--jaspers-positive, #15803d);
413}
414
415.sc-v-fail {
416 color: var(--jaspers-negative, #b91c1c);
417}
418
419.sc-v-unclear {
420 color: #a16207;
421}
422
423.sc-v-unverified {
424 color: var(--jaspers-muted-foreground, #737373);
425 font-style: italic;
426}
427
428.sc-table th.sc-verdict-head {
429 padding: 4px 8px;
430 max-width: 140px;
431 overflow: hidden;
432 text-overflow: ellipsis;
433}
434
435.sc-verdict {
436 text-align: left;
437}
438
439.sc-mark {
440 color: var(--jaspers-destructive, #dc2626);
441 font-weight: 600;
442 cursor: help;
443}
444
445/* A ticker with a run's answer behind it opens that company's evidence. */
446
447.sc-ticker-open {
448 border: 0;
449 border-bottom: 1px dotted var(--jaspers-muted-foreground, #737373);
450 background: none;
451 color: inherit;
452 font: inherit;
453 font-weight: 600;
454 padding: 0;
455 cursor: pointer;
456}
457
458.sc-table tbody tr.sc-row-open td {
459 background: var(--jaspers-muted, #f5f5f5);
460}
461
462/* The evidence pane: under the table, as tall as it needs up to under half the view. */
463
464.sc-evidence {
465 flex: 0 1 auto;
466 display: flex;
467 flex-direction: column;
468 max-height: 45%;
469 min-height: 80px;
470 border-top: 1px solid var(--jaspers-border, #e5e5e5);
471}
472
473.sc-evidence-head {
474 flex: 0 0 auto;
475 display: flex;
476 align-items: center;
477 gap: 8px;
478 padding: 4px 8px;
479 border-bottom: 1px solid var(--jaspers-border, #e5e5e5);
480}
481
482.sc-evidence-body {
483 flex: 1 1 auto;
484 overflow: auto;
485 padding: 6px 10px 10px;
486}
487
488.sc-evidence-body p {
489 margin: 0 0 4px;
490 white-space: normal;
491}
492
493.sc-criterion {
494 padding: 6px 0;
495 border-bottom: 1px solid var(--jaspers-border, #e5e5e5);
496}
497
498.sc-criterion:last-child {
499 border-bottom: 0;
500}
501
502.sc-question {
503 font-weight: 600;
504}
505
506.sc-cite {
507 margin: 6px 0;
508 padding: 4px 8px;
509 border-left: 2px solid var(--jaspers-border, #e5e5e5);
510 background: var(--jaspers-muted, #fafafa);
511}
512
513.sc-cite .sc-quote {
514 margin: 0 0 2px;
515}
516
517.sc-cite .sc-cite-foot {
518 margin: 0;
519}
520
521.sc-cite-foot {
522 display: flex;
523 flex-wrap: wrap;
524 gap: 2px 10px;
525 color: var(--jaspers-muted-foreground, #737373);
526 font-size: 12px;
527}
528
529.sc-link {
530 border: 0;
531 background: none;
532 color: var(--jaspers-foreground, #171717);
533 font: inherit;
534 padding: 0;
535 text-decoration: underline;
536 cursor: pointer;
537}
538
539/* Dark values for the screener's own colours: faint text a step under the muted, and, on a page
540 already near black, a backdrop with more black to dim it and a shadow with more to show. */
541@media (prefers-color-scheme: dark) {
542 .sc-btn:disabled,
543 .sc-unit,
544 .sc-arrow {
545 color: #737373;
546 }
547
548 .sc-v-unclear {
549 color: #eab308;
550 }
551
552 .sc-dialog {
553 background: rgba(0, 0, 0, 0.5);
554 }
555
556 .sc-dialog-panel {
557 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
558 }
559}