paneltir 0.12.0 → 0.13.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/CLAUDE_MD_SNIPPET.md +16 -0
- package/INSTALL.md +2 -2
- package/README.md +1 -1
- package/bin/paneltir.mjs +18 -1
- package/dist/index.d.ts +632 -535
- package/dist/index.js +217 -41
- package/fingerprint.json +5 -5
- package/package.json +11 -25
- package/template/.claude/skills/panel/SKILL.md +50 -0
package/dist/index.d.ts
CHANGED
|
@@ -378,6 +378,12 @@ interface BoardMoveResult {
|
|
|
378
378
|
cardId: string;
|
|
379
379
|
fromColumnId: string;
|
|
380
380
|
toColumnId: string;
|
|
381
|
+
/**
|
|
382
|
+
* Where to insert the card in the destination column *once it has been
|
|
383
|
+
* taken out of wherever it was*. So a reducer removes the card and then
|
|
384
|
+
* inserts at exactly this index, with no arithmetic of its own — the
|
|
385
|
+
* adjustment for a card moving down its own column is already made.
|
|
386
|
+
*/
|
|
381
387
|
toIndex: number;
|
|
382
388
|
}
|
|
383
389
|
|
|
@@ -423,438 +429,118 @@ declare function Card$1({ cardId, columnId, index, title, priorityColor, tags, p
|
|
|
423
429
|
*/
|
|
424
430
|
declare function moveCardInColumns<TCard extends BoardCardData>(columns: BoardColumnData<TCard>[], move: BoardMoveResult): BoardColumnData<TCard>[];
|
|
425
431
|
|
|
426
|
-
interface
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
children: React.ReactNode;
|
|
430
|
-
ariaLabel?: string;
|
|
432
|
+
interface DropTarget {
|
|
433
|
+
columnId: string;
|
|
434
|
+
index: number;
|
|
431
435
|
}
|
|
432
436
|
/**
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
437
|
+
* What `onMove` is told, given where the drag began and where it ended.
|
|
438
|
+
*
|
|
439
|
+
* Both are rendered positions: the source card stays in its slot at reduced
|
|
440
|
+
* opacity while it is dragged, so `data-pt-card-index` counts it, and the
|
|
441
|
+
* placeholder is drawn *before* the card at `target.index`. `toIndex` is a
|
|
442
|
+
* different thing — the position to insert at once the card is taken out —
|
|
443
|
+
* and the two differ by exactly one whenever a card moves down its own
|
|
444
|
+
* column. Neither reducer allowed for that, so a card dropped before the
|
|
445
|
+
* fourth card landed after it: one slot lower than the placeholder had
|
|
446
|
+
* promised, every time, in both copies of the arithmetic. Adjusted here, once,
|
|
447
|
+
* where the numbers are still known to be rendered ones.
|
|
448
|
+
*
|
|
449
|
+
* `null` means the drop is where the card already was — its own slot, or the
|
|
450
|
+
* slot immediately after it, which is the same place once it is removed.
|
|
436
451
|
*/
|
|
437
|
-
declare function
|
|
438
|
-
interface DetailSectionProps {
|
|
439
|
-
title: string;
|
|
440
|
-
/** Clarifying phrase in lowercase, printed after an em dash. */
|
|
441
|
-
subtitle?: string;
|
|
442
|
-
children: React.ReactNode;
|
|
443
|
-
}
|
|
444
|
-
declare function DetailSection({ title, subtitle, children }: DetailSectionProps): React.JSX.Element;
|
|
452
|
+
declare function dropIndex(source: DropTarget, target: DropTarget): number | null;
|
|
445
453
|
|
|
446
454
|
/**
|
|
447
|
-
*
|
|
455
|
+
* A board is read in one language and written in another.
|
|
448
456
|
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
457
|
+
* This is the mechanism only — the types and the resolution. The panel's own
|
|
458
|
+
* wording lives with the panel: a library that shipped its copy would be a
|
|
459
|
+
* library deciding what a project's board says.
|
|
452
460
|
*/
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
};
|
|
457
|
-
/** Clears every dismissal, so the guidance can be asked for again. */
|
|
458
|
-
declare function resetGuide(): void;
|
|
459
|
-
interface GuideNoteProps {
|
|
460
|
-
/** Stable across releases: changing it makes the note reappear for everyone. */
|
|
461
|
-
id: string;
|
|
462
|
-
title: React.ReactNode;
|
|
463
|
-
children: React.ReactNode;
|
|
464
|
-
/** Wording of the dismiss button. Say what it does, not "OK". */
|
|
465
|
-
dismissLabel?: string;
|
|
466
|
-
/** Shown after the note, for the one thing it wants the reader to do. */
|
|
467
|
-
action?: {
|
|
468
|
-
label: string;
|
|
469
|
-
onClick: () => void;
|
|
470
|
-
};
|
|
471
|
-
/** Position in a sequence, so a first session reads as a tour, not a pile. */
|
|
472
|
-
step?: {
|
|
473
|
-
index: number;
|
|
474
|
-
total: number;
|
|
475
|
-
};
|
|
476
|
-
/** Told after the note is dismissed, so a sequence can move on. */
|
|
477
|
-
onDismissed?: () => void;
|
|
478
|
-
/**
|
|
479
|
-
* Whether to record the dismissal in localStorage. False when the caller
|
|
480
|
-
* keeps that answer itself, so the two cannot disagree.
|
|
481
|
-
*/
|
|
482
|
-
remember?: boolean;
|
|
483
|
-
}
|
|
484
|
-
declare function GuideNote({ id, title, children, dismissLabel, action, step, onDismissed, remember, }: GuideNoteProps): React.JSX.Element | null;
|
|
485
|
-
interface GuideTourProps {
|
|
486
|
-
/** Notes in order. Only the first undismissed one shows, so it reads as a tour. */
|
|
487
|
-
notes: Array<Omit<GuideNoteProps, 'step' | 'onDismissed'>>;
|
|
488
|
-
/**
|
|
489
|
-
* Which notes are already dismissed, when the caller keeps that somewhere
|
|
490
|
-
* durable. Leave it out and each reader's browser remembers, which is the
|
|
491
|
-
* right home for a fact about one browser — and the wrong one for a panel
|
|
492
|
-
* with a single owner, who would be told the same tour again on a new
|
|
493
|
-
* machine. Passing it does not switch the browser off: a note dismissed
|
|
494
|
-
* here is written to both, because a caller whose store needs saving would
|
|
495
|
-
* otherwise lose the dismissal on reload.
|
|
496
|
-
*/
|
|
497
|
-
dismissed?: string[];
|
|
498
|
-
/** Told when a note is dismissed. Required for `dismissed` to mean anything. */
|
|
499
|
-
onDismiss?: (id: string) => void;
|
|
500
|
-
}
|
|
461
|
+
type Lang = 'en' | 'es';
|
|
462
|
+
declare const LANGS: Lang[];
|
|
463
|
+
declare const LANG_LABELS: Record<Lang, string>;
|
|
501
464
|
/**
|
|
502
|
-
*
|
|
465
|
+
* A string, or the same string per language.
|
|
503
466
|
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
467
|
+
* A plain string is a claim: this text is right in either language — true for
|
|
468
|
+
* a product name, a number, a file id, and almost never for prose. An object
|
|
469
|
+
* is the honest shape for a sentence, and one that carries a single language
|
|
470
|
+
* says "written, not translated yet", which is exactly what the next session
|
|
471
|
+
* needs to know.
|
|
507
472
|
*/
|
|
508
|
-
|
|
473
|
+
type Text = string | Partial<Record<Lang, string>>;
|
|
474
|
+
declare function text(value: Text | undefined, lang: Lang): string;
|
|
475
|
+
/** True when a sentence exists in one language only. */
|
|
476
|
+
declare function untranslated(value: Text | undefined): boolean;
|
|
477
|
+
/**
|
|
478
|
+
* Writing replaces the language being typed and drops the other copy of that
|
|
479
|
+
* field. Deliberate: a translation of a sentence somebody just rewrote is a
|
|
480
|
+
* lie about what the card says, and the gap is the panel telling the next
|
|
481
|
+
* session what to fill in.
|
|
482
|
+
*/
|
|
483
|
+
declare function writeText(lang: Lang, value: string): Text;
|
|
509
484
|
|
|
510
|
-
type
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
* one, or marking the first seen silences the second; the same event must
|
|
515
|
-
* never change it, or it comes back every time the page loads.
|
|
516
|
-
*/
|
|
485
|
+
type Weight = 'primary' | 'secondary' | 'off';
|
|
486
|
+
type Severity = 'high' | 'medium' | 'low';
|
|
487
|
+
type SuggestionStatus = 'new' | 'doing' | 'done' | 'dismissed';
|
|
488
|
+
interface Goal {
|
|
517
489
|
id: string;
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
/** Shown as given, so the caller decides the format its readers expect. */
|
|
521
|
-
at?: string;
|
|
522
|
-
/** `attention` for something waiting on the reader, `info` for the rest. */
|
|
523
|
-
tone?: NotificationTone;
|
|
524
|
-
/** Where this leads. Called before the notification is marked seen. */
|
|
525
|
-
onOpen?: () => void;
|
|
490
|
+
label: Text;
|
|
491
|
+
weight: Weight;
|
|
526
492
|
}
|
|
527
|
-
interface
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
markAll: string;
|
|
532
|
-
/** Given the count, e.g. (n) => `${n} unread`. */
|
|
533
|
-
unread: (count: number) => string;
|
|
493
|
+
interface Constraint {
|
|
494
|
+
id: string;
|
|
495
|
+
label: Text;
|
|
496
|
+
on: boolean;
|
|
534
497
|
}
|
|
535
|
-
interface
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
* What this reader has already seen, when the caller keeps it somewhere
|
|
540
|
-
* durable. Leave it out and the bell remembers in localStorage, which is the
|
|
541
|
-
* right home for a fact about one browser.
|
|
542
|
-
*
|
|
543
|
-
* A panel with one password has one owner, and there "this reader" and "this
|
|
544
|
-
* project" are the same person — so a panel that saves its board can keep
|
|
545
|
-
* this in it and have it survive a new browser. That is the caller's call to
|
|
546
|
-
* make, not the component's, which is why both work.
|
|
547
|
-
*/
|
|
548
|
-
seen?: string[];
|
|
549
|
-
/**
|
|
550
|
-
* Told which ids were just seen. Required for `seen` to mean anything —
|
|
551
|
-
* though the bell also remembers in localStorage either way, so a caller
|
|
552
|
-
* that has not saved yet does not show the same notifications again.
|
|
553
|
-
*/
|
|
554
|
-
onSeen?: (ids: string[]) => void;
|
|
498
|
+
interface Reading {
|
|
499
|
+
date: string;
|
|
500
|
+
value: number;
|
|
501
|
+
note?: Text;
|
|
555
502
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
* and the text is selectable underneath it, rather than the other way round.
|
|
564
|
-
*
|
|
565
|
-
* The clipboard API is not always there. It needs a secure context and can be
|
|
566
|
-
* refused outright, and a copy button that silently does nothing is worse than
|
|
567
|
-
* no button at all: the reader walks away believing they have the text. So the
|
|
568
|
-
* failure falls back to selecting the block, and says which of the two
|
|
569
|
-
* happened.
|
|
570
|
-
*/
|
|
571
|
-
interface CopyBlockProps {
|
|
572
|
-
/** Exactly what lands on the clipboard. Shown verbatim. */
|
|
573
|
-
text: string;
|
|
574
|
-
/** What this block is, above it. */
|
|
575
|
-
label?: React.ReactNode;
|
|
576
|
-
/** Why the reader wants it, and anything they must know before pasting. */
|
|
577
|
-
note?: React.ReactNode;
|
|
578
|
-
/** Wording, so the kit stays language-agnostic. */
|
|
579
|
-
labels?: Partial<CopyBlockLabels>;
|
|
503
|
+
interface Metric {
|
|
504
|
+
id: string;
|
|
505
|
+
label: Text;
|
|
506
|
+
unit: string;
|
|
507
|
+
/** Which direction counts as better. A reading against it is what "drifting" means. */
|
|
508
|
+
goal: 'up' | 'down';
|
|
509
|
+
readings: Reading[];
|
|
580
510
|
}
|
|
581
|
-
interface
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
511
|
+
interface Strategy {
|
|
512
|
+
id: string;
|
|
513
|
+
title: Text;
|
|
514
|
+
thesis: Text;
|
|
515
|
+
horizon: Text;
|
|
516
|
+
/** 0–10, so three bars can be compared at a glance rather than read. */
|
|
517
|
+
effort: number;
|
|
518
|
+
risk: number;
|
|
519
|
+
upside: number;
|
|
520
|
+
moves: Text[];
|
|
521
|
+
}
|
|
522
|
+
interface Suggestion {
|
|
523
|
+
id: string;
|
|
524
|
+
area: string;
|
|
525
|
+
severity: Severity;
|
|
526
|
+
status: SuggestionStatus;
|
|
527
|
+
date: string;
|
|
528
|
+
text: Text;
|
|
586
529
|
}
|
|
587
|
-
declare function CopyBlock({ text, label, note, labels }: CopyBlockProps): React.JSX.Element;
|
|
588
|
-
|
|
589
530
|
/**
|
|
590
|
-
*
|
|
531
|
+
* A question the owner answers and Claude reads.
|
|
591
532
|
*
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
533
|
+
* This is the whole point of the analysis view. A board says what to do; a
|
|
534
|
+
* choice says how the project wants it done, in the owner's words, once —
|
|
535
|
+
* instead of being asked again on every card. Claude reads these before
|
|
536
|
+
* deciding anything, so answering one here is worth more than answering it
|
|
537
|
+
* in a message that scrolls away.
|
|
596
538
|
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
539
|
+
* `value` of null is not missing data. It means nobody has decided, which is
|
|
540
|
+
* information: Claude must not guess an answer to a question the owner has
|
|
541
|
+
* deliberately left open.
|
|
600
542
|
*/
|
|
601
|
-
|
|
602
|
-
interface SetupRequirement {
|
|
603
|
-
id: string;
|
|
604
|
-
/** What it is, in the reader's terms rather than the variable's. */
|
|
605
|
-
label: React.ReactNode;
|
|
606
|
-
status: SetupStatus;
|
|
607
|
-
/** The environment variable behind it, when there is one to name. */
|
|
608
|
-
variable?: string;
|
|
609
|
-
/** What it lets the panel do — so a missing one has a visible cost. */
|
|
610
|
-
enables?: React.ReactNode;
|
|
611
|
-
/** Exactly what to do, shown only when it is missing. */
|
|
612
|
-
fix?: React.ReactNode;
|
|
613
|
-
/**
|
|
614
|
-
* Information rather than a gate: drawn in the list, never walked as a step.
|
|
615
|
-
*
|
|
616
|
-
* Every other requirement here is an environment variable, and the
|
|
617
|
-
* walkthrough's one action — re-read the environment from the server —
|
|
618
|
-
* is what clears it. A requirement that action cannot clear parks the
|
|
619
|
-
* walkthrough on it for ever, and because `unknown` is judged across the
|
|
620
|
-
* whole list, one advisory row that nobody wired made every step answer a
|
|
621
|
-
* successful check with "the panel could not ask its own server". That is
|
|
622
|
-
* what the copied gate did the day it was added.
|
|
623
|
-
*/
|
|
624
|
-
advisory?: boolean;
|
|
625
|
-
}
|
|
626
|
-
interface SetupGuideProps {
|
|
627
|
-
requirements: SetupRequirement[];
|
|
628
|
-
/** Shown when everything is in place, instead of an empty list. */
|
|
629
|
-
readyMessage?: React.ReactNode;
|
|
630
|
-
/** Wording, so the kit stays language-agnostic. */
|
|
631
|
-
labels?: Partial<{
|
|
632
|
-
ok: string;
|
|
633
|
-
missing: string;
|
|
634
|
-
unknown: string;
|
|
635
|
-
whatToDo: string;
|
|
636
|
-
}>;
|
|
637
|
-
}
|
|
638
|
-
declare function SetupGuide({ requirements, readyMessage, labels }: SetupGuideProps): React.JSX.Element;
|
|
639
|
-
|
|
640
|
-
/**
|
|
641
|
-
* The setup, walked one step at a time.
|
|
642
|
-
*
|
|
643
|
-
* `SetupGuide` is the list: the right shape for somebody auditing a panel that
|
|
644
|
-
* already works. This is the other reader — the one who installed it twenty
|
|
645
|
-
* minutes ago, has four things missing and no idea which to do first. A wall
|
|
646
|
-
* of four is not four times as useful as one; it is the thing people close.
|
|
647
|
-
*
|
|
648
|
-
* Two rules hold it honest. It **checks rather than asks**: every step ends in
|
|
649
|
-
* a button that re-reads the environment from the server, so the reader is
|
|
650
|
-
* told whether it worked instead of ticking a box themselves. And it **never
|
|
651
|
-
* claims what it cannot see**: a panel that could not reach its own endpoint
|
|
652
|
-
* says the check failed, never that a variable is missing, because telling
|
|
653
|
-
* somebody to add a token they already added is the worse of the two wrong
|
|
654
|
-
* answers.
|
|
655
|
-
*
|
|
656
|
-
* There is no copy of the values here and nothing is written: the panel cannot
|
|
657
|
-
* set an environment variable, and a button that appeared to would be the same
|
|
658
|
-
* fault as one that appeared to install an update.
|
|
659
|
-
*/
|
|
660
|
-
interface SetupWizardLabels {
|
|
661
|
-
title: string;
|
|
662
|
-
/** `(index, total)` — "Step 2 of 5". */
|
|
663
|
-
progress: (index: number, total: number) => string;
|
|
664
|
-
/** Shown above the one step in hand. */
|
|
665
|
-
nowDo: string;
|
|
666
|
-
/** The button that re-reads the environment. */
|
|
667
|
-
check: string;
|
|
668
|
-
checking: string;
|
|
669
|
-
/** Said when a check ran and the step is still not in place. */
|
|
670
|
-
stillMissing: string;
|
|
671
|
-
/** Said when the panel could not ask its own server. */
|
|
672
|
-
cannotTell: string;
|
|
673
|
-
/** The heading over the finished steps. */
|
|
674
|
-
doneTitle: string;
|
|
675
|
-
/** Shown in place of a step when every requirement is in place. */
|
|
676
|
-
readyTitle: string;
|
|
677
|
-
readyBody: string;
|
|
678
|
-
/** The two things to try before trusting the gate, as list items. */
|
|
679
|
-
verify: string[];
|
|
680
|
-
/** Closes the wizard. */
|
|
681
|
-
close: string;
|
|
682
|
-
/** The escape hatch: heading, explanation, and the prompt to copy. */
|
|
683
|
-
stuckTitle: string;
|
|
684
|
-
stuckBody: string;
|
|
685
|
-
stuckPrompt: string;
|
|
686
|
-
copy: string;
|
|
687
|
-
copied: string;
|
|
688
|
-
}
|
|
689
|
-
interface SetupWizardProps {
|
|
690
|
-
/** In the order they are to be done. The caller owns that order. */
|
|
691
|
-
requirements: SetupRequirement[];
|
|
692
|
-
labels: SetupWizardLabels;
|
|
693
|
-
/** Re-reads the environment. Resolves when the answer has been applied. */
|
|
694
|
-
onCheck: () => Promise<void> | void;
|
|
695
|
-
/** Told when the reader closes it. */
|
|
696
|
-
onClose?: () => void;
|
|
697
|
-
}
|
|
698
|
-
declare function SetupWizard({ requirements, labels, onCheck, onClose }: SetupWizardProps): React.JSX.Element;
|
|
699
|
-
|
|
700
|
-
/**
|
|
701
|
-
* Where a reader is in the setup, as one answer instead of a list to read.
|
|
702
|
-
*
|
|
703
|
-
* `SetupGuide` draws every requirement at once, which is the right shape for
|
|
704
|
-
* somebody checking a panel that already works. It is the wrong shape for
|
|
705
|
-
* somebody who has just installed one: five things at once, four of them
|
|
706
|
-
* missing, and no order to do them in reads as a wall rather than as a first
|
|
707
|
-
* step. So the same requirements are walked one at a time, and this is the
|
|
708
|
-
* function that decides which one.
|
|
709
|
-
*
|
|
710
|
-
* The order the caller gives is the order they are done in — a token that
|
|
711
|
-
* cannot be scoped before the repository is named is a step in the wrong
|
|
712
|
-
* place, and that is the caller's knowledge, not this function's.
|
|
713
|
-
*/
|
|
714
|
-
interface WizardStep {
|
|
715
|
-
/** The requirement to show, or null when nothing is left to do. */
|
|
716
|
-
current: SetupRequirement | null;
|
|
717
|
-
/** 1-based, for "step 2 of 5". Zero when there is nothing left. */
|
|
718
|
-
index: number;
|
|
719
|
-
/** How many steps this setup has in total. */
|
|
720
|
-
total: number;
|
|
721
|
-
/** Every requirement that is in place, in the caller's order. */
|
|
722
|
-
done: SetupRequirement[];
|
|
723
|
-
/** True when every requirement is `ok`. */
|
|
724
|
-
ready: boolean;
|
|
725
|
-
/**
|
|
726
|
-
* True when the panel could not ask the server, so nothing is claimed.
|
|
727
|
-
*
|
|
728
|
-
* This is not "not ready": a panel that cannot reach its own endpoint knows
|
|
729
|
-
* nothing about the environment, and telling somebody to add a token they
|
|
730
|
-
* already added is worse than saying the check itself failed.
|
|
731
|
-
*/
|
|
732
|
-
unknown: boolean;
|
|
733
|
-
}
|
|
734
|
-
declare function wizardStep(requirements: SetupRequirement[]): WizardStep;
|
|
735
|
-
/**
|
|
736
|
-
* Whether this panel has never been worked, which is what "first install"
|
|
737
|
-
* means here.
|
|
738
|
-
*
|
|
739
|
-
* `paneltir init` writes a board with the columns already there and nothing
|
|
740
|
-
* on them, so a board carrying no cards and no runs is one nobody has used
|
|
741
|
-
* yet. That is a better signal than a version on disk: a project can install
|
|
742
|
-
* the package, never open the panel, and update three times before anyone
|
|
743
|
-
* sees it — and it is still that reader's first run.
|
|
744
|
-
*
|
|
745
|
-
* A panel that has been worked and then loses a variable is deliberately not
|
|
746
|
-
* this. It needs the same instructions and gets them from the same wizard,
|
|
747
|
-
* but it is told rather than taken over: somebody mid-sentence on a board
|
|
748
|
-
* they know does not want a walkthrough opening on top of it.
|
|
749
|
-
*/
|
|
750
|
-
declare function isFirstRun(board: {
|
|
751
|
-
cards?: unknown[];
|
|
752
|
-
runs?: unknown[];
|
|
753
|
-
}): boolean;
|
|
754
|
-
|
|
755
|
-
type Handler = (payload: Record<string, string>, event: MouseEvent) => void;
|
|
756
|
-
/**
|
|
757
|
-
* A single click listener on document that dispatches by data-* attributes,
|
|
758
|
-
* instead of one handler per element. Meant for a project's own actions; the
|
|
759
|
-
* Board does not use it internally.
|
|
760
|
-
*
|
|
761
|
-
* Example: useDelegatedClick('data-action', (payload) => { if (payload.action === 'delete') ... })
|
|
762
|
-
* with buttons such as <button data-action="delete" data-id="42">
|
|
763
|
-
*/
|
|
764
|
-
declare function useDelegatedClick(attribute: string, handler: Handler): void;
|
|
765
|
-
|
|
766
|
-
declare function useMediaQuery(query: string): boolean;
|
|
767
|
-
|
|
768
|
-
/**
|
|
769
|
-
* A board is read in one language and written in another.
|
|
770
|
-
*
|
|
771
|
-
* This is the mechanism only — the types and the resolution. The panel's own
|
|
772
|
-
* wording lives with the panel: a library that shipped its copy would be a
|
|
773
|
-
* library deciding what a project's board says.
|
|
774
|
-
*/
|
|
775
|
-
type Lang = 'en' | 'es';
|
|
776
|
-
declare const LANGS: Lang[];
|
|
777
|
-
declare const LANG_LABELS: Record<Lang, string>;
|
|
778
|
-
/**
|
|
779
|
-
* A string, or the same string per language.
|
|
780
|
-
*
|
|
781
|
-
* A plain string is a claim: this text is right in either language — true for
|
|
782
|
-
* a product name, a number, a file id, and almost never for prose. An object
|
|
783
|
-
* is the honest shape for a sentence, and one that carries a single language
|
|
784
|
-
* says "written, not translated yet", which is exactly what the next session
|
|
785
|
-
* needs to know.
|
|
786
|
-
*/
|
|
787
|
-
type Text = string | Partial<Record<Lang, string>>;
|
|
788
|
-
declare function text(value: Text | undefined, lang: Lang): string;
|
|
789
|
-
/** True when a sentence exists in one language only. */
|
|
790
|
-
declare function untranslated(value: Text | undefined): boolean;
|
|
791
|
-
/**
|
|
792
|
-
* Writing replaces the language being typed and drops the other copy of that
|
|
793
|
-
* field. Deliberate: a translation of a sentence somebody just rewrote is a
|
|
794
|
-
* lie about what the card says, and the gap is the panel telling the next
|
|
795
|
-
* session what to fill in.
|
|
796
|
-
*/
|
|
797
|
-
declare function writeText(lang: Lang, value: string): Text;
|
|
798
|
-
|
|
799
|
-
type Weight = 'primary' | 'secondary' | 'off';
|
|
800
|
-
type Severity = 'high' | 'medium' | 'low';
|
|
801
|
-
type SuggestionStatus = 'new' | 'doing' | 'done' | 'dismissed';
|
|
802
|
-
interface Goal {
|
|
803
|
-
id: string;
|
|
804
|
-
label: Text;
|
|
805
|
-
weight: Weight;
|
|
806
|
-
}
|
|
807
|
-
interface Constraint {
|
|
808
|
-
id: string;
|
|
809
|
-
label: Text;
|
|
810
|
-
on: boolean;
|
|
811
|
-
}
|
|
812
|
-
interface Reading {
|
|
813
|
-
date: string;
|
|
814
|
-
value: number;
|
|
815
|
-
note?: Text;
|
|
816
|
-
}
|
|
817
|
-
interface Metric {
|
|
818
|
-
id: string;
|
|
819
|
-
label: Text;
|
|
820
|
-
unit: string;
|
|
821
|
-
/** Which direction counts as better. A reading against it is what "drifting" means. */
|
|
822
|
-
goal: 'up' | 'down';
|
|
823
|
-
readings: Reading[];
|
|
824
|
-
}
|
|
825
|
-
interface Strategy {
|
|
826
|
-
id: string;
|
|
827
|
-
title: Text;
|
|
828
|
-
thesis: Text;
|
|
829
|
-
horizon: Text;
|
|
830
|
-
/** 0–10, so three bars can be compared at a glance rather than read. */
|
|
831
|
-
effort: number;
|
|
832
|
-
risk: number;
|
|
833
|
-
upside: number;
|
|
834
|
-
moves: Text[];
|
|
835
|
-
}
|
|
836
|
-
interface Suggestion {
|
|
837
|
-
id: string;
|
|
838
|
-
area: string;
|
|
839
|
-
severity: Severity;
|
|
840
|
-
status: SuggestionStatus;
|
|
841
|
-
date: string;
|
|
842
|
-
text: Text;
|
|
843
|
-
}
|
|
844
|
-
/**
|
|
845
|
-
* A question the owner answers and Claude reads.
|
|
846
|
-
*
|
|
847
|
-
* This is the whole point of the analysis view. A board says what to do; a
|
|
848
|
-
* choice says how the project wants it done, in the owner's words, once —
|
|
849
|
-
* instead of being asked again on every card. Claude reads these before
|
|
850
|
-
* deciding anything, so answering one here is worth more than answering it
|
|
851
|
-
* in a message that scrolls away.
|
|
852
|
-
*
|
|
853
|
-
* `value` of null is not missing data. It means nobody has decided, which is
|
|
854
|
-
* information: Claude must not guess an answer to a question the owner has
|
|
855
|
-
* deliberately left open.
|
|
856
|
-
*/
|
|
857
|
-
interface Choice {
|
|
543
|
+
interface Choice {
|
|
858
544
|
id: string;
|
|
859
545
|
question: Text;
|
|
860
546
|
/** What actually changes depending on the answer. Never "choose an option". */
|
|
@@ -1050,148 +736,540 @@ interface Run {
|
|
|
1050
736
|
/** The kit fingerprint at the time, twelve characters is enough to compare. */
|
|
1051
737
|
fingerprint?: string;
|
|
1052
738
|
}
|
|
1053
|
-
interface HealthFact {
|
|
1054
|
-
label: Text;
|
|
1055
|
-
value: Text;
|
|
1056
|
-
status: 'ok' | 'warning' | 'bad' | 'unknown';
|
|
739
|
+
interface HealthFact {
|
|
740
|
+
label: Text;
|
|
741
|
+
value: Text;
|
|
742
|
+
status: 'ok' | 'warning' | 'bad' | 'unknown';
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* A theme Claude derived from the project it is installed in.
|
|
746
|
+
*
|
|
747
|
+
* Stored with where it came from, not only with its colours. "Update the
|
|
748
|
+
* theme" is only a meaningful request if the next answer can be compared with
|
|
749
|
+
* this one — otherwise a regenerated theme is a different theme for reasons
|
|
750
|
+
* nobody can see. `from` is what makes the difference readable.
|
|
751
|
+
*
|
|
752
|
+
* `refreshRequested` is a message, not a trigger: a panel cannot run Claude.
|
|
753
|
+
* Tapping Update writes the date here, the bell shows it, and the next session
|
|
754
|
+
* regenerates. Pretending the button does the work itself would leave the
|
|
755
|
+
* owner waiting for something that was never going to happen.
|
|
756
|
+
*/
|
|
757
|
+
interface ImportedTheme {
|
|
758
|
+
/** What to call it in the theme list. */
|
|
759
|
+
label: Text;
|
|
760
|
+
tokens: Record<string, string>;
|
|
761
|
+
form?: Record<string, string | number>;
|
|
762
|
+
from: {
|
|
763
|
+
/** Files the colours were read out of, so the claim can be checked. */
|
|
764
|
+
files: string[];
|
|
765
|
+
/** The colours actually found, before they were mapped to tokens. */
|
|
766
|
+
colours: string[];
|
|
767
|
+
/** Why these tokens and not others. One or two sentences. */
|
|
768
|
+
reasoning: Text;
|
|
769
|
+
};
|
|
770
|
+
importedAt: string;
|
|
771
|
+
importedBy: 'claude';
|
|
772
|
+
/** Set by the panel when the owner asks for it to be derived again. */
|
|
773
|
+
refreshRequested?: string | null;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* What the owner has chosen, kept in the board so it survives a new browser.
|
|
777
|
+
*
|
|
778
|
+
* The kit's default is to remember these per browser, in localStorage, because
|
|
779
|
+
* for a component "which notes this reader dismissed" is a fact about a reader
|
|
780
|
+
* and not about a project. A panel with one password is the case where those
|
|
781
|
+
* two are the same person: keeping it here means the theme, the language and
|
|
782
|
+
* what has already been read follow the owner to a new machine instead of
|
|
783
|
+
* greeting them with a tour they finished months ago.
|
|
784
|
+
*
|
|
785
|
+
* The cost is real and worth naming: anyone who shares the password shares
|
|
786
|
+
* this. It holds a theme name and a list of ids — nothing worth stealing, and
|
|
787
|
+
* nothing anyone else would want.
|
|
788
|
+
*/
|
|
789
|
+
interface Preferences {
|
|
790
|
+
theme?: string;
|
|
791
|
+
lang?: Lang;
|
|
792
|
+
/** Ids of notifications already read. Bounded when saved. */
|
|
793
|
+
seen?: string[];
|
|
794
|
+
/** Ids of guide notes already dismissed. */
|
|
795
|
+
guideDismissed?: string[];
|
|
796
|
+
/**
|
|
797
|
+
* Whether an update card is written as an order rather than a suggestion.
|
|
798
|
+
*
|
|
799
|
+
* It does not make anything automatic — the panel cannot run npm, and this
|
|
800
|
+
* changes `order` on the card it offers to write, nothing else. Off means
|
|
801
|
+
* the card asks; on means it tells.
|
|
802
|
+
*/
|
|
803
|
+
updateAsOrder?: boolean;
|
|
804
|
+
}
|
|
805
|
+
interface PanelState {
|
|
806
|
+
v: 2;
|
|
807
|
+
project: string;
|
|
808
|
+
environment: Text;
|
|
809
|
+
updatedAt: string;
|
|
810
|
+
notice?: Text;
|
|
811
|
+
columns: ColumnDef[];
|
|
812
|
+
areas: {
|
|
813
|
+
id: string;
|
|
814
|
+
label: Text;
|
|
815
|
+
}[];
|
|
816
|
+
health: HealthFact[];
|
|
817
|
+
cards: Card[];
|
|
818
|
+
/** Newest first. Capped when saving, so the file cannot grow without end. */
|
|
819
|
+
runs: Run[];
|
|
820
|
+
/** What any of the work is for: goals, constraints, metrics, strategies. */
|
|
821
|
+
analysis?: Analysis;
|
|
822
|
+
/** The owner's choices, so they survive a new browser. */
|
|
823
|
+
preferences?: Preferences;
|
|
824
|
+
/** A theme Claude derived from this project, with where it came from. */
|
|
825
|
+
importedTheme?: ImportedTheme;
|
|
826
|
+
}
|
|
827
|
+
declare const LEVELS: Level[];
|
|
828
|
+
declare const OWNERS: Owner[];
|
|
829
|
+
declare function newId(): string;
|
|
830
|
+
declare function today(): string;
|
|
831
|
+
/** A card is created empty and named second — the same as tapping "+" on paper. */
|
|
832
|
+
declare function emptyCard(column: string, area: string): Card;
|
|
833
|
+
/**
|
|
834
|
+
* How long a finished card stays on the board before it becomes history.
|
|
835
|
+
*
|
|
836
|
+
* A done column only grows. It is the one column nothing ever removes from,
|
|
837
|
+
* so a board that is being worked turns into a wall of things already
|
|
838
|
+
* finished, and the three columns that need reading get a third of the screen
|
|
839
|
+
* between them. A week is the window in which "what did we ship" is still a
|
|
840
|
+
* live question; after that it is a record, and a record does not need to be
|
|
841
|
+
* in the way.
|
|
842
|
+
*/
|
|
843
|
+
declare const ARCHIVE_AFTER_DAYS = 7;
|
|
844
|
+
/**
|
|
845
|
+
* Whether a finished card has been finished long enough to stop being drawn.
|
|
846
|
+
*
|
|
847
|
+
* Derived on every render and never written, which is the arrangement
|
|
848
|
+
* `boardFacts()` already uses: a flag stored on the card would be wrong the
|
|
849
|
+
* day after it was set, and setting it would mark the panel dirty for the
|
|
850
|
+
* passage of time. Here the answer simply becomes true on its own.
|
|
851
|
+
*
|
|
852
|
+
* **A card with no `completedAt` is never archived**, whatever column it sits
|
|
853
|
+
* in. It reached done without being stamped — a board edited by hand, or one
|
|
854
|
+
* from before the stamp existed — so its age is not known, and hiding a card
|
|
855
|
+
* whose age nobody knows is the same mistake as reporting a token missing
|
|
856
|
+
* because the check could not run. `unknown` is not `old`.
|
|
857
|
+
*/
|
|
858
|
+
declare function isArchived(card: Card, doneColumns: ReadonlySet<string>, now?: string, afterDays?: number): boolean;
|
|
859
|
+
/**
|
|
860
|
+
* Applies the column's own meaning to a card that just moved into it: a card
|
|
861
|
+
* entering the working column is started, one reaching a done column is
|
|
862
|
+
* finished, and one dragged back out of done is not finished any more.
|
|
863
|
+
*/
|
|
864
|
+
declare function stampForColumn(card: Card, columns: ColumnDef[]): Card;
|
|
865
|
+
/**
|
|
866
|
+
* Where a card the panel writes on the reader's behalf goes.
|
|
867
|
+
*
|
|
868
|
+
* The update card and the four "ask for this again" cards used to be born in
|
|
869
|
+
* a column literally called `next`. The default board has one; a board whose
|
|
870
|
+
* owner renamed it does not, and a card in a column that does not exist is
|
|
871
|
+
* the exact fault `validateBoard` names — drawn nowhere, and refused by the
|
|
872
|
+
* write path, so one tap on a button turned a saveable board into one that
|
|
873
|
+
* could not be saved and said so about a column the reader had never heard of.
|
|
874
|
+
*
|
|
875
|
+
* `next` when there is one, because that is what the button means. Otherwise
|
|
876
|
+
* the first column that is neither in flight nor finished nor the owner's:
|
|
877
|
+
* queued work goes where queued work already sits. And the first column of
|
|
878
|
+
* all when nothing else fits, since a board with no columns cannot pass the
|
|
879
|
+
* validator either way.
|
|
880
|
+
*/
|
|
881
|
+
declare function queueColumn(columns: ColumnDef[]): string;
|
|
882
|
+
/** A card claiming to be finished that was never picked up. */
|
|
883
|
+
declare function claimedWithoutStarting(card: Card): boolean;
|
|
884
|
+
interface Counts {
|
|
885
|
+
open: number;
|
|
886
|
+
orders: number;
|
|
887
|
+
yours: number;
|
|
888
|
+
decisions: number;
|
|
889
|
+
highRisk: number;
|
|
890
|
+
done: number;
|
|
891
|
+
}
|
|
892
|
+
declare function countCards(state: PanelState): Counts;
|
|
893
|
+
/** How many checklist steps are ticked, for the counter on a card. */
|
|
894
|
+
declare function checkProgress(card: Card): {
|
|
895
|
+
done: number;
|
|
896
|
+
total: number;
|
|
897
|
+
} | undefined;
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Cards the panel writes when the owner asks for something it cannot do.
|
|
901
|
+
*
|
|
902
|
+
* The panel runs nothing. It cannot bump a version, install a plugin or
|
|
903
|
+
* produce a market report, and a button that appeared to would be the fault
|
|
904
|
+
* this project keeps refusing. What it can do is write the request down where
|
|
905
|
+
* work reaches Claude — an order card on the board — and open it, so the tap
|
|
906
|
+
* is visibly a request and the next session carries it out.
|
|
907
|
+
*
|
|
908
|
+
* Two rules live here rather than in the component, because they are the part
|
|
909
|
+
* with decisions in it and a component cannot be tested in this repository:
|
|
910
|
+
*
|
|
911
|
+
* - **One open request per subject.** A second tap while the first is still
|
|
912
|
+
* waiting opens the card already asked for. Two version bumps queued at
|
|
913
|
+
* once is a project two versions ahead of where anybody meant, and two
|
|
914
|
+
* install cards for one tool is the same edit made twice.
|
|
915
|
+
* - **A finished request does not block a new one.** Once the card reaches a
|
|
916
|
+
* done column the subject is free again: next week's bump is a new card.
|
|
917
|
+
*/
|
|
918
|
+
|
|
919
|
+
/** The kinds of request the panel writes. */
|
|
920
|
+
type RequestKind = 'version-bump' | 'install';
|
|
921
|
+
/** `version-bump-2026-09-26`, `install-deploybudget-2026-09-26`. */
|
|
922
|
+
declare function requestId(kind: RequestKind, day: string, subject?: string): string;
|
|
923
|
+
/**
|
|
924
|
+
* The request of this kind, for this subject, still waiting on the board.
|
|
925
|
+
*
|
|
926
|
+
* "Waiting" is anything outside a done column — queued, in flight, or parked
|
|
927
|
+
* in the owner's column with a question on it. Only finishing it frees the
|
|
928
|
+
* subject for a new request.
|
|
929
|
+
*/
|
|
930
|
+
declare function pendingRequest(cards: Card[], kind: RequestKind, doneColumns: ReadonlySet<string>, subject?: string): Card | undefined;
|
|
931
|
+
|
|
932
|
+
interface DetailSheetProps {
|
|
933
|
+
open: boolean;
|
|
934
|
+
onClose: () => void;
|
|
935
|
+
children: React.ReactNode;
|
|
936
|
+
ariaLabel?: string;
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Below 760px this is a sheet rising from the bottom; from 760px up the same
|
|
940
|
+
* markup becomes a centred floating dialog. The breakpoint is handled in CSS,
|
|
941
|
+
* so there are not two different components.
|
|
942
|
+
*/
|
|
943
|
+
declare function DetailSheet({ open, onClose, children, ariaLabel }: DetailSheetProps): React.JSX.Element | null;
|
|
944
|
+
interface DetailSectionProps {
|
|
945
|
+
title: string;
|
|
946
|
+
/** Clarifying phrase in lowercase, printed after an em dash. */
|
|
947
|
+
subtitle?: string;
|
|
948
|
+
children: React.ReactNode;
|
|
949
|
+
}
|
|
950
|
+
declare function DetailSection({ title, subtitle, children }: DetailSectionProps): React.JSX.Element;
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* Whether a note has been dismissed, and how to dismiss it.
|
|
954
|
+
*
|
|
955
|
+
* Starts as "shown but not yet decided" so the first paint matches the server
|
|
956
|
+
* and nothing flashes into place; the stored answer arrives on the first
|
|
957
|
+
* effect.
|
|
958
|
+
*/
|
|
959
|
+
declare function useGuideNote(id: string, remember?: boolean): {
|
|
960
|
+
visible: boolean;
|
|
961
|
+
dismiss: () => void;
|
|
962
|
+
};
|
|
963
|
+
/** Clears every dismissal, so the guidance can be asked for again. */
|
|
964
|
+
declare function resetGuide(): void;
|
|
965
|
+
interface GuideNoteProps {
|
|
966
|
+
/** Stable across releases: changing it makes the note reappear for everyone. */
|
|
967
|
+
id: string;
|
|
968
|
+
title: React.ReactNode;
|
|
969
|
+
children: React.ReactNode;
|
|
970
|
+
/** Wording of the dismiss button. Say what it does, not "OK". */
|
|
971
|
+
dismissLabel?: string;
|
|
972
|
+
/** Shown after the note, for the one thing it wants the reader to do. */
|
|
973
|
+
action?: {
|
|
974
|
+
label: string;
|
|
975
|
+
onClick: () => void;
|
|
976
|
+
};
|
|
977
|
+
/** Position in a sequence, so a first session reads as a tour, not a pile. */
|
|
978
|
+
step?: {
|
|
979
|
+
index: number;
|
|
980
|
+
total: number;
|
|
981
|
+
};
|
|
982
|
+
/** Told after the note is dismissed, so a sequence can move on. */
|
|
983
|
+
onDismissed?: () => void;
|
|
984
|
+
/**
|
|
985
|
+
* Whether to record the dismissal in localStorage. False when the caller
|
|
986
|
+
* keeps that answer itself, so the two cannot disagree.
|
|
987
|
+
*/
|
|
988
|
+
remember?: boolean;
|
|
989
|
+
}
|
|
990
|
+
declare function GuideNote({ id, title, children, dismissLabel, action, step, onDismissed, remember, }: GuideNoteProps): React.JSX.Element | null;
|
|
991
|
+
interface GuideTourProps {
|
|
992
|
+
/** Notes in order. Only the first undismissed one shows, so it reads as a tour. */
|
|
993
|
+
notes: Array<Omit<GuideNoteProps, 'step' | 'onDismissed'>>;
|
|
994
|
+
/**
|
|
995
|
+
* Which notes are already dismissed, when the caller keeps that somewhere
|
|
996
|
+
* durable. Leave it out and each reader's browser remembers, which is the
|
|
997
|
+
* right home for a fact about one browser — and the wrong one for a panel
|
|
998
|
+
* with a single owner, who would be told the same tour again on a new
|
|
999
|
+
* machine. Passing it does not switch the browser off: a note dismissed
|
|
1000
|
+
* here is written to both, because a caller whose store needs saving would
|
|
1001
|
+
* otherwise lose the dismissal on reload.
|
|
1002
|
+
*/
|
|
1003
|
+
dismissed?: string[];
|
|
1004
|
+
/** Told when a note is dismissed. Required for `dismissed` to mean anything. */
|
|
1005
|
+
onDismiss?: (id: string) => void;
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* One note at a time, in order.
|
|
1009
|
+
*
|
|
1010
|
+
* Five notes at once is a wall nobody reads. Shown one at a time, dismissing
|
|
1011
|
+
* each reveals the next, and a reader who dismisses all of them has been
|
|
1012
|
+
* through the tour rather than having closed a box.
|
|
1013
|
+
*/
|
|
1014
|
+
declare function GuideTour({ notes, dismissed, onDismiss }: GuideTourProps): React.JSX.Element | null;
|
|
1015
|
+
|
|
1016
|
+
type NotificationTone = 'info' | 'attention';
|
|
1017
|
+
interface PanelNotification {
|
|
1018
|
+
/**
|
|
1019
|
+
* Stable for the life of the event. Two different events must never share
|
|
1020
|
+
* one, or marking the first seen silences the second; the same event must
|
|
1021
|
+
* never change it, or it comes back every time the page loads.
|
|
1022
|
+
*/
|
|
1023
|
+
id: string;
|
|
1024
|
+
title: React.ReactNode;
|
|
1025
|
+
detail?: React.ReactNode;
|
|
1026
|
+
/** Shown as given, so the caller decides the format its readers expect. */
|
|
1027
|
+
at?: string;
|
|
1028
|
+
/** `attention` for something waiting on the reader, `info` for the rest. */
|
|
1029
|
+
tone?: NotificationTone;
|
|
1030
|
+
/** Where this leads. Called before the notification is marked seen. */
|
|
1031
|
+
onOpen?: () => void;
|
|
1032
|
+
}
|
|
1033
|
+
interface NotificationLabels {
|
|
1034
|
+
/** Names the control for a screen reader and on hover. */
|
|
1035
|
+
title: string;
|
|
1036
|
+
empty: string;
|
|
1037
|
+
markAll: string;
|
|
1038
|
+
/** Given the count, e.g. (n) => `${n} unread`. */
|
|
1039
|
+
unread: (count: number) => string;
|
|
1040
|
+
}
|
|
1041
|
+
interface NotificationBellProps {
|
|
1042
|
+
notifications: PanelNotification[];
|
|
1043
|
+
labels?: Partial<NotificationLabels>;
|
|
1044
|
+
/**
|
|
1045
|
+
* What this reader has already seen, when the caller keeps it somewhere
|
|
1046
|
+
* durable. Leave it out and the bell remembers in localStorage, which is the
|
|
1047
|
+
* right home for a fact about one browser.
|
|
1048
|
+
*
|
|
1049
|
+
* A panel with one password has one owner, and there "this reader" and "this
|
|
1050
|
+
* project" are the same person — so a panel that saves its board can keep
|
|
1051
|
+
* this in it and have it survive a new browser. That is the caller's call to
|
|
1052
|
+
* make, not the component's, which is why both work.
|
|
1053
|
+
*/
|
|
1054
|
+
seen?: string[];
|
|
1055
|
+
/**
|
|
1056
|
+
* Told which ids were just seen. Required for `seen` to mean anything —
|
|
1057
|
+
* though the bell also remembers in localStorage either way, so a caller
|
|
1058
|
+
* that has not saved yet does not show the same notifications again.
|
|
1059
|
+
*/
|
|
1060
|
+
onSeen?: (ids: string[]) => void;
|
|
1061
|
+
}
|
|
1062
|
+
declare function NotificationBell({ notifications, labels, seen: given, onSeen }: NotificationBellProps): React.JSX.Element | null;
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* A block of text whose whole purpose is to end up somewhere else.
|
|
1066
|
+
*
|
|
1067
|
+
* Anything a panel tells someone to paste — a settings fragment, two commands,
|
|
1068
|
+
* a shell line — is read once and copied. So the copy is the primary action
|
|
1069
|
+
* and the text is selectable underneath it, rather than the other way round.
|
|
1070
|
+
*
|
|
1071
|
+
* The clipboard API is not always there. It needs a secure context and can be
|
|
1072
|
+
* refused outright, and a copy button that silently does nothing is worse than
|
|
1073
|
+
* no button at all: the reader walks away believing they have the text. So the
|
|
1074
|
+
* failure falls back to selecting the block, and says which of the two
|
|
1075
|
+
* happened.
|
|
1076
|
+
*/
|
|
1077
|
+
interface CopyBlockProps {
|
|
1078
|
+
/** Exactly what lands on the clipboard. Shown verbatim. */
|
|
1079
|
+
text: string;
|
|
1080
|
+
/** What this block is, above it. */
|
|
1081
|
+
label?: React.ReactNode;
|
|
1082
|
+
/** Why the reader wants it, and anything they must know before pasting. */
|
|
1083
|
+
note?: React.ReactNode;
|
|
1084
|
+
/** Wording, so the kit stays language-agnostic. */
|
|
1085
|
+
labels?: Partial<CopyBlockLabels>;
|
|
1086
|
+
}
|
|
1087
|
+
interface CopyBlockLabels {
|
|
1088
|
+
copy: string;
|
|
1089
|
+
copied: string;
|
|
1090
|
+
/** Used when the clipboard refused and the text was selected instead. */
|
|
1091
|
+
selected: string;
|
|
1092
|
+
}
|
|
1093
|
+
declare function CopyBlock({ text, label, note, labels }: CopyBlockProps): React.JSX.Element;
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* What the panel needs in order to work, and what to do about what is missing.
|
|
1097
|
+
*
|
|
1098
|
+
* A panel with no token does not look broken — it looks fine until the moment
|
|
1099
|
+
* someone presses Save, and then it fails with something the person reading it
|
|
1100
|
+
* did not cause and cannot place. This turns that into a list they can read
|
|
1101
|
+
* before it happens, and act on without leaving the panel.
|
|
1102
|
+
*
|
|
1103
|
+
* The status comes from the server, which is the only place that can see the
|
|
1104
|
+
* environment. It reports *whether* a variable is set and never its value, so
|
|
1105
|
+
* this component has nothing worth leaking.
|
|
1106
|
+
*/
|
|
1107
|
+
type SetupStatus = 'ok' | 'missing' | 'unknown';
|
|
1108
|
+
interface SetupRequirement {
|
|
1109
|
+
id: string;
|
|
1110
|
+
/** What it is, in the reader's terms rather than the variable's. */
|
|
1111
|
+
label: React.ReactNode;
|
|
1112
|
+
status: SetupStatus;
|
|
1113
|
+
/** The environment variable behind it, when there is one to name. */
|
|
1114
|
+
variable?: string;
|
|
1115
|
+
/** What it lets the panel do — so a missing one has a visible cost. */
|
|
1116
|
+
enables?: React.ReactNode;
|
|
1117
|
+
/** Exactly what to do, shown only when it is missing. */
|
|
1118
|
+
fix?: React.ReactNode;
|
|
1119
|
+
/**
|
|
1120
|
+
* Information rather than a gate: drawn in the list, never walked as a step.
|
|
1121
|
+
*
|
|
1122
|
+
* Every other requirement here is an environment variable, and the
|
|
1123
|
+
* walkthrough's one action — re-read the environment from the server —
|
|
1124
|
+
* is what clears it. A requirement that action cannot clear parks the
|
|
1125
|
+
* walkthrough on it for ever, and because `unknown` is judged across the
|
|
1126
|
+
* whole list, one advisory row that nobody wired made every step answer a
|
|
1127
|
+
* successful check with "the panel could not ask its own server". That is
|
|
1128
|
+
* what the copied gate did the day it was added.
|
|
1129
|
+
*/
|
|
1130
|
+
advisory?: boolean;
|
|
1131
|
+
}
|
|
1132
|
+
interface SetupGuideProps {
|
|
1133
|
+
requirements: SetupRequirement[];
|
|
1134
|
+
/** Shown when everything is in place, instead of an empty list. */
|
|
1135
|
+
readyMessage?: React.ReactNode;
|
|
1136
|
+
/** Wording, so the kit stays language-agnostic. */
|
|
1137
|
+
labels?: Partial<{
|
|
1138
|
+
ok: string;
|
|
1139
|
+
missing: string;
|
|
1140
|
+
unknown: string;
|
|
1141
|
+
whatToDo: string;
|
|
1142
|
+
}>;
|
|
1057
1143
|
}
|
|
1144
|
+
declare function SetupGuide({ requirements, readyMessage, labels }: SetupGuideProps): React.JSX.Element;
|
|
1145
|
+
|
|
1058
1146
|
/**
|
|
1059
|
-
*
|
|
1147
|
+
* The setup, walked one step at a time.
|
|
1060
1148
|
*
|
|
1061
|
-
*
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1064
|
-
*
|
|
1149
|
+
* `SetupGuide` is the list: the right shape for somebody auditing a panel that
|
|
1150
|
+
* already works. This is the other reader — the one who installed it twenty
|
|
1151
|
+
* minutes ago, has four things missing and no idea which to do first. A wall
|
|
1152
|
+
* of four is not four times as useful as one; it is the thing people close.
|
|
1065
1153
|
*
|
|
1066
|
-
*
|
|
1067
|
-
*
|
|
1068
|
-
*
|
|
1069
|
-
*
|
|
1154
|
+
* Two rules hold it honest. It **checks rather than asks**: every step ends in
|
|
1155
|
+
* a button that re-reads the environment from the server, so the reader is
|
|
1156
|
+
* told whether it worked instead of ticking a box themselves. And it **never
|
|
1157
|
+
* claims what it cannot see**: a panel that could not reach its own endpoint
|
|
1158
|
+
* says the check failed, never that a variable is missing, because telling
|
|
1159
|
+
* somebody to add a token they already added is the worse of the two wrong
|
|
1160
|
+
* answers.
|
|
1161
|
+
*
|
|
1162
|
+
* There is no copy of the values here and nothing is written: the panel cannot
|
|
1163
|
+
* set an environment variable, and a button that appeared to would be the same
|
|
1164
|
+
* fault as one that appeared to install an update.
|
|
1070
1165
|
*/
|
|
1071
|
-
interface
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
/**
|
|
1087
|
-
|
|
1166
|
+
interface SetupWizardLabels {
|
|
1167
|
+
title: string;
|
|
1168
|
+
/** `(index, total)` — "Step 2 of 5". */
|
|
1169
|
+
progress: (index: number, total: number) => string;
|
|
1170
|
+
/** Shown above the one step in hand. */
|
|
1171
|
+
nowDo: string;
|
|
1172
|
+
/** The button that re-reads the environment. */
|
|
1173
|
+
check: string;
|
|
1174
|
+
checking: string;
|
|
1175
|
+
/** Said when a check ran and the step is still not in place. */
|
|
1176
|
+
stillMissing: string;
|
|
1177
|
+
/** Said when the panel could not ask its own server. */
|
|
1178
|
+
cannotTell: string;
|
|
1179
|
+
/** The heading over the finished steps. */
|
|
1180
|
+
doneTitle: string;
|
|
1181
|
+
/** Shown in place of a step when every requirement is in place. */
|
|
1182
|
+
readyTitle: string;
|
|
1183
|
+
readyBody: string;
|
|
1184
|
+
/** The two things to try before trusting the gate, as list items. */
|
|
1185
|
+
verify: string[];
|
|
1186
|
+
/** Closes the wizard. */
|
|
1187
|
+
close: string;
|
|
1188
|
+
/** The escape hatch: heading, explanation, and the prompt to copy. */
|
|
1189
|
+
stuckTitle: string;
|
|
1190
|
+
stuckBody: string;
|
|
1191
|
+
stuckPrompt: string;
|
|
1192
|
+
copy: string;
|
|
1193
|
+
copied: string;
|
|
1194
|
+
}
|
|
1195
|
+
interface SetupWizardProps {
|
|
1196
|
+
/** In the order they are to be done. The caller owns that order. */
|
|
1197
|
+
requirements: SetupRequirement[];
|
|
1198
|
+
labels: SetupWizardLabels;
|
|
1199
|
+
/** Re-reads the environment. Resolves when the answer has been applied. */
|
|
1200
|
+
onCheck: () => Promise<void> | void;
|
|
1201
|
+
/** Told when the reader closes it. */
|
|
1202
|
+
onClose?: () => void;
|
|
1088
1203
|
}
|
|
1204
|
+
declare function SetupWizard({ requirements, labels, onCheck, onClose }: SetupWizardProps): React.JSX.Element;
|
|
1205
|
+
|
|
1089
1206
|
/**
|
|
1090
|
-
*
|
|
1207
|
+
* Where a reader is in the setup, as one answer instead of a list to read.
|
|
1091
1208
|
*
|
|
1092
|
-
*
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
1095
|
-
*
|
|
1096
|
-
*
|
|
1097
|
-
*
|
|
1209
|
+
* `SetupGuide` draws every requirement at once, which is the right shape for
|
|
1210
|
+
* somebody checking a panel that already works. It is the wrong shape for
|
|
1211
|
+
* somebody who has just installed one: five things at once, four of them
|
|
1212
|
+
* missing, and no order to do them in reads as a wall rather than as a first
|
|
1213
|
+
* step. So the same requirements are walked one at a time, and this is the
|
|
1214
|
+
* function that decides which one.
|
|
1098
1215
|
*
|
|
1099
|
-
* The
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1216
|
+
* The order the caller gives is the order they are done in — a token that
|
|
1217
|
+
* cannot be scoped before the repository is named is a step in the wrong
|
|
1218
|
+
* place, and that is the caller's knowledge, not this function's.
|
|
1102
1219
|
*/
|
|
1103
|
-
interface
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
/**
|
|
1107
|
-
|
|
1108
|
-
/**
|
|
1109
|
-
|
|
1220
|
+
interface WizardStep {
|
|
1221
|
+
/** The requirement to show, or null when nothing is left to do. */
|
|
1222
|
+
current: SetupRequirement | null;
|
|
1223
|
+
/** 1-based, for "step 2 of 5". Zero when there is nothing left. */
|
|
1224
|
+
index: number;
|
|
1225
|
+
/** How many steps this setup has in total. */
|
|
1226
|
+
total: number;
|
|
1227
|
+
/** Every requirement that is in place, in the caller's order. */
|
|
1228
|
+
done: SetupRequirement[];
|
|
1229
|
+
/** True when every requirement is `ok`. */
|
|
1230
|
+
ready: boolean;
|
|
1110
1231
|
/**
|
|
1111
|
-
*
|
|
1232
|
+
* True when the panel could not ask the server, so nothing is claimed.
|
|
1112
1233
|
*
|
|
1113
|
-
*
|
|
1114
|
-
*
|
|
1115
|
-
*
|
|
1234
|
+
* This is not "not ready": a panel that cannot reach its own endpoint knows
|
|
1235
|
+
* nothing about the environment, and telling somebody to add a token they
|
|
1236
|
+
* already added is worse than saying the check itself failed.
|
|
1116
1237
|
*/
|
|
1117
|
-
|
|
1118
|
-
}
|
|
1119
|
-
interface PanelState {
|
|
1120
|
-
v: 2;
|
|
1121
|
-
project: string;
|
|
1122
|
-
environment: Text;
|
|
1123
|
-
updatedAt: string;
|
|
1124
|
-
notice?: Text;
|
|
1125
|
-
columns: ColumnDef[];
|
|
1126
|
-
areas: {
|
|
1127
|
-
id: string;
|
|
1128
|
-
label: Text;
|
|
1129
|
-
}[];
|
|
1130
|
-
health: HealthFact[];
|
|
1131
|
-
cards: Card[];
|
|
1132
|
-
/** Newest first. Capped when saving, so the file cannot grow without end. */
|
|
1133
|
-
runs: Run[];
|
|
1134
|
-
/** What any of the work is for: goals, constraints, metrics, strategies. */
|
|
1135
|
-
analysis?: Analysis;
|
|
1136
|
-
/** The owner's choices, so they survive a new browser. */
|
|
1137
|
-
preferences?: Preferences;
|
|
1138
|
-
/** A theme Claude derived from this project, with where it came from. */
|
|
1139
|
-
importedTheme?: ImportedTheme;
|
|
1238
|
+
unknown: boolean;
|
|
1140
1239
|
}
|
|
1141
|
-
declare
|
|
1142
|
-
declare const OWNERS: Owner[];
|
|
1143
|
-
declare function newId(): string;
|
|
1144
|
-
declare function today(): string;
|
|
1145
|
-
/** A card is created empty and named second — the same as tapping "+" on paper. */
|
|
1146
|
-
declare function emptyCard(column: string, area: string): Card;
|
|
1147
|
-
/**
|
|
1148
|
-
* How long a finished card stays on the board before it becomes history.
|
|
1149
|
-
*
|
|
1150
|
-
* A done column only grows. It is the one column nothing ever removes from,
|
|
1151
|
-
* so a board that is being worked turns into a wall of things already
|
|
1152
|
-
* finished, and the three columns that need reading get a third of the screen
|
|
1153
|
-
* between them. A week is the window in which "what did we ship" is still a
|
|
1154
|
-
* live question; after that it is a record, and a record does not need to be
|
|
1155
|
-
* in the way.
|
|
1156
|
-
*/
|
|
1157
|
-
declare const ARCHIVE_AFTER_DAYS = 7;
|
|
1240
|
+
declare function wizardStep(requirements: SetupRequirement[]): WizardStep;
|
|
1158
1241
|
/**
|
|
1159
|
-
* Whether
|
|
1242
|
+
* Whether this panel has never been worked, which is what "first install"
|
|
1243
|
+
* means here.
|
|
1160
1244
|
*
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1163
|
-
*
|
|
1164
|
-
*
|
|
1245
|
+
* `paneltir init` writes a board with the columns already there and nothing
|
|
1246
|
+
* on them, so a board carrying no cards and no runs is one nobody has used
|
|
1247
|
+
* yet. That is a better signal than a version on disk: a project can install
|
|
1248
|
+
* the package, never open the panel, and update three times before anyone
|
|
1249
|
+
* sees it — and it is still that reader's first run.
|
|
1165
1250
|
*
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1170
|
-
* because the check could not run. `unknown` is not `old`.
|
|
1251
|
+
* A panel that has been worked and then loses a variable is deliberately not
|
|
1252
|
+
* this. It needs the same instructions and gets them from the same wizard,
|
|
1253
|
+
* but it is told rather than taken over: somebody mid-sentence on a board
|
|
1254
|
+
* they know does not want a walkthrough opening on top of it.
|
|
1171
1255
|
*/
|
|
1172
|
-
declare function
|
|
1256
|
+
declare function isFirstRun(board: {
|
|
1257
|
+
cards?: unknown[];
|
|
1258
|
+
runs?: unknown[];
|
|
1259
|
+
}): boolean;
|
|
1260
|
+
|
|
1261
|
+
type Handler = (payload: Record<string, string>, event: MouseEvent) => void;
|
|
1173
1262
|
/**
|
|
1174
|
-
*
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1263
|
+
* A single click listener on document that dispatches by data-* attributes,
|
|
1264
|
+
* instead of one handler per element. Meant for a project's own actions; the
|
|
1265
|
+
* Board does not use it internally.
|
|
1266
|
+
*
|
|
1267
|
+
* Example: useDelegatedClick('data-action', (payload) => { if (payload.action === 'delete') ... })
|
|
1268
|
+
* with buttons such as <button data-action="delete" data-id="42">
|
|
1177
1269
|
*/
|
|
1178
|
-
declare function
|
|
1179
|
-
|
|
1180
|
-
declare function
|
|
1181
|
-
interface Counts {
|
|
1182
|
-
open: number;
|
|
1183
|
-
orders: number;
|
|
1184
|
-
yours: number;
|
|
1185
|
-
decisions: number;
|
|
1186
|
-
highRisk: number;
|
|
1187
|
-
done: number;
|
|
1188
|
-
}
|
|
1189
|
-
declare function countCards(state: PanelState): Counts;
|
|
1190
|
-
/** How many checklist steps are ticked, for the counter on a card. */
|
|
1191
|
-
declare function checkProgress(card: Card): {
|
|
1192
|
-
done: number;
|
|
1193
|
-
total: number;
|
|
1194
|
-
} | undefined;
|
|
1270
|
+
declare function useDelegatedClick(attribute: string, handler: Handler): void;
|
|
1271
|
+
|
|
1272
|
+
declare function useMediaQuery(query: string): boolean;
|
|
1195
1273
|
|
|
1196
1274
|
/**
|
|
1197
1275
|
* Reads a board and says what is wrong with it, in words.
|
|
@@ -1426,6 +1504,18 @@ interface UiStrings {
|
|
|
1426
1504
|
releaseNone: string;
|
|
1427
1505
|
updateCardTitle: (version: string) => string;
|
|
1428
1506
|
updateCardBody: (version: string) => string;
|
|
1507
|
+
versionBumpAsk: string;
|
|
1508
|
+
versionBumpOpen: string;
|
|
1509
|
+
versionBumpHow: string;
|
|
1510
|
+
versionBumpTitle: string;
|
|
1511
|
+
versionBumpBody: string;
|
|
1512
|
+
versionBumpChecks: string[];
|
|
1513
|
+
installAsk: string;
|
|
1514
|
+
installAskOpen: string;
|
|
1515
|
+
installAskHow: string;
|
|
1516
|
+
installCardTitle: (tool: string) => string;
|
|
1517
|
+
installCardBody: (tool: string, file: string, snippet: string) => string;
|
|
1518
|
+
installCardChecks: string[];
|
|
1429
1519
|
cardDetail: string;
|
|
1430
1520
|
panelSettings: string;
|
|
1431
1521
|
title: string;
|
|
@@ -1959,8 +2049,15 @@ interface MarketplaceViewProps {
|
|
|
1959
2049
|
ui: UiStrings;
|
|
1960
2050
|
/** Absent where there is no server to ask, so no dead button is drawn. */
|
|
1961
2051
|
onRefresh?: () => void;
|
|
2052
|
+
/**
|
|
2053
|
+
* Writes a card asking Claude to install a plugin. Absent where the board
|
|
2054
|
+
* cannot be saved, so the button is not drawn where the card would be lost.
|
|
2055
|
+
*/
|
|
2056
|
+
onRequestInstall?: (tool: MarketplaceTool, marketplace: Marketplace) => void;
|
|
2057
|
+
/** Whether an install card for this tool is already waiting on the board. */
|
|
2058
|
+
installRequested?: (toolId: string) => boolean;
|
|
1962
2059
|
}
|
|
1963
|
-
declare function MarketplaceView({ report, loading, failure, lang, ui, onRefresh }: MarketplaceViewProps): React.JSX.Element;
|
|
2060
|
+
declare function MarketplaceView({ report, loading, failure, lang, ui, onRefresh, onRequestInstall, installRequested, }: MarketplaceViewProps): React.JSX.Element;
|
|
1964
2061
|
|
|
1965
2062
|
interface CardSheetProps {
|
|
1966
2063
|
card: Card | null;
|
|
@@ -1973,4 +2070,4 @@ interface CardSheetProps {
|
|
|
1973
2070
|
}
|
|
1974
2071
|
declare function CardSheet({ card, state, lang, ui, onChange, onDelete, onClose }: CardSheetProps): React.JSX.Element | null;
|
|
1975
2072
|
|
|
1976
|
-
export { ANALYSIS_SECTIONS, ARCHIVE_AFTER_DAYS, AlertBanner, type AlertBannerProps, type Analysis, type AnalysisSection, AnalysisView, BOARD_VERSION, Board, type BoardCardData, type BoardCheck, type BoardColumnData, type BoardFacts, type BoardMoveResult, type BoardProblem, type BoardProps, type BoardTag, CAPABILITIES, type Capability, Card$1 as Card, type CardProps, CardSheet, type Check, type Choice, type ChoiceOption, type ColumnDef, type Competitor, type Constraint, CopyBlock, type CopyBlockLabels, type CopyBlockProps, type Counts, DashboardHeader, type DashboardHeaderProps, type DashboardThemeForm, DashboardThemeProvider, type DashboardThemeTokens, DetailSection, type DetailSectionProps, DetailSheet, type DetailSheetProps, FilterChip, type FilterChipProps, FilterChipRow, type GateReading, type GateStamp, type GateVerdict, type Goal, GuideNote, type GuideNoteProps, GuideTour, type GuideTourProps, type HealthFact, HealthPill, type HealthPillProps, HealthPillRow, type HealthStatus, INTENTS, INTENT_COPY, type ImportedTheme, type InstallState, type Intent, type IntentCopy, LANGS, LANG_LABELS, LEVELS, type Lang, type Level, MARKETPLACE_NAME, MARKETPLACE_URL, type Market, type MarketplaceReport, type MarketplaceTool, MarketplaceView, type Metric, type Note, NotificationBell, type NotificationBellProps, type NotificationLabels, type NotificationTone, OWNERS, type Owner, PANELTIR_FILE_COUNT, PANELTIR_FINGERPRINT, PANELTIR_TEMPLATE_HASH, PANELTIR_VERSION, PRESET_PANEL_THEMES, PanelApp, type PanelAppProps, type Card as PanelCard, type PanelNotification, type PanelState, type PanelTheme, type PanelThemes, type Preferences, type Reading, type Run, SetupGuide, type SetupGuideProps, type SetupRequirement, type SetupStatus, SetupWizard, type SetupWizardLabels, type SetupWizardProps, type Severity, type SituationItem, SituationRow, type SituationRowProps, type SituationTone, StatTile, StatTileGrid, type StatTileProps, type Strategy, type Suggestion, type SuggestionStatus, THEME_FORMS, THEME_PRESETS, type Text, type ThemeFormName, type ThemePresetName, type Trend, UI, type UiStrings, type Weight, type WizardStep, applyMove, boardFacts, checkProgress, claimedWithoutStarting, claudeTheme, commandSnippet, countCards, cyberpunkTheme, decided, emptyCard, explainBoard, gateStatus, isArchived, isCard, isFirstRun, ledgerForm, midnightTheme, moveCardInColumns, newId, offeredThemes, oldMoneyTheme, panelForm, paneltirBuild, paperForm, readBoard, readStoredLang, repositorySnippet, resetGuide, sparkPoints, stampForColumn, storeLang, text, today, trendOf, undecided, untranslated, useDashboardForm, useDashboardTheme, useDelegatedClick, useGuideNote, useMediaQuery, validateBoard, warRoomForm, wizardStep, writeText };
|
|
2073
|
+
export { ANALYSIS_SECTIONS, ARCHIVE_AFTER_DAYS, AlertBanner, type AlertBannerProps, type Analysis, type AnalysisSection, AnalysisView, BOARD_VERSION, Board, type BoardCardData, type BoardCheck, type BoardColumnData, type BoardFacts, type BoardMoveResult, type BoardProblem, type BoardProps, type BoardTag, CAPABILITIES, type Capability, Card$1 as Card, type CardProps, CardSheet, type Check, type Choice, type ChoiceOption, type ColumnDef, type Competitor, type Constraint, CopyBlock, type CopyBlockLabels, type CopyBlockProps, type Counts, DashboardHeader, type DashboardHeaderProps, type DashboardThemeForm, DashboardThemeProvider, type DashboardThemeTokens, DetailSection, type DetailSectionProps, DetailSheet, type DetailSheetProps, FilterChip, type FilterChipProps, FilterChipRow, type GateReading, type GateStamp, type GateVerdict, type Goal, GuideNote, type GuideNoteProps, GuideTour, type GuideTourProps, type HealthFact, HealthPill, type HealthPillProps, HealthPillRow, type HealthStatus, INTENTS, INTENT_COPY, type ImportedTheme, type InstallState, type Intent, type IntentCopy, LANGS, LANG_LABELS, LEVELS, type Lang, type Level, MARKETPLACE_NAME, MARKETPLACE_URL, type Market, type MarketplaceReport, type MarketplaceTool, MarketplaceView, type Metric, type Note, NotificationBell, type NotificationBellProps, type NotificationLabels, type NotificationTone, OWNERS, type Owner, PANELTIR_FILE_COUNT, PANELTIR_FINGERPRINT, PANELTIR_TEMPLATE_HASH, PANELTIR_VERSION, PRESET_PANEL_THEMES, PanelApp, type PanelAppProps, type Card as PanelCard, type PanelNotification, type PanelState, type PanelTheme, type PanelThemes, type Preferences, type Reading, type RequestKind, type Run, SetupGuide, type SetupGuideProps, type SetupRequirement, type SetupStatus, SetupWizard, type SetupWizardLabels, type SetupWizardProps, type Severity, type SituationItem, SituationRow, type SituationRowProps, type SituationTone, StatTile, StatTileGrid, type StatTileProps, type Strategy, type Suggestion, type SuggestionStatus, THEME_FORMS, THEME_PRESETS, type Text, type ThemeFormName, type ThemePresetName, type Trend, UI, type UiStrings, type Weight, type WizardStep, applyMove, boardFacts, checkProgress, claimedWithoutStarting, claudeTheme, commandSnippet, countCards, cyberpunkTheme, decided, dropIndex, emptyCard, explainBoard, gateStatus, isArchived, isCard, isFirstRun, ledgerForm, midnightTheme, moveCardInColumns, newId, offeredThemes, oldMoneyTheme, panelForm, paneltirBuild, paperForm, pendingRequest, queueColumn, readBoard, readStoredLang, repositorySnippet, requestId, resetGuide, sparkPoints, stampForColumn, storeLang, text, today, trendOf, undecided, untranslated, useDashboardForm, useDashboardTheme, useDelegatedClick, useGuideNote, useMediaQuery, validateBoard, warRoomForm, wizardStep, writeText };
|