micro-cc 0.2.85__tar.gz → 0.2.87__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.
- {micro_cc-0.2.85/src/micro_cc.egg-info → micro_cc-0.2.87}/PKG-INFO +1 -1
- {micro_cc-0.2.85 → micro_cc-0.2.87}/pyproject.toml +1 -1
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/claude_loop_.py +1 -1
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/postgres_store/pg_store_.py +24 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/postgres_store/schema_.py +17 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/start_live_tui_.py +79 -10
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/monitor_.py +1 -1
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/alt_screen_.py +47 -2
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/diff_viewer_.py +8 -8
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/etype_handler_.py +4 -4
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/list_picker_.py +3 -3
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/md_render_.py +39 -1
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/message_row_.py +15 -6
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/multi_select_list_.py +3 -3
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/question_panel_.py +3 -3
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/screen_cmds_.py +18 -3
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/text_.py +7 -2
- micro_cc-0.2.87/src/micro_cc/tui_native/theme_.py +54 -0
- micro_cc-0.2.87/src/micro_cc/utils/banner_.py +55 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/command_registry.py +1 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/hidden_prompts.py +17 -8
- micro_cc-0.2.87/src/micro_cc/utils/hints.py +43 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/status_marker.py +13 -1
- micro_cc-0.2.87/src/micro_cc/utils/theme_store_.py +249 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87/src/micro_cc.egg-info}/PKG-INFO +1 -1
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc.egg-info/SOURCES.txt +1 -0
- micro_cc-0.2.85/src/micro_cc/tui_native/theme_.py +0 -33
- micro_cc-0.2.85/src/micro_cc/utils/banner_.py +0 -41
- micro_cc-0.2.85/src/micro_cc/utils/hints.py +0 -64
- {micro_cc-0.2.85 → micro_cc-0.2.87}/LICENSE +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/README.md +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/setup.cfg +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/__init__.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/browser/__init__.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/browser/_cookies.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/browser/_md_convert.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/browser/browser_manager.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/browser/simpletextbrowser.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/cache/__init__.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/cache/state_store.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/execute_tool.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/__init__.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/adapters/__init__.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/adapters/anthropic.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/adapters/chat.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/adapters/responses.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/anthropic.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/client.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/foundry.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/litellm.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/ollama.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/openai.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/openrouter.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/priming.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/registry.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/schema.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/types.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/postgres_store/__init__.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/self_heal_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/SKILL.md +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/ArsenalSC-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Boldonse-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Boldonse-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/DMMono-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/DMMono-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/EricaOne-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/EricaOne-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/GeistMono-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/GeistMono-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/GeistMono-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Gloock-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Gloock-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Italiana-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Italiana-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Jura-Light.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Jura-Medium.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Jura-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/LibreBaskerville-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Lora-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Lora-Italic.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Lora-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Lora-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/NationalPark-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/NationalPark-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/NationalPark-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Outfit-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Outfit-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Outfit-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/PixelifySans-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/PoiretOne-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Silkscreen-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/SmoochSans-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Tektur-Medium.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Tektur-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Tektur-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Bold.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Italic.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/WorkSans-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/YoungSerif-OFL.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/references/blueprint-tokens.md +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/github/SKILL.md +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/graph-orchestration/SKILL.md +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/SKILL.md +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/brief.md +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/scripts/build_deck.js +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/scripts/deck_to_images.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/scripts/office/__init__.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/scripts/office/soffice.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/scripts/render_svg.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/svg-slides.md +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/skill_loader.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/system-design-review/REFERENCE.md +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/system-design-review/SKILL.md +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/system-design-review/scripts/design_graph.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/system-design-review/scripts/design_review.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/xlsx/SKILL.md +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/start_headless_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/__init__.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/ask_user_tool.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/bash_tool.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/browser_tool_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/computer_tool_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/file_tools_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/mcp_client_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/memory_tool_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/message_session_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/monitor_schedule_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/monitor_schedule_runtime.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/monitor_watch_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/search_history_tool_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/search_tool_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/skill_tools_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/todo_tools_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/vision_tools_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/web_tools_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/__init__.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/detect_images_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/keys_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/kill_ring_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/prompt_input_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/scroll_view_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/self_terminal_setup_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/self_update_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/stack_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/stdin_buffer_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/terminal_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/text_utils_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/truncated_text_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/undo_stack_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/word_navigation_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/__init__.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/annotate_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/claude_md_loader.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/claude_subscription.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/default_status_sh.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/helpers.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/history_mount_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/inbox_store_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/memory_store_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/msg_normalize_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/msg_store_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/project_files_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/self_reload_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/session_ipc_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/settings_store_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/subagent_tracker_.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/terminal_setup.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/tokenization_simple.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/tool_limits.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/tool_result_storage.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/__init__.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/bridge.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/dist/assets/index-9B5FcRm2.css +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/dist/assets/index-CDG9HO3B.js +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/dist/index.html +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/history.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/launch.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/server.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/session.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc.egg-info/dependency_links.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc.egg-info/entry_points.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc.egg-info/requires.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc.egg-info/top_level.txt +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/tests/test_model_consumers.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/tests/test_models.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/tests/test_schedule.py +0 -0
- {micro_cc-0.2.85 → micro_cc-0.2.87}/tests/test_self_reload.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "micro-cc"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.87"
|
|
8
8
|
description = "Harness that gives frontier models full system access — shell, filesystem, browser, MCP — running directly on the metal."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -232,7 +232,7 @@ async def claude_loop(
|
|
|
232
232
|
- You are running LOCALLY on the user's machine (on the metal), NOT a remote server
|
|
233
233
|
- You have direct access to the local filesystem, Desktop, Documents, etc.
|
|
234
234
|
- Project directory: {project_dir}
|
|
235
|
-
- Your own source code lives at: {package_dir} (not project_dir)
|
|
235
|
+
- Your own source code lives at: {package_dir} (not project_dir) — plain, editable .py. You can change the harness itself: edit it and the running process restarts into your change (auto at the next idle turn, or /reload to force it now); the conversation persists across the restart.
|
|
236
236
|
- Date: {datetime.datetime.now().strftime("%B %d, %Y")}
|
|
237
237
|
|
|
238
238
|
## Core Tools (always available)
|
|
@@ -470,6 +470,30 @@ def load_settings() -> dict | None:
|
|
|
470
470
|
return row[0] if row else None
|
|
471
471
|
|
|
472
472
|
|
|
473
|
+
def load_theme() -> dict | None:
|
|
474
|
+
"""None on a missing row — same "no row yet" contract as load_settings,
|
|
475
|
+
so theme_store_._load() can tell "never written" apart from "written"."""
|
|
476
|
+
with _connect() as conn, conn.cursor() as cur:
|
|
477
|
+
cur.execute("SELECT data FROM micro_cc_theme WHERE id = 'global'")
|
|
478
|
+
row = cur.fetchone()
|
|
479
|
+
return row[0] if row else None
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def save_theme(store: dict) -> None:
|
|
483
|
+
"""Whole-dict upsert, mirroring save_settings — a look is edited by a
|
|
484
|
+
human via /theme, not a hot path."""
|
|
485
|
+
with _connect() as conn, conn.cursor() as cur:
|
|
486
|
+
cur.execute(
|
|
487
|
+
"""
|
|
488
|
+
INSERT INTO micro_cc_theme (id, data, updated_at)
|
|
489
|
+
VALUES ('global', %s, now())
|
|
490
|
+
ON CONFLICT (id) DO UPDATE
|
|
491
|
+
SET data = EXCLUDED.data, updated_at = now()
|
|
492
|
+
""",
|
|
493
|
+
(json.dumps(store),),
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
|
|
473
497
|
def save_settings(store: dict) -> None:
|
|
474
498
|
"""Whole-dict upsert — settings are edited by a human via the TUI/webui,
|
|
475
499
|
not a per-message hot path, so there's no append/lock story to match
|
|
@@ -5,6 +5,7 @@ _sessions_table_ready = False
|
|
|
5
5
|
_messages_table_ready = False
|
|
6
6
|
_memory_table_ready = False
|
|
7
7
|
_settings_table_ready = False
|
|
8
|
+
_theme_table_ready = False
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def _ensure_sessions_table(conn) -> None:
|
|
@@ -102,8 +103,24 @@ def _ensure_settings_table(conn) -> None:
|
|
|
102
103
|
_settings_table_ready = True
|
|
103
104
|
|
|
104
105
|
|
|
106
|
+
def _ensure_theme_table(conn) -> None:
|
|
107
|
+
global _theme_table_ready
|
|
108
|
+
if _theme_table_ready:
|
|
109
|
+
return
|
|
110
|
+
with conn.cursor() as cur:
|
|
111
|
+
cur.execute("""
|
|
112
|
+
CREATE TABLE IF NOT EXISTS micro_cc_theme (
|
|
113
|
+
id TEXT PRIMARY KEY DEFAULT 'global',
|
|
114
|
+
data JSONB NOT NULL,
|
|
115
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
116
|
+
)
|
|
117
|
+
""")
|
|
118
|
+
_theme_table_ready = True
|
|
119
|
+
|
|
120
|
+
|
|
105
121
|
def ensure_all(conn) -> None:
|
|
106
122
|
_ensure_sessions_table(conn)
|
|
107
123
|
_ensure_messages_table(conn)
|
|
108
124
|
_ensure_memory_table(conn)
|
|
109
125
|
_ensure_settings_table(conn)
|
|
126
|
+
_ensure_theme_table(conn)
|
|
@@ -27,7 +27,12 @@ from micro_cc.utils.helpers import (
|
|
|
27
27
|
sanitize_and_encode_image_,
|
|
28
28
|
)
|
|
29
29
|
from micro_cc.cache import state_store
|
|
30
|
-
from micro_cc.utils
|
|
30
|
+
from micro_cc.utils import banner_
|
|
31
|
+
from micro_cc.utils import theme_store_
|
|
32
|
+
|
|
33
|
+
# Theme tokens used inline in markup strings are read via theme_store_.get()
|
|
34
|
+
# right where they're interpolated — never bound to a module constant, which
|
|
35
|
+
# would freeze the palette at import and make a live /theme switch a no-op.
|
|
31
36
|
from micro_cc.tui_native.screen_cmds_ import SLASH_COMMANDS, PREFIX_COMMANDS, _pending_subagents
|
|
32
37
|
from micro_cc.utils import command_registry
|
|
33
38
|
from micro_cc.tui_native.self_update_ import check_and_update
|
|
@@ -417,7 +422,10 @@ class MicroTui():
|
|
|
417
422
|
|
|
418
423
|
# --- widget tree -----------------------------------------------------
|
|
419
424
|
def _build_widget_tree(self) -> None:
|
|
420
|
-
|
|
425
|
+
# banner() returns (markup, hue); the hue is kept so the hint bar
|
|
426
|
+
# can tint to match this launch's wordmark.
|
|
427
|
+
_banner_markup, self._banner_hue = banner_.banner()
|
|
428
|
+
self.banner = RichStatic(_banner_markup)
|
|
421
429
|
|
|
422
430
|
self.prompt = PromptInput()
|
|
423
431
|
# handle_key("enter") calls on_submit synchronously (no await) —
|
|
@@ -532,6 +540,11 @@ class MicroTui():
|
|
|
532
540
|
self.ask_user_picker = ListPicker()
|
|
533
541
|
self.ask_user_select = MultiSelectList()
|
|
534
542
|
self.bgproc_detail_picker = ListPicker()
|
|
543
|
+
self.theme_picker = ListPicker()
|
|
544
|
+
self.theme_picker.set_items([
|
|
545
|
+
PickerItem(n, theme_store_.THEME_LABELS.get(n, n))
|
|
546
|
+
for n in theme_store_.PRESETS
|
|
547
|
+
])
|
|
535
548
|
|
|
536
549
|
self.model_picker.on_select = self._on_model_picked
|
|
537
550
|
self.model_picker.on_cancel = lambda: self._close_picker("_model_picker_overlay")
|
|
@@ -552,6 +565,8 @@ class MicroTui():
|
|
|
552
565
|
# _show_bgproc_detail/ctrl+b).
|
|
553
566
|
self.bgproc_detail_picker.on_select = lambda item: self._hide_bgproc_detail()
|
|
554
567
|
self.bgproc_detail_picker.on_cancel = self._hide_bgproc_detail
|
|
568
|
+
self.theme_picker.on_select = self._on_theme_picked
|
|
569
|
+
self.theme_picker.on_cancel = lambda: self._close_picker("_theme_picker_overlay")
|
|
555
570
|
|
|
556
571
|
self._model_picker_overlay = None
|
|
557
572
|
self._rewind_picker_overlay = None
|
|
@@ -559,6 +574,7 @@ class MicroTui():
|
|
|
559
574
|
self._slash_picker_overlay = None
|
|
560
575
|
self._at_picker_overlay = None
|
|
561
576
|
self._login_picker_overlay = None
|
|
577
|
+
self._theme_picker_overlay = None
|
|
562
578
|
|
|
563
579
|
# --- render loop / focus ---------------------------------------------
|
|
564
580
|
# Used everywhere below — foundational, not tied to any one feature.
|
|
@@ -1056,6 +1072,54 @@ class MicroTui():
|
|
|
1056
1072
|
self._refresh_status()
|
|
1057
1073
|
self.set_focus(self.prompt)
|
|
1058
1074
|
|
|
1075
|
+
def _on_theme_picked(self, item: PickerItem) -> None:
|
|
1076
|
+
"""Apply a preset. theme_store_.set_preset persists the color set and
|
|
1077
|
+
fires the on_change listeners — _apply_theme is the one registered
|
|
1078
|
+
here, which re-emits the terminal's default bg/fg and invalidates the
|
|
1079
|
+
render caches so every row repaints in the new set."""
|
|
1080
|
+
self._close_picker("_theme_picker_overlay")
|
|
1081
|
+
msg = theme_store_.set_preset(item.value)
|
|
1082
|
+
self._flash_status(msg, seconds=3)
|
|
1083
|
+
self.set_focus(self.prompt)
|
|
1084
|
+
|
|
1085
|
+
def _apply_theme(self, _name: str = "") -> None:
|
|
1086
|
+
"""Repaint an already-running UI after the color set changed.
|
|
1087
|
+
|
|
1088
|
+
Two halves, and both are needed: (1) the terminal's own default
|
|
1089
|
+
bg/fg must be re-emitted, because OSC 11/10 only ran once at
|
|
1090
|
+
alt-screen entry — without this the ground stays whatever it was;
|
|
1091
|
+
(2) every cached render must be dropped, because widgets cache their
|
|
1092
|
+
flattened lines per width (message_row_.RichStatic/MessageRow,
|
|
1093
|
+
prompt_input_, the pickers) and would otherwise keep serving lines
|
|
1094
|
+
painted in the old palette until something else invalidated them.
|
|
1095
|
+
"""
|
|
1096
|
+
from micro_cc.tui_native.alt_screen_ import default_colors_sequence
|
|
1097
|
+
|
|
1098
|
+
sys.stdout.write(default_colors_sequence())
|
|
1099
|
+
sys.stdout.flush()
|
|
1100
|
+
|
|
1101
|
+
# The splash is a RichStatic built from a theme-derived string, so it
|
|
1102
|
+
# has to be rebuilt, not just invalidated — invalidate() would repaint
|
|
1103
|
+
# the SAME stale markup in the old palette. banner() also hands back
|
|
1104
|
+
# the hue it chose, which the hint bar tints to match.
|
|
1105
|
+
banner_markup, hue = banner_.banner()
|
|
1106
|
+
self._banner_hue = hue
|
|
1107
|
+
self.banner.update(banner_markup)
|
|
1108
|
+
self.banner.invalidate()
|
|
1109
|
+
for entry in self.message_list.entries:
|
|
1110
|
+
entry.component.invalidate()
|
|
1111
|
+
for entry in self.subagent_message_list.entries:
|
|
1112
|
+
entry.component.invalidate()
|
|
1113
|
+
for widget in (
|
|
1114
|
+
self.working, self.ask_question_text, self.memory_flash,
|
|
1115
|
+
self.static_hintbar, self.statusbar, self.subagent_status,
|
|
1116
|
+
self.bgproc_status, self.prompt,
|
|
1117
|
+
):
|
|
1118
|
+
widget.invalidate()
|
|
1119
|
+
self.tui.invalidate_overlays()
|
|
1120
|
+
self._static_hint_text()
|
|
1121
|
+
self.request_render()
|
|
1122
|
+
|
|
1059
1123
|
def _on_rewind_picked(self, item: PickerItem) -> None:
|
|
1060
1124
|
self._close_picker("_rewind_picker_overlay")
|
|
1061
1125
|
self._safe_task(self._do_rewind(int(item.value)), "rewind")
|
|
@@ -1522,11 +1586,11 @@ class MicroTui():
|
|
|
1522
1586
|
# A self-change is queued but the app hasn't restarted yet (mid-turn
|
|
1523
1587
|
# or a subagent still live) — say so, so the delay is legible rather
|
|
1524
1588
|
# than looking like nothing happened.
|
|
1525
|
-
text += f" | [
|
|
1589
|
+
text += f" | [{theme_store_.get('accent')}]{self._self_reload_hint()}[/{theme_store_.get('accent')}]"
|
|
1526
1590
|
elif self._update_available:
|
|
1527
|
-
text += f" | [
|
|
1591
|
+
text += f" | [{theme_store_.get('accent')}]⚠ micro-cc {self._update_available} available — run /update[/{theme_store_.get('accent')}]"
|
|
1528
1592
|
elif settings_store_.was_reset_for_corruption():
|
|
1529
|
-
text += " | [
|
|
1593
|
+
text += f" | [{theme_store_.get('error')}]⚠ ~/.micro-cc/settings.json was corrupt — reset to defaults[/{theme_store_.get('error')}]"
|
|
1530
1594
|
else:
|
|
1531
1595
|
# newline_hint_text() returns None once the user has actually
|
|
1532
1596
|
# used a newline once (backslash+enter/shift+enter/ctrl+j) — no
|
|
@@ -1535,7 +1599,8 @@ class MicroTui():
|
|
|
1535
1599
|
# Tinted with the same colour as this launch's banner, so the
|
|
1536
1600
|
# hint reads as "micro-cc talking" rather than dim status text.
|
|
1537
1601
|
hint = newline_hint_text() or self.get_random_hint()
|
|
1538
|
-
|
|
1602
|
+
hue = self._banner_hue
|
|
1603
|
+
text += f" | [{hue}]{hint}[/{hue}]"
|
|
1539
1604
|
self.static_hintbar.update(
|
|
1540
1605
|
text[: self._STATIC_HINT_LINE_MAXLEN]
|
|
1541
1606
|
)
|
|
@@ -1629,7 +1694,7 @@ class MicroTui():
|
|
|
1629
1694
|
# ever got a chance to render. _flash_status's own timer is what
|
|
1630
1695
|
# brings the normal text back after `seconds`.
|
|
1631
1696
|
self._flash_status(
|
|
1632
|
-
"[bold
|
|
1697
|
+
f"[bold {theme_store_.get('warn')}]✂ compacted past conversation just now[/bold {theme_store_.get('warn')}]",
|
|
1633
1698
|
seconds=8,
|
|
1634
1699
|
)
|
|
1635
1700
|
return
|
|
@@ -1825,7 +1890,7 @@ class MicroTui():
|
|
|
1825
1890
|
# this used to be a second, longer copy that could (and did) drift out
|
|
1826
1891
|
# of sync with the pending hint shown in the bar.
|
|
1827
1892
|
msg = reason or self._self_reload_hint()
|
|
1828
|
-
self.static_hintbar.update(f"[
|
|
1893
|
+
self.static_hintbar.update(f"[{theme_store_.get('accent')}]{msg}[/{theme_store_.get('accent')}]")
|
|
1829
1894
|
self.request_render()
|
|
1830
1895
|
try:
|
|
1831
1896
|
# Let the frame land before the screen tears down. Cancellation
|
|
@@ -1903,7 +1968,7 @@ class MicroTui():
|
|
|
1903
1968
|
if landed:
|
|
1904
1969
|
self._flash_status(f"✓ copied {len(text)} chars")
|
|
1905
1970
|
else:
|
|
1906
|
-
self._flash_status(f"[
|
|
1971
|
+
self._flash_status(f"[{theme_store_.get('error')}]✗ pyperclip failed ({pyperclip_error}) — tried terminal clipboard, enable clipboard access if it didn't land[/{theme_store_.get('error')}]", seconds=5)
|
|
1907
1972
|
|
|
1908
1973
|
# ============================================= statusbar
|
|
1909
1974
|
|
|
@@ -2659,7 +2724,7 @@ class MicroTui():
|
|
|
2659
2724
|
# looks like it's waiting on an interactive prompt) gets a distinct glyph/color
|
|
2660
2725
|
# so it's visible at a glance without waiting for the model to next mention it.
|
|
2661
2726
|
lines = [
|
|
2662
|
-
f"[
|
|
2727
|
+
f"[{theme_store_.get('warn')}]⚠ {p['pid']} · {p['age']}s · stalled[/{theme_store_.get('warn')}]" if p.get("stalled")
|
|
2663
2728
|
else f"◇ {p['pid']} · {p['age']}s"
|
|
2664
2729
|
for p in procs
|
|
2665
2730
|
]
|
|
@@ -2986,6 +3051,10 @@ class MicroTui():
|
|
|
2986
3051
|
# cleared implicitly by never being set on the headless path.
|
|
2987
3052
|
from micro_cc.tools import monitor_schedule_runtime as _schedule_rt
|
|
2988
3053
|
|
|
3054
|
+
# A /theme switch repaints an already-running UI through this
|
|
3055
|
+
# listener — see _apply_theme (re-emits the terminal's default
|
|
3056
|
+
# bg/fg, then invalidates every cached render).
|
|
3057
|
+
theme_store_.on_change(self._apply_theme)
|
|
2989
3058
|
_schedule_rt.set_wake_callback(self._on_monitor_event)
|
|
2990
3059
|
_schedule_rt.set_start_tick(self._ensure_schedule_tick)
|
|
2991
3060
|
self._ensure_schedule_tick()
|
|
@@ -176,7 +176,7 @@ def format_status_glyph(name: str, status: str, pending: int = 0, tokens: dict |
|
|
|
176
176
|
is the verbose sibling for monitor_'s tool-call output, where
|
|
177
177
|
the model actually needs the full path/summary."""
|
|
178
178
|
symbol = status_marker.SYMBOL.get(status, "?")
|
|
179
|
-
color = status_marker.
|
|
179
|
+
color = status_marker.color(status)
|
|
180
180
|
if len(name) > _STATUS_GLYPH_NAME_MAXLEN:
|
|
181
181
|
name = name[: _STATUS_GLYPH_NAME_MAXLEN - 1] + "…"
|
|
182
182
|
mail = "✉" if pending else ""
|
|
@@ -26,8 +26,43 @@ class Component(Protocol):
|
|
|
26
26
|
BEGIN_SYNCHRONIZED_OUTPUT = "\x1b[?2026h" # one atomic terminal paint starts
|
|
27
27
|
END_SYNCHRONIZED_OUTPUT = "\x1b[?2026l" # ...and ends here
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
# The alt screen also sets the terminal's own DEFAULT background/foreground
|
|
30
|
+
# (OSC 11/10) from the active theme set. Doing it here rather than painting a
|
|
31
|
+
# background per rendered line is what themes the whole UI with no renderer
|
|
32
|
+
# changes: every span that doesn't set its own bgcolor (body text, padding,
|
|
33
|
+
# dividers) inherits this default, and every widget that DOES set one keeps
|
|
34
|
+
# working. OSC 111/110 on exit restore the user's own terminal profile — the
|
|
35
|
+
# alt screen already restores its contents.
|
|
36
|
+
#
|
|
37
|
+
# Both sequences are FUNCTIONS, not constants: a /theme switch has to be able
|
|
38
|
+
# to re-emit them mid-session (see reapply_default_colors below), so binding
|
|
39
|
+
# them once at import would freeze the ground for the process's whole life.
|
|
40
|
+
def _enter() -> str:
|
|
41
|
+
from micro_cc.utils import theme_store_
|
|
42
|
+
return (
|
|
43
|
+
"\x1b[?1049h"
|
|
44
|
+
f"\x1b]11;{theme_store_.get('bg')}\x07"
|
|
45
|
+
f"\x1b]10;{theme_store_.get('fg')}\x07"
|
|
46
|
+
"\x1b[2J"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _exit() -> str:
|
|
51
|
+
return "\x1b]111\x07\x1b]110\x07\x1b[?1049l"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def default_colors_sequence() -> str:
|
|
55
|
+
"""Just the OSC 11/10 pair, no alt-screen switch — what a live theme
|
|
56
|
+
change re-emits to repaint the ground under an already-running UI."""
|
|
57
|
+
from micro_cc.utils import theme_store_
|
|
58
|
+
return (
|
|
59
|
+
f"\x1b]11;{theme_store_.get('bg')}\x07"
|
|
60
|
+
f"\x1b]10;{theme_store_.get('fg')}\x07"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
ENTER_ALT_SCREEN = _enter()
|
|
65
|
+
EXIT_ALT_SCREEN = _exit()
|
|
31
66
|
|
|
32
67
|
# A focused component emits this at the exact spot in its own render()
|
|
33
68
|
# output where the hardware cursor should land. extract_cursor_position
|
|
@@ -454,6 +489,16 @@ class TuiAltScreen:
|
|
|
454
489
|
return size.columns, size.lines
|
|
455
490
|
|
|
456
491
|
# --- overlays ---------------------------------------------------
|
|
492
|
+
def invalidate_overlays(self) -> None:
|
|
493
|
+
"""Drop every open overlay's cached render — what a live theme switch
|
|
494
|
+
calls so pickers/panels repaint in the new color set instead of
|
|
495
|
+
serving lines built with the old one."""
|
|
496
|
+
for entry in self.overlay_stack:
|
|
497
|
+
try:
|
|
498
|
+
entry.component.invalidate()
|
|
499
|
+
except Exception:
|
|
500
|
+
pass
|
|
501
|
+
|
|
457
502
|
def show_overlay(self, component: Component, options: OverlayOptions | None = None) -> OverlayHandle:
|
|
458
503
|
self.focus_order_counter += 1
|
|
459
504
|
entry = OverlayEntry(component, options or OverlayOptions(), self.focused_overlay, self.focus_order_counter)
|
|
@@ -5,9 +5,9 @@ from rich.text import Text
|
|
|
5
5
|
|
|
6
6
|
# Matches CleanMarkdown's own code-block theme (md_render_.py) — a diff and
|
|
7
7
|
# a plain code block should read as the same visual family.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
# Colors come from the active theme set (utils/theme_store_) — the syntax
|
|
9
|
+
# theme is a Pygments name, the line tints are hex backgrounds.
|
|
10
|
+
from micro_cc.utils import theme_store_
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _format_range(start: int, stop: int) -> str:
|
|
@@ -21,7 +21,7 @@ def _format_range(start: int, stop: int) -> str:
|
|
|
21
21
|
def _highlighted_lines(code: str, lexer_name: str) -> list[Text]:
|
|
22
22
|
if not code:
|
|
23
23
|
return []
|
|
24
|
-
syntax = Syntax(code, lexer_name, theme=
|
|
24
|
+
syntax = Syntax(code, lexer_name, theme=theme_store_.get("syntax"), word_wrap=False, background_color="default")
|
|
25
25
|
return syntax.highlight(code).split("\n")
|
|
26
26
|
|
|
27
27
|
|
|
@@ -60,13 +60,13 @@ def build_diff_lines(old: str, new: str, file_path: str = "", context: int = 3)
|
|
|
60
60
|
continue
|
|
61
61
|
if tag in ("delete", "replace"):
|
|
62
62
|
for i in range(i1, i2):
|
|
63
|
-
line = Text("- ", style="bold
|
|
64
|
-
line.stylize(
|
|
63
|
+
line = Text("- ", style=f"bold {theme_store_.get('diff_del_fg')}") + old_hi[i]
|
|
64
|
+
line.stylize(f"on {theme_store_.get('diff_del_bg')}")
|
|
65
65
|
out.append(line)
|
|
66
66
|
if tag in ("insert", "replace"):
|
|
67
67
|
for j in range(j1, j2):
|
|
68
|
-
line = Text("+ ", style="bold
|
|
69
|
-
line.stylize(
|
|
68
|
+
line = Text("+ ", style=f"bold {theme_store_.get('diff_add_fg')}") + new_hi[j]
|
|
69
|
+
line.stylize(f"on {theme_store_.get('diff_add_bg')}")
|
|
70
70
|
out.append(line)
|
|
71
71
|
return out
|
|
72
72
|
|
|
@@ -14,6 +14,7 @@ touchpoint list this mirrors.
|
|
|
14
14
|
|
|
15
15
|
import asyncio
|
|
16
16
|
|
|
17
|
+
from micro_cc.utils import theme_store_
|
|
17
18
|
from micro_cc.utils.msg_store_ import store_msgs
|
|
18
19
|
from micro_cc.utils.tokenization_simple import CACHE_TTL_S
|
|
19
20
|
from micro_cc.tui_native.message_row_ import MessageRow, StreamingRow
|
|
@@ -199,10 +200,9 @@ async def handle_cache_invalidate(app, event, container, tool_call_rows, approva
|
|
|
199
200
|
|
|
200
201
|
tokens = f"{event.get('missed_tokens', 0):,}"
|
|
201
202
|
label = f"cache miss ({reason})" if reason else "cache miss"
|
|
202
|
-
#
|
|
203
|
-
# [
|
|
204
|
-
|
|
205
|
-
app._flash_status(f"[red]⚠ {label}: {tokens} tokens re-billed[/red]", seconds=5)
|
|
203
|
+
# TextLine's markup vocabulary is [red] and [#rrggbb] hex only — no
|
|
204
|
+
# [yellow] — so this uses the theme's error token directly.
|
|
205
|
+
app._flash_status(f"[{theme_store_.get('error')}]⚠ {label}: {tokens} tokens re-billed[/{theme_store_.get('error')}]", seconds=5)
|
|
206
206
|
|
|
207
207
|
|
|
208
208
|
ETYPES = {
|
|
@@ -16,7 +16,7 @@ the current Textual pickers do: Option(label, id=label)).
|
|
|
16
16
|
from dataclasses import dataclass, field
|
|
17
17
|
|
|
18
18
|
from micro_cc.tui_native.text_utils_ import slice_by_column, visible_width
|
|
19
|
-
from micro_cc.tui_native.theme_ import
|
|
19
|
+
from micro_cc.tui_native.theme_ import default_theme, Theme
|
|
20
20
|
|
|
21
21
|
DEFAULT_MAX_VISIBLE = 8
|
|
22
22
|
PRIMARY_COLUMN_GAP = 2
|
|
@@ -114,14 +114,14 @@ class ListPicker:
|
|
|
114
114
|
|
|
115
115
|
def __init__(self, items: list[PickerItem] | None = None, max_visible: int = DEFAULT_MAX_VISIBLE,
|
|
116
116
|
primary_min: int = DEFAULT_PRIMARY_MIN, primary_max: int = DEFAULT_PRIMARY_MAX,
|
|
117
|
-
theme: Theme =
|
|
117
|
+
theme: Theme | None = None, max_desc_lines: int = MAX_DESC_LINES):
|
|
118
118
|
self.on_select = None # Callable[[PickerItem], None]
|
|
119
119
|
self.on_cancel = None # Callable[[], None]
|
|
120
120
|
self.on_selection_change = None # Callable[[PickerItem], None]
|
|
121
121
|
self.max_visible = max(1, max_visible)
|
|
122
122
|
self.primary_min = max(1, min(primary_min, primary_max))
|
|
123
123
|
self.primary_max = max(1, max(primary_min, primary_max))
|
|
124
|
-
self.theme = theme
|
|
124
|
+
self.theme = theme or default_theme()
|
|
125
125
|
# Per-item description wrap cap. Was the module constant MAX_DESC_LINES
|
|
126
126
|
# baked in directly, which ellipsized any hint past 3 lines even when
|
|
127
127
|
# the terminal had plenty of room — callers with a real height
|
|
@@ -11,6 +11,38 @@ from rich.syntax import Syntax
|
|
|
11
11
|
from rich.style import Style
|
|
12
12
|
from rich.console import Console, ConsoleOptions, RenderResult
|
|
13
13
|
from rich.text import Text
|
|
14
|
+
from rich.theme import Theme
|
|
15
|
+
|
|
16
|
+
from micro_cc.utils import theme_store_
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def rich_theme() -> Theme:
|
|
20
|
+
"""Rich's built-in markdown styles, rebuilt from the active color set.
|
|
21
|
+
|
|
22
|
+
Rich's own defaults were picked against a dark terminal — `markdown.code`
|
|
23
|
+
is literally cyan-on-black, and the table/bullet accents are pale cyans
|
|
24
|
+
that vanish on white. So these are overridden from theme_store_ rather
|
|
25
|
+
than left at Rich's defaults; every value is a hex the file supplies, so
|
|
26
|
+
editing theme.json restyles the markdown too.
|
|
27
|
+
|
|
28
|
+
Built fresh per call (a Theme is cheap, and rendering is cached upstream
|
|
29
|
+
per width) so a live /theme switch can't keep painting the old set.
|
|
30
|
+
"""
|
|
31
|
+
c = theme_store_.get
|
|
32
|
+
return Theme({
|
|
33
|
+
"markdown.code": f"bold {c('inline_code')} on {c('code_bg')}",
|
|
34
|
+
"markdown.code_block": c("code_fg"),
|
|
35
|
+
"markdown.block_quote": c("quote"),
|
|
36
|
+
"markdown.h2": f"bold {c('heading')} underline",
|
|
37
|
+
"markdown.h3": f"bold {c('heading')}",
|
|
38
|
+
"markdown.h4": f"italic {c('heading')}",
|
|
39
|
+
"markdown.link": c("link"),
|
|
40
|
+
"markdown.link_url": f"{c('link_url')} underline",
|
|
41
|
+
"markdown.list": c("list"),
|
|
42
|
+
"markdown.item.number": c("list"),
|
|
43
|
+
"markdown.table.border": c("table_border"),
|
|
44
|
+
"markdown.table.header": f"bold {c('table_header')}",
|
|
45
|
+
})
|
|
14
46
|
|
|
15
47
|
|
|
16
48
|
class CleanCodeBlock(CodeBlock):
|
|
@@ -58,8 +90,14 @@ class CleanMarkdown(Markdown):
|
|
|
58
90
|
elements["heading_open"] = CleanHeading
|
|
59
91
|
|
|
60
92
|
def __init__(self, markup: str, **kwargs):
|
|
93
|
+
# code_theme is a Pygments theme NAME from the active color set — a
|
|
94
|
+
# palette can't express a syntax theme, so theme.json carries it as
|
|
95
|
+
# the "syntax" token (e.g. "friendly" for light, "monokai" for dark).
|
|
96
|
+
# Applies to fenced blocks and inline `code` alike (Rich falls back
|
|
97
|
+
# to code_theme for inline unless inline_code_theme says otherwise).
|
|
98
|
+
kwargs.setdefault("code_theme", theme_store_.get("syntax"))
|
|
61
99
|
super().__init__(markup, **kwargs)
|
|
62
|
-
self.style = "
|
|
100
|
+
self.style = theme_store_.get("fg")
|
|
63
101
|
|
|
64
102
|
|
|
65
103
|
class DimMarkdown(CleanMarkdown):
|
|
@@ -27,7 +27,8 @@ from rich.console import Console, Group
|
|
|
27
27
|
from rich.text import Text
|
|
28
28
|
from PIL import Image as PILImage
|
|
29
29
|
|
|
30
|
-
from micro_cc.tui_native.md_render_ import render_md
|
|
30
|
+
from micro_cc.tui_native.md_render_ import render_md, rich_theme
|
|
31
|
+
from micro_cc.utils import theme_store_
|
|
31
32
|
from micro_cc.tui_native.diff_viewer_ import build_diff_lines
|
|
32
33
|
from micro_cc.tui_native.detect_images_ import (
|
|
33
34
|
detect_capabilities, calculate_image_cell_size, encode_kitty,
|
|
@@ -64,7 +65,14 @@ def _cap_diff_lines(diff_lines: list, max_lines: int = _MAX_EXPANDED_LINES) -> l
|
|
|
64
65
|
def _render_to_lines(renderable, width: int) -> list[str]:
|
|
65
66
|
"""Flatten a Rich renderable into plain ANSI-coded lines at exactly
|
|
66
67
|
`width` columns — real SGR escape codes, not Segment objects, so the
|
|
67
|
-
result is a drop-in list[str] for the Component protocol.
|
|
68
|
+
result is a drop-in list[str] for the Component protocol.
|
|
69
|
+
|
|
70
|
+
LIGHT_THEME (md_render_.py) is applied here rather than left to each
|
|
71
|
+
renderable, because this is the single Console every widget in the tree
|
|
72
|
+
funnels through — banner, message rows, RichStatic status/hint lines
|
|
73
|
+
alike. One theme at the choke point is what keeps Rich's built-in
|
|
74
|
+
markdown styles (inline code is cyan-on-black by default) from landing
|
|
75
|
+
unreadable on the alt screen's white background."""
|
|
68
76
|
buf = io.StringIO()
|
|
69
77
|
console = Console(
|
|
70
78
|
file=buf,
|
|
@@ -73,6 +81,7 @@ def _render_to_lines(renderable, width: int) -> list[str]:
|
|
|
73
81
|
color_system="truecolor",
|
|
74
82
|
highlight=False,
|
|
75
83
|
soft_wrap=False,
|
|
84
|
+
theme=rich_theme(),
|
|
76
85
|
)
|
|
77
86
|
console.print(renderable, end="")
|
|
78
87
|
return buf.getvalue().split("\n")
|
|
@@ -158,7 +167,7 @@ class MessageRow:
|
|
|
158
167
|
if t == "user":
|
|
159
168
|
return Text(f"› {msg['content']}", style="bold")
|
|
160
169
|
elif t == "user_queued":
|
|
161
|
-
return Text(f"⋯queued {msg['content']}", style="dim italic
|
|
170
|
+
return Text(f"⋯queued {msg['content']}", style=f"dim italic {theme_store_.get('queued')}")
|
|
162
171
|
elif t == "text":
|
|
163
172
|
return render_md(msg["content"])
|
|
164
173
|
elif t == "thinking":
|
|
@@ -172,7 +181,7 @@ class MessageRow:
|
|
|
172
181
|
result = msg.get('result')
|
|
173
182
|
expanded = msg.get('expanded')
|
|
174
183
|
if result is None:
|
|
175
|
-
return Text(f"◇ {name} ", style="italic
|
|
184
|
+
return Text(f"◇ {name} ", style=f"italic {theme_store_.get('warn')}")
|
|
176
185
|
if expanded:
|
|
177
186
|
if name == "edit_":
|
|
178
187
|
fp = inp.get('file_path', '')
|
|
@@ -212,14 +221,14 @@ class MessageRow:
|
|
|
212
221
|
preview = first_line[:40] + ("…" if truncated else "")
|
|
213
222
|
return Text(f"⟐ {name} → {preview} (click to expand)", style="dim")
|
|
214
223
|
elif t == "error":
|
|
215
|
-
return Text(f"△ {msg['content']}", style="italic
|
|
224
|
+
return Text(f"△ {msg['content']}", style=f"italic {theme_store_.get('error')}")
|
|
216
225
|
elif t == "approval":
|
|
217
226
|
inp = msg.get('input', {})
|
|
218
227
|
name = msg['name']
|
|
219
228
|
expanded = msg.get('expanded')
|
|
220
229
|
hint = "click to collapse" if expanded else "click to view full"
|
|
221
230
|
header = Text.assemble(
|
|
222
|
-
("◆ ", "bold
|
|
231
|
+
(f"◆ ", f"bold {theme_store_.get('warn')}"), (name, f"bold {theme_store_.get('warn')}"),
|
|
223
232
|
(f" enter to approve · esc to reject · {hint}", "dim"),
|
|
224
233
|
)
|
|
225
234
|
if name == "bash_":
|
|
@@ -7,15 +7,15 @@ rendering/navigation/filtering by composition rather than duplicating it."""
|
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
|
|
9
9
|
from micro_cc.tui_native.list_picker_ import MAX_DESC_LINES, ListPicker, PickerItem, wrap_in_border
|
|
10
|
-
from micro_cc.tui_native.theme_ import
|
|
10
|
+
from micro_cc.tui_native.theme_ import default_theme, Theme
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class MultiSelectList:
|
|
14
14
|
def __init__(self, items: list[PickerItem] | None = None, max_visible: int = 8,
|
|
15
|
-
primary_min: int = 12, primary_max: int = 32, theme: Theme =
|
|
15
|
+
primary_min: int = 12, primary_max: int = 32, theme: Theme | None = None,
|
|
16
16
|
max_desc_lines: int = MAX_DESC_LINES):
|
|
17
17
|
self._picker = ListPicker(items, max_visible, primary_min, primary_max, theme, max_desc_lines)
|
|
18
|
-
self.theme = theme
|
|
18
|
+
self.theme = theme or default_theme()
|
|
19
19
|
self.checked: set[str] = set()
|
|
20
20
|
|
|
21
21
|
self.on_confirm = None # Callable[[list[PickerItem]], None]
|
|
@@ -15,7 +15,7 @@ tools/ask_user_tool.py: {question, header, options: [{label, description}], mult
|
|
|
15
15
|
|
|
16
16
|
from micro_cc.tui_native.list_picker_ import ListPicker, PickerItem
|
|
17
17
|
from micro_cc.tui_native.multi_select_list_ import MultiSelectList
|
|
18
|
-
from micro_cc.tui_native.theme_ import
|
|
18
|
+
from micro_cc.tui_native.theme_ import default_theme, Theme
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def _options_to_items(options: list[dict]) -> list[PickerItem]:
|
|
@@ -24,8 +24,8 @@ def _options_to_items(options: list[dict]) -> list[PickerItem]:
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class QuestionPanel:
|
|
27
|
-
def __init__(self, questions: list[dict], theme: Theme =
|
|
28
|
-
self.theme = theme
|
|
27
|
+
def __init__(self, questions: list[dict], theme: Theme | None = None):
|
|
28
|
+
self.theme = theme or default_theme()
|
|
29
29
|
self.questions = questions
|
|
30
30
|
self.stage = 0
|
|
31
31
|
self.answers: dict[str, object] = {}
|
|
@@ -89,6 +89,15 @@ async def cmd_model(app):
|
|
|
89
89
|
app.prompt.clear()
|
|
90
90
|
|
|
91
91
|
|
|
92
|
+
async def cmd_theme(app):
|
|
93
|
+
"""Open the theme picker. The actual switch happens in
|
|
94
|
+
app._on_theme_picked -> theme_store_.set_preset, which persists the new
|
|
95
|
+
color set and fires the on_change listeners that repaint the ground and
|
|
96
|
+
invalidate the render caches."""
|
|
97
|
+
app._open_picker(app.theme_picker, "_theme_picker_overlay")
|
|
98
|
+
app.prompt.clear()
|
|
99
|
+
|
|
100
|
+
|
|
92
101
|
async def cmd_copy(app):
|
|
93
102
|
lines = []
|
|
94
103
|
for msg in app._msg_rows:
|
|
@@ -156,7 +165,12 @@ async def cmd_update(app):
|
|
|
156
165
|
await _run_update(app)
|
|
157
166
|
|
|
158
167
|
|
|
159
|
-
|
|
168
|
+
def _pink() -> str:
|
|
169
|
+
"""The accent color, read live from the active theme set (see
|
|
170
|
+
theme_store_) — a function rather than a constant so a /theme switch
|
|
171
|
+
isn't frozen at import time."""
|
|
172
|
+
from micro_cc.utils import theme_store_
|
|
173
|
+
return theme_store_.get("accent")
|
|
160
174
|
|
|
161
175
|
|
|
162
176
|
async def _run_update(app):
|
|
@@ -191,7 +205,7 @@ async def _run_update(app):
|
|
|
191
205
|
i = 0
|
|
192
206
|
while True:
|
|
193
207
|
frame = app._SPINNER[i % len(app._SPINNER)]
|
|
194
|
-
app.static_hintbar.update(f"[{
|
|
208
|
+
app.static_hintbar.update(f"[{_pink()}]{frame} updating micro-cc {current_version} → {latest}…[/{_pink()}]")
|
|
195
209
|
app.request_render()
|
|
196
210
|
i += 1
|
|
197
211
|
try:
|
|
@@ -209,7 +223,7 @@ async def _run_update(app):
|
|
|
209
223
|
app._static_hint_text()
|
|
210
224
|
return
|
|
211
225
|
|
|
212
|
-
app.static_hintbar.update(f"[{
|
|
226
|
+
app.static_hintbar.update(f"[{_pink()}]✓ updated {current_version} → {latest} — restarting…[/{_pink()}]")
|
|
213
227
|
app.request_render()
|
|
214
228
|
await asyncio.sleep(0.8)
|
|
215
229
|
|
|
@@ -463,6 +477,7 @@ SLASH_COMMANDS = {
|
|
|
463
477
|
"/clear": cmd_clear,
|
|
464
478
|
"/copy": cmd_copy,
|
|
465
479
|
"/model": cmd_model,
|
|
480
|
+
"/theme": cmd_theme,
|
|
466
481
|
"/dangerous": cmd_dangerous,
|
|
467
482
|
"/rewind": cmd_rewind,
|
|
468
483
|
"/login": cmd_login,
|