opendocker 0.1.5 → 0.1.6
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/highlights-eq9cgrbb.scm +604 -0
- package/dist/highlights-ghv9g403.scm +205 -0
- package/dist/highlights-hk7bwhj4.scm +284 -0
- package/dist/highlights-r812a2qc.scm +150 -0
- package/dist/highlights-x6tmsnaa.scm +115 -0
- package/dist/injections-73j83es3.scm +27 -0
- package/dist/main.js +1 -1
- package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/package.json +2 -5
- package/.github/FUNDING.yml +0 -15
- package/AGENTS.md +0 -22
- package/TODO.md +0 -53
- package/docker-compose.yml +0 -24
- package/scripts/build.ts +0 -139
- package/src/assets/images/banner.jpg +0 -0
- package/src/components/Border.tsx +0 -19
- package/src/components/ContainersPane.tsx +0 -146
- package/src/components/ImagesPane.tsx +0 -83
- package/src/components/LogsPane.tsx +0 -132
- package/src/components/Pane.tsx +0 -34
- package/src/components/VolumesPanes.tsx +0 -84
- package/src/components/ui/Shimmer.tsx +0 -64
- package/src/layouts/BaseLayout.tsx +0 -60
- package/src/lib/docker.ts +0 -68
- package/src/main.tsx +0 -43
- package/src/stores/application.ts +0 -11
- package/src/stores/containers.ts +0 -22
- package/src/stores/images.ts +0 -11
- package/src/stores/volumes.ts +0 -11
- package/src/utils/styling.ts +0 -198
- package/tsconfig.json +0 -15
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
; Query from: https://raw.githubusercontent.com/nvim-treesitter/nvim-treesitter/99ddf573531c4dbe53f743ecbc1595af5eb1d32f/queries/markdown_inline/highlights.scm
|
|
2
|
+
; From MDeiml/tree-sitter-markdown
|
|
3
|
+
(code_span) @markup.raw @nospell
|
|
4
|
+
|
|
5
|
+
(emphasis) @markup.italic
|
|
6
|
+
|
|
7
|
+
(strong_emphasis) @markup.strong
|
|
8
|
+
|
|
9
|
+
(strikethrough) @markup.strikethrough
|
|
10
|
+
|
|
11
|
+
(shortcut_link
|
|
12
|
+
(link_text) @nospell)
|
|
13
|
+
|
|
14
|
+
[
|
|
15
|
+
(backslash_escape)
|
|
16
|
+
(hard_line_break)
|
|
17
|
+
] @string.escape
|
|
18
|
+
|
|
19
|
+
; Conceal codeblock and text style markers
|
|
20
|
+
([
|
|
21
|
+
(code_span_delimiter)
|
|
22
|
+
(emphasis_delimiter)
|
|
23
|
+
] @conceal
|
|
24
|
+
(#set! conceal ""))
|
|
25
|
+
|
|
26
|
+
; Inline links - style all parts
|
|
27
|
+
(inline_link
|
|
28
|
+
["[" "(" ")"] @markup.link)
|
|
29
|
+
|
|
30
|
+
(inline_link
|
|
31
|
+
"]" @markup.link.bracket.close)
|
|
32
|
+
|
|
33
|
+
; Conceal opening bracket
|
|
34
|
+
((inline_link
|
|
35
|
+
"[" @conceal)
|
|
36
|
+
(#set! conceal ""))
|
|
37
|
+
|
|
38
|
+
; Conceal closing bracket with space replacement
|
|
39
|
+
((inline_link
|
|
40
|
+
"]" @conceal)
|
|
41
|
+
(#set! conceal " "))
|
|
42
|
+
|
|
43
|
+
; Conceal image links
|
|
44
|
+
(image
|
|
45
|
+
[
|
|
46
|
+
"!"
|
|
47
|
+
"["
|
|
48
|
+
"]"
|
|
49
|
+
"("
|
|
50
|
+
(link_destination)
|
|
51
|
+
")"
|
|
52
|
+
] @markup.link
|
|
53
|
+
(#set! conceal ""))
|
|
54
|
+
|
|
55
|
+
; Conceal full reference links
|
|
56
|
+
(full_reference_link
|
|
57
|
+
[
|
|
58
|
+
"["
|
|
59
|
+
"]"
|
|
60
|
+
(link_label)
|
|
61
|
+
] @markup.link
|
|
62
|
+
(#set! conceal ""))
|
|
63
|
+
|
|
64
|
+
; Conceal collapsed reference links
|
|
65
|
+
(collapsed_reference_link
|
|
66
|
+
[
|
|
67
|
+
"["
|
|
68
|
+
"]"
|
|
69
|
+
] @markup.link
|
|
70
|
+
(#set! conceal ""))
|
|
71
|
+
|
|
72
|
+
; Conceal shortcut links
|
|
73
|
+
(shortcut_link
|
|
74
|
+
[
|
|
75
|
+
"["
|
|
76
|
+
"]"
|
|
77
|
+
] @markup.link
|
|
78
|
+
(#set! conceal ""))
|
|
79
|
+
|
|
80
|
+
[
|
|
81
|
+
(link_destination)
|
|
82
|
+
(uri_autolink)
|
|
83
|
+
] @markup.link.url @nospell
|
|
84
|
+
|
|
85
|
+
[
|
|
86
|
+
(link_label)
|
|
87
|
+
(link_text)
|
|
88
|
+
(link_title)
|
|
89
|
+
(image_description)
|
|
90
|
+
] @markup.link.label
|
|
91
|
+
|
|
92
|
+
; Replace common HTML entities.
|
|
93
|
+
((entity_reference) @character.special
|
|
94
|
+
(#eq? @character.special " ")
|
|
95
|
+
(#set! conceal ""))
|
|
96
|
+
|
|
97
|
+
((entity_reference) @character.special
|
|
98
|
+
(#eq? @character.special "<")
|
|
99
|
+
(#set! conceal "<"))
|
|
100
|
+
|
|
101
|
+
((entity_reference) @character.special
|
|
102
|
+
(#eq? @character.special ">")
|
|
103
|
+
(#set! conceal ">"))
|
|
104
|
+
|
|
105
|
+
((entity_reference) @character.special
|
|
106
|
+
(#eq? @character.special "&")
|
|
107
|
+
(#set! conceal "&"))
|
|
108
|
+
|
|
109
|
+
((entity_reference) @character.special
|
|
110
|
+
(#eq? @character.special """)
|
|
111
|
+
(#set! conceal "\""))
|
|
112
|
+
|
|
113
|
+
((entity_reference) @character.special
|
|
114
|
+
(#any-of? @character.special " " " ")
|
|
115
|
+
(#set! conceal " "))
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
; Query from: https://raw.githubusercontent.com/nvim-treesitter/nvim-treesitter/refs/heads/master/queries/markdown/injections.scm
|
|
2
|
+
(fenced_code_block
|
|
3
|
+
(info_string
|
|
4
|
+
(language) @_lang)
|
|
5
|
+
(code_fence_content) @injection.content
|
|
6
|
+
(#set-lang-from-info-string! @_lang))
|
|
7
|
+
|
|
8
|
+
((html_block) @injection.content
|
|
9
|
+
(#set! injection.language "html")
|
|
10
|
+
(#set! injection.combined)
|
|
11
|
+
(#set! injection.include-children))
|
|
12
|
+
|
|
13
|
+
((minus_metadata) @injection.content
|
|
14
|
+
(#set! injection.language "yaml")
|
|
15
|
+
(#offset! @injection.content 1 0 -1 0)
|
|
16
|
+
(#set! injection.include-children))
|
|
17
|
+
|
|
18
|
+
((plus_metadata) @injection.content
|
|
19
|
+
(#set! injection.language "toml")
|
|
20
|
+
(#offset! @injection.content 1 0 -1 0)
|
|
21
|
+
(#set! injection.include-children))
|
|
22
|
+
|
|
23
|
+
([
|
|
24
|
+
(inline)
|
|
25
|
+
(pipe_table_cell)
|
|
26
|
+
] @injection.content
|
|
27
|
+
(#set! injection.language "markdown_inline"))
|
package/dist/main.js
CHANGED
|
@@ -31933,7 +31933,7 @@ function BaseLayout({ children }) {
|
|
|
31933
31933
|
const { activePane, setActivePane } = useApplicationStore((state) => state);
|
|
31934
31934
|
const dimensions = useTerminalDimensions();
|
|
31935
31935
|
const [pwd, setPwd] = import_react17.useState("");
|
|
31936
|
-
const version = "v0.1.
|
|
31936
|
+
const version = "v0.1.6";
|
|
31937
31937
|
import_react17.useEffect(() => {
|
|
31938
31938
|
Bun.$`pwd`.quiet().then((result) => setPwd(result.text()));
|
|
31939
31939
|
setActivePane("containers");
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"name": "opendocker",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"bin": {
|
|
8
8
|
"opendocker": "./dist/main.js"
|
|
9
9
|
},
|
|
10
|
-
"
|
|
11
|
-
"exports": {
|
|
12
|
-
"./*": "./src/*.ts"
|
|
13
|
-
},
|
|
10
|
+
"files": ["dist/", "package.json", "README.md"],
|
|
14
11
|
"scripts": {
|
|
15
12
|
"build": "bun build ./src/main.tsx --outdir ./dist --target bun",
|
|
16
13
|
"start": "bun run dist/main.js",
|
package/.github/FUNDING.yml
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# These are supported funding model platforms
|
|
2
|
-
|
|
3
|
-
github: [votsuk]
|
|
4
|
-
patreon: # Replace with a single Patreon username
|
|
5
|
-
open_collective: # Replace with a single Open Collective username
|
|
6
|
-
ko_fi: # Replace with a single Ko-fi username
|
|
7
|
-
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
8
|
-
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
9
|
-
liberapay: # Replace with a single Liberapay username
|
|
10
|
-
issuehunt: # Replace with a single IssueHunt username
|
|
11
|
-
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
|
12
|
-
polar: # Replace with a single Polar username
|
|
13
|
-
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
|
14
|
-
thanks_dev: # Replace with a single thanks.dev username
|
|
15
|
-
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
package/AGENTS.md
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# Agent Development Guide
|
|
2
|
-
|
|
3
|
-
## Commands
|
|
4
|
-
- **Run**: `bun run src/main.tsx`
|
|
5
|
-
- **Dev**: `bun --watch run src/main.tsx`
|
|
6
|
-
- **Test**: `bun test` (uses `bun:test` imports)
|
|
7
|
-
- **Install**: `bun install`
|
|
8
|
-
|
|
9
|
-
## Code Style
|
|
10
|
-
- **Framework**: React with @opentui/react for terminal UI
|
|
11
|
-
- **Imports**: Use path aliases `@/*` for src/ and `@tui/*` for src/cli/cmd/tui/
|
|
12
|
-
- **Components**: Default exports, PascalCase naming
|
|
13
|
-
- **Keyboard**: Vim-style navigation (j/k for up/down, q to quit)
|
|
14
|
-
- **Colors**: Use centralized colors from `src/utils/colors.ts`
|
|
15
|
-
- **Error Handling**: Try-catch with state-based error handling
|
|
16
|
-
- **Shell Commands**: Use `Bun.$` for shell execution
|
|
17
|
-
|
|
18
|
-
## Key Patterns
|
|
19
|
-
- Use `useKeyboard()` hook for terminal input handling
|
|
20
|
-
- Components use flexbox layout with terminal dimensions
|
|
21
|
-
- State management with React hooks (useState, useEffect)
|
|
22
|
-
- Terminal UI elements: `<box>`, `<text>` with styling props
|
package/TODO.md
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# OpenDocker TODO List
|
|
2
|
-
|
|
3
|
-
## Code Quality Improvements
|
|
4
|
-
|
|
5
|
-
### High Priority
|
|
6
|
-
- [ ] Fix inconsistent component naming (`VolumesPanes.tsx` → `VolumesPane.tsx`)
|
|
7
|
-
- [ ] Add comprehensive error handling and error boundaries
|
|
8
|
-
- [ ] Implement proper null safety checks (especially for `activeContainer`)
|
|
9
|
-
- [ ] Add TypeScript interfaces for Docker API responses
|
|
10
|
-
- [ ] Standardize state management across all components
|
|
11
|
-
|
|
12
|
-
### Medium Priority
|
|
13
|
-
- [ ] Add loading states to Images and Volumes panes
|
|
14
|
-
- [ ] Implement proper cleanup for processes and event listeners
|
|
15
|
-
- [ ] Add prop type documentation to components
|
|
16
|
-
- [ ] Standardize error message formatting
|
|
17
|
-
|
|
18
|
-
## Performance & Architecture
|
|
19
|
-
|
|
20
|
-
### High Priority
|
|
21
|
-
- [ ] Create centralized Docker service layer (abstract CLI calls)
|
|
22
|
-
- [ ] Implement debouncing/throttling for Docker commands
|
|
23
|
-
- [ ] Add process cleanup to prevent memory leaks
|
|
24
|
-
- [ ] Implement data caching mechanism
|
|
25
|
-
|
|
26
|
-
### Medium Priority
|
|
27
|
-
- [ ] Separate data fetching logic from UI components
|
|
28
|
-
- [ ] Add configuration management (environment variables)
|
|
29
|
-
- [ ] Implement proper logging system
|
|
30
|
-
|
|
31
|
-
## Testing & Documentation
|
|
32
|
-
|
|
33
|
-
## Features & UX
|
|
34
|
-
|
|
35
|
-
### High Priority
|
|
36
|
-
- [ ] Implement filter logs input
|
|
37
|
-
- [ ] Add real-time updates with proper polling mechanism
|
|
38
|
-
- [ ] Create help screen with keyboard shortcuts
|
|
39
|
-
- [ ] Add visual feedback for user actions
|
|
40
|
-
|
|
41
|
-
### Medium Priority
|
|
42
|
-
- [ ] Implement container management actions (start/stop/remove)
|
|
43
|
-
- [ ] Add confirmation dialogs for destructive operations
|
|
44
|
-
- [ ] Create settings/configuration screen
|
|
45
|
-
- [ ] Add export/import functionality for logs
|
|
46
|
-
|
|
47
|
-
## Infrastructure
|
|
48
|
-
|
|
49
|
-
### Medium Priority
|
|
50
|
-
- [ ] Add Docker health checks and connection validation
|
|
51
|
-
- [ ] Implement proper error recovery mechanisms
|
|
52
|
-
- [ ] Add performance monitoring
|
|
53
|
-
- [ ] Create build/development optimization
|
package/docker-compose.yml
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
version: '3.8'
|
|
2
|
-
|
|
3
|
-
services:
|
|
4
|
-
logger1:
|
|
5
|
-
image: alpine:latest
|
|
6
|
-
container_name: logger1
|
|
7
|
-
command: >
|
|
8
|
-
sh -c '
|
|
9
|
-
while true; do
|
|
10
|
-
echo "[$$(date +%Y-%m-%d\ %H:%M:%S)] Container1: $$(openssl rand -hex 8)"
|
|
11
|
-
sleep 1
|
|
12
|
-
done'
|
|
13
|
-
restart: unless-stopped
|
|
14
|
-
|
|
15
|
-
logger2:
|
|
16
|
-
image: alpine:latest
|
|
17
|
-
container_name: logger2
|
|
18
|
-
command: >
|
|
19
|
-
sh -c '
|
|
20
|
-
while true; do
|
|
21
|
-
echo "[$$(date +%Y-%m-%d\ %H:%M:%S)] Container2: $$(openssl rand -hex 8)"
|
|
22
|
-
sleep 1
|
|
23
|
-
done'
|
|
24
|
-
restart: unless-stopped
|
package/scripts/build.ts
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
import path from "path"
|
|
4
|
-
import fs from "fs"
|
|
5
|
-
import { $ } from "bun"
|
|
6
|
-
import { fileURLToPath } from "url"
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
-
const __dirname = path.dirname(__filename)
|
|
10
|
-
const dir = path.resolve(__dirname, "..")
|
|
11
|
-
|
|
12
|
-
process.chdir(dir)
|
|
13
|
-
|
|
14
|
-
import pkg from "../package.json"
|
|
15
|
-
import { Script } from "@opencode-ai/script"
|
|
16
|
-
|
|
17
|
-
const singleFlag = process.argv.includes("--single")
|
|
18
|
-
|
|
19
|
-
const allTargets: {
|
|
20
|
-
os: string
|
|
21
|
-
arch: "arm64" | "x64"
|
|
22
|
-
abi?: "musl"
|
|
23
|
-
avx2?: false
|
|
24
|
-
}[] = [
|
|
25
|
-
{
|
|
26
|
-
os: "linux",
|
|
27
|
-
arch: "arm64",
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
os: "linux",
|
|
31
|
-
arch: "x64",
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
os: "linux",
|
|
35
|
-
arch: "x64",
|
|
36
|
-
avx2: false,
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
os: "linux",
|
|
40
|
-
arch: "arm64",
|
|
41
|
-
abi: "musl",
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
os: "linux",
|
|
45
|
-
arch: "x64",
|
|
46
|
-
abi: "musl",
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
os: "linux",
|
|
50
|
-
arch: "x64",
|
|
51
|
-
abi: "musl",
|
|
52
|
-
avx2: false,
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
os: "darwin",
|
|
56
|
-
arch: "arm64",
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
os: "darwin",
|
|
60
|
-
arch: "x64",
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
os: "darwin",
|
|
64
|
-
arch: "x64",
|
|
65
|
-
avx2: false,
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
os: "win32",
|
|
69
|
-
arch: "x64",
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
os: "win32",
|
|
73
|
-
arch: "x64",
|
|
74
|
-
avx2: false,
|
|
75
|
-
},
|
|
76
|
-
]
|
|
77
|
-
|
|
78
|
-
const targets = singleFlag
|
|
79
|
-
? allTargets.filter((item) => item.os === process.platform && item.arch === process.arch)
|
|
80
|
-
: allTargets
|
|
81
|
-
|
|
82
|
-
await $`rm -rf dist`
|
|
83
|
-
|
|
84
|
-
const binaries: Record<string, string> = {}
|
|
85
|
-
await $`bun install --os="*" --cpu="*" @opentui/core@${pkg.dependencies["@opentui/core"]}`
|
|
86
|
-
await $`bun install --os="*" --cpu="*" @parcel/watcher@${pkg.dependencies["@parcel/watcher"]}`
|
|
87
|
-
for (const item of targets) {
|
|
88
|
-
const name = [
|
|
89
|
-
pkg.name,
|
|
90
|
-
// changing to win32 flags npm for some reason
|
|
91
|
-
item.os === "win32" ? "windows" : item.os,
|
|
92
|
-
item.arch,
|
|
93
|
-
item.avx2 === false ? "baseline" : undefined,
|
|
94
|
-
item.abi === undefined ? undefined : item.abi,
|
|
95
|
-
]
|
|
96
|
-
.filter(Boolean)
|
|
97
|
-
.join("-")
|
|
98
|
-
console.log(`building ${name}`)
|
|
99
|
-
await $`mkdir -p dist/${name}/bin`
|
|
100
|
-
|
|
101
|
-
const parserWorker = fs.realpathSync(path.resolve(dir, "./node_modules/@opentui/core/parser.worker.js"))
|
|
102
|
-
const workerPath = "./src/cli/cmd/tui/worker.ts"
|
|
103
|
-
|
|
104
|
-
await Bun.build({
|
|
105
|
-
conditions: ["browser"],
|
|
106
|
-
tsconfig: "./tsconfig.json",
|
|
107
|
-
sourcemap: "external",
|
|
108
|
-
compile: {
|
|
109
|
-
target: name.replace(pkg.name, "bun") as any,
|
|
110
|
-
outfile: `dist/${name}/bin/opendocker`,
|
|
111
|
-
execArgv: [`--user-agent=opencode/${Script.version}`, `--env-file=""`, `--`],
|
|
112
|
-
windows: {},
|
|
113
|
-
},
|
|
114
|
-
entrypoints: ["./src/index.ts", parserWorker, workerPath],
|
|
115
|
-
define: {
|
|
116
|
-
OPENCODE_VERSION: `'${Script.version}'`,
|
|
117
|
-
OTUI_TREE_SITTER_WORKER_PATH: "/$bunfs/root/" + path.relative(dir, parserWorker).replaceAll("\\", "/"),
|
|
118
|
-
OPENCODE_WORKER_PATH: workerPath,
|
|
119
|
-
OPENCODE_CHANNEL: `'${Script.channel}'`,
|
|
120
|
-
},
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
await $`rm -rf ./dist/${name}/bin/tui`
|
|
124
|
-
await Bun.file(`dist/${name}/package.json`).write(
|
|
125
|
-
JSON.stringify(
|
|
126
|
-
{
|
|
127
|
-
name,
|
|
128
|
-
version: Script.version,
|
|
129
|
-
os: [item.os],
|
|
130
|
-
cpu: [item.arch],
|
|
131
|
-
},
|
|
132
|
-
null,
|
|
133
|
-
2,
|
|
134
|
-
),
|
|
135
|
-
)
|
|
136
|
-
binaries[name] = Script.version
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export { binaries }
|
|
Binary file
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { colors } from "../utils/styling";
|
|
2
|
-
|
|
3
|
-
export const SplitBorder = {
|
|
4
|
-
border: ["left" as const],
|
|
5
|
-
borderColor: colors.border,
|
|
6
|
-
customBorderChars: {
|
|
7
|
-
topLeft: "",
|
|
8
|
-
bottomLeft: "",
|
|
9
|
-
vertical: "┃",
|
|
10
|
-
topRight: "",
|
|
11
|
-
bottomRight: "",
|
|
12
|
-
horizontal: "",
|
|
13
|
-
bottomT: "",
|
|
14
|
-
topT: "",
|
|
15
|
-
cross: "",
|
|
16
|
-
leftT: "",
|
|
17
|
-
rightT: "",
|
|
18
|
-
},
|
|
19
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useRef } from "react";
|
|
2
|
-
import { useKeyboard } from "@opentui/react";
|
|
3
|
-
import { colors, termColors } from "../utils/styling";
|
|
4
|
-
import Pane from "./Pane";
|
|
5
|
-
import { useContainerStore } from "../stores/containers";
|
|
6
|
-
import { useApplicationStore } from "../stores/application";
|
|
7
|
-
import type { ScrollBoxRenderable } from "@opentui/core";
|
|
8
|
-
import { Docker } from "../lib/docker";
|
|
9
|
-
import { TextAttributes } from "@opentui/core";
|
|
10
|
-
|
|
11
|
-
export default function ContainersPane() {
|
|
12
|
-
const { activePane, setActivePane } = useApplicationStore((state) => state);
|
|
13
|
-
const { containers, setContainers, activeContainer, setActiveContainer } = useContainerStore((state) => state);
|
|
14
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
15
|
-
const paneActive = activePane === "containers";
|
|
16
|
-
const scrollBoxRef = useRef<ScrollBoxRenderable>(null);
|
|
17
|
-
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
if (!paneActive) return;
|
|
20
|
-
|
|
21
|
-
const docker = new Docker();
|
|
22
|
-
|
|
23
|
-
docker.watch((dockerContainers) => {
|
|
24
|
-
const transformed = dockerContainers.map(container => ({
|
|
25
|
-
name: container.Names[0].replace("/", ""),
|
|
26
|
-
status: container.Status,
|
|
27
|
-
state: container.State,
|
|
28
|
-
health: container.Health,
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
setContainers(transformed);
|
|
32
|
-
|
|
33
|
-
if (transformed.length === 0) {
|
|
34
|
-
setActiveContainer(undefined);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Get the CURRENT active container from store, not from stale closure
|
|
38
|
-
const currentActive = useContainerStore.getState().activeContainer;
|
|
39
|
-
|
|
40
|
-
if (currentActive) {
|
|
41
|
-
const updatedContainer = transformed.find((c) => c.name === currentActive.name);
|
|
42
|
-
if (updatedContainer) {
|
|
43
|
-
setActiveContainer(updatedContainer);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (transformed.length > 0) {
|
|
49
|
-
setActiveContainer(transformed[0]);
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}, [paneActive]);
|
|
53
|
-
|
|
54
|
-
useKeyboard((key) => {
|
|
55
|
-
if (!paneActive) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (key.name === "left") {
|
|
60
|
-
setActivePane("volumes");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (key.name === "right") {
|
|
64
|
-
setActivePane("images");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (key.name === 'j' || key.name === 'down') {
|
|
68
|
-
const index = Math.min(selectedIndex + 1, containers.length - 1);
|
|
69
|
-
setSelectedIndex(index);
|
|
70
|
-
setActiveContainer(containers[index]);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (key.name === 'k' || key.name === 'up') {
|
|
74
|
-
const index = Math.max(selectedIndex - 1, 0);
|
|
75
|
-
setActiveContainer(containers[index]);
|
|
76
|
-
setSelectedIndex(index);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
return (
|
|
81
|
-
<Pane
|
|
82
|
-
title="Containers"
|
|
83
|
-
flexDirection="column"
|
|
84
|
-
width="100%"
|
|
85
|
-
active={paneActive}
|
|
86
|
-
>
|
|
87
|
-
<scrollbox
|
|
88
|
-
ref={scrollBoxRef}
|
|
89
|
-
scrollY={true}
|
|
90
|
-
stickyScroll={true}
|
|
91
|
-
stickyStart="bottom"
|
|
92
|
-
viewportOptions={{
|
|
93
|
-
flexGrow: 1
|
|
94
|
-
}}
|
|
95
|
-
>
|
|
96
|
-
{containers.map((item, index) => {
|
|
97
|
-
function getStateColor() {
|
|
98
|
-
if (paneActive && activeContainer?.name === item.name) {
|
|
99
|
-
return colors.backgroundPanel;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (item?.state === "running") {
|
|
103
|
-
if (item.status.includes("starting")) {
|
|
104
|
-
return termColors.orange11;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (item.status.includes("unhealthy")) {
|
|
108
|
-
return termColors.red11;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return termColors.green11;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (item?.state === "exited") {
|
|
115
|
-
return termColors.red11;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return termColors.blue11;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return (
|
|
122
|
-
<box
|
|
123
|
-
key={index}
|
|
124
|
-
backgroundColor={paneActive && activeContainer?.name === item.name ? colors.primary : undefined}
|
|
125
|
-
flexDirection="row"
|
|
126
|
-
justifyContent="space-between"
|
|
127
|
-
paddingLeft={1}
|
|
128
|
-
paddingRight={1}
|
|
129
|
-
>
|
|
130
|
-
<text
|
|
131
|
-
content={item.name}
|
|
132
|
-
fg={paneActive && activeContainer?.name === item.name ? colors.backgroundPanel : colors.textMuted}
|
|
133
|
-
attributes={paneActive && activeContainer?.name === item.name && TextAttributes.BOLD}
|
|
134
|
-
/>
|
|
135
|
-
<text
|
|
136
|
-
content={item.state}
|
|
137
|
-
fg={getStateColor()}
|
|
138
|
-
/>
|
|
139
|
-
</box>
|
|
140
|
-
)
|
|
141
|
-
})}
|
|
142
|
-
{containers.length < 1 && <text fg={colors.textMuted}>No Containers</text>}
|
|
143
|
-
</scrollbox>
|
|
144
|
-
</Pane>
|
|
145
|
-
)
|
|
146
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
2
|
-
import { useKeyboard } from "@opentui/react";
|
|
3
|
-
import Pane from "./Pane";
|
|
4
|
-
import { colors } from "../utils/styling";
|
|
5
|
-
import { useImageStore } from "../stores/images";
|
|
6
|
-
import { useApplicationStore } from "../stores/application";
|
|
7
|
-
import type { ScrollBoxRenderable } from "@opentui/core";
|
|
8
|
-
|
|
9
|
-
export default function ImagesPane() {
|
|
10
|
-
const { activePane, setActivePane } = useApplicationStore((state) => state);
|
|
11
|
-
const { images, setImages } = useImageStore();
|
|
12
|
-
const scrollBoxRef = useRef<ScrollBoxRenderable>(null);
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
const process = Bun.spawn(
|
|
16
|
-
["docker", "images", "--format", "{{.Repository}}"],
|
|
17
|
-
{stdout: "pipe", stderr: "pipe"},
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
async function read() {
|
|
21
|
-
try {
|
|
22
|
-
const reader = process.stdout.getReader();
|
|
23
|
-
|
|
24
|
-
while (true) {
|
|
25
|
-
const { done, value } = await reader.read();
|
|
26
|
-
if (done) break;
|
|
27
|
-
const decodedValues = new TextDecoder().decode(value);
|
|
28
|
-
const lines = decodedValues.split("\n").filter(Boolean);
|
|
29
|
-
setImages(lines);
|
|
30
|
-
}
|
|
31
|
-
} catch (error) {
|
|
32
|
-
console.error(error);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
read();
|
|
37
|
-
|
|
38
|
-
return () => process.kill();
|
|
39
|
-
}, []);
|
|
40
|
-
|
|
41
|
-
useKeyboard((key) => {
|
|
42
|
-
if (activePane !== "images") {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (key.name === "left") {
|
|
47
|
-
setActivePane("containers");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (key.name === "right" || key.name === "tab") {
|
|
51
|
-
setActivePane("volumes");
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
<Pane
|
|
57
|
-
title="Images"
|
|
58
|
-
width="100%"
|
|
59
|
-
active={activePane === "images"}
|
|
60
|
-
flexDirection="column"
|
|
61
|
-
>
|
|
62
|
-
<scrollbox
|
|
63
|
-
ref={scrollBoxRef}
|
|
64
|
-
scrollY={true}
|
|
65
|
-
stickyScroll={true}
|
|
66
|
-
stickyStart="top"
|
|
67
|
-
>
|
|
68
|
-
{images.map((item: string, index: number) => {
|
|
69
|
-
return (
|
|
70
|
-
<box key={index}>
|
|
71
|
-
<text
|
|
72
|
-
key={index}
|
|
73
|
-
content={item}
|
|
74
|
-
fg={colors.textMuted}
|
|
75
|
-
/>
|
|
76
|
-
</box>
|
|
77
|
-
)
|
|
78
|
-
})}
|
|
79
|
-
{images.length < 1 && <text fg={colors.textMuted}>No Images</text>}
|
|
80
|
-
</scrollbox>
|
|
81
|
-
</Pane>
|
|
82
|
-
)
|
|
83
|
-
}
|