@f0rbit/overview 0.2.0 → 0.2.2

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.
Files changed (69) hide show
  1. package/bin/overview +1 -1
  2. package/dist/overview.js +11898 -0
  3. package/package.json +10 -13
  4. package/bunfig.toml +0 -7
  5. package/packages/core/__tests__/concurrency.test.ts +0 -111
  6. package/packages/core/__tests__/helpers.ts +0 -60
  7. package/packages/core/__tests__/integration/git-status.test.ts +0 -62
  8. package/packages/core/__tests__/integration/scanner.test.ts +0 -140
  9. package/packages/core/__tests__/ocn.test.ts +0 -164
  10. package/packages/core/package.json +0 -13
  11. package/packages/core/src/cache.ts +0 -31
  12. package/packages/core/src/concurrency.ts +0 -44
  13. package/packages/core/src/devpad.ts +0 -61
  14. package/packages/core/src/git-graph.ts +0 -54
  15. package/packages/core/src/git-stats.ts +0 -201
  16. package/packages/core/src/git-status.ts +0 -316
  17. package/packages/core/src/github.ts +0 -286
  18. package/packages/core/src/index.ts +0 -58
  19. package/packages/core/src/ocn.ts +0 -74
  20. package/packages/core/src/scanner.ts +0 -118
  21. package/packages/core/src/types.ts +0 -199
  22. package/packages/core/src/watcher.ts +0 -128
  23. package/packages/core/src/worktree.ts +0 -80
  24. package/packages/core/tsconfig.json +0 -5
  25. package/packages/render/bunfig.toml +0 -8
  26. package/packages/render/jsx-runtime.d.ts +0 -3
  27. package/packages/render/package.json +0 -18
  28. package/packages/render/src/components/__tests__/scrollbox-height.test.tsx +0 -780
  29. package/packages/render/src/components/__tests__/widget-container.integration.test.tsx +0 -306
  30. package/packages/render/src/components/git-graph.tsx +0 -127
  31. package/packages/render/src/components/help-overlay.tsx +0 -108
  32. package/packages/render/src/components/index.ts +0 -7
  33. package/packages/render/src/components/repo-list.tsx +0 -127
  34. package/packages/render/src/components/stats-panel.tsx +0 -116
  35. package/packages/render/src/components/status-badge.tsx +0 -70
  36. package/packages/render/src/components/status-bar.tsx +0 -56
  37. package/packages/render/src/components/widget-container.tsx +0 -286
  38. package/packages/render/src/components/widgets/__tests__/widget-rendering.test.tsx +0 -326
  39. package/packages/render/src/components/widgets/branch-list.tsx +0 -93
  40. package/packages/render/src/components/widgets/commit-activity.tsx +0 -112
  41. package/packages/render/src/components/widgets/devpad-milestones.tsx +0 -88
  42. package/packages/render/src/components/widgets/devpad-tasks.tsx +0 -81
  43. package/packages/render/src/components/widgets/file-changes.tsx +0 -78
  44. package/packages/render/src/components/widgets/git-status.tsx +0 -125
  45. package/packages/render/src/components/widgets/github-ci.tsx +0 -98
  46. package/packages/render/src/components/widgets/github-issues.tsx +0 -101
  47. package/packages/render/src/components/widgets/github-prs.tsx +0 -119
  48. package/packages/render/src/components/widgets/github-release.tsx +0 -73
  49. package/packages/render/src/components/widgets/index.ts +0 -12
  50. package/packages/render/src/components/widgets/recent-commits.tsx +0 -64
  51. package/packages/render/src/components/widgets/registry.ts +0 -23
  52. package/packages/render/src/components/widgets/repo-meta.tsx +0 -80
  53. package/packages/render/src/config/index.ts +0 -104
  54. package/packages/render/src/lib/__tests__/fetch-context.test.ts +0 -200
  55. package/packages/render/src/lib/__tests__/widget-grid.test.ts +0 -665
  56. package/packages/render/src/lib/actions.ts +0 -68
  57. package/packages/render/src/lib/fetch-context.ts +0 -102
  58. package/packages/render/src/lib/filter.ts +0 -94
  59. package/packages/render/src/lib/format.ts +0 -36
  60. package/packages/render/src/lib/use-devpad.ts +0 -167
  61. package/packages/render/src/lib/use-github.ts +0 -75
  62. package/packages/render/src/lib/widget-grid.ts +0 -204
  63. package/packages/render/src/lib/widget-state.ts +0 -96
  64. package/packages/render/src/overview.tsx +0 -16
  65. package/packages/render/src/screens/index.ts +0 -1
  66. package/packages/render/src/screens/main-screen.tsx +0 -410
  67. package/packages/render/src/theme/index.ts +0 -37
  68. package/packages/render/tsconfig.json +0 -9
  69. package/tsconfig.json +0 -23
@@ -1,199 +0,0 @@
1
- // Git file change
2
- export interface GitFileChange {
3
- path: string;
4
- status: "modified" | "added" | "deleted" | "renamed" | "copied" | "untracked" | "ignored" | "conflicted";
5
- staged: boolean;
6
- }
7
-
8
- // Branch info
9
- export interface BranchInfo {
10
- name: string;
11
- is_current: boolean;
12
- upstream: string | null;
13
- ahead: number;
14
- behind: number;
15
- last_commit_time: number; // unix timestamp
16
- }
17
-
18
- // Stash entry
19
- export interface StashEntry {
20
- index: number;
21
- message: string;
22
- date: string;
23
- }
24
-
25
- // Recent commit
26
- export interface RecentCommit {
27
- hash: string;
28
- message: string;
29
- author: string;
30
- time: number; // unix timestamp
31
- }
32
-
33
- // OpenCode session status (from ocn)
34
- export type OcnSessionStatus = "idle" | "busy" | "prompting" | "error";
35
-
36
- export interface OcnStatus {
37
- pid: number;
38
- status: OcnSessionStatus;
39
- session_id: string;
40
- }
41
-
42
- // Health status
43
- export type HealthStatus = "clean" | "dirty" | "ahead" | "behind" | "diverged" | "conflict";
44
-
45
- // Full repo status
46
- export interface RepoStatus {
47
- // Identity
48
- path: string;
49
- name: string;
50
- display_path: string; // relative to scan root
51
-
52
- // Current state
53
- current_branch: string;
54
- head_commit: string;
55
- head_message: string;
56
- head_time: number;
57
-
58
- // Tracking
59
- remote_url: string | null;
60
- ahead: number;
61
- behind: number;
62
-
63
- // Working tree
64
- modified_count: number;
65
- staged_count: number;
66
- untracked_count: number;
67
- conflict_count: number;
68
- changes: GitFileChange[];
69
-
70
- // Stash
71
- stash_count: number;
72
- stashes: StashEntry[];
73
-
74
- // Branches
75
- branches: BranchInfo[];
76
- local_branch_count: number;
77
- remote_branch_count: number;
78
-
79
- // Metadata
80
- tags: string[];
81
- total_commits: number;
82
- repo_size_bytes: number;
83
- contributor_count: number;
84
-
85
- // Recent activity
86
- recent_commits: RecentCommit[];
87
-
88
- // Commit activity (populated by fetchDetails, not initial scan)
89
- commit_activity: { daily_counts: number[]; total_this_week: number; total_last_week: number } | null;
90
-
91
- // OpenCode session status (from ocn)
92
- ocn_status: OcnStatus | null;
93
-
94
- // Derived
95
- is_clean: boolean;
96
- health: HealthStatus;
97
- }
98
-
99
- // Worktree info
100
- export interface WorktreeInfo {
101
- path: string;
102
- branch: string;
103
- head: string;
104
- is_bare: boolean;
105
- is_main: boolean;
106
- }
107
-
108
- // Git graph output
109
- export interface GitGraphOutput {
110
- lines: string[];
111
- total_lines: number;
112
- repo_path: string;
113
- }
114
-
115
- // Repo tree node
116
- export interface RepoNode {
117
- name: string;
118
- path: string;
119
- type: "directory" | "repo" | "worktree";
120
- status: RepoStatus | null;
121
- worktrees: WorktreeInfo[];
122
- children: RepoNode[];
123
- depth: number;
124
- expanded: boolean;
125
- }
126
-
127
- // Widget system
128
- export type WidgetId =
129
- | "git-status"
130
- | "recent-commits"
131
- | "branch-list"
132
- | "github-prs"
133
- | "github-issues"
134
- | "github-ci"
135
- | "devpad-tasks"
136
- | "devpad-milestones"
137
- | "repo-meta"
138
- | "file-changes"
139
- | "commit-activity"
140
- | "github-release";
141
-
142
- export interface WidgetConfig {
143
- id: WidgetId;
144
- enabled: boolean;
145
- priority: number;
146
- collapsed: boolean;
147
- }
148
-
149
- export type WidgetSpan = "full" | "half" | "third" | "auto";
150
-
151
- export interface WidgetSizeHint {
152
- span: WidgetSpan;
153
- min_height: number;
154
- }
155
-
156
- export interface WidgetRenderProps {
157
- width: number;
158
- focused: boolean;
159
- }
160
-
161
- // Config
162
- export interface OverviewConfig {
163
- scan_dirs: string[];
164
- depth: number;
165
- refresh_interval: number;
166
- layout: {
167
- left_width_pct: number;
168
- graph_height_pct: number;
169
- };
170
- sort: "name" | "status" | "last-commit";
171
- filter: "all" | "dirty" | "clean" | "ahead" | "behind";
172
- ignore: string[];
173
- actions: {
174
- ggi: string;
175
- editor: string;
176
- sessionizer: string | null;
177
- };
178
- }
179
-
180
- // Default config factory
181
- export function defaultConfig(): OverviewConfig {
182
- return {
183
- scan_dirs: ["~/dev"],
184
- depth: 3,
185
- refresh_interval: 30,
186
- layout: {
187
- left_width_pct: 35,
188
- graph_height_pct: 45,
189
- },
190
- sort: "name",
191
- filter: "all",
192
- ignore: ["node_modules", ".git"],
193
- actions: {
194
- ggi: "ggi",
195
- editor: "$EDITOR",
196
- sessionizer: null,
197
- },
198
- };
199
- }
@@ -1,128 +0,0 @@
1
- import { watch, type FSWatcher } from "node:fs";
2
- import { join } from "node:path";
3
- import { readFile, lstat } from "node:fs/promises";
4
-
5
- export interface WatcherOptions {
6
- /** Debounce interval in ms (default 500) */
7
- debounce_ms?: number;
8
- /** Callback when a repo changes */
9
- on_change: (repoPath: string) => void;
10
- }
11
-
12
- export interface RepoWatcher {
13
- /** Start watching a list of repo paths */
14
- watch(repoPaths: string[]): void;
15
- /** Stop watching all repos */
16
- close(): void;
17
- /** Add a single repo to watch */
18
- add(repoPath: string): void;
19
- /** Remove a single repo from watching */
20
- remove(repoPath: string): void;
21
- }
22
-
23
- async function resolveGitDir(repo_path: string): Promise<string | null> {
24
- const git_path = join(repo_path, ".git");
25
- try {
26
- const stats = await lstat(git_path);
27
- if (stats.isDirectory()) return git_path;
28
- if (stats.isFile()) {
29
- const content = await readFile(git_path, "utf-8");
30
- const match = content.match(/^gitdir:\s*(.+)$/m);
31
- const target = match?.[1]?.trim();
32
- if (!target) return null;
33
- return target.startsWith("/") ? target : join(repo_path, target);
34
- }
35
- return null;
36
- } catch {
37
- return null;
38
- }
39
- }
40
-
41
- function tryWatch(
42
- target: string,
43
- options: { recursive?: boolean },
44
- callback: () => void,
45
- ): FSWatcher | null {
46
- try {
47
- const watcher = watch(target, options, callback);
48
- watcher.on("error", () => {});
49
- return watcher;
50
- } catch {
51
- return null;
52
- }
53
- }
54
-
55
- export function createRepoWatcher(options: WatcherOptions): RepoWatcher {
56
- const debounce_ms = options.debounce_ms ?? 500;
57
- const watchers = new Map<string, FSWatcher[]>();
58
- const timers = new Map<string, ReturnType<typeof setTimeout>>();
59
-
60
- function debouncedChange(repo_path: string) {
61
- const existing = timers.get(repo_path);
62
- if (existing) clearTimeout(existing);
63
- timers.set(
64
- repo_path,
65
- setTimeout(() => {
66
- timers.delete(repo_path);
67
- options.on_change(repo_path);
68
- }, debounce_ms),
69
- );
70
- }
71
-
72
- async function addRepo(repo_path: string) {
73
- if (watchers.has(repo_path)) return;
74
-
75
- const git_dir = await resolveGitDir(repo_path);
76
- if (!git_dir) {
77
- console.warn(`[watcher] skipping ${repo_path}: could not resolve .git directory`);
78
- return;
79
- }
80
-
81
- const repo_watchers: FSWatcher[] = [];
82
- const on_event = () => debouncedChange(repo_path);
83
-
84
- const index_watcher = tryWatch(join(git_dir, "index"), {}, on_event);
85
- if (index_watcher) repo_watchers.push(index_watcher);
86
-
87
- const refs_watcher = tryWatch(join(git_dir, "refs"), { recursive: true }, on_event);
88
- if (refs_watcher) repo_watchers.push(refs_watcher);
89
-
90
- if (repo_watchers.length === 0) {
91
- console.warn(`[watcher] skipping ${repo_path}: no watchable targets`);
92
- return;
93
- }
94
-
95
- watchers.set(repo_path, repo_watchers);
96
- }
97
-
98
- function removeRepo(repo_path: string) {
99
- const repo_watchers = watchers.get(repo_path);
100
- if (repo_watchers) {
101
- repo_watchers.forEach((w) => w.close());
102
- watchers.delete(repo_path);
103
- }
104
- const timer = timers.get(repo_path);
105
- if (timer) {
106
- clearTimeout(timer);
107
- timers.delete(repo_path);
108
- }
109
- }
110
-
111
- return {
112
- watch(repo_paths: string[]) {
113
- repo_paths.forEach((p) => addRepo(p));
114
- },
115
- close() {
116
- watchers.forEach((ws) => ws.forEach((w) => w.close()));
117
- watchers.clear();
118
- timers.forEach((t) => clearTimeout(t));
119
- timers.clear();
120
- },
121
- add(repo_path: string) {
122
- addRepo(repo_path);
123
- },
124
- remove(repo_path: string) {
125
- removeRepo(repo_path);
126
- },
127
- };
128
- }
@@ -1,80 +0,0 @@
1
- import { ok, err, type Result } from "@f0rbit/corpus";
2
- import type { WorktreeInfo } from "./types";
3
-
4
- export type WorktreeError =
5
- | { kind: "not_a_repo"; path: string }
6
- | { kind: "worktree_failed"; path: string; cause: string };
7
-
8
- async function gitCommand(
9
- args: string[],
10
- cwd: string,
11
- ): Promise<Result<string, WorktreeError>> {
12
- const proc = Bun.spawn(["git", ...args], {
13
- cwd,
14
- stdout: "pipe",
15
- stderr: "pipe",
16
- });
17
-
18
- await proc.exited;
19
-
20
- if (proc.exitCode !== 0) {
21
- const stderr = await new Response(proc.stderr).text();
22
- const is_not_repo =
23
- stderr.includes("not a git repository") ||
24
- stderr.includes("not a git repo");
25
- if (is_not_repo) {
26
- return err({ kind: "not_a_repo", path: cwd });
27
- }
28
- return err({
29
- kind: "worktree_failed",
30
- path: cwd,
31
- cause: stderr.trim(),
32
- });
33
- }
34
-
35
- const stdout = await new Response(proc.stdout).text();
36
- return ok(stdout);
37
- }
38
-
39
- function parseWorktreeBlock(
40
- lines: string[],
41
- is_main: boolean,
42
- ): WorktreeInfo | null {
43
- const path = lines
44
- .find((l) => l.startsWith("worktree "))
45
- ?.slice("worktree ".length);
46
- if (!path) return null;
47
-
48
- const head_line = lines.find((l) => l.startsWith("HEAD "));
49
- const head = head_line ? head_line.slice("HEAD ".length, "HEAD ".length + 7) : "0000000";
50
-
51
- const branch_line = lines.find((l) => l.startsWith("branch "));
52
- const is_bare = lines.some((l) => l === "bare");
53
- const branch = branch_line
54
- ? branch_line.slice("branch refs/heads/".length)
55
- : is_bare
56
- ? "bare"
57
- : "detached";
58
-
59
- return { path, branch, head, is_bare, is_main };
60
- }
61
-
62
- export async function detectWorktrees(
63
- repoPath: string,
64
- ): Promise<Result<WorktreeInfo[], WorktreeError>> {
65
- const result = await gitCommand(["worktree", "list", "--porcelain"], repoPath);
66
- if (!result.ok) return result;
67
-
68
- const blocks = result.value
69
- .split("\n\n")
70
- .map((b) => b.trim())
71
- .filter((b) => b.length > 0);
72
-
73
- const worktrees = blocks
74
- .map((block, i) => parseWorktreeBlock(block.split("\n"), i === 0))
75
- .filter((w): w is WorktreeInfo => w !== null);
76
-
77
- if (worktrees.length <= 1) return ok([]);
78
-
79
- return ok(worktrees);
80
- }
@@ -1,5 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "include": ["src/**/*"],
4
- "exclude": ["node_modules", "dist"]
5
- }
@@ -1,8 +0,0 @@
1
- preload = ["@opentui/solid/preload"]
2
-
3
- [test]
4
- preload = ["@opentui/solid/preload"]
5
-
6
- [compilerOptions]
7
- jsxImportSource = "solid-js"
8
- jsx = "preserve"
@@ -1,3 +0,0 @@
1
- // JSX types provided by @opentui/solid/jsx-runtime via jsxImportSource config.
2
- // This file is included in tsconfig for any future type augmentations.
3
- export {}
@@ -1,18 +0,0 @@
1
- {
2
- "name": "@overview/render",
3
- "version": "0.1.0",
4
- "main": "src/overview.tsx",
5
- "scripts": {
6
- "dev": "bun run src/overview.tsx",
7
- "test": "bun test",
8
- "typecheck": "tsc --noEmit"
9
- },
10
- "dependencies": {
11
- "@opentui/core": "^0.1.80",
12
- "@opentui/solid": "^0.1.80",
13
- "solid-js": "^1.9.11",
14
- "@overview/core": "workspace:*",
15
- "@f0rbit/corpus": "^0.3.5",
16
- "@devpad/api": "^2.1.3"
17
- }
18
- }