induscode 0.1.0__py3-none-any.whl
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.
- induscode/__init__.py +56 -0
- induscode/addons/__init__.py +176 -0
- induscode/addons/contract.py +923 -0
- induscode/addons/dispatch/__init__.py +43 -0
- induscode/addons/dispatch/event_dispatcher.py +348 -0
- induscode/addons/dispatch/tool_interceptor.py +349 -0
- induscode/addons/host.py +469 -0
- induscode/addons/loader.py +314 -0
- induscode/addons/manifest.py +232 -0
- induscode/addons/surface.py +199 -0
- induscode/boot/__init__.py +108 -0
- induscode/boot/auth_vault.py +323 -0
- induscode/boot/boot.py +210 -0
- induscode/boot/contract.py +223 -0
- induscode/boot/invocation.py +117 -0
- induscode/boot/runners/__init__.py +42 -0
- induscode/boot/runners/link_runner.py +82 -0
- induscode/boot/runners/oneshot_runner.py +85 -0
- induscode/boot/runners/registry.py +46 -0
- induscode/boot/runners/repl_runner.py +340 -0
- induscode/boot/runners/session.py +549 -0
- induscode/boot/stages.py +198 -0
- induscode/boot/upgrade/__init__.py +36 -0
- induscode/boot/upgrade/apply.py +125 -0
- induscode/boot/upgrade/upgrades.py +136 -0
- induscode/briefing/__init__.py +115 -0
- induscode/briefing/compose.py +414 -0
- induscode/briefing/contract.py +528 -0
- induscode/briefing/macros.py +721 -0
- induscode/briefing/skills.py +417 -0
- induscode/capability_deck/__init__.py +233 -0
- induscode/capability_deck/bridge_ledger/__init__.py +66 -0
- induscode/capability_deck/bridge_ledger/key.py +181 -0
- induscode/capability_deck/bridge_ledger/ledger.py +276 -0
- induscode/capability_deck/bridge_ledger/network.py +336 -0
- induscode/capability_deck/builtin_bridge.py +358 -0
- induscode/capability_deck/cards/__init__.py +116 -0
- induscode/capability_deck/cards/bg_process.py +482 -0
- induscode/capability_deck/cards/memory.py +226 -0
- induscode/capability_deck/cards/saas.py +280 -0
- induscode/capability_deck/cards/task.py +256 -0
- induscode/capability_deck/cards/todo.py +312 -0
- induscode/capability_deck/contract.py +450 -0
- induscode/capability_deck/manifest.py +126 -0
- induscode/capability_deck/provision.py +217 -0
- induscode/channels/__init__.py +146 -0
- induscode/channels/contract.py +585 -0
- induscode/channels/framer.py +132 -0
- induscode/channels/link/__init__.py +50 -0
- induscode/channels/link/dialog.py +246 -0
- induscode/channels/link/driver.py +308 -0
- induscode/channels/link/server.py +217 -0
- induscode/channels/oneshot.py +178 -0
- induscode/channels/ops.py +140 -0
- induscode/channels/session_ops.py +172 -0
- induscode/conductor/__init__.py +240 -0
- induscode/conductor/catalog.py +309 -0
- induscode/conductor/conductor.py +1084 -0
- induscode/conductor/contract.py +1035 -0
- induscode/conductor/matcher.py +291 -0
- induscode/conductor/serialize.py +575 -0
- induscode/conductor/signal_hub.py +382 -0
- induscode/conductor/skill_parse.py +294 -0
- induscode/conductor/transcript_store.py +449 -0
- induscode/console/__init__.py +236 -0
- induscode/console/app.py +1677 -0
- induscode/console/components/__init__.py +62 -0
- induscode/console/components/banner.py +499 -0
- induscode/console/components/banner_sweep.py +188 -0
- induscode/console/components/emblem.py +181 -0
- induscode/console/components/status_bar.py +102 -0
- induscode/console/contract.py +836 -0
- induscode/console/input/__init__.py +107 -0
- induscode/console/input/chord.py +197 -0
- induscode/console/input/dir_reader.py +113 -0
- induscode/console/input/intents.py +258 -0
- induscode/console/input/providers.py +469 -0
- induscode/console/mount.py +137 -0
- induscode/console/overlays/__init__.py +94 -0
- induscode/console/overlays/auth.py +503 -0
- induscode/console/overlays/pickers.py +526 -0
- induscode/console/overlays/router.py +129 -0
- induscode/console/overlays/sessions.py +232 -0
- induscode/console/reducer.py +145 -0
- induscode/console/resume_picker.py +156 -0
- induscode/console/slash_commands/__init__.py +78 -0
- induscode/console/slash_commands/builtins.py +254 -0
- induscode/console/slash_commands/dynamic.py +217 -0
- induscode/console/slash_commands/integrations.py +949 -0
- induscode/console/slash_commands/transcript.py +404 -0
- induscode/console/slash_commands/workbench.py +430 -0
- induscode/console/startup.py +434 -0
- induscode/console/theme/__init__.py +44 -0
- induscode/console/theme/adapter.py +168 -0
- induscode/console/theme/palette.py +128 -0
- induscode/console/theme/resolve.py +123 -0
- induscode/console/theme/tokens.py +185 -0
- induscode/console_slash/__init__.py +111 -0
- induscode/console_slash/contract.py +185 -0
- induscode/console_slash/registry.py +140 -0
- induscode/console_slash/resolve.py +194 -0
- induscode/console_slash/shared.py +172 -0
- induscode/entry.py +108 -0
- induscode/insight/__init__.py +153 -0
- induscode/insight/collector.py +73 -0
- induscode/insight/replay.py +305 -0
- induscode/insight/wrapper.py +1115 -0
- induscode/kit/__init__.py +82 -0
- induscode/kit/clipboard_image.py +215 -0
- induscode/kit/external_editor.py +120 -0
- induscode/kit/image.py +188 -0
- induscode/kit/shell.py +89 -0
- induscode/kit/tool_fetch.py +288 -0
- induscode/launch/__init__.py +224 -0
- induscode/launch/catalog.py +310 -0
- induscode/launch/contract.py +569 -0
- induscode/launch/credentials.py +852 -0
- induscode/launch/invocation/__init__.py +39 -0
- induscode/launch/invocation/attachments.py +281 -0
- induscode/launch/invocation/flags.py +210 -0
- induscode/launch/invocation/read.py +369 -0
- induscode/launch/invocation/usage.py +110 -0
- induscode/launch/oauth.py +808 -0
- induscode/launch/packages.py +299 -0
- induscode/launch/pickers.py +291 -0
- induscode/py.typed +0 -0
- induscode/runtime_bridge/__init__.py +166 -0
- induscode/runtime_bridge/bridges/__init__.py +66 -0
- induscode/runtime_bridge/bridges/_drive.py +268 -0
- induscode/runtime_bridge/bridges/builtins.py +177 -0
- induscode/runtime_bridge/bridges/claude_cli.py +198 -0
- induscode/runtime_bridge/bridges/codex_cli.py +203 -0
- induscode/runtime_bridge/bridges/indusagi_cli.py +217 -0
- induscode/runtime_bridge/broker.py +397 -0
- induscode/runtime_bridge/contract.py +734 -0
- induscode/runtime_bridge/sink.py +351 -0
- induscode/sessions/__init__.py +25 -0
- induscode/sessions/contract.py +119 -0
- induscode/sessions/library.py +350 -0
- induscode/settings/__init__.py +47 -0
- induscode/settings/contract.py +313 -0
- induscode/settings/manager.py +268 -0
- induscode/transcript_export/__init__.py +109 -0
- induscode/transcript_export/contract.py +522 -0
- induscode/transcript_export/publish.py +455 -0
- induscode/transcript_export/sgr.py +566 -0
- induscode/transcript_export/template.py +319 -0
- induscode/transcript_export/theme_bridge.py +325 -0
- induscode/window_budget/__init__.py +76 -0
- induscode/window_budget/budget/__init__.py +26 -0
- induscode/window_budget/budget/estimate.py +273 -0
- induscode/window_budget/budget/gate.py +60 -0
- induscode/window_budget/budget/slice.py +145 -0
- induscode/window_budget/condenser.py +170 -0
- induscode/window_budget/contract.py +329 -0
- induscode/window_budget/summarize/__init__.py +33 -0
- induscode/window_budget/summarize/condense.py +212 -0
- induscode/window_budget/summarize/prompt.py +241 -0
- induscode/workspace/__init__.py +30 -0
- induscode/workspace/brand.py +96 -0
- induscode/workspace/locator.py +269 -0
- induscode-0.1.0.dist-info/METADATA +97 -0
- induscode-0.1.0.dist-info/RECORD +167 -0
- induscode-0.1.0.dist-info/WHEEL +4 -0
- induscode-0.1.0.dist-info/entry_points.txt +3 -0
- induscode-0.1.0.dist-info/licenses/CREDITS.md +22 -0
- induscode-0.1.0.dist-info/licenses/NOTICE +7 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Theme palettes — the console's own re-derived accent ramps.
|
|
2
|
+
|
|
3
|
+
Port of TS ``src/console/theme/palette.ts``; the hex values are carried over
|
|
4
|
+
VERBATIM. This module owns the four raw
|
|
5
|
+
:class:`~induscode.console.contract.ThemePalette` values the console ships
|
|
6
|
+
with, one per :data:`~induscode.console.contract.ThemeScheme`. A palette is
|
|
7
|
+
the *source* nine-stop ramp a scheme is built from: three accent hues (a cool
|
|
8
|
+
primary, a warm secondary, a muted support tertiary), a three-stop neutral
|
|
9
|
+
text gradient (ink/body/muted), and the three status hues
|
|
10
|
+
(affirm/caution/alarm). Everything the console renders is computed from these
|
|
11
|
+
stops by the token derivation step — no module downstream of here writes a
|
|
12
|
+
literal hex.
|
|
13
|
+
|
|
14
|
+
The hex values are an original ramp authored for this console. The cool
|
|
15
|
+
primary is a desaturated spring-green/teal, the warm secondary a tangerine
|
|
16
|
+
amber, and the support tertiary a soft periwinkle; the neutral gradient is a
|
|
17
|
+
cool slate. The ``daylight`` variant re-derives the same roles at higher
|
|
18
|
+
saturation / lower luminance so they read on a bright terminal background.
|
|
19
|
+
None of these stops reuse an upstream accent — they were picked fresh against
|
|
20
|
+
a contrast target for each role.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from dataclasses import replace
|
|
26
|
+
from types import MappingProxyType
|
|
27
|
+
from typing import Final, Mapping
|
|
28
|
+
|
|
29
|
+
from ..contract import ThemePalette, ThemeScheme
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"DAYLIGHT_CB_PALETTE",
|
|
33
|
+
"DAYLIGHT_PALETTE",
|
|
34
|
+
"MIDNIGHT_CB_PALETTE",
|
|
35
|
+
"MIDNIGHT_PALETTE",
|
|
36
|
+
"PALETTES",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
# The two raw ramps
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
#: The dark-terminal ramp.
|
|
45
|
+
#:
|
|
46
|
+
#: Cool spring-teal primary against a cool-slate neutral gradient, with a warm
|
|
47
|
+
#: amber secondary for contrast accents and a soft periwinkle support tone.
|
|
48
|
+
MIDNIGHT_PALETTE: Final[ThemePalette] = ThemePalette(
|
|
49
|
+
primary="#3ad6b4",
|
|
50
|
+
secondary="#f0a24b",
|
|
51
|
+
tertiary="#7c9fe8",
|
|
52
|
+
ink="#f4f6fb",
|
|
53
|
+
body="#c3c8d4",
|
|
54
|
+
muted="#7e8696",
|
|
55
|
+
affirm="#46c98a",
|
|
56
|
+
caution="#e8b341",
|
|
57
|
+
alarm="#ef6a6a",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
#: The light-terminal ramp.
|
|
61
|
+
#:
|
|
62
|
+
#: The same role assignments re-derived deeper and more saturated so each hue
|
|
63
|
+
#: keeps its contrast against a bright background; the neutral gradient
|
|
64
|
+
#: inverts to a dark-on-light slate.
|
|
65
|
+
DAYLIGHT_PALETTE: Final[ThemePalette] = ThemePalette(
|
|
66
|
+
primary="#0f8f78",
|
|
67
|
+
secondary="#c26a10",
|
|
68
|
+
tertiary="#3a5ec0",
|
|
69
|
+
ink="#1c2230",
|
|
70
|
+
body="#3a4253",
|
|
71
|
+
muted="#6b7382",
|
|
72
|
+
affirm="#1d9d63",
|
|
73
|
+
caution="#a9740a",
|
|
74
|
+
alarm="#c4322f",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
# The daltonized (color-blind-friendly) ramps
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
#: The dark-terminal color-blind-safe ramp.
|
|
83
|
+
#:
|
|
84
|
+
#: A clone of :data:`MIDNIGHT_PALETTE` that re-derives the three *status*
|
|
85
|
+
#: hues so a red-green color-blind user (deuteran/protan, ~8% of men) can
|
|
86
|
+
#: tell success from failure without relying on the red-green axis. Success
|
|
87
|
+
#: moves off green onto a vivid blue (``affirm → #4aa3ff``); failure stays
|
|
88
|
+
#: red but is deepened to a darker tone (``alarm → #c83232``) so it sits well
|
|
89
|
+
#: below the bright blue in lightness, and the amber warning is nudged
|
|
90
|
+
#: brighter — so the three status tones separate by *lightness*, not hue
|
|
91
|
+
#: alone. The three accent hues and the neutral gradient are carried over
|
|
92
|
+
#: unchanged from the base midnight ramp — only the status stops move, so
|
|
93
|
+
#: the overall look stays the midnight scheme.
|
|
94
|
+
MIDNIGHT_CB_PALETTE: Final[ThemePalette] = replace(
|
|
95
|
+
MIDNIGHT_PALETTE,
|
|
96
|
+
affirm="#4aa3ff",
|
|
97
|
+
caution="#f2c75c",
|
|
98
|
+
alarm="#c83232",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
#: The light-terminal color-blind-safe ramp.
|
|
102
|
+
#:
|
|
103
|
+
#: The daylight counterpart of :data:`MIDNIGHT_CB_PALETTE`: clones
|
|
104
|
+
#: :data:`DAYLIGHT_PALETTE` and remaps success to a deeper blue tuned for a
|
|
105
|
+
#: bright background (``affirm → #1f6fd6``), deepens the red alarm
|
|
106
|
+
#: (``alarm → #b02622``) for lightness separation from that blue, and darkens
|
|
107
|
+
#: the amber warning so the three status tones stay separable by lightness on
|
|
108
|
+
#: a light terminal too.
|
|
109
|
+
DAYLIGHT_CB_PALETTE: Final[ThemePalette] = replace(
|
|
110
|
+
DAYLIGHT_PALETTE,
|
|
111
|
+
affirm="#1f6fd6",
|
|
112
|
+
caution="#8a5e00",
|
|
113
|
+
alarm="#b02622",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
#: The raw ramp for each scheme, keyed by
|
|
117
|
+
#: :data:`~induscode.console.contract.ThemeScheme`.
|
|
118
|
+
#:
|
|
119
|
+
#: The token derivation step reads from this table; the resolver returns the
|
|
120
|
+
#: scheme whose palette it expanded.
|
|
121
|
+
PALETTES: Final[Mapping[ThemeScheme, ThemePalette]] = MappingProxyType(
|
|
122
|
+
{
|
|
123
|
+
"midnight": MIDNIGHT_PALETTE,
|
|
124
|
+
"daylight": DAYLIGHT_PALETTE,
|
|
125
|
+
"midnight-cb": MIDNIGHT_CB_PALETTE,
|
|
126
|
+
"daylight-cb": DAYLIGHT_CB_PALETTE,
|
|
127
|
+
}
|
|
128
|
+
)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Theme resolution — assemble the fully-resolved :class:`ConsoleTheme` for
|
|
2
|
+
each scheme and expose the picker entry points.
|
|
3
|
+
|
|
4
|
+
Port of TS ``src/console/theme/resolve.ts``. This is the top of the theme
|
|
5
|
+
engine. It runs the pipeline once per scheme at module load:
|
|
6
|
+
|
|
7
|
+
.. code-block:: text
|
|
8
|
+
|
|
9
|
+
palette ──derive_tokens──▶ tokens ──theme_bundle──▶ framework bundle
|
|
10
|
+
└─────────────────────────┴────────────────────────────┘
|
|
11
|
+
ConsoleTheme
|
|
12
|
+
|
|
13
|
+
The four assembled themes are frozen into :data:`THEMES`;
|
|
14
|
+
:func:`resolve_theme` is the single sanctioned way the surface obtains the
|
|
15
|
+
theme for a scheme name. Because the heavy work (adapter + Textual Theme +
|
|
16
|
+
Pygments style construction) happens here at the config-load boundary, the
|
|
17
|
+
render path only ever reads an already-built :class:`ConsoleTheme` — it never
|
|
18
|
+
re-derives colours.
|
|
19
|
+
|
|
20
|
+
Port delta (locked; analysis 02 §5): each theme carries a full framework
|
|
21
|
+
``ThemeBundle``; the M5-wave-2 ``ConsoleApp`` registers every bundle's
|
|
22
|
+
Textual Theme at startup so ``scheme:set`` becomes a native
|
|
23
|
+
``app.theme = scheme`` live retheme. The Textual dark flag is derived from
|
|
24
|
+
the ramp the same way the token math picks its background anchor: a light
|
|
25
|
+
``ink`` implies a dark terminal.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from types import MappingProxyType
|
|
31
|
+
from typing import Final, Mapping
|
|
32
|
+
|
|
33
|
+
from ..contract import (
|
|
34
|
+
DEFAULT_SCHEME,
|
|
35
|
+
ConsoleTheme,
|
|
36
|
+
ThemePalette,
|
|
37
|
+
ThemeScheme,
|
|
38
|
+
is_theme_scheme,
|
|
39
|
+
)
|
|
40
|
+
from .adapter import theme_bundle
|
|
41
|
+
from .palette import PALETTES
|
|
42
|
+
from .tokens import derive_tokens, luminance
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"THEMES",
|
|
46
|
+
"THEME_SCHEMES",
|
|
47
|
+
"resolve_theme",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
# Assembly
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _assemble_theme(scheme: ThemeScheme, palette: ThemePalette) -> ConsoleTheme:
|
|
57
|
+
"""Run the full pipeline for one scheme: derive tokens from the ramp,
|
|
58
|
+
build the framework bundle from those tokens, and box everything into a
|
|
59
|
+
:class:`ConsoleTheme`.
|
|
60
|
+
|
|
61
|
+
:param scheme: the scheme identity this resolves
|
|
62
|
+
:param palette: the raw ramp the scheme is built from
|
|
63
|
+
"""
|
|
64
|
+
tokens = derive_tokens(palette)
|
|
65
|
+
# A light high-contrast ink means a dark terminal — the same anchor logic
|
|
66
|
+
# the diff-background derivation uses.
|
|
67
|
+
bundle = theme_bundle(scheme, tokens, dark=luminance(palette.ink) >= 0.5)
|
|
68
|
+
return ConsoleTheme(
|
|
69
|
+
scheme=scheme,
|
|
70
|
+
palette=palette,
|
|
71
|
+
tokens=tokens,
|
|
72
|
+
adapter=bundle.adapter,
|
|
73
|
+
bundle=bundle,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# The resolved theme table
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
#: The built-in themes, fully resolved and keyed by
|
|
82
|
+
#: :data:`~induscode.console.contract.ThemeScheme`.
|
|
83
|
+
#:
|
|
84
|
+
#: Assembled once at load time from :data:`~.palette.PALETTES`. This is the
|
|
85
|
+
#: table the theme picker lists and :func:`resolve_theme` reads; it is the
|
|
86
|
+
#: console's complete shipped theme set.
|
|
87
|
+
THEMES: Final[Mapping[ThemeScheme, ConsoleTheme]] = MappingProxyType(
|
|
88
|
+
{
|
|
89
|
+
"midnight": _assemble_theme("midnight", PALETTES["midnight"]),
|
|
90
|
+
"daylight": _assemble_theme("daylight", PALETTES["daylight"]),
|
|
91
|
+
"midnight-cb": _assemble_theme("midnight-cb", PALETTES["midnight-cb"]),
|
|
92
|
+
"daylight-cb": _assemble_theme("daylight-cb", PALETTES["daylight-cb"]),
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# The resolver
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def resolve_theme(scheme: str | None = None) -> ConsoleTheme:
|
|
103
|
+
"""Resolve a scheme name to its fully-built :class:`ConsoleTheme`.
|
|
104
|
+
|
|
105
|
+
The single entry point the surface and the loader use to turn a scheme
|
|
106
|
+
name (from settings, a slash argument, or the
|
|
107
|
+
:data:`~induscode.console.contract.DEFAULT_SCHEME`) into a theme. An
|
|
108
|
+
unrecognised or absent name falls back to the default rather than
|
|
109
|
+
raising, so a corrupt preference never blanks the console.
|
|
110
|
+
|
|
111
|
+
:param scheme: a candidate scheme name, possibly invalid or ``None``
|
|
112
|
+
:returns: the resolved theme for that scheme, or the default theme
|
|
113
|
+
"""
|
|
114
|
+
if isinstance(scheme, str) and is_theme_scheme(scheme):
|
|
115
|
+
return THEMES[scheme]
|
|
116
|
+
return THEMES[DEFAULT_SCHEME]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
#: The schemes the console offers, in picker order.
|
|
120
|
+
#:
|
|
121
|
+
#: Derived from :data:`THEMES` so adding a scheme to the table adds it to the
|
|
122
|
+
#: picker with no second list to update.
|
|
123
|
+
THEME_SCHEMES: Final[tuple[ThemeScheme, ...]] = tuple(THEMES.keys())
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Token derivation — expand a raw :class:`ThemePalette` into the semantic
|
|
2
|
+
:class:`ThemeTokens` the console actually renders against.
|
|
3
|
+
|
|
4
|
+
Port of TS ``src/console/theme/tokens.ts``; the hex math (parse / format /
|
|
5
|
+
blend / luminance / anchor) is carried over exactly, including JS
|
|
6
|
+
``Math.round`` semantics for channel rounding.
|
|
7
|
+
|
|
8
|
+
The console body never names a hex or a palette stop; it names a *role*
|
|
9
|
+
(``signal``, ``frame``, ``body_text``, ``alarm``, …). This module is the one
|
|
10
|
+
place the thirteen roles are bound to ramp stops. The mapping is pure and
|
|
11
|
+
total: every field of :class:`ThemeTokens` is assigned exactly once from a
|
|
12
|
+
:class:`ThemePalette` field, so a ramp that is missing a stop fails at
|
|
13
|
+
construction rather than producing an undefined colour at render time.
|
|
14
|
+
|
|
15
|
+
Role → stop assignments (identical structure for both schemes; the schemes
|
|
16
|
+
differ only in the underlying ramp):
|
|
17
|
+
|
|
18
|
+
- ``signal`` ← ``primary`` (the dominant accent: focus, selection)
|
|
19
|
+
- ``frame`` ← ``tertiary`` (default structural border lines)
|
|
20
|
+
- ``quiet_frame`` ← ``muted`` (de-emphasised separators)
|
|
21
|
+
- ``prompt_surface`` ← ``secondary`` (the composer's active tint)
|
|
22
|
+
- ``card_accent`` ← ``secondary`` (row gutters and headers)
|
|
23
|
+
- ``body_text`` ← ``body`` (default answer foreground)
|
|
24
|
+
- ``muted_text`` ← ``muted`` (timestamps, hints, metadata)
|
|
25
|
+
- ``ink_text`` ← ``ink`` (high-contrast text on an accent)
|
|
26
|
+
- ``notice`` ← ``tertiary`` (informational tone)
|
|
27
|
+
- ``affirm`` ← ``affirm`` (success tone)
|
|
28
|
+
- ``caution`` ← ``caution`` (warning tone)
|
|
29
|
+
- ``alarm`` ← ``alarm`` (error/fault tone)
|
|
30
|
+
- ``pending`` ← ``primary`` (busy/in-flight tone)
|
|
31
|
+
|
|
32
|
+
Rich-render roles (markdown / diff / syntax highlighting) are derived from
|
|
33
|
+
the same nine stops so the styled-transcript / colored-diff / fenced-code
|
|
34
|
+
surfaces recolour with the scheme and need no extra palette stop:
|
|
35
|
+
|
|
36
|
+
- ``code_inline`` ← ``primary`` (inline code accent)
|
|
37
|
+
- ``heading`` ← ``primary`` (heading accent)
|
|
38
|
+
- ``blockquote_bar`` ← ``muted`` (dim quote bar)
|
|
39
|
+
- ``diff_added_bg`` ← ``affirm`` blended to anchor (added-line tint)
|
|
40
|
+
- ``diff_removed_bg`` ← ``alarm`` blended to anchor (removed-line tint)
|
|
41
|
+
- ``diff_added_text`` ← ``affirm`` (added fg / ``+``)
|
|
42
|
+
- ``diff_removed_text`` ← ``alarm`` (removed fg / ``-``)
|
|
43
|
+
- ``syn_keyword`` ← ``primary`` (keywords)
|
|
44
|
+
- ``syn_string`` ← ``affirm`` (strings)
|
|
45
|
+
- ``syn_number`` ← ``caution`` (numbers)
|
|
46
|
+
- ``syn_comment`` ← ``muted`` (comments)
|
|
47
|
+
- ``syn_type`` ← ``tertiary`` (types / classes)
|
|
48
|
+
|
|
49
|
+
Deriving the two diff backgrounds (rather than adding raw stops) keeps the
|
|
50
|
+
:class:`ThemePalette` a flat nine-stop ramp while guaranteeing every scheme —
|
|
51
|
+
including the color-blind variants — gets a legible ``+``/``-`` tint that
|
|
52
|
+
tracks its own success/alarm hue.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
from __future__ import annotations
|
|
56
|
+
|
|
57
|
+
import math
|
|
58
|
+
|
|
59
|
+
from ..contract import ThemePalette, ThemeTokens
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
"derive_tokens",
|
|
63
|
+
"luminance",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# ---------------------------------------------------------------------------
|
|
68
|
+
# Hex helpers — derive the diff-background tints
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _parse_hex(hex_color: str) -> tuple[int, int, int]:
|
|
73
|
+
"""Parse a ``#rrggbb`` (or ``#rgb``) string into an ``(r, g, b)`` triple
|
|
74
|
+
(0–255)."""
|
|
75
|
+
h = hex_color.strip()
|
|
76
|
+
if h.startswith("#"):
|
|
77
|
+
h = h[1:]
|
|
78
|
+
if len(h) == 3:
|
|
79
|
+
h = "".join(c + c for c in h)
|
|
80
|
+
try:
|
|
81
|
+
n = int(h[:6].ljust(6, "0"), 16)
|
|
82
|
+
except ValueError:
|
|
83
|
+
# TS Number.parseInt yields NaN here; the bit-shifts then read 0.
|
|
84
|
+
n = 0
|
|
85
|
+
return ((n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _js_round(value: float) -> int:
|
|
89
|
+
"""JS ``Math.round`` — half-up toward +infinity (Python ``round`` banks)."""
|
|
90
|
+
return math.floor(value + 0.5)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _to_hex(rgb: tuple[float, float, float]) -> str:
|
|
94
|
+
"""Format an ``(r, g, b)`` triple back into a ``#rrggbb`` string."""
|
|
95
|
+
r, g, b = rgb
|
|
96
|
+
clamped = (max(0, min(255, _js_round(v))) for v in (r, g, b))
|
|
97
|
+
return "#" + "".join(f"{v:02x}" for v in clamped)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _mix_hex(a: str, b: str, t: float) -> str:
|
|
101
|
+
"""Linearly blend two hex colours; ``t`` is the weight of ``b`` (0 → ``a``,
|
|
102
|
+
1 → ``b``). Used to pull a saturated status hue toward a neutral anchor so
|
|
103
|
+
the resulting diff-line tint is dark/quiet enough for content to read on
|
|
104
|
+
top of it."""
|
|
105
|
+
ar, ag, ab = _parse_hex(a)
|
|
106
|
+
br, bg, bb = _parse_hex(b)
|
|
107
|
+
return _to_hex(
|
|
108
|
+
(
|
|
109
|
+
ar + (br - ar) * t,
|
|
110
|
+
ag + (bg - ag) * t,
|
|
111
|
+
ab + (bb - ab) * t,
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _diff_background(hue: str, anchor: str) -> str:
|
|
117
|
+
"""Derive a diff-line background tint from a status hue by blending it
|
|
118
|
+
heavily (78%) toward the scheme's implied terminal background. We keep
|
|
119
|
+
~22% of the hue so the tint is recognisably green/red (or blue/red on the
|
|
120
|
+
CB schemes) without overpowering the foreground text painted on top."""
|
|
121
|
+
return _mix_hex(hue, anchor, 0.78)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def luminance(hex_color: str) -> float:
|
|
125
|
+
"""Relative luminance (0 dark → 1 light) of a hex colour, for anchor
|
|
126
|
+
choice (and the resolver's Textual dark-flag derivation)."""
|
|
127
|
+
r, g, b = _parse_hex(hex_color)
|
|
128
|
+
return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _background_anchor(palette: ThemePalette) -> str:
|
|
132
|
+
"""The neutral a diff tint should be pulled toward — the scheme's implied
|
|
133
|
+
terminal background. A scheme whose high-contrast ``ink`` is light (a
|
|
134
|
+
dark terminal) implies a near-black background, so we blend the status
|
|
135
|
+
hue toward black; a scheme with dark ``ink`` (a light terminal) blends
|
|
136
|
+
toward white. This keeps the added/removed tints quiet enough for the
|
|
137
|
+
diff content painted on top to stay legible in either scheme family."""
|
|
138
|
+
return "#000000" if luminance(palette.ink) >= 0.5 else "#ffffff"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# ---------------------------------------------------------------------------
|
|
142
|
+
# Derivation
|
|
143
|
+
# ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def derive_tokens(palette: ThemePalette) -> ThemeTokens:
|
|
147
|
+
"""Map a raw accent ramp onto the closed semantic token set.
|
|
148
|
+
|
|
149
|
+
Pure and deterministic: the same palette always yields the same tokens,
|
|
150
|
+
and the returned object is a fresh frozen value the resolver can cache.
|
|
151
|
+
The role assignments are shared by both schemes — only the ramp handed in
|
|
152
|
+
differs.
|
|
153
|
+
|
|
154
|
+
:param palette: the raw nine-stop ramp for a scheme
|
|
155
|
+
:returns: the full semantic token map (status + rich-render roles)
|
|
156
|
+
"""
|
|
157
|
+
anchor = _background_anchor(palette)
|
|
158
|
+
return ThemeTokens(
|
|
159
|
+
signal=palette.primary,
|
|
160
|
+
frame=palette.tertiary,
|
|
161
|
+
quiet_frame=palette.muted,
|
|
162
|
+
prompt_surface=palette.secondary,
|
|
163
|
+
card_accent=palette.secondary,
|
|
164
|
+
body_text=palette.body,
|
|
165
|
+
muted_text=palette.muted,
|
|
166
|
+
ink_text=palette.ink,
|
|
167
|
+
notice=palette.tertiary,
|
|
168
|
+
affirm=palette.affirm,
|
|
169
|
+
caution=palette.caution,
|
|
170
|
+
alarm=palette.alarm,
|
|
171
|
+
pending=palette.primary,
|
|
172
|
+
# Rich-render roles — derived from the same nine stops.
|
|
173
|
+
code_inline=palette.primary,
|
|
174
|
+
heading=palette.primary,
|
|
175
|
+
blockquote_bar=palette.muted,
|
|
176
|
+
diff_added_bg=_diff_background(palette.affirm, anchor),
|
|
177
|
+
diff_removed_bg=_diff_background(palette.alarm, anchor),
|
|
178
|
+
diff_added_text=palette.affirm,
|
|
179
|
+
diff_removed_text=palette.alarm,
|
|
180
|
+
syn_keyword=palette.primary,
|
|
181
|
+
syn_string=palette.affirm,
|
|
182
|
+
syn_number=palette.caution,
|
|
183
|
+
syn_comment=palette.muted,
|
|
184
|
+
syn_type=palette.tertiary,
|
|
185
|
+
)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Slash command framework — contracts, registry assembly, line resolution,
|
|
2
|
+
and the shared command toolkit.
|
|
3
|
+
|
|
4
|
+
Port of the *framework* half of TS ``src/console/slash`` (``contract.ts``'s
|
|
5
|
+
slash types, ``registry.ts``, ``resolve.ts``, ``commands/shared.ts``), in
|
|
6
|
+
three pure pieces:
|
|
7
|
+
|
|
8
|
+
- **The contracts** — :class:`SlashCommand` rows, the
|
|
9
|
+
:class:`Handled` / :class:`Prompt` / :class:`Unknown` outcome union, the
|
|
10
|
+
:class:`SlashContext` dataclass of callables a handler acts through, and
|
|
11
|
+
the frozen :class:`SlashRegistry` shape.
|
|
12
|
+
- **The registry** — :func:`build_registry` folds an ordered command list
|
|
13
|
+
into the registry (commands + a name/alias index, duplicate tokens
|
|
14
|
+
rejected loudly), and the derived lookups (:func:`match_prefix`,
|
|
15
|
+
:func:`find_command`, :func:`commands_in_family`, :func:`list_families`)
|
|
16
|
+
power resolution, completion, and grouped listing.
|
|
17
|
+
- **Resolution** — :func:`resolve_slash` parses a typed composer line and
|
|
18
|
+
resolves it against a registry to a discriminated
|
|
19
|
+
:class:`NotSlash` / :class:`Match` / :class:`Miss` outcome.
|
|
20
|
+
|
|
21
|
+
Milestone note (M1)
|
|
22
|
+
-------------------
|
|
23
|
+
This package lands under ``induscode.console_slash`` for M1: it is a pure
|
|
24
|
+
leaf with no console dependency. The full console package
|
|
25
|
+
(``induscode.console``) arrives with M5 and will re-export this surface as
|
|
26
|
+
its ``slash`` layer; M5 also brings the command catalog itself —
|
|
27
|
+
``builtins.py`` with the explicit ``build_catalog(cwd, home)`` assembly (no
|
|
28
|
+
import-time I/O) and the pre-folded default registry (the TS
|
|
29
|
+
``DEFAULT_SLASH_REGISTRY``) — plus the command groups
|
|
30
|
+
(``transcript`` / ``workbench`` / ``integrations`` / ``dynamic``). The
|
|
31
|
+
effectful dispatcher (which awaits a matched command's ``run``) likewise
|
|
32
|
+
lives outside this barrel; everything exported here is pure.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from .contract import (
|
|
36
|
+
Handled,
|
|
37
|
+
OpenModal,
|
|
38
|
+
Prompt,
|
|
39
|
+
SlashCommand,
|
|
40
|
+
SlashContext,
|
|
41
|
+
SlashOutcome,
|
|
42
|
+
SlashRegistry,
|
|
43
|
+
SlashRun,
|
|
44
|
+
Unknown,
|
|
45
|
+
)
|
|
46
|
+
from .registry import (
|
|
47
|
+
build_registry,
|
|
48
|
+
commands_in_family,
|
|
49
|
+
find_command,
|
|
50
|
+
list_families,
|
|
51
|
+
match_prefix,
|
|
52
|
+
tokens_of,
|
|
53
|
+
)
|
|
54
|
+
from .resolve import (
|
|
55
|
+
SLASH_PREFIX,
|
|
56
|
+
Match,
|
|
57
|
+
Miss,
|
|
58
|
+
NotSlash,
|
|
59
|
+
SlashLine,
|
|
60
|
+
SlashResolution,
|
|
61
|
+
looks_like_slash,
|
|
62
|
+
parse_slash,
|
|
63
|
+
resolve_slash,
|
|
64
|
+
)
|
|
65
|
+
from .shared import (
|
|
66
|
+
FAMILY,
|
|
67
|
+
HANDLED,
|
|
68
|
+
FamilyLabels,
|
|
69
|
+
SubCommand,
|
|
70
|
+
VerbSplit,
|
|
71
|
+
family_runner,
|
|
72
|
+
info,
|
|
73
|
+
split_verb,
|
|
74
|
+
warn,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
__all__ = [
|
|
78
|
+
"FAMILY",
|
|
79
|
+
"FamilyLabels",
|
|
80
|
+
"HANDLED",
|
|
81
|
+
"Handled",
|
|
82
|
+
"Match",
|
|
83
|
+
"Miss",
|
|
84
|
+
"NotSlash",
|
|
85
|
+
"OpenModal",
|
|
86
|
+
"Prompt",
|
|
87
|
+
"SLASH_PREFIX",
|
|
88
|
+
"SlashCommand",
|
|
89
|
+
"SlashContext",
|
|
90
|
+
"SlashLine",
|
|
91
|
+
"SlashOutcome",
|
|
92
|
+
"SlashRegistry",
|
|
93
|
+
"SlashResolution",
|
|
94
|
+
"SlashRun",
|
|
95
|
+
"SubCommand",
|
|
96
|
+
"Unknown",
|
|
97
|
+
"VerbSplit",
|
|
98
|
+
"build_registry",
|
|
99
|
+
"commands_in_family",
|
|
100
|
+
"family_runner",
|
|
101
|
+
"find_command",
|
|
102
|
+
"info",
|
|
103
|
+
"list_families",
|
|
104
|
+
"looks_like_slash",
|
|
105
|
+
"match_prefix",
|
|
106
|
+
"parse_slash",
|
|
107
|
+
"resolve_slash",
|
|
108
|
+
"split_verb",
|
|
109
|
+
"tokens_of",
|
|
110
|
+
"warn",
|
|
111
|
+
]
|