functualize-inline 0.1.0__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,101 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg-info/
7
+ *.egg
8
+ dist/
9
+ build/
10
+ *.whl
11
+
12
+ # Agents
13
+ .spec/archive/
14
+ .spec/features/
15
+ .spec/scrutiny-reports/
16
+ .spec/.agentic-coding
17
+ .spec/STATE.md
18
+ .spec/PROJECT.md
19
+ .spec/REQUIREMENTS.md
20
+ .spec/ROADMAP.md
21
+ .opencode/
22
+
23
+
24
+ # Virtual environments
25
+ .venv/
26
+ venv/
27
+ ENV/
28
+
29
+ # Testing
30
+ .coverage
31
+ .pytest_cache/
32
+ htmlcov/
33
+ .hypothesis/
34
+ snapshot_report.html
35
+ _*_result*.txt
36
+ _debug.txt
37
+ _tui_debug.txt
38
+ _tui_eval_debug.txt
39
+
40
+ # IDE
41
+ .idea/
42
+ *.swp
43
+ *.swo
44
+ *~
45
+ *.code-workspace
46
+
47
+ # Coding-agent tooling state (guards — these dirs are not part of the repo)
48
+ .kiro/
49
+ .moai/
50
+
51
+ # OS
52
+ .DS_Store
53
+ Thumbs.db
54
+
55
+ # Environment / secrets
56
+ .env
57
+ .env.*
58
+ !.env.example
59
+
60
+ # Agent scratch space (test output, temp scripts)
61
+ tmp/
62
+
63
+ # Local-only files (not for the repo)
64
+ *.local.md
65
+ *.local.*
66
+
67
+ # Personal notes
68
+ HUMAN_NOTE.md
69
+
70
+ # Distribution
71
+ dist/
72
+
73
+ # Documentation site build output
74
+ site/
75
+
76
+ # uv
77
+ .python-version
78
+ .functualize/cache.json
79
+ .functualize_cache.json
80
+ .todos/
81
+ .sidecar/
82
+ .sidecar-agent
83
+ .sidecar-task
84
+ .sidecar-pr
85
+ .sidecar-start.sh
86
+ .sidecar-base
87
+ .td-root
88
+ .functualize/
89
+ .import_linter_cache/
90
+ .mypy_cache/
91
+ .pytest_cache/
92
+ .ruff_cache/
93
+
94
+ # OmO / OpenCode agent run-continuation scratch state
95
+ .omo/
96
+ .mcp.json
97
+ .agentsroom/handoff-transcript-*.txt
98
+ .agentsroom/handoff-summary-*.md
99
+
100
+ # Internal pre-release audit reports (contain session IDs / local infra notes)
101
+ .release/
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: functualize-inline
3
+ Version: 0.1.0
4
+ Summary: Textual inline interactivity plugin for functualize prompts
5
+ Author-email: Mohammad Hakim Adiprasetya <viltohmyst@gmail.com>
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Typing :: Typed
13
+ Requires-Python: >=3.11
14
+ Requires-Dist: functualize<1.0.0,>=0.1.0
15
+ Requires-Dist: textual>=0.40.0
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
18
+ Requires-Dist: pytest>=7.4.0; extra == 'dev'
19
+ Description-Content-Type: text/markdown
20
+
21
+ # functualize-inline
22
+
23
+ > **Status: Published** — Independently installable from PyPI.
24
+
25
+ Textual-based inline interactivity provider for the functualize framework. This plugin renders rich terminal widgets (confirmations, selections, text inputs, progress bars) inline within the terminal using [Textual's](https://textual.textualize.io/) inline mode — without taking over the full screen. When Textual is unavailable (non-TTY, CI environments), it gracefully falls back to plain CLI `input()` prompts.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install functualize-inline
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ```python
36
+ from functualize.plugin import PromptIntent, PromptRequest
37
+ from functualize_inline import InlinePlugin
38
+
39
+ plugin = InlinePlugin()
40
+
41
+ request = PromptRequest(
42
+ intent=PromptIntent.CONFIRM_NEUTRAL,
43
+ question="Deploy to staging?",
44
+ default=True,
45
+ )
46
+ response = plugin.collect(request)
47
+ print(f"User answered: {response.value} (source: {response.source})")
48
+ ```
49
+
50
+ ## Features
51
+
52
+ - **Rich inline widgets** — Confirmation dialogs, single/multi-select lists, text inputs, secret inputs, and acknowledgment prompts rendered with Textual styling
53
+ - **Automatic CLI fallback** — Detects non-TTY environments and falls back to plain `input()` prompts so jobs run unattended in CI
54
+ - **Structured output rendering** — Implements `OutputRenderer` protocol with `render_log`, `render_phase`, and `render_progress` for formatted terminal output with icons and progress bars
55
+ - **Timeout support** — Prompts can auto-dismiss after a configurable timeout, returning the default value
56
+ - **Entry-point auto-discovery** — Install the package and it registers itself as an interactivity provider via the `functualize.interactivity_providers` entry point
57
+
58
+ ## API Reference
59
+
60
+ Public classes and functions exported by this plugin:
61
+
62
+ ### `functualize_inline` (top-level)
63
+
64
+ - `InlinePlugin` — Main plugin class implementing `InputProvider` and `OutputRenderer` protocols. Entry point for prompt collection and terminal output rendering.
65
+
66
+ ### `functualize_inline.apps`
67
+
68
+ - `InlinePromptApp` — A short-lived Textual `App` (inline mode) that mounts a prompt widget and returns the user's response as a `(value, source)` tuple.
69
+
70
+ ### `functualize_inline.widgets`
71
+
72
+ - `PromptResult` — Textual `Message` posted by widgets when the user provides a response. Carries `.value` and `.source` attributes.
73
+ - `ConfirmDestructiveWidget` — Red-bordered widget requiring the user to type "yes" to confirm dangerous actions.
74
+ - `ConfirmNeutralWidget` — Standard Y/n confirmation widget with keyboard shortcuts.
75
+ - `SelectWidget` — Single-select `OptionList` widget (max 12 visible items).
76
+ - `MultiSelectWidget` — Checkbox-style multi-select widget with toggle and submit.
77
+ - `TextInputWidget` — Free-text input with optional placeholder and default value.
78
+ - `SecretInputWidget` — Masked password input widget.
79
+ - `AcknowledgeWidget` — Press-any-key dismiss widget for informational prompts.
80
+
81
+ ## Development
82
+
83
+ Run plugin tests:
84
+
85
+ ```bash
86
+ uv run pytest plugins/functualize-inline/tests/ -v
87
+ ```
88
+
89
+ Run linting and formatting:
90
+
91
+ ```bash
92
+ uv run ruff check plugins/functualize-inline/
93
+ uv run ruff format plugins/functualize-inline/
94
+ ```
@@ -0,0 +1,74 @@
1
+ # functualize-inline
2
+
3
+ > **Status: Published** — Independently installable from PyPI.
4
+
5
+ Textual-based inline interactivity provider for the functualize framework. This plugin renders rich terminal widgets (confirmations, selections, text inputs, progress bars) inline within the terminal using [Textual's](https://textual.textualize.io/) inline mode — without taking over the full screen. When Textual is unavailable (non-TTY, CI environments), it gracefully falls back to plain CLI `input()` prompts.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install functualize-inline
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```python
16
+ from functualize.plugin import PromptIntent, PromptRequest
17
+ from functualize_inline import InlinePlugin
18
+
19
+ plugin = InlinePlugin()
20
+
21
+ request = PromptRequest(
22
+ intent=PromptIntent.CONFIRM_NEUTRAL,
23
+ question="Deploy to staging?",
24
+ default=True,
25
+ )
26
+ response = plugin.collect(request)
27
+ print(f"User answered: {response.value} (source: {response.source})")
28
+ ```
29
+
30
+ ## Features
31
+
32
+ - **Rich inline widgets** — Confirmation dialogs, single/multi-select lists, text inputs, secret inputs, and acknowledgment prompts rendered with Textual styling
33
+ - **Automatic CLI fallback** — Detects non-TTY environments and falls back to plain `input()` prompts so jobs run unattended in CI
34
+ - **Structured output rendering** — Implements `OutputRenderer` protocol with `render_log`, `render_phase`, and `render_progress` for formatted terminal output with icons and progress bars
35
+ - **Timeout support** — Prompts can auto-dismiss after a configurable timeout, returning the default value
36
+ - **Entry-point auto-discovery** — Install the package and it registers itself as an interactivity provider via the `functualize.interactivity_providers` entry point
37
+
38
+ ## API Reference
39
+
40
+ Public classes and functions exported by this plugin:
41
+
42
+ ### `functualize_inline` (top-level)
43
+
44
+ - `InlinePlugin` — Main plugin class implementing `InputProvider` and `OutputRenderer` protocols. Entry point for prompt collection and terminal output rendering.
45
+
46
+ ### `functualize_inline.apps`
47
+
48
+ - `InlinePromptApp` — A short-lived Textual `App` (inline mode) that mounts a prompt widget and returns the user's response as a `(value, source)` tuple.
49
+
50
+ ### `functualize_inline.widgets`
51
+
52
+ - `PromptResult` — Textual `Message` posted by widgets when the user provides a response. Carries `.value` and `.source` attributes.
53
+ - `ConfirmDestructiveWidget` — Red-bordered widget requiring the user to type "yes" to confirm dangerous actions.
54
+ - `ConfirmNeutralWidget` — Standard Y/n confirmation widget with keyboard shortcuts.
55
+ - `SelectWidget` — Single-select `OptionList` widget (max 12 visible items).
56
+ - `MultiSelectWidget` — Checkbox-style multi-select widget with toggle and submit.
57
+ - `TextInputWidget` — Free-text input with optional placeholder and default value.
58
+ - `SecretInputWidget` — Masked password input widget.
59
+ - `AcknowledgeWidget` — Press-any-key dismiss widget for informational prompts.
60
+
61
+ ## Development
62
+
63
+ Run plugin tests:
64
+
65
+ ```bash
66
+ uv run pytest plugins/functualize-inline/tests/ -v
67
+ ```
68
+
69
+ Run linting and formatting:
70
+
71
+ ```bash
72
+ uv run ruff check plugins/functualize-inline/
73
+ uv run ruff format plugins/functualize-inline/
74
+ ```
@@ -0,0 +1,22 @@
1
+ # functualize-inline Examples
2
+
3
+ Inline Textual widgets for prompts, selections, and progress — rendered under your shell prompt during job runs.
4
+
5
+ | Directory | Demonstrates |
6
+ |-----------|--------------|
7
+ | [`prompted_deploy/`](prompted_deploy/) | A job that pauses for a confirmation and a selection, rendered as inline terminal widgets |
8
+
9
+ ## Manual verification (interactive widgets aren't headless-testable)
10
+
11
+ ```bash
12
+ cd plugins/functualize-inline/examples/prompted_deploy
13
+ func deploy.py run
14
+ ```
15
+
16
+ Expected behavior:
17
+
18
+ 1. A destructive-styled confirmation widget appears inline ("Deploy to production?") — y / n.
19
+ 2. On confirm, a selection list of regions appears — pick with arrows + Enter.
20
+ 3. The job continues, logging the chosen region; the widgets collapse back into the scrollback.
21
+
22
+ In a headless run (CI, pipe), the same prompts resolve from their defaults instead of blocking. To drive that path in a test, substitute `AutoPrompt` from `functualize.testing`.
@@ -0,0 +1,26 @@
1
+ """A job that asks for confirmation and a region via inline widgets.
2
+
3
+ With functualize-inline installed, `rc.prompt` renders real terminal
4
+ widgets; headless runs resolve from defaults.
5
+
6
+ Run with:
7
+ func deploy.py run
8
+ """
9
+
10
+ from functualize.job import RunContext
11
+
12
+
13
+ def run(rc: RunContext) -> str:
14
+ """Deploy after an interactive confirmation and region pick."""
15
+ if not rc.prompt_confirm("Deploy to production?", destructive=True, default=False):
16
+ rc.log("Deploy cancelled")
17
+ return "cancelled"
18
+
19
+ region = rc.prompt_choice(
20
+ "Which region?",
21
+ ["us-east-1", "eu-west-1", "ap-southeast-1"],
22
+ default="us-east-1",
23
+ )
24
+ rc.log(f"Deploying to {region}...")
25
+ rc.log("Deploy complete")
26
+ return region
@@ -0,0 +1,41 @@
1
+ [project]
2
+ name = "functualize-inline"
3
+ version = "0.1.0"
4
+ description = "Textual inline interactivity plugin for functualize prompts"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ authors = [
8
+ { name = "Mohammad Hakim Adiprasetya", email = "viltohmyst@gmail.com" }
9
+ ]
10
+ requires-python = ">=3.11"
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: Python :: 3.11",
15
+ "Programming Language :: Python :: 3.12",
16
+ "Programming Language :: Python :: 3.13",
17
+ "Typing :: Typed",
18
+ ]
19
+ dependencies = [
20
+ "functualize>=0.1.0,<1.0.0",
21
+ "textual>=0.40.0",
22
+ ]
23
+
24
+ [project.entry-points."functualize.interactivity_providers"]
25
+ inline = "functualize_inline:InlinePlugin"
26
+
27
+ [project.optional-dependencies]
28
+ dev = [
29
+ "pytest>=7.4.0",
30
+ "pytest-cov>=4.1.0",
31
+ ]
32
+
33
+ [build-system]
34
+ requires = ["hatchling"]
35
+ build-backend = "hatchling.build"
36
+
37
+ [tool.uv.sources]
38
+ functualize = { workspace = true }
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/functualize_inline"]
@@ -0,0 +1,5 @@
1
+ """Functualize Inline Plugin - Textual inline interactivity for terminal prompts."""
2
+
3
+ from functualize_inline.plugin import InlinePlugin
4
+
5
+ __all__ = ["InlinePlugin"]
@@ -0,0 +1,74 @@
1
+ """Textual inline applications for prompt rendering.
2
+
3
+ Uses Textual's `inline=True` mode to render widgets inline within the terminal
4
+ without taking over the full screen. Each prompt spawns a short-lived inline app
5
+ that returns the user's response.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any
11
+
12
+ from textual import on
13
+ from textual.app import App, ComposeResult
14
+
15
+ from functualize_inline.widgets import (
16
+ PromptResult,
17
+ )
18
+
19
+
20
+ class InlinePromptApp(App[tuple[Any, str]]):
21
+ """A short-lived Textual app that renders a prompt widget inline.
22
+
23
+ Returns a tuple of (value, source) when the user interacts with the widget.
24
+ The app runs in inline mode and exits once a PromptResult message is received.
25
+ """
26
+
27
+ CSS = """
28
+ Screen {
29
+ layout: vertical;
30
+ overflow-y: auto;
31
+ }
32
+ """
33
+
34
+ BINDINGS = [
35
+ ("ctrl+c", "force_cancel", "Cancel"),
36
+ ]
37
+
38
+ def __init__(
39
+ self,
40
+ widget_class: type,
41
+ widget_kwargs: dict[str, Any],
42
+ timeout: float | None = None,
43
+ **kwargs: Any,
44
+ ) -> None:
45
+ super().__init__(inline=True, **kwargs)
46
+ self._widget_class = widget_class
47
+ self._widget_kwargs = widget_kwargs
48
+ self._timeout = timeout
49
+ self._timeout_timer: Any = None
50
+
51
+ def compose(self) -> ComposeResult:
52
+ yield self._widget_class(**self._widget_kwargs)
53
+
54
+ def on_mount(self) -> None:
55
+ """Start timeout timer if configured."""
56
+ if self._timeout is not None and self._timeout > 0:
57
+ self._timeout_timer = self.set_timer(self._timeout, self._on_timeout)
58
+
59
+ def _on_timeout(self) -> None:
60
+ """Auto-dismiss on timeout."""
61
+ self.exit((None, "timeout"))
62
+
63
+ @on(PromptResult)
64
+ def _on_prompt_result(self, message: PromptResult) -> None:
65
+ """Handle result from the prompt widget."""
66
+ if self._timeout_timer is not None:
67
+ self._timeout_timer.stop()
68
+ self.exit((message.value, message.source))
69
+
70
+ def action_force_cancel(self) -> None:
71
+ """Handle Ctrl+C."""
72
+ if self._timeout_timer is not None:
73
+ self._timeout_timer.stop()
74
+ self.exit((None, "cancelled"))