paneltir 0.12.1 → 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 +7 -0
- package/INSTALL.md +2 -2
- package/README.md +1 -1
- package/dist/index.d.ts +737 -685
- package/dist/index.js +163 -18
- package/fingerprint.json +5 -5
- package/package.json +15 -43
- package/template/.claude/skills/panel/SKILL.md +24 -0
package/dist/index.d.ts
CHANGED
|
@@ -451,792 +451,825 @@ interface DropTarget {
|
|
|
451
451
|
*/
|
|
452
452
|
declare function dropIndex(source: DropTarget, target: DropTarget): number | null;
|
|
453
453
|
|
|
454
|
-
interface DetailSheetProps {
|
|
455
|
-
open: boolean;
|
|
456
|
-
onClose: () => void;
|
|
457
|
-
children: React.ReactNode;
|
|
458
|
-
ariaLabel?: string;
|
|
459
|
-
}
|
|
460
454
|
/**
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
455
|
+
* A board is read in one language and written in another.
|
|
456
|
+
*
|
|
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.
|
|
464
460
|
*/
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
/** Clarifying phrase in lowercase, printed after an em dash. */
|
|
469
|
-
subtitle?: string;
|
|
470
|
-
children: React.ReactNode;
|
|
471
|
-
}
|
|
472
|
-
declare function DetailSection({ title, subtitle, children }: DetailSectionProps): React.JSX.Element;
|
|
473
|
-
|
|
461
|
+
type Lang = 'en' | 'es';
|
|
462
|
+
declare const LANGS: Lang[];
|
|
463
|
+
declare const LANG_LABELS: Record<Lang, string>;
|
|
474
464
|
/**
|
|
475
|
-
*
|
|
465
|
+
* A string, or the same string per language.
|
|
476
466
|
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
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.
|
|
480
472
|
*/
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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;
|
|
484
|
+
|
|
485
|
+
type Weight = 'primary' | 'secondary' | 'off';
|
|
486
|
+
type Severity = 'high' | 'medium' | 'low';
|
|
487
|
+
type SuggestionStatus = 'new' | 'doing' | 'done' | 'dismissed';
|
|
488
|
+
interface Goal {
|
|
489
489
|
id: string;
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
/** Wording of the dismiss button. Say what it does, not "OK". */
|
|
493
|
-
dismissLabel?: string;
|
|
494
|
-
/** Shown after the note, for the one thing it wants the reader to do. */
|
|
495
|
-
action?: {
|
|
496
|
-
label: string;
|
|
497
|
-
onClick: () => void;
|
|
498
|
-
};
|
|
499
|
-
/** Position in a sequence, so a first session reads as a tour, not a pile. */
|
|
500
|
-
step?: {
|
|
501
|
-
index: number;
|
|
502
|
-
total: number;
|
|
503
|
-
};
|
|
504
|
-
/** Told after the note is dismissed, so a sequence can move on. */
|
|
505
|
-
onDismissed?: () => void;
|
|
506
|
-
/**
|
|
507
|
-
* Whether to record the dismissal in localStorage. False when the caller
|
|
508
|
-
* keeps that answer itself, so the two cannot disagree.
|
|
509
|
-
*/
|
|
510
|
-
remember?: boolean;
|
|
490
|
+
label: Text;
|
|
491
|
+
weight: Weight;
|
|
511
492
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
/**
|
|
527
|
-
|
|
493
|
+
interface Constraint {
|
|
494
|
+
id: string;
|
|
495
|
+
label: Text;
|
|
496
|
+
on: boolean;
|
|
497
|
+
}
|
|
498
|
+
interface Reading {
|
|
499
|
+
date: string;
|
|
500
|
+
value: number;
|
|
501
|
+
note?: Text;
|
|
502
|
+
}
|
|
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[];
|
|
510
|
+
}
|
|
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;
|
|
528
529
|
}
|
|
529
530
|
/**
|
|
530
|
-
*
|
|
531
|
+
* A question the owner answers and Claude reads.
|
|
531
532
|
*
|
|
532
|
-
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
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.
|
|
538
|
+
*
|
|
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.
|
|
535
542
|
*/
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
type NotificationTone = 'info' | 'attention';
|
|
539
|
-
interface PanelNotification {
|
|
540
|
-
/**
|
|
541
|
-
* Stable for the life of the event. Two different events must never share
|
|
542
|
-
* one, or marking the first seen silences the second; the same event must
|
|
543
|
-
* never change it, or it comes back every time the page loads.
|
|
544
|
-
*/
|
|
543
|
+
interface Choice {
|
|
545
544
|
id: string;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
onOpen?: () => void;
|
|
545
|
+
question: Text;
|
|
546
|
+
/** What actually changes depending on the answer. Never "choose an option". */
|
|
547
|
+
why?: Text;
|
|
548
|
+
options: ChoiceOption[];
|
|
549
|
+
value: string | null;
|
|
550
|
+
decidedAt?: string;
|
|
551
|
+
decidedBy?: string;
|
|
554
552
|
}
|
|
555
|
-
interface
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
/** Given the count, e.g. (n) => `${n} unread`. */
|
|
561
|
-
unread: (count: number) => string;
|
|
553
|
+
interface ChoiceOption {
|
|
554
|
+
id: string;
|
|
555
|
+
label: Text;
|
|
556
|
+
/** What picking this commits the project to. */
|
|
557
|
+
detail?: Text;
|
|
562
558
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
559
|
+
/** Who the work is for, and against what. */
|
|
560
|
+
interface Market {
|
|
561
|
+
audience: Text;
|
|
562
|
+
positioning: Text;
|
|
563
|
+
competitors: Competitor[];
|
|
564
|
+
pricing?: Text;
|
|
565
|
+
notes?: Text[];
|
|
566
|
+
}
|
|
567
|
+
interface Competitor {
|
|
568
|
+
id: string;
|
|
569
|
+
name: string;
|
|
570
|
+
/** What they do better. Written honestly or it is worthless. */
|
|
571
|
+
strength: Text;
|
|
572
|
+
/** Where they leave room. */
|
|
573
|
+
gap: Text;
|
|
574
|
+
}
|
|
575
|
+
/** The four things the analysis is about, each its own screen. */
|
|
576
|
+
type AnalysisSection = 'board' | 'project' | 'strategy' | 'market';
|
|
577
|
+
declare const ANALYSIS_SECTIONS: AnalysisSection[];
|
|
578
|
+
interface Analysis {
|
|
579
|
+
brief: Text;
|
|
580
|
+
stage: string;
|
|
581
|
+
goals: Goal[];
|
|
582
|
+
constraints: Constraint[];
|
|
583
|
+
metrics: Metric[];
|
|
584
|
+
strategies: Strategy[];
|
|
585
|
+
/** The path the owner picked. Null is a decision waiting, and it is theirs. */
|
|
586
|
+
chosen: string | null;
|
|
587
|
+
suggestions: Suggestion[];
|
|
588
|
+
/** Who this is for and against what. Optional: not every board is a product. */
|
|
589
|
+
market?: Market;
|
|
577
590
|
/**
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
* that has not saved yet does not show the same notifications again.
|
|
591
|
+
* Standing answers, grouped by the screen they belong to. Claude reads them
|
|
592
|
+
* before deciding anything the board does not already settle.
|
|
581
593
|
*/
|
|
582
|
-
|
|
594
|
+
choices?: Partial<Record<AnalysisSection, Choice[]>>;
|
|
583
595
|
}
|
|
584
|
-
|
|
596
|
+
/** Everything the owner has actually decided, newest first. */
|
|
597
|
+
declare function decided(analysis: Analysis): {
|
|
598
|
+
section: AnalysisSection;
|
|
599
|
+
choice: Choice;
|
|
600
|
+
option: ChoiceOption;
|
|
601
|
+
}[];
|
|
602
|
+
/** Questions still open. A count of these is the honest state of the analysis. */
|
|
603
|
+
declare function undecided(analysis: Analysis): {
|
|
604
|
+
section: AnalysisSection;
|
|
605
|
+
choice: Choice;
|
|
606
|
+
}[];
|
|
607
|
+
/**
|
|
608
|
+
* What the board itself says, counted rather than declared.
|
|
609
|
+
*
|
|
610
|
+
* These are not stored: a number typed into the analysis is a number that
|
|
611
|
+
* disagrees with the board the moment a card moves. They are derived on every
|
|
612
|
+
* render from the cards, so they cannot be wrong.
|
|
613
|
+
*/
|
|
614
|
+
interface BoardFacts {
|
|
615
|
+
total: number;
|
|
616
|
+
open: number;
|
|
617
|
+
byColumn: {
|
|
618
|
+
id: string;
|
|
619
|
+
title: Text;
|
|
620
|
+
count: number;
|
|
621
|
+
}[];
|
|
622
|
+
waitingOnOwner: number;
|
|
623
|
+
orders: number;
|
|
624
|
+
highRisk: number;
|
|
625
|
+
/** Cards claiming to be finished that were never picked up. */
|
|
626
|
+
claimed: Pick<Card, 'id' | 'title'>[];
|
|
627
|
+
/** Age of the oldest card still open. Null when nothing is open. */
|
|
628
|
+
oldestOpenDays: number | null;
|
|
629
|
+
oldestOpen: Pick<Card, 'id' | 'title'> | null;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Counts the board rather than believing a number somebody typed.
|
|
633
|
+
*
|
|
634
|
+
* `today` is passed in so the same cards always produce the same answer — a
|
|
635
|
+
* function that reads the clock cannot be checked.
|
|
636
|
+
*/
|
|
637
|
+
declare function boardFacts(cards: Card[], columns: ColumnDef[], today: string): BoardFacts;
|
|
638
|
+
interface Trend {
|
|
639
|
+
latest: Reading | undefined;
|
|
640
|
+
previous: Reading | undefined;
|
|
641
|
+
delta: number | null;
|
|
642
|
+
/** True when the last move went against the metric's declared direction. */
|
|
643
|
+
against: boolean;
|
|
644
|
+
}
|
|
645
|
+
declare function trendOf(metric: Metric): Trend;
|
|
646
|
+
/**
|
|
647
|
+
* Points for a sparkline in a 0–1 box, so the caller only decides the size.
|
|
648
|
+
* A flat series sits on the middle line rather than collapsing to the floor.
|
|
649
|
+
*/
|
|
650
|
+
declare function sparkPoints(readings: Reading[]): {
|
|
651
|
+
x: number;
|
|
652
|
+
y: number;
|
|
653
|
+
}[];
|
|
585
654
|
|
|
655
|
+
type Level = 'high' | 'medium' | 'low';
|
|
656
|
+
type Owner = 'claude' | 'you';
|
|
586
657
|
/**
|
|
587
|
-
*
|
|
658
|
+
* What the owner wants done with a card next, in one tap.
|
|
588
659
|
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
*
|
|
660
|
+
* Free text would be clearer to write and useless to obey: "solve it if you
|
|
661
|
+
* can" and "solve it yourself" are the same sentence and different
|
|
662
|
+
* instructions. A fixed set gives each one an exact, documented meaning, so a
|
|
663
|
+
* tap is a contract rather than a hint. Absent is a real state: no
|
|
664
|
+
* instruction, use judgement.
|
|
592
665
|
*
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
* no button at all: the reader walks away believing they have the text. So the
|
|
596
|
-
* failure falls back to selecting the block, and says which of the two
|
|
597
|
-
* happened.
|
|
666
|
+
* Three of them — explain, askme, hold — are refusals to act. Honouring them
|
|
667
|
+
* exactly matters more than the work: they are the only way to say "not yet".
|
|
598
668
|
*/
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
669
|
+
declare const INTENTS: readonly ["decide", "explain", "solve", "do", "cheap", "safe", "fast", "askme", "hold"];
|
|
670
|
+
type Intent = (typeof INTENTS)[number];
|
|
671
|
+
interface IntentCopy {
|
|
672
|
+
label: Record<Lang, string>;
|
|
673
|
+
meaning: Record<Lang, string>;
|
|
674
|
+
}
|
|
675
|
+
declare const INTENT_COPY: Record<Intent, IntentCopy>;
|
|
676
|
+
interface Check {
|
|
677
|
+
id: string;
|
|
678
|
+
text: Text;
|
|
679
|
+
done: boolean;
|
|
680
|
+
}
|
|
681
|
+
interface Note {
|
|
682
|
+
id: string;
|
|
683
|
+
by: Owner;
|
|
684
|
+
date: string;
|
|
685
|
+
text: Text;
|
|
686
|
+
}
|
|
687
|
+
interface Card {
|
|
688
|
+
id: string;
|
|
689
|
+
column: string;
|
|
690
|
+
title: Text;
|
|
691
|
+
body: Text;
|
|
692
|
+
area: string;
|
|
693
|
+
priority: Level;
|
|
694
|
+
risk: Level;
|
|
695
|
+
owner: Owner;
|
|
696
|
+
/** The one flag that means "I am asking you to do this". */
|
|
697
|
+
order: boolean;
|
|
698
|
+
intent: Intent | null;
|
|
699
|
+
checks: Check[];
|
|
700
|
+
notes: Note[];
|
|
701
|
+
createdAt: string;
|
|
702
|
+
updatedAt: string;
|
|
703
|
+
/**
|
|
704
|
+
* Stamped by the board when the card enters the working column, and when it
|
|
705
|
+
* reaches a done column. They are written by the move, never typed, so a
|
|
706
|
+
* card that carries `completedAt` with no `startedAt` is one that was
|
|
707
|
+
* declared finished without ever being picked up — which is exactly the
|
|
708
|
+
* thing worth being able to see.
|
|
709
|
+
*/
|
|
710
|
+
startedAt?: string | null;
|
|
711
|
+
completedAt?: string | null;
|
|
608
712
|
}
|
|
609
|
-
interface
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
|
|
713
|
+
interface ColumnDef {
|
|
714
|
+
id: string;
|
|
715
|
+
title: Text;
|
|
716
|
+
/** What the column means, shown on hover. Two words cannot say whether a
|
|
717
|
+
card sitting here is waiting on you or on Claude. */
|
|
718
|
+
hint?: Text;
|
|
719
|
+
/** Work in flight: entering this column stamps the card as started. */
|
|
720
|
+
active?: boolean;
|
|
721
|
+
/** Cards here are finished: they leave the open counts alone. */
|
|
722
|
+
done?: boolean;
|
|
614
723
|
}
|
|
615
|
-
declare function CopyBlock({ text, label, note, labels }: CopyBlockProps): React.JSX.Element;
|
|
616
|
-
|
|
617
724
|
/**
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
*
|
|
621
|
-
* someone presses Save, and then it fails with something the person reading it
|
|
622
|
-
* did not cause and cannot place. This turns that into a list they can read
|
|
623
|
-
* before it happens, and act on without leaving the panel.
|
|
624
|
-
*
|
|
625
|
-
* The status comes from the server, which is the only place that can see the
|
|
626
|
-
* environment. It reports *whether* a variable is set and never its value, so
|
|
627
|
-
* this component has nothing worth leaking.
|
|
725
|
+
* One pass over the board: what a session changed, and which cards it
|
|
726
|
+
* touched. Written at the end of a run, so the panel keeps the account
|
|
727
|
+
* rather than the account living in a chat nobody can re-read.
|
|
628
728
|
*/
|
|
629
|
-
|
|
630
|
-
interface SetupRequirement {
|
|
729
|
+
interface Run {
|
|
631
730
|
id: string;
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
/**
|
|
636
|
-
|
|
637
|
-
/**
|
|
638
|
-
|
|
639
|
-
/** Exactly what to do, shown only when it is missing. */
|
|
640
|
-
fix?: React.ReactNode;
|
|
641
|
-
/**
|
|
642
|
-
* Information rather than a gate: drawn in the list, never walked as a step.
|
|
643
|
-
*
|
|
644
|
-
* Every other requirement here is an environment variable, and the
|
|
645
|
-
* walkthrough's one action — re-read the environment from the server —
|
|
646
|
-
* is what clears it. A requirement that action cannot clear parks the
|
|
647
|
-
* walkthrough on it for ever, and because `unknown` is judged across the
|
|
648
|
-
* whole list, one advisory row that nobody wired made every step answer a
|
|
649
|
-
* successful check with "the panel could not ask its own server". That is
|
|
650
|
-
* what the copied gate did the day it was added.
|
|
651
|
-
*/
|
|
652
|
-
advisory?: boolean;
|
|
731
|
+
date: string;
|
|
732
|
+
by: Owner;
|
|
733
|
+
summary: Text;
|
|
734
|
+
/** Card ids this run touched, so a claim can be checked against the board. */
|
|
735
|
+
cards: string[];
|
|
736
|
+
/** The kit fingerprint at the time, twelve characters is enough to compare. */
|
|
737
|
+
fingerprint?: string;
|
|
653
738
|
}
|
|
654
|
-
interface
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
/** Wording, so the kit stays language-agnostic. */
|
|
659
|
-
labels?: Partial<{
|
|
660
|
-
ok: string;
|
|
661
|
-
missing: string;
|
|
662
|
-
unknown: string;
|
|
663
|
-
whatToDo: string;
|
|
664
|
-
}>;
|
|
739
|
+
interface HealthFact {
|
|
740
|
+
label: Text;
|
|
741
|
+
value: Text;
|
|
742
|
+
status: 'ok' | 'warning' | 'bad' | 'unknown';
|
|
665
743
|
}
|
|
666
|
-
declare function SetupGuide({ requirements, readyMessage, labels }: SetupGuideProps): React.JSX.Element;
|
|
667
|
-
|
|
668
744
|
/**
|
|
669
|
-
*
|
|
670
|
-
*
|
|
671
|
-
* `SetupGuide` is the list: the right shape for somebody auditing a panel that
|
|
672
|
-
* already works. This is the other reader — the one who installed it twenty
|
|
673
|
-
* minutes ago, has four things missing and no idea which to do first. A wall
|
|
674
|
-
* of four is not four times as useful as one; it is the thing people close.
|
|
745
|
+
* A theme Claude derived from the project it is installed in.
|
|
675
746
|
*
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
* says the check failed, never that a variable is missing, because telling
|
|
681
|
-
* somebody to add a token they already added is the worse of the two wrong
|
|
682
|
-
* answers.
|
|
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.
|
|
683
751
|
*
|
|
684
|
-
*
|
|
685
|
-
*
|
|
686
|
-
*
|
|
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.
|
|
687
756
|
*/
|
|
688
|
-
interface
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
/**
|
|
704
|
-
|
|
705
|
-
readyBody: string;
|
|
706
|
-
/** The two things to try before trusting the gate, as list items. */
|
|
707
|
-
verify: string[];
|
|
708
|
-
/** Closes the wizard. */
|
|
709
|
-
close: string;
|
|
710
|
-
/** The escape hatch: heading, explanation, and the prompt to copy. */
|
|
711
|
-
stuckTitle: string;
|
|
712
|
-
stuckBody: string;
|
|
713
|
-
stuckPrompt: string;
|
|
714
|
-
copy: string;
|
|
715
|
-
copied: string;
|
|
716
|
-
}
|
|
717
|
-
interface SetupWizardProps {
|
|
718
|
-
/** In the order they are to be done. The caller owns that order. */
|
|
719
|
-
requirements: SetupRequirement[];
|
|
720
|
-
labels: SetupWizardLabels;
|
|
721
|
-
/** Re-reads the environment. Resolves when the answer has been applied. */
|
|
722
|
-
onCheck: () => Promise<void> | void;
|
|
723
|
-
/** Told when the reader closes it. */
|
|
724
|
-
onClose?: () => void;
|
|
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;
|
|
725
774
|
}
|
|
726
|
-
declare function SetupWizard({ requirements, labels, onCheck, onClose }: SetupWizardProps): React.JSX.Element;
|
|
727
|
-
|
|
728
775
|
/**
|
|
729
|
-
*
|
|
776
|
+
* What the owner has chosen, kept in the board so it survives a new browser.
|
|
730
777
|
*
|
|
731
|
-
*
|
|
732
|
-
*
|
|
733
|
-
*
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
*
|
|
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.
|
|
737
784
|
*
|
|
738
|
-
* The
|
|
739
|
-
*
|
|
740
|
-
*
|
|
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.
|
|
741
788
|
*/
|
|
742
|
-
interface
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
/**
|
|
746
|
-
|
|
747
|
-
/**
|
|
748
|
-
|
|
749
|
-
/** Every requirement that is in place, in the caller's order. */
|
|
750
|
-
done: SetupRequirement[];
|
|
751
|
-
/** True when every requirement is `ok`. */
|
|
752
|
-
ready: boolean;
|
|
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[];
|
|
753
796
|
/**
|
|
754
|
-
*
|
|
797
|
+
* Whether an update card is written as an order rather than a suggestion.
|
|
755
798
|
*
|
|
756
|
-
*
|
|
757
|
-
*
|
|
758
|
-
*
|
|
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.
|
|
759
802
|
*/
|
|
760
|
-
|
|
803
|
+
updateAsOrder?: boolean;
|
|
761
804
|
}
|
|
762
|
-
|
|
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;
|
|
763
844
|
/**
|
|
764
|
-
* Whether
|
|
765
|
-
* means here.
|
|
845
|
+
* Whether a finished card has been finished long enough to stop being drawn.
|
|
766
846
|
*
|
|
767
|
-
*
|
|
768
|
-
*
|
|
769
|
-
*
|
|
770
|
-
*
|
|
771
|
-
* sees it — and it is still that reader's first run.
|
|
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.
|
|
772
851
|
*
|
|
773
|
-
* A
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
*
|
|
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`.
|
|
777
857
|
*/
|
|
778
|
-
declare function
|
|
779
|
-
cards?: unknown[];
|
|
780
|
-
runs?: unknown[];
|
|
781
|
-
}): boolean;
|
|
782
|
-
|
|
783
|
-
type Handler = (payload: Record<string, string>, event: MouseEvent) => void;
|
|
858
|
+
declare function isArchived(card: Card, doneColumns: ReadonlySet<string>, now?: string, afterDays?: number): boolean;
|
|
784
859
|
/**
|
|
785
|
-
*
|
|
786
|
-
*
|
|
787
|
-
*
|
|
788
|
-
*
|
|
789
|
-
* Example: useDelegatedClick('data-action', (payload) => { if (payload.action === 'delete') ... })
|
|
790
|
-
* with buttons such as <button data-action="delete" data-id="42">
|
|
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.
|
|
791
863
|
*/
|
|
792
|
-
declare function
|
|
793
|
-
|
|
794
|
-
declare function useMediaQuery(query: string): boolean;
|
|
795
|
-
|
|
864
|
+
declare function stampForColumn(card: Card, columns: ColumnDef[]): Card;
|
|
796
865
|
/**
|
|
797
|
-
*
|
|
866
|
+
* Where a card the panel writes on the reader's behalf goes.
|
|
798
867
|
*
|
|
799
|
-
*
|
|
800
|
-
*
|
|
801
|
-
*
|
|
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.
|
|
802
880
|
*/
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
declare
|
|
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
|
+
|
|
806
899
|
/**
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
*
|
|
810
|
-
* a
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
*
|
|
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.
|
|
814
917
|
*/
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
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;
|
|
819
923
|
/**
|
|
820
|
-
*
|
|
821
|
-
*
|
|
822
|
-
*
|
|
823
|
-
*
|
|
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.
|
|
824
929
|
*/
|
|
825
|
-
declare function
|
|
930
|
+
declare function pendingRequest(cards: Card[], kind: RequestKind, doneColumns: ReadonlySet<string>, subject?: string): Card | undefined;
|
|
826
931
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
label: Text;
|
|
833
|
-
weight: Weight;
|
|
834
|
-
}
|
|
835
|
-
interface Constraint {
|
|
836
|
-
id: string;
|
|
837
|
-
label: Text;
|
|
838
|
-
on: boolean;
|
|
839
|
-
}
|
|
840
|
-
interface Reading {
|
|
841
|
-
date: string;
|
|
842
|
-
value: number;
|
|
843
|
-
note?: Text;
|
|
844
|
-
}
|
|
845
|
-
interface Metric {
|
|
846
|
-
id: string;
|
|
847
|
-
label: Text;
|
|
848
|
-
unit: string;
|
|
849
|
-
/** Which direction counts as better. A reading against it is what "drifting" means. */
|
|
850
|
-
goal: 'up' | 'down';
|
|
851
|
-
readings: Reading[];
|
|
852
|
-
}
|
|
853
|
-
interface Strategy {
|
|
854
|
-
id: string;
|
|
855
|
-
title: Text;
|
|
856
|
-
thesis: Text;
|
|
857
|
-
horizon: Text;
|
|
858
|
-
/** 0–10, so three bars can be compared at a glance rather than read. */
|
|
859
|
-
effort: number;
|
|
860
|
-
risk: number;
|
|
861
|
-
upside: number;
|
|
862
|
-
moves: Text[];
|
|
932
|
+
interface DetailSheetProps {
|
|
933
|
+
open: boolean;
|
|
934
|
+
onClose: () => void;
|
|
935
|
+
children: React.ReactNode;
|
|
936
|
+
ariaLabel?: string;
|
|
863
937
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
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;
|
|
871
949
|
}
|
|
950
|
+
declare function DetailSection({ title, subtitle, children }: DetailSectionProps): React.JSX.Element;
|
|
951
|
+
|
|
872
952
|
/**
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
* This is the whole point of the analysis view. A board says what to do; a
|
|
876
|
-
* choice says how the project wants it done, in the owner's words, once —
|
|
877
|
-
* instead of being asked again on every card. Claude reads these before
|
|
878
|
-
* deciding anything, so answering one here is worth more than answering it
|
|
879
|
-
* in a message that scrolls away.
|
|
953
|
+
* Whether a note has been dismissed, and how to dismiss it.
|
|
880
954
|
*
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
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.
|
|
884
958
|
*/
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
decidedBy?: string;
|
|
894
|
-
}
|
|
895
|
-
interface ChoiceOption {
|
|
896
|
-
id: string;
|
|
897
|
-
label: Text;
|
|
898
|
-
/** What picking this commits the project to. */
|
|
899
|
-
detail?: Text;
|
|
900
|
-
}
|
|
901
|
-
/** Who the work is for, and against what. */
|
|
902
|
-
interface Market {
|
|
903
|
-
audience: Text;
|
|
904
|
-
positioning: Text;
|
|
905
|
-
competitors: Competitor[];
|
|
906
|
-
pricing?: Text;
|
|
907
|
-
notes?: Text[];
|
|
908
|
-
}
|
|
909
|
-
interface Competitor {
|
|
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. */
|
|
910
967
|
id: string;
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
/**
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
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'>>;
|
|
932
994
|
/**
|
|
933
|
-
*
|
|
934
|
-
*
|
|
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.
|
|
935
1002
|
*/
|
|
936
|
-
|
|
1003
|
+
dismissed?: string[];
|
|
1004
|
+
/** Told when a note is dismissed. Required for `dismissed` to mean anything. */
|
|
1005
|
+
onDismiss?: (id: string) => void;
|
|
937
1006
|
}
|
|
938
|
-
/** Everything the owner has actually decided, newest first. */
|
|
939
|
-
declare function decided(analysis: Analysis): {
|
|
940
|
-
section: AnalysisSection;
|
|
941
|
-
choice: Choice;
|
|
942
|
-
option: ChoiceOption;
|
|
943
|
-
}[];
|
|
944
|
-
/** Questions still open. A count of these is the honest state of the analysis. */
|
|
945
|
-
declare function undecided(analysis: Analysis): {
|
|
946
|
-
section: AnalysisSection;
|
|
947
|
-
choice: Choice;
|
|
948
|
-
}[];
|
|
949
1007
|
/**
|
|
950
|
-
*
|
|
1008
|
+
* One note at a time, in order.
|
|
951
1009
|
*
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
*
|
|
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.
|
|
955
1013
|
*/
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
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;
|
|
972
1061
|
}
|
|
1062
|
+
declare function NotificationBell({ notifications, labels, seen: given, onSeen }: NotificationBellProps): React.JSX.Element | null;
|
|
1063
|
+
|
|
973
1064
|
/**
|
|
974
|
-
*
|
|
1065
|
+
* A block of text whose whole purpose is to end up somewhere else.
|
|
975
1066
|
*
|
|
976
|
-
*
|
|
977
|
-
*
|
|
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.
|
|
978
1076
|
*/
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
/**
|
|
985
|
-
|
|
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>;
|
|
986
1086
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
y: number;
|
|
995
|
-
}[];
|
|
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;
|
|
996
1094
|
|
|
997
|
-
type Level = 'high' | 'medium' | 'low';
|
|
998
|
-
type Owner = 'claude' | 'you';
|
|
999
1095
|
/**
|
|
1000
|
-
* What the
|
|
1096
|
+
* What the panel needs in order to work, and what to do about what is missing.
|
|
1001
1097
|
*
|
|
1002
|
-
*
|
|
1003
|
-
*
|
|
1004
|
-
*
|
|
1005
|
-
*
|
|
1006
|
-
* instruction, use judgement.
|
|
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.
|
|
1007
1102
|
*
|
|
1008
|
-
*
|
|
1009
|
-
*
|
|
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.
|
|
1010
1106
|
*/
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
interface IntentCopy {
|
|
1014
|
-
label: Record<Lang, string>;
|
|
1015
|
-
meaning: Record<Lang, string>;
|
|
1016
|
-
}
|
|
1017
|
-
declare const INTENT_COPY: Record<Intent, IntentCopy>;
|
|
1018
|
-
interface Check {
|
|
1019
|
-
id: string;
|
|
1020
|
-
text: Text;
|
|
1021
|
-
done: boolean;
|
|
1022
|
-
}
|
|
1023
|
-
interface Note {
|
|
1024
|
-
id: string;
|
|
1025
|
-
by: Owner;
|
|
1026
|
-
date: string;
|
|
1027
|
-
text: Text;
|
|
1028
|
-
}
|
|
1029
|
-
interface Card {
|
|
1107
|
+
type SetupStatus = 'ok' | 'missing' | 'unknown';
|
|
1108
|
+
interface SetupRequirement {
|
|
1030
1109
|
id: string;
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
/**
|
|
1039
|
-
|
|
1040
|
-
intent: Intent | null;
|
|
1041
|
-
checks: Check[];
|
|
1042
|
-
notes: Note[];
|
|
1043
|
-
createdAt: string;
|
|
1044
|
-
updatedAt: 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;
|
|
1045
1119
|
/**
|
|
1046
|
-
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
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.
|
|
1051
1129
|
*/
|
|
1052
|
-
|
|
1053
|
-
completedAt?: string | null;
|
|
1054
|
-
}
|
|
1055
|
-
interface ColumnDef {
|
|
1056
|
-
id: string;
|
|
1057
|
-
title: Text;
|
|
1058
|
-
/** What the column means, shown on hover. Two words cannot say whether a
|
|
1059
|
-
card sitting here is waiting on you or on Claude. */
|
|
1060
|
-
hint?: Text;
|
|
1061
|
-
/** Work in flight: entering this column stamps the card as started. */
|
|
1062
|
-
active?: boolean;
|
|
1063
|
-
/** Cards here are finished: they leave the open counts alone. */
|
|
1064
|
-
done?: boolean;
|
|
1065
|
-
}
|
|
1066
|
-
/**
|
|
1067
|
-
* One pass over the board: what a session changed, and which cards it
|
|
1068
|
-
* touched. Written at the end of a run, so the panel keeps the account
|
|
1069
|
-
* rather than the account living in a chat nobody can re-read.
|
|
1070
|
-
*/
|
|
1071
|
-
interface Run {
|
|
1072
|
-
id: string;
|
|
1073
|
-
date: string;
|
|
1074
|
-
by: Owner;
|
|
1075
|
-
summary: Text;
|
|
1076
|
-
/** Card ids this run touched, so a claim can be checked against the board. */
|
|
1077
|
-
cards: string[];
|
|
1078
|
-
/** The kit fingerprint at the time, twelve characters is enough to compare. */
|
|
1079
|
-
fingerprint?: string;
|
|
1130
|
+
advisory?: boolean;
|
|
1080
1131
|
}
|
|
1081
|
-
interface
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
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
|
+
}>;
|
|
1085
1143
|
}
|
|
1144
|
+
declare function SetupGuide({ requirements, readyMessage, labels }: SetupGuideProps): React.JSX.Element;
|
|
1145
|
+
|
|
1086
1146
|
/**
|
|
1087
|
-
*
|
|
1147
|
+
* The setup, walked one step at a time.
|
|
1088
1148
|
*
|
|
1089
|
-
*
|
|
1090
|
-
*
|
|
1091
|
-
*
|
|
1092
|
-
*
|
|
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.
|
|
1093
1153
|
*
|
|
1094
|
-
*
|
|
1095
|
-
*
|
|
1096
|
-
*
|
|
1097
|
-
*
|
|
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.
|
|
1098
1165
|
*/
|
|
1099
|
-
interface
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
/**
|
|
1115
|
-
|
|
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;
|
|
1116
1203
|
}
|
|
1204
|
+
declare function SetupWizard({ requirements, labels, onCheck, onClose }: SetupWizardProps): React.JSX.Element;
|
|
1205
|
+
|
|
1117
1206
|
/**
|
|
1118
|
-
*
|
|
1207
|
+
* Where a reader is in the setup, as one answer instead of a list to read.
|
|
1119
1208
|
*
|
|
1120
|
-
*
|
|
1121
|
-
*
|
|
1122
|
-
*
|
|
1123
|
-
*
|
|
1124
|
-
*
|
|
1125
|
-
*
|
|
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.
|
|
1126
1215
|
*
|
|
1127
|
-
* The
|
|
1128
|
-
*
|
|
1129
|
-
*
|
|
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.
|
|
1130
1219
|
*/
|
|
1131
|
-
interface
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
/**
|
|
1135
|
-
|
|
1136
|
-
/**
|
|
1137
|
-
|
|
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;
|
|
1138
1231
|
/**
|
|
1139
|
-
*
|
|
1232
|
+
* True when the panel could not ask the server, so nothing is claimed.
|
|
1140
1233
|
*
|
|
1141
|
-
*
|
|
1142
|
-
*
|
|
1143
|
-
*
|
|
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.
|
|
1144
1237
|
*/
|
|
1145
|
-
|
|
1146
|
-
}
|
|
1147
|
-
interface PanelState {
|
|
1148
|
-
v: 2;
|
|
1149
|
-
project: string;
|
|
1150
|
-
environment: Text;
|
|
1151
|
-
updatedAt: string;
|
|
1152
|
-
notice?: Text;
|
|
1153
|
-
columns: ColumnDef[];
|
|
1154
|
-
areas: {
|
|
1155
|
-
id: string;
|
|
1156
|
-
label: Text;
|
|
1157
|
-
}[];
|
|
1158
|
-
health: HealthFact[];
|
|
1159
|
-
cards: Card[];
|
|
1160
|
-
/** Newest first. Capped when saving, so the file cannot grow without end. */
|
|
1161
|
-
runs: Run[];
|
|
1162
|
-
/** What any of the work is for: goals, constraints, metrics, strategies. */
|
|
1163
|
-
analysis?: Analysis;
|
|
1164
|
-
/** The owner's choices, so they survive a new browser. */
|
|
1165
|
-
preferences?: Preferences;
|
|
1166
|
-
/** A theme Claude derived from this project, with where it came from. */
|
|
1167
|
-
importedTheme?: ImportedTheme;
|
|
1238
|
+
unknown: boolean;
|
|
1168
1239
|
}
|
|
1169
|
-
declare
|
|
1170
|
-
declare const OWNERS: Owner[];
|
|
1171
|
-
declare function newId(): string;
|
|
1172
|
-
declare function today(): string;
|
|
1173
|
-
/** A card is created empty and named second — the same as tapping "+" on paper. */
|
|
1174
|
-
declare function emptyCard(column: string, area: string): Card;
|
|
1175
|
-
/**
|
|
1176
|
-
* How long a finished card stays on the board before it becomes history.
|
|
1177
|
-
*
|
|
1178
|
-
* A done column only grows. It is the one column nothing ever removes from,
|
|
1179
|
-
* so a board that is being worked turns into a wall of things already
|
|
1180
|
-
* finished, and the three columns that need reading get a third of the screen
|
|
1181
|
-
* between them. A week is the window in which "what did we ship" is still a
|
|
1182
|
-
* live question; after that it is a record, and a record does not need to be
|
|
1183
|
-
* in the way.
|
|
1184
|
-
*/
|
|
1185
|
-
declare const ARCHIVE_AFTER_DAYS = 7;
|
|
1240
|
+
declare function wizardStep(requirements: SetupRequirement[]): WizardStep;
|
|
1186
1241
|
/**
|
|
1187
|
-
* Whether
|
|
1242
|
+
* Whether this panel has never been worked, which is what "first install"
|
|
1243
|
+
* means here.
|
|
1188
1244
|
*
|
|
1189
|
-
*
|
|
1190
|
-
*
|
|
1191
|
-
*
|
|
1192
|
-
*
|
|
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.
|
|
1193
1250
|
*
|
|
1194
|
-
*
|
|
1195
|
-
*
|
|
1196
|
-
*
|
|
1197
|
-
*
|
|
1198
|
-
* because the check could not run. `unknown` is not `old`.
|
|
1199
|
-
*/
|
|
1200
|
-
declare function isArchived(card: Card, doneColumns: ReadonlySet<string>, now?: string, afterDays?: number): boolean;
|
|
1201
|
-
/**
|
|
1202
|
-
* Applies the column's own meaning to a card that just moved into it: a card
|
|
1203
|
-
* entering the working column is started, one reaching a done column is
|
|
1204
|
-
* finished, and one dragged back out of done is not finished any more.
|
|
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.
|
|
1205
1255
|
*/
|
|
1206
|
-
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;
|
|
1207
1262
|
/**
|
|
1208
|
-
*
|
|
1209
|
-
*
|
|
1210
|
-
*
|
|
1211
|
-
* a column literally called `next`. The default board has one; a board whose
|
|
1212
|
-
* owner renamed it does not, and a card in a column that does not exist is
|
|
1213
|
-
* the exact fault `validateBoard` names — drawn nowhere, and refused by the
|
|
1214
|
-
* write path, so one tap on a button turned a saveable board into one that
|
|
1215
|
-
* could not be saved and said so about a column the reader had never heard of.
|
|
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.
|
|
1216
1266
|
*
|
|
1217
|
-
*
|
|
1218
|
-
*
|
|
1219
|
-
* queued work goes where queued work already sits. And the first column of
|
|
1220
|
-
* all when nothing else fits, since a board with no columns cannot pass the
|
|
1221
|
-
* validator either way.
|
|
1267
|
+
* Example: useDelegatedClick('data-action', (payload) => { if (payload.action === 'delete') ... })
|
|
1268
|
+
* with buttons such as <button data-action="delete" data-id="42">
|
|
1222
1269
|
*/
|
|
1223
|
-
declare function
|
|
1224
|
-
|
|
1225
|
-
declare function
|
|
1226
|
-
interface Counts {
|
|
1227
|
-
open: number;
|
|
1228
|
-
orders: number;
|
|
1229
|
-
yours: number;
|
|
1230
|
-
decisions: number;
|
|
1231
|
-
highRisk: number;
|
|
1232
|
-
done: number;
|
|
1233
|
-
}
|
|
1234
|
-
declare function countCards(state: PanelState): Counts;
|
|
1235
|
-
/** How many checklist steps are ticked, for the counter on a card. */
|
|
1236
|
-
declare function checkProgress(card: Card): {
|
|
1237
|
-
done: number;
|
|
1238
|
-
total: number;
|
|
1239
|
-
} | undefined;
|
|
1270
|
+
declare function useDelegatedClick(attribute: string, handler: Handler): void;
|
|
1271
|
+
|
|
1272
|
+
declare function useMediaQuery(query: string): boolean;
|
|
1240
1273
|
|
|
1241
1274
|
/**
|
|
1242
1275
|
* Reads a board and says what is wrong with it, in words.
|
|
@@ -1471,6 +1504,18 @@ interface UiStrings {
|
|
|
1471
1504
|
releaseNone: string;
|
|
1472
1505
|
updateCardTitle: (version: string) => string;
|
|
1473
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[];
|
|
1474
1519
|
cardDetail: string;
|
|
1475
1520
|
panelSettings: string;
|
|
1476
1521
|
title: string;
|
|
@@ -2004,8 +2049,15 @@ interface MarketplaceViewProps {
|
|
|
2004
2049
|
ui: UiStrings;
|
|
2005
2050
|
/** Absent where there is no server to ask, so no dead button is drawn. */
|
|
2006
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;
|
|
2007
2059
|
}
|
|
2008
|
-
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;
|
|
2009
2061
|
|
|
2010
2062
|
interface CardSheetProps {
|
|
2011
2063
|
card: Card | null;
|
|
@@ -2018,4 +2070,4 @@ interface CardSheetProps {
|
|
|
2018
2070
|
}
|
|
2019
2071
|
declare function CardSheet({ card, state, lang, ui, onChange, onDelete, onClose }: CardSheetProps): React.JSX.Element | null;
|
|
2020
2072
|
|
|
2021
|
-
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, dropIndex, emptyCard, explainBoard, gateStatus, isArchived, isCard, isFirstRun, ledgerForm, midnightTheme, moveCardInColumns, newId, offeredThemes, oldMoneyTheme, panelForm, paneltirBuild, paperForm, queueColumn, 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 };
|