1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Project (balls-clone) enumeration and nested-delivery detection
//! (DESIGN §5.1 #1, §15 Y14).
//!
//! A **project** is one balls invocation path — the `bl` control plane keys its
//! per-project state at `$XDG_STATE_HOME/balls/clones/<percent-encoded-path>/`
//! (balls arch §1). The project's *identity* is the decoded path, and it is the
//! cwd every `bl … --json` runs in ([`balls`], DESIGN §5.1 #2). Enumeration is
//! therefore `readdir + percent-decode basename` — one query, nothing stored.
//!
//! **Nested-delivery detection** (§5.1 #1): a decoded path that itself lies
//! under the balls `plugins/bl-delivery/` tree is a ball's own work-worktree
//! that became a balls project — an *internal* clone. [`visible`] drops them,
//! unconditionally.
//!
//! **There is no toggle (bl-e3e7).** There was: an "internal clones" checkbox
//! at the top of the §11 balls section, backed by a `ui.json` boolean and a
//! re-read in the derivation worker. It was deleted, not renamed, because the
//! set it revealed is never a thing to work in. Such a store exists only
//! because something ran `bl` with its cwd inside a `work/<id>` worktree, and
//! balls' own guide says that addresses "a *different* (usually empty) store";
//! the clone dir lives under `clones/`, *outside* the worktree, so it outlives
//! the `bl close` that tears the worktree down. Revealing them therefore put
//! phantom projects on the roster — each with a new-ball form that would file
//! a ball into a throwaway store, and each becoming an orphaned-project row
//! once its worktree was gone. No yog verb ever acted on one. A label cannot
//! rescue a view whose ON state is a trap, so the toggle went with it.
use cratepercent_decode;
use ;
/// One enumerated balls project (§5.1 #1). `path` is the decoded invocation
/// path — the identity and the `bl` cwd; `internal` flags a nested-delivery
/// clone (hidden by default, [`visible`]). Both are derived from the clone
/// basename alone — nothing about the project is stored by yog.
/// The bl-delivery territory a nested-delivery clone lives under, derived from
/// the clones dir (its parent is the balls state root; balls arch §1). `None`
/// only when `clones_dir` has no parent — then no path can be internal.
/// True iff `project` (a decoded invocation path) lies under the bl-delivery
/// tree — a ball's own worktree that became a balls project (§5.1 #1).
/// Enumerate every balls project under `clones_dir` (§5.1 #1): each child dir's
/// basename percent-decodes to the project path, flagged `internal` when it
/// falls under bl-delivery. Sorted by path for a stable roster. A missing or
/// unreadable clones dir yields an empty Vec — the general path with no inputs,
/// not a bootstrap special case (the [`crate::binding`] discipline).
/// The longest a project's roster label may run before it elides (§11): the
/// left panel is a column of names, and a name this long has stopped naming.
const LABEL_MAX: usize = 32;
/// The roster label for each of `paths`, in order (§11, bl-ac3d): the shortest
/// trailing run of components that tells a project apart from every other one
/// enumerated — its basename wherever that is already unique — elided at
/// [`LABEL_MAX`] characters.
///
/// The label is **cosmetic**: the path is the project's identity (§5.1 #1) and
/// stays one hover away, so two labels that elide alike cost nothing. Rendering
/// the path itself is what sized the whole left panel to the widest project a
/// scratch directory could name.
/// The shortest trailing suffix of `path` that no *other* path in `paths`
/// shares, falling back to the whole path when the set holds it twice — then
/// nothing shorter could separate them, and nothing needs to.
/// `path`'s last `k` components, rendered — the whole path when it is shorter.
/// `s` capped at [`LABEL_MAX`] characters, head kept and the cut marked — the
/// house spelling of a clipped preview (`git_tree::detect::truncate_preview`).
/// The projects a surface shows (§5.1 #1): the non-internal clones. A
/// nested-delivery clone is never one of them — see the module doc for why the
/// operator toggle that used to reveal them was deleted (bl-e3e7).