clack-tui 0.1.1__tar.gz

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.
@@ -0,0 +1,37 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ id-token: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - uses: astral-sh/setup-uv@v5
17
+ with:
18
+ enable-cache: true
19
+
20
+ - name: Set up Python
21
+ run: uv python install 3.11
22
+
23
+ - name: Build package
24
+ run: uv build
25
+
26
+ - name: Validate package metadata
27
+ run: uvx twine check dist/*
28
+
29
+ - name: Smoke test installed CLI
30
+ run: |
31
+ uv venv .smoke-test
32
+ . .smoke-test/bin/activate
33
+ uv pip install dist/*.whl
34
+ python -c "import shutil; import clack; import clack.__main__; assert shutil.which('clack'); assert shutil.which('clack-tui')"
35
+
36
+ - name: Publish to PyPI
37
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .envrc
4
+ .venv/
5
+ .mypy_cache/
6
+ .ruff_cache/
7
+ *.egg-info/
8
+ dist/
9
+ build/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Janine
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: clack-tui
3
+ Version: 0.1.1
4
+ Summary: TUI for browsing, searching, and resuming Claude Code sessions
5
+ Project-URL: Homepage, https://github.com/jcc-ne/clack
6
+ Project-URL: Repository, https://github.com/jcc-ne/clack
7
+ Project-URL: Issues, https://github.com/jcc-ne/clack/issues
8
+ Author: Janine
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai,claude,claude-code,terminal,textual,tui
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Terminals
19
+ Classifier: Topic :: Utilities
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: duckdb>=1.1.0
22
+ Requires-Dist: jinja2>=3.1.0
23
+ Requires-Dist: libtmux>=0.40.0
24
+ Requires-Dist: textual>=1.0.0
25
+ Description-Content-Type: text/markdown
26
+
27
+ # clack
28
+
29
+ [![PyPI](https://img.shields.io/pypi/v/clack-tui)](https://pypi.org/project/clack-tui/)
30
+ [![Python](https://img.shields.io/pypi/pyversions/clack-tui)](https://pypi.org/project/clack-tui/)
31
+ [![License](https://img.shields.io/github/license/jcc-ne/clack)](LICENSE)
32
+
33
+ A terminal UI for browsing, searching, and resuming [Claude Code](https://claude.ai/code) sessions.
34
+
35
+ Browse your full session history, read past conversations, jump into stats, and resume any session — all without leaving the terminal.
36
+
37
+ ---
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ # pipx
43
+ pipx install clack-tui
44
+
45
+ # uvx (run without installing)
46
+ uvx clack-tui
47
+ ```
48
+
49
+ The package name is `clack-tui` because `clack` is already taken on PyPI. The package installs both `clack` and `clack-tui` executables.
50
+
51
+ Requires Python 3.11+ and [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) installed.
52
+
53
+ ---
54
+
55
+ ## Quick start
56
+
57
+ ```bash
58
+ clack
59
+ ```
60
+
61
+ clack reads your Claude Code session files directly from `~/.claude/projects/` — no configuration needed.
62
+
63
+ ---
64
+
65
+ ## Features
66
+
67
+ | Tab | Key | What it does |
68
+ |-----|-----|--------------|
69
+ | Dashboard | `1` | Browse all sessions, search with full-text search (DuckDB FTS / BM25) |
70
+ | Stats | `2` | Token usage and model breakdown, daily sparklines |
71
+ | Dialog | `3` | Read any conversation turn-by-turn, export to HTML |
72
+ | Query | `4` | Write SQL directly against your session data (DuckDB) |
73
+
74
+ ### Dashboard key bindings
75
+
76
+ | Key | Action |
77
+ |-----|--------|
78
+ | `/` | Focus search |
79
+ | `Esc` | Clear search |
80
+ | `Enter` | Resume session (opens `claude --resume`) |
81
+ | `v` | View full conversation |
82
+ | `r` | Refresh session list |
83
+ | `q` | Quit |
84
+
85
+ **tmux:** If clack is running inside a tmux session, resuming opens the session in a new tmux window. Otherwise it suspends the TUI, runs `claude --resume`, and returns when you exit.
86
+
87
+ If the DuckDB FTS extension is unavailable, dashboard search falls back to simple substring matching.
88
+
89
+ ### Query console
90
+
91
+ The Query console exposes your session data as DuckDB SQL views:
92
+
93
+ | View | Contents |
94
+ |------|----------|
95
+ | `v_sessions` | One row per session — date, project, summary, model, turn count |
96
+ | `v_assistant_turns` | Individual assistant turns with token counts |
97
+ | `v_stats` | Aggregated usage by model |
98
+ | `v_sessions_by_day` | Daily session and token totals |
99
+ | `raw_records` | Raw JSONL records |
100
+
101
+ Example queries:
102
+
103
+ ```sql
104
+ -- Sessions from the last week
105
+ SELECT title, cwd, turn_count FROM v_sessions
106
+ WHERE last_active > now() - INTERVAL '7 days';
107
+
108
+ -- Most token-heavy sessions
109
+ SELECT sessionId, SUM(output_tokens) AS total
110
+ FROM v_assistant_turns GROUP BY 1 ORDER BY 2 DESC LIMIT 10;
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Dev setup
116
+
117
+ ```bash
118
+ git clone https://github.com/jcc-ne/clack
119
+ cd clack
120
+ uv sync
121
+ uv run clack
122
+ ```
123
+
124
+ Release notes for TestPyPI and Trusted Publishing live in [docs/releasing.md](docs/releasing.md).
125
+
126
+ ---
127
+
128
+ ## Requirements
129
+
130
+ - Python 3.11+
131
+ - [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) (session files at `~/.claude/projects/`)
132
+ - tmux (optional — enables opening resumed sessions in a new window)
@@ -0,0 +1,106 @@
1
+ # clack
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/clack-tui)](https://pypi.org/project/clack-tui/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/clack-tui)](https://pypi.org/project/clack-tui/)
5
+ [![License](https://img.shields.io/github/license/jcc-ne/clack)](LICENSE)
6
+
7
+ A terminal UI for browsing, searching, and resuming [Claude Code](https://claude.ai/code) sessions.
8
+
9
+ Browse your full session history, read past conversations, jump into stats, and resume any session — all without leaving the terminal.
10
+
11
+ ---
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ # pipx
17
+ pipx install clack-tui
18
+
19
+ # uvx (run without installing)
20
+ uvx clack-tui
21
+ ```
22
+
23
+ The package name is `clack-tui` because `clack` is already taken on PyPI. The package installs both `clack` and `clack-tui` executables.
24
+
25
+ Requires Python 3.11+ and [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) installed.
26
+
27
+ ---
28
+
29
+ ## Quick start
30
+
31
+ ```bash
32
+ clack
33
+ ```
34
+
35
+ clack reads your Claude Code session files directly from `~/.claude/projects/` — no configuration needed.
36
+
37
+ ---
38
+
39
+ ## Features
40
+
41
+ | Tab | Key | What it does |
42
+ |-----|-----|--------------|
43
+ | Dashboard | `1` | Browse all sessions, search with full-text search (DuckDB FTS / BM25) |
44
+ | Stats | `2` | Token usage and model breakdown, daily sparklines |
45
+ | Dialog | `3` | Read any conversation turn-by-turn, export to HTML |
46
+ | Query | `4` | Write SQL directly against your session data (DuckDB) |
47
+
48
+ ### Dashboard key bindings
49
+
50
+ | Key | Action |
51
+ |-----|--------|
52
+ | `/` | Focus search |
53
+ | `Esc` | Clear search |
54
+ | `Enter` | Resume session (opens `claude --resume`) |
55
+ | `v` | View full conversation |
56
+ | `r` | Refresh session list |
57
+ | `q` | Quit |
58
+
59
+ **tmux:** If clack is running inside a tmux session, resuming opens the session in a new tmux window. Otherwise it suspends the TUI, runs `claude --resume`, and returns when you exit.
60
+
61
+ If the DuckDB FTS extension is unavailable, dashboard search falls back to simple substring matching.
62
+
63
+ ### Query console
64
+
65
+ The Query console exposes your session data as DuckDB SQL views:
66
+
67
+ | View | Contents |
68
+ |------|----------|
69
+ | `v_sessions` | One row per session — date, project, summary, model, turn count |
70
+ | `v_assistant_turns` | Individual assistant turns with token counts |
71
+ | `v_stats` | Aggregated usage by model |
72
+ | `v_sessions_by_day` | Daily session and token totals |
73
+ | `raw_records` | Raw JSONL records |
74
+
75
+ Example queries:
76
+
77
+ ```sql
78
+ -- Sessions from the last week
79
+ SELECT title, cwd, turn_count FROM v_sessions
80
+ WHERE last_active > now() - INTERVAL '7 days';
81
+
82
+ -- Most token-heavy sessions
83
+ SELECT sessionId, SUM(output_tokens) AS total
84
+ FROM v_assistant_turns GROUP BY 1 ORDER BY 2 DESC LIMIT 10;
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Dev setup
90
+
91
+ ```bash
92
+ git clone https://github.com/jcc-ne/clack
93
+ cd clack
94
+ uv sync
95
+ uv run clack
96
+ ```
97
+
98
+ Release notes for TestPyPI and Trusted Publishing live in [docs/releasing.md](docs/releasing.md).
99
+
100
+ ---
101
+
102
+ ## Requirements
103
+
104
+ - Python 3.11+
105
+ - [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) (session files at `~/.claude/projects/`)
106
+ - tmux (optional — enables opening resumed sessions in a new window)
@@ -0,0 +1,71 @@
1
+ # Releasing clack-tui
2
+
3
+ ## Manual dry run with TestPyPI
4
+
5
+ Create a TestPyPI account, enable 2FA, and create a TestPyPI API token.
6
+
7
+ Store the token in `~/.pypirc`:
8
+
9
+ ```ini
10
+ [distutils]
11
+ index-servers =
12
+ pypi
13
+ testpypi
14
+
15
+ [pypi]
16
+ username = __token__
17
+ password = pypi-REPLACE_ME
18
+
19
+ [testpypi]
20
+ repository = https://test.pypi.org/legacy/
21
+ username = __token__
22
+ password = pypi-REPLACE_ME
23
+ ```
24
+
25
+ Lock the file down:
26
+
27
+ ```bash
28
+ chmod 600 ~/.pypirc
29
+ ```
30
+
31
+ Build and upload:
32
+
33
+ ```bash
34
+ uv build
35
+ uvx twine check dist/*
36
+ uvx twine upload -r testpypi dist/*
37
+ ```
38
+
39
+ Smoke-test install from TestPyPI:
40
+
41
+ ```bash
42
+ python3 -m venv /tmp/clack-test
43
+ source /tmp/clack-test/bin/activate
44
+ python -m pip install --upgrade pip
45
+ python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple clack-tui
46
+ clack
47
+ ```
48
+
49
+ If `0.1.0` has already been uploaded to TestPyPI, bump `version` in `pyproject.toml` before uploading again.
50
+
51
+ ## Trusted Publishing to PyPI
52
+
53
+ The repo includes a GitHub Actions workflow at `.github/workflows/publish.yml` that publishes on GitHub Release publication.
54
+
55
+ Configure PyPI Trusted Publishing for the project:
56
+
57
+ 1. Create the `clack-tui` project on PyPI if it does not exist yet, either by an initial manual upload or by creating the project through PyPI's publisher flow.
58
+ 2. In PyPI, open the project, then go to `Manage` -> `Publishing`.
59
+ 3. Add a GitHub publisher with:
60
+ - Owner: `jcc-ne`
61
+ - Repository: `clack`
62
+ - Workflow filename: `publish.yml`
63
+ - Environment name: `pypi`
64
+ 4. In GitHub, create an environment named `pypi` for the repository. Add approval rules if you want a manual gate.
65
+ 5. Publish a GitHub Release. The workflow will build with `uv build` and publish to PyPI via OIDC.
66
+
67
+ Notes:
68
+
69
+ - Trusted Publishing does not require a PyPI API token in GitHub secrets.
70
+ - The workflow requests `id-token: write`, which PyPI uses to mint a short-lived upload token.
71
+ - You can keep using the manual TestPyPI flow locally even after enabling Trusted Publishing for production releases.
@@ -0,0 +1,59 @@
1
+ [project]
2
+ name = "clack-tui"
3
+ version = "0.1.1"
4
+ description = "TUI for browsing, searching, and resuming Claude Code sessions"
5
+ readme = "README.md"
6
+ license = { text = "MIT" }
7
+ authors = [{ name = "Janine" }]
8
+ requires-python = ">=3.11"
9
+ keywords = ["claude", "tui", "terminal", "ai", "textual", "claude-code"]
10
+ classifiers = [
11
+ "Environment :: Console",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Topic :: Terminals",
18
+ "Topic :: Utilities",
19
+ ]
20
+ dependencies = [
21
+ "textual>=1.0.0",
22
+ "duckdb>=1.1.0",
23
+ "libtmux>=0.40.0",
24
+ "jinja2>=3.1.0",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/jcc-ne/clack"
29
+ Repository = "https://github.com/jcc-ne/clack"
30
+ Issues = "https://github.com/jcc-ne/clack/issues"
31
+
32
+ [project.scripts]
33
+ clack = "clack.__main__:main"
34
+ clack-tui = "clack.__main__:main"
35
+
36
+ [build-system]
37
+ requires = ["hatchling"]
38
+ build-backend = "hatchling.build"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/clack"]
42
+
43
+ [tool.hatch.build.targets.sdist]
44
+ exclude = [
45
+ "/.claude",
46
+ "/.envrc",
47
+ "/uv.lock",
48
+ ]
49
+
50
+ [tool.ruff]
51
+ target-version = "py311"
52
+ src = ["src"]
53
+ line-length = 100
54
+
55
+ [tool.ruff.lint]
56
+ select = ["E", "F", "I", "UP", "B", "SIM"]
57
+
58
+ [tool.ty.environment]
59
+ root = "src"
@@ -0,0 +1 @@
1
+ """clack — TUI for Claude Code session management."""
@@ -0,0 +1,12 @@
1
+ """Entry point for `python -m clack` and the `clack` CLI command."""
2
+
3
+ from clack.app import ClackApp
4
+
5
+
6
+ def main():
7
+ app = ClackApp()
8
+ app.run()
9
+
10
+
11
+ if __name__ == "__main__":
12
+ main()
@@ -0,0 +1,142 @@
1
+ """Main Textual application for clack."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import duckdb
6
+ from textual import work
7
+ from textual.actions import SkipAction
8
+ from textual.app import App, ComposeResult
9
+ from textual.binding import Binding
10
+ from textual.events import Key
11
+ from textual.widgets import Footer, Header, Input, LoadingIndicator, TabbedContent, TabPane, Tree
12
+ from textual.worker import Worker, WorkerState
13
+
14
+ from clack.widgets.dashboard import DashboardTab
15
+ from clack.widgets.dialog_viewer import DialogViewer
16
+ from clack.widgets.query_console import QueryConsole
17
+ from clack.widgets.stats import StatsTab
18
+
19
+
20
+ class ClackApp(App):
21
+ CSS_PATH = "css/app.tcss"
22
+ TITLE = "clack"
23
+ BINDINGS = [
24
+ Binding("1", "show_tab('dashboard')", "Dashboard", show=True),
25
+ Binding("2", "show_tab('stats')", "Stats", show=True),
26
+ Binding("3", "show_tab('dialog')", "Dialog", show=True),
27
+ Binding("4", "show_tab('query')", "Query", show=True),
28
+ Binding("q", "quit", "Quit", show=True),
29
+ Binding("t", "switch_theme", "Theme", show=True),
30
+ Binding("G", "nav_end", show=False),
31
+ Binding("ctrl+f", "nav_page_down", show=False),
32
+ Binding("ctrl+b", "nav_page_up", show=False),
33
+ ]
34
+
35
+ THEMES = ("solarized-dark", "solarized-light")
36
+
37
+ db: duckdb.DuckDBPyConnection | None = None
38
+ _dashboard: DashboardTab | None = None
39
+ _g_pending: bool = False
40
+
41
+ def compose(self) -> ComposeResult:
42
+ yield Header()
43
+ yield LoadingIndicator(id="loading-indicator")
44
+ with TabbedContent(id="tabs"):
45
+ with TabPane("Dashboard", id="dashboard"):
46
+ yield DashboardTab()
47
+ with TabPane("Stats", id="stats"):
48
+ yield StatsTab()
49
+ with TabPane("Dialog", id="dialog"):
50
+ yield DialogViewer()
51
+ with TabPane("Query", id="query"):
52
+ yield QueryConsole()
53
+ yield Footer()
54
+
55
+ def on_mount(self) -> None:
56
+ self.theme = "solarized-dark"
57
+ self.query_one("#tabs").display = False
58
+ self._load_data()
59
+ self.set_interval(60, self._auto_refresh_dashboard)
60
+
61
+ @work(thread=True, group="db_init")
62
+ def _load_data(self) -> duckdb.DuckDBPyConnection:
63
+ from clack.db import get_connection
64
+
65
+ return get_connection()
66
+
67
+ def on_worker_state_changed(self, event: Worker.StateChanged) -> None:
68
+ if event.worker.group == "db_init" and event.state == WorkerState.SUCCESS:
69
+ self.db = event.worker.result
70
+ self.query_one("#loading-indicator").display = False
71
+ self.query_one("#tabs").display = True
72
+ assert self.db is not None
73
+ self._dashboard = self.query_one(DashboardTab)
74
+ self._dashboard.load_data(self.db)
75
+ self.query_one(StatsTab).load_data(self.db)
76
+ self.query_one(QueryConsole).set_db(self.db)
77
+
78
+ def _auto_refresh_dashboard(self) -> None:
79
+ """Periodic refresh for the dashboard tab."""
80
+ if self.db is not None and self._dashboard is not None:
81
+ try:
82
+ self._dashboard._refresh_data()
83
+ except Exception:
84
+ pass
85
+
86
+ def on_key(self, event: Key) -> None:
87
+ # Skip vim nav when an Input widget has focus
88
+ if isinstance(self.focused, Input):
89
+ self._g_pending = False
90
+ return
91
+ if event.key == "g":
92
+ if self._g_pending:
93
+ # gg -> go to top
94
+ self._g_pending = False
95
+ event.prevent_default()
96
+ self.action_nav_home()
97
+ else:
98
+ self._g_pending = True
99
+ event.prevent_default()
100
+ return
101
+ self._g_pending = False
102
+
103
+ def _nav_action(self, *actions: str) -> None:
104
+ """Try navigation actions on the focused widget, using the first one found."""
105
+ widget = self.focused
106
+ if widget is None:
107
+ return
108
+ for action in actions:
109
+ method = getattr(widget, f"action_{action}", None)
110
+ if method is not None:
111
+ try:
112
+ method()
113
+ except SkipAction:
114
+ continue
115
+ return
116
+
117
+ def action_nav_home(self) -> None:
118
+ self._nav_action("scroll_top", "scroll_home")
119
+
120
+ def action_nav_end(self) -> None:
121
+ self._nav_action("scroll_bottom", "scroll_end")
122
+
123
+ def action_nav_page_down(self) -> None:
124
+ self._nav_action("page_down")
125
+
126
+ def action_nav_page_up(self) -> None:
127
+ self._nav_action("page_up")
128
+
129
+ def action_switch_theme(self) -> None:
130
+ current = self.THEMES.index(self.theme) if self.theme in self.THEMES else -1
131
+ self.theme = self.THEMES[(current + 1) % len(self.THEMES)]
132
+
133
+ def action_show_tab(self, tab_id: str) -> None:
134
+ self.query_one(TabbedContent).active = tab_id
135
+
136
+ def show_dialog(self, session_id: str, title: str) -> None:
137
+ """Switch to dialog tab and load a session."""
138
+ self.query_one(TabbedContent).active = "dialog"
139
+ assert self.db is not None
140
+ viewer = self.query_one(DialogViewer)
141
+ viewer.load_session(self.db, session_id, title)
142
+ viewer.query_one("#dialog-tree", Tree).focus()
@@ -0,0 +1,104 @@
1
+ Screen {
2
+ background: $surface;
3
+ }
4
+
5
+ #loading-indicator {
6
+ width: 100%;
7
+ height: 100%;
8
+ content-align: center middle;
9
+ }
10
+
11
+ /* Dashboard */
12
+ DashboardTab {
13
+ height: 1fr;
14
+ }
15
+
16
+ DashboardTab DataTable {
17
+ height: 1fr;
18
+ }
19
+
20
+ DashboardTab #detail-bar {
21
+ height: 3;
22
+ padding: 0 1;
23
+ background: $surface-darken-1;
24
+ color: $text-muted;
25
+ }
26
+
27
+ DashboardTab #search-input {
28
+ dock: top;
29
+ width: 100%;
30
+ margin: 0;
31
+ }
32
+
33
+ /* Stats */
34
+ StatsTab {
35
+ height: 1fr;
36
+ layout: grid;
37
+ grid-size: 2;
38
+ grid-gutter: 1;
39
+ }
40
+
41
+ StatsTab #model-table {
42
+ height: 1fr;
43
+ }
44
+
45
+ StatsTab #stats-summary {
46
+ height: 1fr;
47
+ padding: 1;
48
+ }
49
+
50
+ /* Dialog Viewer */
51
+ DialogViewer {
52
+ height: 1fr;
53
+ }
54
+
55
+ DialogViewer #dialog-header {
56
+ height: 2;
57
+ padding: 0 1;
58
+ background: $primary-darken-2;
59
+ color: $text;
60
+ }
61
+
62
+ DialogViewer #dialog-search {
63
+ dock: top;
64
+ width: 100%;
65
+ margin: 0;
66
+ }
67
+
68
+ DialogViewer #dialog-tree {
69
+ height: 1fr;
70
+ }
71
+
72
+ DialogViewer #dialog-footer {
73
+ height: 1;
74
+ padding: 0 1;
75
+ background: $surface-darken-1;
76
+ color: $text-muted;
77
+ }
78
+
79
+ /* Query Console */
80
+ QueryConsole {
81
+ height: 1fr;
82
+ }
83
+
84
+ QueryConsole #query-results {
85
+ height: 1fr;
86
+ }
87
+
88
+ QueryConsole #query-input {
89
+ height: 5;
90
+ border: tall $primary;
91
+ }
92
+
93
+ QueryConsole #query-status {
94
+ height: 1;
95
+ padding: 0 1;
96
+ background: $surface-darken-1;
97
+ color: $text-muted;
98
+ }
99
+
100
+ QueryConsole #views-help {
101
+ height: 1;
102
+ padding: 0 1;
103
+ color: $text-muted;
104
+ }