@mythicalos/ui-core 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -0
- package/dist/index.js +23 -0
- package/dist/logic/file-explorer.d.ts +374 -0
- package/dist/logic/file-explorer.js +624 -0
- package/dist/logic/popover.d.ts +242 -0
- package/dist/logic/popover.js +303 -0
- package/dist/logic/queue.d.ts +142 -0
- package/dist/logic/queue.js +143 -0
- package/dist/logic/sendbar.d.ts +105 -0
- package/dist/logic/sendbar.js +136 -0
- package/dist/logic/session-card.d.ts +263 -0
- package/dist/logic/session-card.js +443 -0
- package/dist/logic/terminal.d.ts +222 -0
- package/dist/logic/terminal.js +264 -0
- package/package.json +1 -1
- package/src/select/mythical-select.js +3 -2
- package/styles.css +652 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
export { buttonClass, type BtnVariant, type BtnState } from "./logic/button.js";
|
|
2
2
|
export { nextPollDelay, makePollEpochGuard, runPollTick, shouldResetEpoch, POLL_JITTER_RATIO, POLL_BACKOFF_CAP_MS, type PollResult, type PollEpochGuard, type PollTickIO, } from "./logic/poll.js";
|
|
3
3
|
export { typedNameMatches, BULLET_ICON, type DialogBullet } from "./logic/dialog.js";
|
|
4
|
+
export { POPOVER_CARET, POPOVER_CHECK, POPOVER_GAP_PX, POPOVER_BREATHING_PX, POPOVER_EMPTY_VALUE, POPOVER_DEFAULT_POSITION, POPOVER_CLASS, POPOVER_DECORATIVE_ARIA, POPOVER_SEPARATOR_ARIA, resolvePopoverPlacement, resolvePopoverAlign, resolvePopoverPosition, samePopoverPosition, popoverTriggerClass, popoverPanelClass, popoverItemClass, popoverIds, popoverTriggerAria, popoverPanelAria, popoverMenuAria, popoverItemAria, popoverTriggerKeyAction, popoverPanelKeyAction, popoverKeyHandled, popoverAppliesToFocus, edgePopoverIndex, stepPopoverIndex, initialPopoverIndex, resolvePopoverIndex, popoverTriggerText, popoverHasSlotContent, type PopoverItem, type PopoverPlacement, type PopoverAlign, type PopoverPosition, type PopoverRect, type PopoverSize, type PopoverViewport, type PopoverTriggerState, type PopoverIds, type PopoverTriggerAria, type PopoverPanelAria, type PopoverMenuAria, type PopoverItemAria, type PopoverTriggerKeyAction, type PopoverPanelKeyAction, type PopoverTriggerText, type PopoverTriggerTextOptions, } from "./logic/popover.js";
|
|
4
5
|
export { composeToastText, TOAST_SEP, type ToastStatus, type ToastSpec, type ToastBus, } from "./logic/toast.js";
|
|
5
6
|
export { gaugeTone, gaugeGeom, type GaugeGeom } from "./logic/gauge.js";
|
|
6
7
|
export { chipClass, statusLineClass, bannerClass, BANNER_ICON, type Tone, type ChipTone, type StatusTone, type BannerTone, } from "./logic/tone.js";
|
|
7
8
|
export { saveBarNote, saveBarDirty, saveBarClass, SAVE_BAR_PARTS, SAVE_BAR_SEP, SAVE_BAR_DISCARD_LABEL, SAVE_BAR_SAVE_LABEL, type SaveBarNote, } from "./logic/save-bar.js";
|
|
8
9
|
export { statTilesClass, statTileClass, STAT_TILE_PARTS, formatStatCompact, formatStatCount, formatStatUsd, formatStatPercent, STAT_TILE_EMPTY, STAT_TILE_MINUS, type StatTile, type StatTileTone, } from "./logic/stat-tiles.js";
|
|
9
10
|
export { gitFlags, hasGitStatus, gitBranchLabel, gitChipClass, gitFlagClass, gitChipNote, GIT_CHIP_PARTS, GIT_BRANCH_GLYPH, GIT_BRANCH_UNKNOWN, GIT_DETACHED_LABEL, GIT_CLEAN_LABEL, GIT_LOADING_NOTE, GIT_UNAVAILABLE_NOTE, GIT_STALE_LABEL, GIT_STALE_TITLE, type GitStatus, type GitFlag, type GitFlagTone, type GitChipState, } from "./logic/git-chip.js";
|
|
11
|
+
export { deriveFileTreeRows, countFileRows, countLoadedFiles, classifyDirNode, repoBadges, isUsableEntryName, isUsableRootKey, MAX_TREE_DEPTH, NODE_GLYPH, CHEVRON_OPEN, CHEVRON_CLOSED, chevronGlyph, scopeGlyph, SCOPE_CARET, fileGlyph, isMarkdownName, NODE_ID_SEP, nodeId, splitNodeId, childRelPath, parentRelPath, ancestorRelPaths, BREADCRUMB_SEP, breadcrumbSegments, buildBreadcrumb, gitMarkTone, gitMarkLabel, gitMarkClass, previewBadge, previewBadgeClass, HONEST_STATUSES, isHonestStatus, honestNoteText, honestNoteClass, treeNoteClass, treeNoteRowClass, previewNoteText, previewNoteClass, previewBodyMode, previewMeta, formatFileSize, formatRelativeTime, SECONDS_THRESHOLD, MAX_INDENT_DEPTH, indentClass, dirRowClass, fileRowClass, badgeClass, scopeItemClass, type FileTreeMode, type FileNodeKind, type GitMark, type GitMarkTone, type FileTreeRootSpec, type FileTreeEntry, type DirState, type HonestStatus, type HonestSurface, type FilePreviewState, type FileRowBadge, type PreviewBadgeState, type PreviewBadge, type FileTreeDirRow, type FileTreeFileRow, type FileTreeNoteRow, type FileTreeRow, type DeriveFileTreeInput, type FileScopeOption, } from "./logic/file-explorer.js";
|
|
12
|
+
export { ctxBand, ctxReading, ctxFillPct, ctxValueText, ctxNoteText, ctxMeterClass, ctxBarGeom, normalizeCtxThresholds, sessionStatus, sessionStatusClass, sessionStatusText, sessionCardIsStale, sessionCardStale, sessionCardClass, sessionAvatarInitial, sessionSubline, sessionSpineNodes, sessionSpineLabel, sessionSpineSavedText, sessionSpineSummary, spineNodeClass, CTX_THRESHOLDS_DEFAULT, CTX_UNKNOWN_TEXT, CTX_LABEL, CTX_BAR_SPAN, CTX_BAR_HEIGHT, CTX_BAR_VIEWBOX, CTX_BAR_TICK_WIDTH, SESSION_CARD_SEP, SESSION_STATUS_UNKNOWN, SESSION_AVATAR_UNKNOWN, SPINE_SAVED_UNKNOWN, SPINE_MAX_NODES, type CtxThresholds, type CtxBand, type CtxBarTick, type CtxBarGeom, type SessionLifecycle, type SessionActivity, type SessionStatusTone, type SessionStatusKey, type SessionStatus, type SessionStatusInput, type SessionSpine, type SpineNode, type SpineSummary, } from "./logic/session-card.js";
|
|
13
|
+
export { DEFAULT_DELIVERY_CLASS, DELIVERY_CLASSES, DELIVERY_HINT, SEND_PLACEHOLDER, SEND_DISABLED_FALLBACK, SEND_BUTTON_LABEL, SENDBAR_CLASSES, canSend, clearDraftOnSend, deliveryClassButtonClass, deliveryClassLabel, keyAction, makeSendGate, resolveSend, sendBarClass, sendPlaceholder, showDeliveryHint, type DeliveryClass, type KeyAction, type SendGate, } from "./logic/sendbar.js";
|
|
14
|
+
export { QUEUE_CANCEL_ASK, QUEUE_CANCEL_LABEL, QUEUE_CANCEL_NO, QUEUE_CANCEL_YES, QUEUE_EMPTY_COPY, QUEUE_LOADING_COPY, QUEUE_STALE_COPY, QUEUE_UNAVAILABLE_COPY, QUEUE_CLASSES, canCancelRow, cancelReducer, isFreshSource, queueBadgeClass, queueBadgeLabel, queueRowClass, queueStatusLabel, queueView, shouldDisarmCancel, unavailableText, type CancelEvent, type CancelState, type QueueItem, type QueueItemStatus, type QueueSource, type QueueUnavailableReason, type QueueView, } from "./logic/queue.js";
|
|
15
|
+
export { ROW_SCOPE, TERM_CLASS, TERM_CLASSES, TERM_FAILED_COPY, TERM_LOADING_COPY, TERM_MISSING_COPY, TERM_NO_EVENTS_COPY, TERM_ROW_KINDS, TERM_STALE_COPY, TERM_STOP_KEY_HINT, TERM_STOP_LABEL, TERM_UNADDRESSABLE_COPY, TERM_WAKE_UNAVAILABLE_COPY, TURN_IDLE_COPY, TURN_IN_FLIGHT_COPY, ROW_COLLAPSE_LABEL, ROW_EXPAND_LABEL, currentWakeRows, expandLabel, historySegmentCaption, historyToggleLabel, isExpandable, lastBoundaryIndex, noiseShow, rowKey, rowKeys, scopedRowKeys, segmentKeys, shouldStopOnKey, sourceRows, stopButtonClass, termRowClass, termTitleText, transcriptView, turnCaption, uniqueKeys, visibleRows, type TermRow, type TermRowKind, type TermSource, type TermView, type TranscriptSegment, } from "./logic/terminal.js";
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export { buttonClass } from "./logic/button.js";
|
|
5
5
|
export { nextPollDelay, makePollEpochGuard, runPollTick, shouldResetEpoch, POLL_JITTER_RATIO, POLL_BACKOFF_CAP_MS, } from "./logic/poll.js";
|
|
6
6
|
export { typedNameMatches, BULLET_ICON } from "./logic/dialog.js";
|
|
7
|
+
export { POPOVER_CARET, POPOVER_CHECK, POPOVER_GAP_PX, POPOVER_BREATHING_PX, POPOVER_EMPTY_VALUE, POPOVER_DEFAULT_POSITION, POPOVER_CLASS, POPOVER_DECORATIVE_ARIA, POPOVER_SEPARATOR_ARIA, resolvePopoverPlacement, resolvePopoverAlign, resolvePopoverPosition, samePopoverPosition, popoverTriggerClass, popoverPanelClass, popoverItemClass, popoverIds, popoverTriggerAria, popoverPanelAria, popoverMenuAria, popoverItemAria, popoverTriggerKeyAction, popoverPanelKeyAction, popoverKeyHandled, popoverAppliesToFocus, edgePopoverIndex, stepPopoverIndex, initialPopoverIndex, resolvePopoverIndex, popoverTriggerText, popoverHasSlotContent, } from "./logic/popover.js";
|
|
7
8
|
export { composeToastText, TOAST_SEP, } from "./logic/toast.js";
|
|
8
9
|
export { gaugeTone, gaugeGeom } from "./logic/gauge.js";
|
|
9
10
|
export { chipClass, statusLineClass, bannerClass, BANNER_ICON, } from "./logic/tone.js";
|
|
@@ -11,3 +12,25 @@ export { chipClass, statusLineClass, bannerClass, BANNER_ICON, } from "./logic/t
|
|
|
11
12
|
export { saveBarNote, saveBarDirty, saveBarClass, SAVE_BAR_PARTS, SAVE_BAR_SEP, SAVE_BAR_DISCARD_LABEL, SAVE_BAR_SAVE_LABEL, } from "./logic/save-bar.js";
|
|
12
13
|
export { statTilesClass, statTileClass, STAT_TILE_PARTS, formatStatCompact, formatStatCount, formatStatUsd, formatStatPercent, STAT_TILE_EMPTY, STAT_TILE_MINUS, } from "./logic/stat-tiles.js";
|
|
13
14
|
export { gitFlags, hasGitStatus, gitBranchLabel, gitChipClass, gitFlagClass, gitChipNote, GIT_CHIP_PARTS, GIT_BRANCH_GLYPH, GIT_BRANCH_UNKNOWN, GIT_DETACHED_LABEL, GIT_CLEAN_LABEL, GIT_LOADING_NOTE, GIT_UNAVAILABLE_NOTE, GIT_STALE_LABEL, GIT_STALE_TITLE, } from "./logic/git-chip.js";
|
|
15
|
+
// ── file explorer & markdown preview (ds/components-file-explorer) ──
|
|
16
|
+
export {
|
|
17
|
+
// tree derivation
|
|
18
|
+
deriveFileTreeRows, countFileRows, countLoadedFiles, classifyDirNode, repoBadges, isUsableEntryName, isUsableRootKey, MAX_TREE_DEPTH,
|
|
19
|
+
// glyphs
|
|
20
|
+
NODE_GLYPH, CHEVRON_OPEN, CHEVRON_CLOSED, chevronGlyph, scopeGlyph, SCOPE_CARET, fileGlyph, isMarkdownName,
|
|
21
|
+
// path + breadcrumb composition
|
|
22
|
+
NODE_ID_SEP, nodeId, splitNodeId, childRelPath, parentRelPath, ancestorRelPaths, BREADCRUMB_SEP, breadcrumbSegments, buildBreadcrumb,
|
|
23
|
+
// git marks
|
|
24
|
+
gitMarkTone, gitMarkLabel, gitMarkClass, previewBadge, previewBadgeClass,
|
|
25
|
+
// honest states
|
|
26
|
+
HONEST_STATUSES, isHonestStatus, honestNoteText, honestNoteClass, treeNoteClass, treeNoteRowClass, previewNoteText, previewNoteClass, previewBodyMode, previewMeta,
|
|
27
|
+
// formatting
|
|
28
|
+
formatFileSize, formatRelativeTime, SECONDS_THRESHOLD,
|
|
29
|
+
// class strings
|
|
30
|
+
MAX_INDENT_DEPTH, indentClass, dirRowClass, fileRowClass, badgeClass, scopeItemClass, } from "./logic/file-explorer.js";
|
|
31
|
+
// ── session-card (design registry `session-card` v1) ──
|
|
32
|
+
export { ctxBand, ctxReading, ctxFillPct, ctxValueText, ctxNoteText, ctxMeterClass, ctxBarGeom, normalizeCtxThresholds, sessionStatus, sessionStatusClass, sessionStatusText, sessionCardIsStale, sessionCardStale, sessionCardClass, sessionAvatarInitial, sessionSubline, sessionSpineNodes, sessionSpineLabel, sessionSpineSavedText, sessionSpineSummary, spineNodeClass, CTX_THRESHOLDS_DEFAULT, CTX_UNKNOWN_TEXT, CTX_LABEL, CTX_BAR_SPAN, CTX_BAR_HEIGHT, CTX_BAR_VIEWBOX, CTX_BAR_TICK_WIDTH, SESSION_CARD_SEP, SESSION_STATUS_UNKNOWN, SESSION_AVATAR_UNKNOWN, SPINE_SAVED_UNKNOWN, SPINE_MAX_NODES, } from "./logic/session-card.js";
|
|
33
|
+
// ── terminal set (ds/components-terminal v2): terminal · queue-row · send-bar ──
|
|
34
|
+
export { DEFAULT_DELIVERY_CLASS, DELIVERY_CLASSES, DELIVERY_HINT, SEND_PLACEHOLDER, SEND_DISABLED_FALLBACK, SEND_BUTTON_LABEL, SENDBAR_CLASSES, canSend, clearDraftOnSend, deliveryClassButtonClass, deliveryClassLabel, keyAction, makeSendGate, resolveSend, sendBarClass, sendPlaceholder, showDeliveryHint, } from "./logic/sendbar.js";
|
|
35
|
+
export { QUEUE_CANCEL_ASK, QUEUE_CANCEL_LABEL, QUEUE_CANCEL_NO, QUEUE_CANCEL_YES, QUEUE_EMPTY_COPY, QUEUE_LOADING_COPY, QUEUE_STALE_COPY, QUEUE_UNAVAILABLE_COPY, QUEUE_CLASSES, canCancelRow, cancelReducer, isFreshSource, queueBadgeClass, queueBadgeLabel, queueRowClass, queueStatusLabel, queueView, shouldDisarmCancel, unavailableText, } from "./logic/queue.js";
|
|
36
|
+
export { ROW_SCOPE, TERM_CLASS, TERM_CLASSES, TERM_FAILED_COPY, TERM_LOADING_COPY, TERM_MISSING_COPY, TERM_NO_EVENTS_COPY, TERM_ROW_KINDS, TERM_STALE_COPY, TERM_STOP_KEY_HINT, TERM_STOP_LABEL, TERM_UNADDRESSABLE_COPY, TERM_WAKE_UNAVAILABLE_COPY, TURN_IDLE_COPY, TURN_IN_FLIGHT_COPY, ROW_COLLAPSE_LABEL, ROW_EXPAND_LABEL, currentWakeRows, expandLabel, historySegmentCaption, historyToggleLabel, isExpandable, lastBoundaryIndex, noiseShow, rowKey, rowKeys, scopedRowKeys, segmentKeys, shouldStopOnKey, sourceRows, stopButtonClass, termRowClass, termTitleText, transcriptView, turnCaption, uniqueKeys, visibleRows, } from "./logic/terminal.js";
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/** Which tree the rail is drawing. See the header — the two differ in substance, not styling. */
|
|
2
|
+
export type FileTreeMode = "all-mounts" | "project";
|
|
3
|
+
/** What a row IS, once the mode + depth have been taken into account. */
|
|
4
|
+
export type FileNodeKind = "mount" | "repo" | "dir" | "file";
|
|
5
|
+
/** A git worktree mark, per the card's legend (`M` warn · `A` ok) plus the marks a real
|
|
6
|
+
* `git status` also produces. */
|
|
7
|
+
export type GitMark = "M" | "A" | "D" | "R" | "?";
|
|
8
|
+
/** The tone axis for a mark. Never "error": an uncommitted file is not a failure. */
|
|
9
|
+
export type GitMarkTone = "warn" | "ok" | "muted";
|
|
10
|
+
/** One root of the tree. In "all-mounts" mode these are the bind mounts; in "project" mode they are
|
|
11
|
+
* the selected project's repos. `primary` / `projectCount` are project-mode badge inputs and are
|
|
12
|
+
* ignored in all-mounts mode (a bind mount is not a repo and has no project membership). */
|
|
13
|
+
export interface FileTreeRootSpec {
|
|
14
|
+
key: string;
|
|
15
|
+
label: string;
|
|
16
|
+
/** Project mode: this repo is its project's primary repo. */
|
|
17
|
+
primary?: boolean;
|
|
18
|
+
/** Project mode: how many projects reference this repo. Only reported when > 1 (shared). */
|
|
19
|
+
projectCount?: number;
|
|
20
|
+
}
|
|
21
|
+
/** One entry inside a directory listing. `repo` lets a caller that actually KNOWS whether a
|
|
22
|
+
* directory is a git repo say so; when it is undefined the card's structural rule applies (see
|
|
23
|
+
* `classifyDirNode`). */
|
|
24
|
+
export interface FileTreeEntry {
|
|
25
|
+
name: string;
|
|
26
|
+
kind: "dir" | "file";
|
|
27
|
+
repo?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** The state of ONE directory's listing. `unavailable` and a loaded-but-`entries: []` listing are
|
|
30
|
+
* two of the four honest states — an unreadable directory is not the same claim as an empty one,
|
|
31
|
+
* and the tree must never collapse them together. */
|
|
32
|
+
export type DirState = {
|
|
33
|
+
status: "loading";
|
|
34
|
+
} | {
|
|
35
|
+
status: "unavailable";
|
|
36
|
+
} | {
|
|
37
|
+
status: "loaded";
|
|
38
|
+
entries: readonly FileTreeEntry[];
|
|
39
|
+
truncated?: boolean;
|
|
40
|
+
};
|
|
41
|
+
/** The four honest states, named. Exported as a value so a caller (and this package's tests) can
|
|
42
|
+
* enumerate them rather than restate the list. */
|
|
43
|
+
export type HonestStatus = "unavailable" | "empty" | "too-large" | "binary";
|
|
44
|
+
export declare const HONEST_STATUSES: readonly HonestStatus[];
|
|
45
|
+
/** Whether a string names one of the four honest states. */
|
|
46
|
+
export declare function isHonestStatus(value: unknown): value is HonestStatus;
|
|
47
|
+
/** What the preview pane has to show. `text` is the only branch carrying content; the other four
|
|
48
|
+
* non-loading branches are the honest states. The caller ADAPTS its own file-content source into
|
|
49
|
+
* this union — the atom never fetches (see the package README / the component's props). */
|
|
50
|
+
export type FilePreviewState = {
|
|
51
|
+
status: "loading";
|
|
52
|
+
} | {
|
|
53
|
+
status: "unavailable";
|
|
54
|
+
} | {
|
|
55
|
+
status: "empty";
|
|
56
|
+
} | {
|
|
57
|
+
status: "too-large";
|
|
58
|
+
bytes?: number;
|
|
59
|
+
} | {
|
|
60
|
+
status: "binary";
|
|
61
|
+
bytes?: number;
|
|
62
|
+
} | {
|
|
63
|
+
status: "text";
|
|
64
|
+
text: string;
|
|
65
|
+
bytes?: number;
|
|
66
|
+
mtime?: number;
|
|
67
|
+
};
|
|
68
|
+
/** The directory glyph per node kind. A plain sub-directory deliberately carries NO glyph: the
|
|
69
|
+
* chevron already renders "▸", and repeating it would draw "▸ ▸ name". The bindings still emit the
|
|
70
|
+
* (empty) glyph cell so every row's name stays on one vertical rule. */
|
|
71
|
+
export declare const NODE_GLYPH: Record<"mount" | "repo" | "dir", string>;
|
|
72
|
+
/** The disclosure chevron — the card's ▾ / ▸. The WHOLE ROW is the hit target (the bindings render
|
|
73
|
+
* one button per row); this is the affordance, never a separate control. */
|
|
74
|
+
export declare const CHEVRON_OPEN = "\u25BE";
|
|
75
|
+
export declare const CHEVRON_CLOSED = "\u25B8";
|
|
76
|
+
export declare function chevronGlyph(open: boolean): string;
|
|
77
|
+
/** The scope picker’s disclosure caret. Lives here, not in the bindings, so the two cannot drift. */
|
|
78
|
+
export declare const SCOPE_CARET = "\u2304";
|
|
79
|
+
/** The scope-picker glyph for a mode: ⌂ for all-mounts, ◈ for a project. */
|
|
80
|
+
export declare function scopeGlyph(mode: FileTreeMode): string;
|
|
81
|
+
/** A mono type glyph for a FILE row: `#` markdown · `{}` json · `·` anything else. */
|
|
82
|
+
export declare function fileGlyph(name: string): string;
|
|
83
|
+
/** Whether a name should render as MARKDOWN rather than as plain mono text. */
|
|
84
|
+
export declare function isMarkdownName(name: string): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* A stable identity for one node: root key + relative path, joined on NUL.
|
|
87
|
+
*
|
|
88
|
+
* The join is unambiguous — and `splitNodeId` is an exact inverse — precisely WHEN neither field
|
|
89
|
+
* contains the separator. That is not assumed: `deriveFileTreeRows` screens every root key through
|
|
90
|
+
* `isUsableRootKey` and every entry name through `isUsableEntryName`, so no id the component emits
|
|
91
|
+
* can round-trip lossily. A caller building an id by hand from an UNSCREENED root key is outside
|
|
92
|
+
* that guarantee (a key containing the separator splits back into a different pair) — screen with
|
|
93
|
+
* `isUsableRootKey` first, exactly as the walk does.
|
|
94
|
+
*/
|
|
95
|
+
export declare const NODE_ID_SEP = "\0";
|
|
96
|
+
export declare function nodeId(rootKey: string, relPath: string): string;
|
|
97
|
+
/** The inverse of `nodeId`, split at the FIRST NUL. A string without one is not a node id and
|
|
98
|
+
* yields null rather than a half-parsed pair. */
|
|
99
|
+
export declare function splitNodeId(id: string): {
|
|
100
|
+
rootKey: string;
|
|
101
|
+
relPath: string;
|
|
102
|
+
} | null;
|
|
103
|
+
/** Join a parent relative path with a child name (at a root, the bare name). */
|
|
104
|
+
export declare function childRelPath(relPath: string, name: string): string;
|
|
105
|
+
/** Split a relative path into its parent path and its own name
|
|
106
|
+
* (`"a/b/c"` ⇒ `{"a/b","c"}` · `"c"` ⇒ `{"","c"}`). */
|
|
107
|
+
export declare function parentRelPath(relPath: string): {
|
|
108
|
+
parentRel: string;
|
|
109
|
+
name: string;
|
|
110
|
+
};
|
|
111
|
+
/** The ancestor DIRECTORIES of a relative file path, root-first and including the root itself:
|
|
112
|
+
* `"docs/api/x.md"` ⇒ `["", "docs", "docs/api"]`. */
|
|
113
|
+
export declare function ancestorRelPaths(relPath: string): string[];
|
|
114
|
+
/** The card's breadcrumb separator — a wide-spaced "›". */
|
|
115
|
+
export declare const BREADCRUMB_SEP = " \u203A ";
|
|
116
|
+
/** Breadcrumb segments from a root label down through the path. The ROOT IS ALWAYS INCLUDED (the
|
|
117
|
+
* card: "Breadcrumb always includes the project root"), so a bare filename can never read as
|
|
118
|
+
* belonging to nowhere. */
|
|
119
|
+
export declare function breadcrumbSegments(rootLabel: string, relPath: string): string[];
|
|
120
|
+
/** The breadcrumb as one string. */
|
|
121
|
+
export declare function buildBreadcrumb(rootLabel: string, relPath: string): string;
|
|
122
|
+
/** Classify a DIRECTORY node for the active mode.
|
|
123
|
+
*
|
|
124
|
+
* · project mode — depth 0 IS a repo (the card: the project's repos ARE the roots); deeper
|
|
125
|
+
* directories are plain directories.
|
|
126
|
+
* · all-mounts mode — depth 0 is a bind MOUNT; the level immediately beneath it is where git repos
|
|
127
|
+
* live, so depth 1 classifies as "repo"; deeper is a plain directory.
|
|
128
|
+
*
|
|
129
|
+
* `entryRepo` is the caller's own knowledge and always wins when supplied: a data source that can
|
|
130
|
+
* actually tell a repo from an ordinary folder should say so, and a depth-1 directory that is NOT a
|
|
131
|
+
* repo then renders as the plain directory it is. When it is undefined the card's structural rule
|
|
132
|
+
* above applies. Pure. */
|
|
133
|
+
export declare function classifyDirNode(mode: FileTreeMode, depth: number, entryRepo?: boolean): "mount" | "repo" | "dir";
|
|
134
|
+
/** A pill on a repo row. `accent` = a claim about THIS project (primary); `muted` = a neutral fact
|
|
135
|
+
* about sharing. */
|
|
136
|
+
export interface FileRowBadge {
|
|
137
|
+
text: string;
|
|
138
|
+
tone: "accent" | "muted";
|
|
139
|
+
}
|
|
140
|
+
/** The badges for a repo row — PROJECT MODE ONLY, because both facts are statements about project
|
|
141
|
+
* membership and neither is meaningful for a bind mount.
|
|
142
|
+
*
|
|
143
|
+
* · `primary` — this repo is its project's primary repo.
|
|
144
|
+
* · `N projects` — this repo is referenced by N (> 1) projects, i.e. it is shared.
|
|
145
|
+
*
|
|
146
|
+
* Both can hold at once (a shared repo that is also primary here), and both are then rendered: they
|
|
147
|
+
* are independent facts, and suppressing the sharing count would hide it exactly where it matters
|
|
148
|
+
* most. Pure. */
|
|
149
|
+
export declare function repoBadges(root: FileTreeRootSpec, mode: FileTreeMode): FileRowBadge[];
|
|
150
|
+
export declare function badgeClass(tone: FileRowBadge["tone"]): string;
|
|
151
|
+
/** The tone for a mark, per the card's legend (`M` warn · `A` ok) extended to the marks a real
|
|
152
|
+
* status also yields. A RENAME reduces to one glyph whether it is staged or unstaged, so it takes
|
|
153
|
+
* the conservative `warn` rather than a false all-good green for still-uncommitted work. */
|
|
154
|
+
export declare function gitMarkTone(mark: GitMark): GitMarkTone;
|
|
155
|
+
/** A human word for a mark — the accessible name, because a bare "M" is opaque to a screen reader. */
|
|
156
|
+
export declare function gitMarkLabel(mark: GitMark): string;
|
|
157
|
+
/** The mono 10px mark cell's class. */
|
|
158
|
+
export declare function gitMarkClass(mark: GitMark): string;
|
|
159
|
+
/** The preview header's status pill. `clean` is only ever an HONEST clean — the caller passes it
|
|
160
|
+
* when the file's repo genuinely reported no change for it. When there is no git knowledge at all
|
|
161
|
+
* the caller passes null and NO pill renders, because a fabricated "unchanged" would be a claim
|
|
162
|
+
* nobody proved. */
|
|
163
|
+
export type PreviewBadgeState = {
|
|
164
|
+
kind: "mark";
|
|
165
|
+
mark: GitMark;
|
|
166
|
+
} | {
|
|
167
|
+
kind: "clean";
|
|
168
|
+
} | null;
|
|
169
|
+
export interface PreviewBadge {
|
|
170
|
+
text: string;
|
|
171
|
+
tone: GitMarkTone;
|
|
172
|
+
}
|
|
173
|
+
export declare function previewBadge(state: PreviewBadgeState): PreviewBadge | null;
|
|
174
|
+
export declare function previewBadgeClass(tone: GitMarkTone): string;
|
|
175
|
+
/** Human file size — "512 B", "14.2 KB". A negative or non-finite input reads "0 B" rather than
|
|
176
|
+
* rendering NaN. Pure. */
|
|
177
|
+
export declare function formatFileSize(bytes: unknown): string;
|
|
178
|
+
/**
|
|
179
|
+
* Relative time — "2m ago". `now` is injected so this stays pure and testable. A future timestamp
|
|
180
|
+
* clamps to "0s ago" rather than rendering a negative age. Pure.
|
|
181
|
+
*
|
|
182
|
+
* Seconds-vs-milliseconds is disambiguated by magnitude, with the threshold at 1e11 rather than the
|
|
183
|
+
* 1e12 some sibling surfaces use. 1e12 ms is 2001-09-09, so under that rule ANY millisecond
|
|
184
|
+
* timestamp older than 2001 is mistaken for seconds, multiplied by 1000 into the far future, and
|
|
185
|
+
* then clamped — reporting a genuinely old file as "0s ago", i.e. a plausible-looking lie. At 1e11
|
|
186
|
+
* the seconds branch still covers every second-timestamp up to the year 5138, while millisecond
|
|
187
|
+
* timestamps are read correctly all the way back to 1973.
|
|
188
|
+
*/
|
|
189
|
+
export declare const SECONDS_THRESHOLD = 100000000000;
|
|
190
|
+
export declare function formatRelativeTime(t: unknown, now?: number): string;
|
|
191
|
+
/** Where an honest state is being reported. The SENTENCE differs ("this directory" vs "this file");
|
|
192
|
+
* the tone never does. */
|
|
193
|
+
export type HonestSurface = "dir" | "file";
|
|
194
|
+
/**
|
|
195
|
+
* The sentence for an honest state. Plain, specific, and never apologetic — each says what IS true,
|
|
196
|
+
* not what went wrong. `bytes` is woven in for the two size-bearing states when it is known.
|
|
197
|
+
*/
|
|
198
|
+
export declare function honestNoteText(status: HonestStatus, surface?: HonestSurface, bytes?: number): string;
|
|
199
|
+
/**
|
|
200
|
+
* The class for an honest-state note. Every one of the four gets its OWN modifier (so each is
|
|
201
|
+
* distinctly renderable and independently styleable) while the base class carries a single,
|
|
202
|
+
* deliberately INFORMATIONAL tone.
|
|
203
|
+
*
|
|
204
|
+
* There is no `--error` / `--warn` variant here, and that is the point: an unreadable directory, an
|
|
205
|
+
* empty file, an oversized file and a binary file are all ordinary things a filesystem contains. The
|
|
206
|
+
* component reports them; it does not accuse. Pure.
|
|
207
|
+
*/
|
|
208
|
+
export declare function honestNoteClass(status: HonestStatus): string;
|
|
209
|
+
/** The note class for the tree's two NON-honest informational rows (an in-flight listing and a
|
|
210
|
+
* server-truncated one). Same neutral base — neither is a failure either. */
|
|
211
|
+
export declare function treeNoteClass(status: "loading" | "truncated"): string;
|
|
212
|
+
/** A note row INSIDE the tree — the note class plus the depth indent, so a note lines up under the
|
|
213
|
+
* node it describes instead of sitting flush against the rail. The indent is composed here rather
|
|
214
|
+
* than inside `honestNoteClass` because the same honest-state classes are reused by the PREVIEW
|
|
215
|
+
* pane, where a tree indent would be meaningless. */
|
|
216
|
+
export declare function treeNoteRowClass(status: "loading" | "truncated" | HonestStatus, depth: number): string;
|
|
217
|
+
/** The preview body's sentence for any state that has no content to render, or null for `text`
|
|
218
|
+
* (where the real content renders instead). */
|
|
219
|
+
export declare function previewNoteText(state: FilePreviewState): string | null;
|
|
220
|
+
/** The preview body note's class, or null for `text`. */
|
|
221
|
+
export declare function previewNoteClass(state: FilePreviewState): string | null;
|
|
222
|
+
/**
|
|
223
|
+
* The size/mtime the preview HEADER should show. The state's own values win where it carries them —
|
|
224
|
+
* they describe the bytes actually on screen, so the header can never show a stale size next to
|
|
225
|
+
* fresh content — and the caller's listing values (`fallback`) fill the rest.
|
|
226
|
+
*
|
|
227
|
+
* `too-large` / `binary` carry a size but no mtime (nothing was read), so the mtime falls back to
|
|
228
|
+
* the listing's. A value that is genuinely unknown stays `undefined` rather than becoming a zero the
|
|
229
|
+
* formatter would render as a real "0 B" / "56y ago". Pure.
|
|
230
|
+
*/
|
|
231
|
+
export declare function previewMeta(state: FilePreviewState, fallback?: {
|
|
232
|
+
bytes?: number;
|
|
233
|
+
mtime?: number;
|
|
234
|
+
}): {
|
|
235
|
+
bytes?: number;
|
|
236
|
+
mtime?: number;
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* How the preview should render the body — the SINGLE decision both bindings follow, so neither
|
|
240
|
+
* re-derives it and the two cannot drift:
|
|
241
|
+
* · "note" — there is no content to show (loading, or any of the honest states);
|
|
242
|
+
* · "markdown" — a markdown name AND a renderer to hand it to;
|
|
243
|
+
* · "plain" — everything else, including a markdown file with NO renderer supplied.
|
|
244
|
+
*
|
|
245
|
+
* `hasMarkdownRenderer` is part of the decision rather than a binding-side afterthought: markdown
|
|
246
|
+
* rendering is the caller's to provide, and without it the honest outcome is plain text — never the
|
|
247
|
+
* component inventing a renderer or injecting raw HTML.
|
|
248
|
+
*/
|
|
249
|
+
export declare function previewBodyMode(name: string, state: FilePreviewState, hasMarkdownRenderer?: boolean): "markdown" | "plain" | "note";
|
|
250
|
+
/** Depth-class ceiling — rows deeper than this share the last indent step rather than growing the
|
|
251
|
+
* stylesheet without bound. */
|
|
252
|
+
export declare const MAX_INDENT_DEPTH = 10;
|
|
253
|
+
/** Hard ceiling on TRAVERSAL depth. Unlike `MAX_INDENT_DEPTH` (a purely visual cap) this bounds the
|
|
254
|
+
* recursion itself, so no caller-supplied listing can walk the derivation into a stack overflow. */
|
|
255
|
+
export declare const MAX_TREE_DEPTH = 64;
|
|
256
|
+
/**
|
|
257
|
+
* Whether an entry name is structurally usable as one path segment.
|
|
258
|
+
*
|
|
259
|
+
* An EMPTY name is the dangerous one: `childRelPath("", "")` returns `""`, which is the ROOT's own
|
|
260
|
+
* relative path — so an expanded root listing itself as a nameless directory would re-enter the same
|
|
261
|
+
* node id forever. A name containing "/" or a NUL is rejected for the same class of reason: it would
|
|
262
|
+
* silently compose a path that addresses a different node than the one listed (and NUL would break
|
|
263
|
+
* `nodeId`'s separator outright). None of these can name a real filesystem entry.
|
|
264
|
+
*/
|
|
265
|
+
export declare function isUsableEntryName(name: string): boolean;
|
|
266
|
+
/**
|
|
267
|
+
* Whether a ROOT key can serve as half of a node id. This is what ENFORCES the invariant `nodeId`
|
|
268
|
+
* documents: a key containing the NUL separator would make `splitNodeId` return a different pair
|
|
269
|
+
* than went in (`"a\0b"` + `""` splits back to `"a"` + `"b\0"`), silently corrupting the identity
|
|
270
|
+
* that `dirs` / `marks` are keyed by. Root keys are caller-supplied and generic, so the component
|
|
271
|
+
* checks rather than assumes.
|
|
272
|
+
*/
|
|
273
|
+
export declare function isUsableRootKey(key: string): boolean;
|
|
274
|
+
/** The indent class for a depth. */
|
|
275
|
+
export declare function indentClass(depth: number): string;
|
|
276
|
+
/** A directory row's class. */
|
|
277
|
+
export declare function dirRowClass(kind: "mount" | "repo" | "dir", depth: number, selected?: boolean): string;
|
|
278
|
+
/** A file row's class. */
|
|
279
|
+
export declare function fileRowClass(depth: number, selected: boolean): string;
|
|
280
|
+
export interface FileTreeDirRow {
|
|
281
|
+
type: "dir";
|
|
282
|
+
id: string;
|
|
283
|
+
rootKey: string;
|
|
284
|
+
relPath: string;
|
|
285
|
+
name: string;
|
|
286
|
+
depth: number;
|
|
287
|
+
kind: "mount" | "repo" | "dir";
|
|
288
|
+
open: boolean;
|
|
289
|
+
chevron: string;
|
|
290
|
+
glyph: string;
|
|
291
|
+
badges: FileRowBadge[];
|
|
292
|
+
className: string;
|
|
293
|
+
}
|
|
294
|
+
export interface FileTreeFileRow {
|
|
295
|
+
type: "file";
|
|
296
|
+
id: string;
|
|
297
|
+
rootKey: string;
|
|
298
|
+
relPath: string;
|
|
299
|
+
name: string;
|
|
300
|
+
depth: number;
|
|
301
|
+
glyph: string;
|
|
302
|
+
mark: GitMark | null;
|
|
303
|
+
selected: boolean;
|
|
304
|
+
className: string;
|
|
305
|
+
}
|
|
306
|
+
export interface FileTreeNoteRow {
|
|
307
|
+
type: "note";
|
|
308
|
+
id: string;
|
|
309
|
+
depth: number;
|
|
310
|
+
/** `unavailable` / `empty` are two of the four honest states; `loading` / `truncated` are the
|
|
311
|
+
* tree's other two informational rows. */
|
|
312
|
+
status: "loading" | "truncated" | HonestStatus;
|
|
313
|
+
text: string;
|
|
314
|
+
className: string;
|
|
315
|
+
}
|
|
316
|
+
export type FileTreeRow = FileTreeDirRow | FileTreeFileRow | FileTreeNoteRow;
|
|
317
|
+
export interface DeriveFileTreeInput {
|
|
318
|
+
mode: FileTreeMode;
|
|
319
|
+
roots: readonly FileTreeRootSpec[];
|
|
320
|
+
/** Every known directory listing, keyed by `nodeId(rootKey, relPath)`. A root's own listing is
|
|
321
|
+
* keyed with `relPath: ""`. A key that is absent reads as "not fetched yet". */
|
|
322
|
+
dirs: Readonly<Record<string, DirState>>;
|
|
323
|
+
/** The node ids currently expanded. */
|
|
324
|
+
expanded: ReadonlySet<string>;
|
|
325
|
+
/** The previewed FILE's node id, if any. */
|
|
326
|
+
selectedId?: string | null;
|
|
327
|
+
/** Git marks keyed by `nodeId(rootKey, relPath)`. Absent = NO mark (never a fabricated clean). */
|
|
328
|
+
marks?: Readonly<Record<string, GitMark>>;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Flatten the tree into the ordered rows the bindings render. Depth-first, roots in the order given;
|
|
332
|
+
* a closed node contributes only its own row.
|
|
333
|
+
*
|
|
334
|
+
* Every non-entry outcome becomes an explicit NOTE row rather than silence:
|
|
335
|
+
* · a listing still in flight ⇒ `loading`
|
|
336
|
+
* · a listing that could not be read ⇒ `unavailable` (honest state)
|
|
337
|
+
* · a listing that is genuinely empty⇒ `empty` (honest state)
|
|
338
|
+
* · a listing the source capped ⇒ `truncated`
|
|
339
|
+
* An expanded node with NO cache entry at all is `loading`, because "the caller has not fetched it
|
|
340
|
+
* yet" is exactly that and must never read as empty.
|
|
341
|
+
*
|
|
342
|
+
* Marks are applied only where the caller supplied one for that exact path — an absent mark renders
|
|
343
|
+
* no glyph, never a fabricated "clean". Pure: no fetching, no caching, no policy about WHEN a
|
|
344
|
+
* listing should be loaded (that is the product's lazy-expansion concern).
|
|
345
|
+
*/
|
|
346
|
+
export declare function deriveFileTreeRows(input: DeriveFileTreeInput): FileTreeRow[];
|
|
347
|
+
/** The number of FILE rows actually rendered — the rail header's default count, and the only one
|
|
348
|
+
* that is provably equal to what the operator can see. Pure. */
|
|
349
|
+
export declare function countFileRows(rows: readonly FileTreeRow[]): number;
|
|
350
|
+
/**
|
|
351
|
+
* Total FILE entries DISCOVERED across every cached listing — a different number from
|
|
352
|
+
* `countFileRows`, and deliberately so.
|
|
353
|
+
*
|
|
354
|
+
* This counts what the caller has listed so far ANYWHERE in its cache, including directories that
|
|
355
|
+
* are currently collapsed or no longer reachable from the active roots. It is therefore NOT the
|
|
356
|
+
* count of visible rows and must not be presented as one: a collapsed `docs/` whose listing is
|
|
357
|
+
* cached still contributes its files here while rendering none. `FileTree` defaults its header to
|
|
358
|
+
* `countFileRows` for exactly that reason; pass this explicitly as `count` only when "files
|
|
359
|
+
* discovered so far" is the number you actually mean.
|
|
360
|
+
*
|
|
361
|
+
* Malformed and duplicate names are excluded on the same tests the walk applies, and `Object.keys`
|
|
362
|
+
* is own-enumerable only, matching the walk's own-key guard. Pure.
|
|
363
|
+
*/
|
|
364
|
+
export declare function countLoadedFiles(dirs: Readonly<Record<string, DirState>>): number;
|
|
365
|
+
/** One option in the rail's scope picker. `count` is the option's root count (the card's right-hand
|
|
366
|
+
* number). `dividerAfter` draws the card's 1px rule — the caller decides where the grouping falls
|
|
367
|
+
* rather than this module guessing a vocabulary of scope keys it does not own. */
|
|
368
|
+
export interface FileScopeOption {
|
|
369
|
+
key: string;
|
|
370
|
+
label: string;
|
|
371
|
+
count: number;
|
|
372
|
+
dividerAfter?: boolean;
|
|
373
|
+
}
|
|
374
|
+
export declare function scopeItemClass(active: boolean): string;
|