deft-console 0.11.1__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.
Files changed (343) hide show
  1. deft_console/__init__.py +38 -0
  2. deft_console/__main__.py +4 -0
  3. deft_console/_built.py +3 -0
  4. deft_console/_loop.py +86 -0
  5. deft_console/_path_fuzzy_search.py +48 -0
  6. deft_console/_path_match.py +141 -0
  7. deft_console/_vendor/VENDORED.md +9 -0
  8. deft_console/_vendor/__init__.py +1 -0
  9. deft_console/_vendor/pixcell/LICENSE +21 -0
  10. deft_console/_vendor/pixcell/__init__.py +12 -0
  11. deft_console/_vendor/pixcell/__main__.py +229 -0
  12. deft_console/_vendor/pixcell/_diacritics.py +43 -0
  13. deft_console/_vendor/pixcell/frames.py +176 -0
  14. deft_console/_vendor/pixcell/halfblock.py +34 -0
  15. deft_console/_vendor/pixcell/iterm2.py +18 -0
  16. deft_console/_vendor/pixcell/kitty.py +215 -0
  17. deft_console/_vendor/pixcell/live.py +238 -0
  18. deft_console/_vendor/pixcell/probe.py +396 -0
  19. deft_console/_vendor/pixcell/sixel.py +68 -0
  20. deft_console/_vendor/pixcell/terminal_facts.py +308 -0
  21. deft_console/_vendor/pixcell/terminal_facts.source +1 -0
  22. deft_console/_vendor/pixcell/terminals.toml +159 -0
  23. deft_console/_vendor/pixcell/textual.py +787 -0
  24. deft_console/_vendor/termaid/LICENSE +21 -0
  25. deft_console/_vendor/termaid/__init__.py +375 -0
  26. deft_console/_vendor/termaid/__main__.py +6 -0
  27. deft_console/_vendor/termaid/claude_hook.py +265 -0
  28. deft_console/_vendor/termaid/cli.py +670 -0
  29. deft_console/_vendor/termaid/diagnostics.py +41 -0
  30. deft_console/_vendor/termaid/graph/__init__.py +4 -0
  31. deft_console/_vendor/termaid/graph/model.py +203 -0
  32. deft_console/_vendor/termaid/graph/shapes.py +23 -0
  33. deft_console/_vendor/termaid/ingest.py +226 -0
  34. deft_console/_vendor/termaid/layout/__init__.py +3 -0
  35. deft_console/_vendor/termaid/layout/coordinates.py +51 -0
  36. deft_console/_vendor/termaid/layout/grid.py +270 -0
  37. deft_console/_vendor/termaid/layout/layers.py +508 -0
  38. deft_console/_vendor/termaid/layout/placement.py +411 -0
  39. deft_console/_vendor/termaid/layout/subgraphs.py +184 -0
  40. deft_console/_vendor/termaid/markdown.py +103 -0
  41. deft_console/_vendor/termaid/model/__init__.py +0 -0
  42. deft_console/_vendor/termaid/model/blockdiagram.py +30 -0
  43. deft_console/_vendor/termaid/model/classdiagram.py +47 -0
  44. deft_console/_vendor/termaid/model/erdiagram.py +41 -0
  45. deft_console/_vendor/termaid/model/gantt.py +101 -0
  46. deft_console/_vendor/termaid/model/gitgraph.py +31 -0
  47. deft_console/_vendor/termaid/model/journey.py +24 -0
  48. deft_console/_vendor/termaid/model/kanban.py +23 -0
  49. deft_console/_vendor/termaid/model/mindmap.py +23 -0
  50. deft_console/_vendor/termaid/model/packet.py +22 -0
  51. deft_console/_vendor/termaid/model/piechart.py +18 -0
  52. deft_console/_vendor/termaid/model/quadrant.py +24 -0
  53. deft_console/_vendor/termaid/model/sequence.py +66 -0
  54. deft_console/_vendor/termaid/model/timeline.py +23 -0
  55. deft_console/_vendor/termaid/model/treemap.py +28 -0
  56. deft_console/_vendor/termaid/model/xychart.py +24 -0
  57. deft_console/_vendor/termaid/output/__init__.py +0 -0
  58. deft_console/_vendor/termaid/output/rich.py +275 -0
  59. deft_console/_vendor/termaid/output/styled.py +66 -0
  60. deft_console/_vendor/termaid/output/widget.py +46 -0
  61. deft_console/_vendor/termaid/parser/__init__.py +4 -0
  62. deft_console/_vendor/termaid/parser/architecture.py +332 -0
  63. deft_console/_vendor/termaid/parser/blockdiagram.py +316 -0
  64. deft_console/_vendor/termaid/parser/classdiagram.py +255 -0
  65. deft_console/_vendor/termaid/parser/erdiagram.py +247 -0
  66. deft_console/_vendor/termaid/parser/flowchart.py +871 -0
  67. deft_console/_vendor/termaid/parser/gantt.py +250 -0
  68. deft_console/_vendor/termaid/parser/gitgraph.py +340 -0
  69. deft_console/_vendor/termaid/parser/journey.py +74 -0
  70. deft_console/_vendor/termaid/parser/kanban.py +80 -0
  71. deft_console/_vendor/termaid/parser/mindmap.py +81 -0
  72. deft_console/_vendor/termaid/parser/packet.py +70 -0
  73. deft_console/_vendor/termaid/parser/piechart.py +67 -0
  74. deft_console/_vendor/termaid/parser/quadrant.py +67 -0
  75. deft_console/_vendor/termaid/parser/sequence.py +250 -0
  76. deft_console/_vendor/termaid/parser/statediagram.py +245 -0
  77. deft_console/_vendor/termaid/parser/timeline.py +68 -0
  78. deft_console/_vendor/termaid/parser/tokens.py +53 -0
  79. deft_console/_vendor/termaid/parser/treemap.py +67 -0
  80. deft_console/_vendor/termaid/parser/xychart.py +143 -0
  81. deft_console/_vendor/termaid/partial.py +150 -0
  82. deft_console/_vendor/termaid/py.typed +0 -0
  83. deft_console/_vendor/termaid/renderer/__init__.py +3 -0
  84. deft_console/_vendor/termaid/renderer/blockdiagram.py +476 -0
  85. deft_console/_vendor/termaid/renderer/canvas.py +366 -0
  86. deft_console/_vendor/termaid/renderer/charset.py +193 -0
  87. deft_console/_vendor/termaid/renderer/classdiagram.py +854 -0
  88. deft_console/_vendor/termaid/renderer/draw.py +829 -0
  89. deft_console/_vendor/termaid/renderer/erdiagram.py +600 -0
  90. deft_console/_vendor/termaid/renderer/gantt.py +315 -0
  91. deft_console/_vendor/termaid/renderer/gitgraph.py +425 -0
  92. deft_console/_vendor/termaid/renderer/journey.py +185 -0
  93. deft_console/_vendor/termaid/renderer/kanban.py +139 -0
  94. deft_console/_vendor/termaid/renderer/mindmap.py +295 -0
  95. deft_console/_vendor/termaid/renderer/packet.py +200 -0
  96. deft_console/_vendor/termaid/renderer/piechart.py +122 -0
  97. deft_console/_vendor/termaid/renderer/quadrant.py +147 -0
  98. deft_console/_vendor/termaid/renderer/relation_tracks.py +31 -0
  99. deft_console/_vendor/termaid/renderer/sequence.py +986 -0
  100. deft_console/_vendor/termaid/renderer/shapes/__init__.py +461 -0
  101. deft_console/_vendor/termaid/renderer/themes.py +179 -0
  102. deft_console/_vendor/termaid/renderer/timeline.py +67 -0
  103. deft_console/_vendor/termaid/renderer/treemap.py +245 -0
  104. deft_console/_vendor/termaid/renderer/xychart.py +381 -0
  105. deft_console/_vendor/termaid/routing/__init__.py +3 -0
  106. deft_console/_vendor/termaid/routing/pathfinder.py +197 -0
  107. deft_console/_vendor/termaid/routing/router.py +1206 -0
  108. deft_console/_vendor/termaid/utils.py +246 -0
  109. deft_console/about.py +93 -0
  110. deft_console/acp/agent.py +1802 -0
  111. deft_console/acp/api.py +123 -0
  112. deft_console/acp/display.py +147 -0
  113. deft_console/acp/encode_tool_call_id.py +12 -0
  114. deft_console/acp/extensions/__init__.py +48 -0
  115. deft_console/acp/extensions/deft.py +107 -0
  116. deft_console/acp/extensions/xai.py +377 -0
  117. deft_console/acp/messages.py +400 -0
  118. deft_console/acp/prompt.py +82 -0
  119. deft_console/acp/protocol.py +575 -0
  120. deft_console/activity.py +211 -0
  121. deft_console/activity_history.py +112 -0
  122. deft_console/agent.py +130 -0
  123. deft_console/agent_actions.py +68 -0
  124. deft_console/agent_process.py +121 -0
  125. deft_console/agent_schema.py +80 -0
  126. deft_console/agents.py +120 -0
  127. deft_console/ansi/__init__.py +1 -0
  128. deft_console/ansi/_ansi.py +1876 -0
  129. deft_console/ansi/_ansi_colors.py +264 -0
  130. deft_console/ansi/_control_codes.py +37 -0
  131. deft_console/ansi/_keys.py +251 -0
  132. deft_console/ansi/_sgr_styles.py +64 -0
  133. deft_console/ansi/_stream_parser.py +348 -0
  134. deft_console/answer.py +24 -0
  135. deft_console/app.py +1094 -0
  136. deft_console/atomic.py +37 -0
  137. deft_console/branding.py +29 -0
  138. deft_console/browser/__init__.py +0 -0
  139. deft_console/browser/cdp.py +389 -0
  140. deft_console/browser/content.py +96 -0
  141. deft_console/browser/install.py +187 -0
  142. deft_console/browser/keys.py +50 -0
  143. deft_console/browser/policy.py +78 -0
  144. deft_console/browser/view.py +991 -0
  145. deft_console/caret.py +57 -0
  146. deft_console/cli.py +378 -0
  147. deft_console/code_analyze.py +27 -0
  148. deft_console/complete.py +38 -0
  149. deft_console/constants.py +58 -0
  150. deft_console/conversation_markdown.py +358 -0
  151. deft_console/danger.py +372 -0
  152. deft_console/data/agents/ampcode.com.toml +55 -0
  153. deft_console/data/agents/augmentcode.com.toml +44 -0
  154. deft_console/data/agents/claude-bridge.deftco.toml +40 -0
  155. deft_console/data/agents/claude.com.toml +44 -0
  156. deft_console/data/agents/claude.deftco.toml +52 -0
  157. deft_console/data/agents/copilot.github.com.toml +29 -0
  158. deft_console/data/agents/crowai.dev.toml +36 -0
  159. deft_console/data/agents/cursor.com.toml +39 -0
  160. deft_console/data/agents/deepagents.langchain.com.toml +32 -0
  161. deft_console/data/agents/demo.deftco.toml +42 -0
  162. deft_console/data/agents/docker.com.toml +60 -0
  163. deft_console/data/agents/geminicli.com.toml +28 -0
  164. deft_console/data/agents/goose.ai.toml +56 -0
  165. deft_console/data/agents/grok.x.ai.toml +37 -0
  166. deft_console/data/agents/inference.huggingface.co.toml +41 -0
  167. deft_console/data/agents/kimi.com.toml +44 -0
  168. deft_console/data/agents/kiro.dev.toml +43 -0
  169. deft_console/data/agents/openai.com.toml +55 -0
  170. deft_console/data/agents/openclaw.ai.toml +37 -0
  171. deft_console/data/agents/opencode.ai.toml +39 -0
  172. deft_console/data/agents/openhands.dev.toml +49 -0
  173. deft_console/data/agents/pi.dev.toml +50 -0
  174. deft_console/data/agents/prime.deftco.toml +51 -0
  175. deft_console/data/agents/sabal.deftco.toml +49 -0
  176. deft_console/data/agents/stakpak.dev.toml +62 -0
  177. deft_console/data/agents/vibe.mistral.ai.toml +35 -0
  178. deft_console/data/agents/vtcode.dev.toml +67 -0
  179. deft_console/data/images/frog.png +0 -0
  180. deft_console/data/sounds/question.wav +0 -0
  181. deft_console/data/sounds/turn-over.wav +0 -0
  182. deft_console/db.py +236 -0
  183. deft_console/dec.py +332 -0
  184. deft_console/deft_console.tcss +384 -0
  185. deft_console/demo_agent/__init__.py +29 -0
  186. deft_console/demo_agent/__main__.py +6 -0
  187. deft_console/demo_agent/agent.py +387 -0
  188. deft_console/demo_agent/connection.py +97 -0
  189. deft_console/demo_agent/content.py +455 -0
  190. deft_console/demo_agent/context.py +248 -0
  191. deft_console/demo_agent/demos.py +748 -0
  192. deft_console/demo_agent/mcp_client.py +66 -0
  193. deft_console/demo_agent/registry.py +123 -0
  194. deft_console/demo_agent/tutorial.py +301 -0
  195. deft_console/directory.py +234 -0
  196. deft_console/directory_suggester.py +93 -0
  197. deft_console/directory_watcher.py +214 -0
  198. deft_console/display_mcp.py +349 -0
  199. deft_console/doing.py +90 -0
  200. deft_console/format_path.py +25 -0
  201. deft_console/fuzzy.py +140 -0
  202. deft_console/fuzzy_index.py +275 -0
  203. deft_console/gist.py +33 -0
  204. deft_console/history.py +138 -0
  205. deft_console/images.py +534 -0
  206. deft_console/jsonrpc.py +587 -0
  207. deft_console/markdown_append.py +32 -0
  208. deft_console/menus.py +14 -0
  209. deft_console/mermaid.py +132 -0
  210. deft_console/mermaid_image.py +306 -0
  211. deft_console/messages.py +166 -0
  212. deft_console/node_runtime.py +215 -0
  213. deft_console/option_content.py +51 -0
  214. deft_console/os.py +0 -0
  215. deft_console/page_preview.py +154 -0
  216. deft_console/page_shot.py +168 -0
  217. deft_console/pane_cli.py +238 -0
  218. deft_console/panes/__init__.py +81 -0
  219. deft_console/panes/actions.py +208 -0
  220. deft_console/panes/api/__init__.py +5 -0
  221. deft_console/panes/api/client.py +115 -0
  222. deft_console/panes/api/running.py +95 -0
  223. deft_console/panes/api/server.py +228 -0
  224. deft_console/panes/content.py +134 -0
  225. deft_console/panes/events.py +145 -0
  226. deft_console/panes/manager.py +543 -0
  227. deft_console/panes/model.py +337 -0
  228. deft_console/panes/textual/__init__.py +503 -0
  229. deft_console/panes/textual/commands.py +95 -0
  230. deft_console/path_complete.py +217 -0
  231. deft_console/path_filter.py +157 -0
  232. deft_console/paths.py +100 -0
  233. deft_console/picture_content.py +95 -0
  234. deft_console/pill.py +23 -0
  235. deft_console/portability/__init__.py +133 -0
  236. deft_console/portability/_posix.py +579 -0
  237. deft_console/portability/_windows.py +652 -0
  238. deft_console/program_status.py +524 -0
  239. deft_console/prompt/attachments.py +188 -0
  240. deft_console/prompt/extract.py +20 -0
  241. deft_console/prompt/resource.py +68 -0
  242. deft_console/protocol.py +28 -0
  243. deft_console/screens/action_modal.py +107 -0
  244. deft_console/screens/agent_modal.py +172 -0
  245. deft_console/screens/agent_sessions.py +148 -0
  246. deft_console/screens/ask_user_question.py +658 -0
  247. deft_console/screens/command_edit_modal.py +74 -0
  248. deft_console/screens/elicitation.py +535 -0
  249. deft_console/screens/main.py +558 -0
  250. deft_console/screens/main.tcss +688 -0
  251. deft_console/screens/new_session.py +75 -0
  252. deft_console/screens/permissions.py +411 -0
  253. deft_console/screens/permissions.tcss +73 -0
  254. deft_console/screens/session_options.py +175 -0
  255. deft_console/screens/session_resume_modal.py +172 -0
  256. deft_console/screens/session_resume_modal.tcss +35 -0
  257. deft_console/screens/sessions.py +177 -0
  258. deft_console/screens/sessions.tcss +153 -0
  259. deft_console/screens/settings.py +254 -0
  260. deft_console/screens/settings.tcss +101 -0
  261. deft_console/screens/store.py +682 -0
  262. deft_console/screens/store.tcss +272 -0
  263. deft_console/screens/withdrawable.py +49 -0
  264. deft_console/session_messages.py +101 -0
  265. deft_console/session_tracker.py +156 -0
  266. deft_console/settings.py +353 -0
  267. deft_console/settings_schema.py +590 -0
  268. deft_console/shell.py +291 -0
  269. deft_console/shell_read.py +42 -0
  270. deft_console/slash_command.py +34 -0
  271. deft_console/terminal_content.py +144 -0
  272. deft_console/tool_summary.py +101 -0
  273. deft_console/twitter.py +38 -0
  274. deft_console/vendor/pele.LICENSE +40 -0
  275. deft_console/vendor/pele.VERSION +1 -0
  276. deft_console/vendor/pele.global.js +150 -0
  277. deft_console/version.py +81 -0
  278. deft_console/visuals/columns.py +292 -0
  279. deft_console/widgets/acp_content.py +50 -0
  280. deft_console/widgets/activity_top.py +480 -0
  281. deft_console/widgets/activity_view.py +292 -0
  282. deft_console/widgets/agent_response.py +77 -0
  283. deft_console/widgets/agent_thought.py +51 -0
  284. deft_console/widgets/blackhole.py +322 -0
  285. deft_console/widgets/check_list.py +67 -0
  286. deft_console/widgets/comets.py +335 -0
  287. deft_console/widgets/command_pane.py +174 -0
  288. deft_console/widgets/condensed_path.py +117 -0
  289. deft_console/widgets/conversation.py +3923 -0
  290. deft_console/widgets/danger_warning.py +65 -0
  291. deft_console/widgets/diff_view.py +59 -0
  292. deft_console/widgets/directory_input.py +8 -0
  293. deft_console/widgets/display_terminal.py +159 -0
  294. deft_console/widgets/fence_cursor.py +59 -0
  295. deft_console/widgets/flash.py +95 -0
  296. deft_console/widgets/future_text.py +126 -0
  297. deft_console/widgets/galaxy.py +293 -0
  298. deft_console/widgets/grid_select.py +234 -0
  299. deft_console/widgets/help_panel.py +13 -0
  300. deft_console/widgets/highlighted_textarea.py +180 -0
  301. deft_console/widgets/inline_question.py +190 -0
  302. deft_console/widgets/mandelbrot.py +294 -0
  303. deft_console/widgets/markdown_note.py +13 -0
  304. deft_console/widgets/menu.py +147 -0
  305. deft_console/widgets/moon.py +330 -0
  306. deft_console/widgets/non_selectable_label.py +5 -0
  307. deft_console/widgets/note.py +18 -0
  308. deft_console/widgets/path_search.py +555 -0
  309. deft_console/widgets/picture_pane.py +121 -0
  310. deft_console/widgets/plan.py +226 -0
  311. deft_console/widgets/project_directory_tree.py +90 -0
  312. deft_console/widgets/prompt.py +987 -0
  313. deft_console/widgets/prompt_queue.py +110 -0
  314. deft_console/widgets/pulsar.py +338 -0
  315. deft_console/widgets/question.py +375 -0
  316. deft_console/widgets/rocket.py +477 -0
  317. deft_console/widgets/session_grid_select.py +71 -0
  318. deft_console/widgets/session_list.py +360 -0
  319. deft_console/widgets/session_message.py +86 -0
  320. deft_console/widgets/session_summary.py +130 -0
  321. deft_console/widgets/session_tabs.py +184 -0
  322. deft_console/widgets/shell_result.py +35 -0
  323. deft_console/widgets/shell_terminal.py +50 -0
  324. deft_console/widgets/side_bar.py +89 -0
  325. deft_console/widgets/slash_complete.py +245 -0
  326. deft_console/widgets/solarsystem.py +300 -0
  327. deft_console/widgets/spark.py +106 -0
  328. deft_console/widgets/starfield.py +295 -0
  329. deft_console/widgets/strike_text.py +70 -0
  330. deft_console/widgets/subagent_view.py +412 -0
  331. deft_console/widgets/terminal.py +756 -0
  332. deft_console/widgets/terminal_pane.py +153 -0
  333. deft_console/widgets/terminal_tool.py +522 -0
  334. deft_console/widgets/throbber.py +100 -0
  335. deft_console/widgets/tool_call.py +347 -0
  336. deft_console/widgets/user_input.py +24 -0
  337. deft_console/widgets/version.py +5 -0
  338. deft_console/widgets/welcome.py +31 -0
  339. deft_console-0.11.1.dist-info/METADATA +255 -0
  340. deft_console-0.11.1.dist-info/RECORD +343 -0
  341. deft_console-0.11.1.dist-info/WHEEL +4 -0
  342. deft_console-0.11.1.dist-info/entry_points.txt +2 -0
  343. deft_console-0.11.1.dist-info/licenses/LICENSE +11 -0
@@ -0,0 +1,38 @@
1
+ from typing import Literal, Mapping
2
+
3
+ NAME = "toad"
4
+ TITLE = "Deft Console"
5
+
6
+ type OS = Literal["linux", "macos", "windows", "*"]
7
+
8
+ from deft_console.portability import SYSTEM as os # noqa: E402
9
+
10
+
11
+ def get_os_matrix(matrix: Mapping[OS, str]) -> str | None:
12
+ """Get a value from a mapping where the key is an OS, falling back to a wildcard ("*").
13
+
14
+ Args:
15
+ matrix: A mapping where an OS literal is the key.
16
+
17
+ Returns:
18
+ The value, if one is found, or `None`.
19
+ """
20
+ if (result := matrix.get(os)) is None:
21
+ result = matrix.get("*")
22
+ return result
23
+
24
+
25
+ def get_version() -> str:
26
+ """Get the current version of Deft Console.
27
+
28
+ Returns:
29
+ str: Version string, e.g "1.2.3"
30
+ """
31
+ from importlib.metadata import version
32
+
33
+ for distribution in ("deft-console", "batrachian-toad"): # the second: older checkouts
34
+ try:
35
+ return version(distribution)
36
+ except Exception:
37
+ pass
38
+ return "0.0.0unknown"
@@ -0,0 +1,4 @@
1
+ from deft_console.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
deft_console/_built.py ADDED
@@ -0,0 +1,3 @@
1
+ """Written by hatch_build.py when the wheel was built."""
2
+
3
+ BUILT_ON = "2026-11-12"
deft_console/_loop.py ADDED
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Iterable, Literal, Sequence, TypeVar
4
+
5
+ T = TypeVar("T")
6
+
7
+
8
+ def loop_first(values: Iterable[T]) -> Iterable[tuple[bool, T]]:
9
+ """Iterate and generate a tuple with a flag for first value."""
10
+ iter_values = iter(values)
11
+ try:
12
+ value = next(iter_values)
13
+ except StopIteration:
14
+ return
15
+ yield True, value
16
+ for value in iter_values:
17
+ yield False, value
18
+
19
+
20
+ def loop_last(values: Iterable[T]) -> Iterable[tuple[bool, T]]:
21
+ """Iterate and generate a tuple with a flag for last value."""
22
+ iter_values = iter(values)
23
+ try:
24
+ previous_value = next(iter_values)
25
+ except StopIteration:
26
+ return
27
+ for value in iter_values:
28
+ yield False, previous_value
29
+ previous_value = value
30
+ yield True, previous_value
31
+
32
+
33
+ def loop_first_last(values: Iterable[T]) -> Iterable[tuple[bool, bool, T]]:
34
+ """Iterate and generate a tuple with a flag for first and last value."""
35
+ iter_values = iter(values)
36
+ try:
37
+ previous_value = next(iter_values)
38
+ except StopIteration:
39
+ return
40
+ first = True
41
+ for value in iter_values:
42
+ yield first, False, previous_value
43
+ first = False
44
+ previous_value = value
45
+ yield first, True, previous_value
46
+
47
+
48
+ def loop_from_index(
49
+ values: Sequence[T],
50
+ index: int,
51
+ direction: Literal[-1, +1] = +1,
52
+ wrap: bool = True,
53
+ ) -> Iterable[tuple[int, T]]:
54
+ """Iterate over values in a sequence from a given starting index, potentially wrapping the index
55
+ if it would go out of bounds.
56
+
57
+ Note that the first value to be yielded is a step from `index`, and `index` will be yielded *last*.
58
+
59
+
60
+ Args:
61
+ values: A sequence of values.
62
+ index: Starting index.
63
+ direction: Direction to move index (+1 for forward, -1 for backward).
64
+ bool: Should the index wrap when out of bounds?
65
+
66
+ Yields:
67
+ A tuple of index and value from the sequence.
68
+ """
69
+ # Sanity check for devs who miss the typing errors
70
+ assert direction in (-1, +1), "direction must be -1 or +1"
71
+ count = len(values)
72
+ if wrap:
73
+ for _ in range(count):
74
+ index = (index + direction) % count
75
+ yield (index, values[index])
76
+ else:
77
+ if direction == +1:
78
+ for _ in range(count):
79
+ if (index := index + 1) >= count:
80
+ break
81
+ yield (index, values[index])
82
+ else:
83
+ for _ in range(count):
84
+ if (index := index - 1) < 0:
85
+ break
86
+ yield (index, values[index])
@@ -0,0 +1,48 @@
1
+ from functools import lru_cache
2
+ from typing import Sequence
3
+ import re
4
+
5
+
6
+ from deft_console.fuzzy import FuzzySearch
7
+
8
+
9
+ class PathFuzzySearch(FuzzySearch):
10
+ @classmethod
11
+ @lru_cache(maxsize=1024)
12
+ def get_first_letters(cls, candidate: str) -> frozenset[int]:
13
+ return frozenset(
14
+ {
15
+ 0,
16
+ *[match.start() + 1 for match in re.finditer(r"/", candidate)],
17
+ }
18
+ )
19
+
20
+ def score(self, candidate: str, positions: Sequence[int]) -> float:
21
+ """Score a search.
22
+
23
+ Args:
24
+ search: Search object.
25
+
26
+ Returns:
27
+ Score.
28
+ """
29
+ first_letters = self.get_first_letters(candidate)
30
+ # This is a heuristic, and can be tweaked for better results
31
+ # Boost first letter matches
32
+ offset_count = len(positions)
33
+ score: float = offset_count + len(first_letters.intersection(positions))
34
+
35
+ groups = 1
36
+ last_offset, *offsets = positions
37
+ for offset in offsets:
38
+ if offset != last_offset + 1:
39
+ groups += 1
40
+ last_offset = offset
41
+
42
+ # Boost to favor less groups
43
+ normalized_groups = (offset_count - (groups - 1)) / offset_count
44
+ score *= 1 + (normalized_groups * normalized_groups)
45
+
46
+ if positions[0] > candidate.rfind("/"):
47
+ score *= 2
48
+ return score
@@ -0,0 +1,141 @@
1
+ from functools import lru_cache
2
+ from operator import itemgetter
3
+ import re
4
+ from typing import Iterable, Sequence
5
+
6
+
7
+ class PathFuzzySearch:
8
+ """Performs a fuzzy search.
9
+
10
+ Unlike a regex solution, this will finds all possible matches.
11
+ """
12
+
13
+ def __init__(
14
+ self, case_sensitive: bool = False, *, cache_size: int = 1024 * 4
15
+ ) -> None:
16
+ """Initialize fuzzy search.
17
+
18
+ Args:
19
+ case_sensitive: Is the match case sensitive?
20
+ cache_size: Number of queries to cache.
21
+ """
22
+
23
+ self.case_sensitive = case_sensitive
24
+
25
+ def match(self, query: str, candidate: str) -> tuple[float, Sequence[int]]:
26
+ """Match against a query.
27
+
28
+ Args:
29
+ query: The fuzzy query.
30
+ candidate: A candidate to check,.
31
+
32
+ Returns:
33
+ A pair of (score, tuple of offsets). `(0, ())` for no result.
34
+ """
35
+ default: tuple[float, Sequence[int]] = (0.0, [])
36
+ result = max(self._match(query, candidate), key=itemgetter(0), default=default)
37
+ return result
38
+
39
+ @classmethod
40
+ @lru_cache(maxsize=1024)
41
+ def get_first_letters(cls, candidate: str) -> frozenset[int]:
42
+ return frozenset(
43
+ {
44
+ 0,
45
+ *[match.start() + 1 for match in re.finditer(r"/", candidate)],
46
+ }
47
+ )
48
+
49
+ def score(self, candidate: str, positions: Sequence[int]) -> float:
50
+ """Score a search.
51
+
52
+ Args:
53
+ search: Search object.
54
+
55
+ Returns:
56
+ Score.
57
+ """
58
+ first_letters = self.get_first_letters(candidate)
59
+ # This is a heuristic, and can be tweaked for better results
60
+ # Boost first letter matches
61
+ offset_count = len(positions)
62
+ score: float = offset_count + len(first_letters.intersection(positions))
63
+
64
+ groups = 1
65
+ last_offset, *offsets = positions
66
+ for offset in offsets:
67
+ if offset != last_offset + 1:
68
+ groups += 1
69
+ last_offset = offset
70
+
71
+ # Boost to favor less groups
72
+ normalized_groups = (offset_count - (groups - 1)) / offset_count
73
+ score *= 1 + (normalized_groups * normalized_groups)
74
+
75
+ if positions[0] > candidate.rfind("/"):
76
+ score *= 2
77
+ return score
78
+
79
+ def _match(
80
+ self, query: str, candidate: str
81
+ ) -> Iterable[tuple[float, Sequence[int]]]:
82
+ letter_positions: list[list[int]] = []
83
+ position = 0
84
+
85
+ if not self.case_sensitive:
86
+ candidate = candidate.casefold()
87
+ query = query.casefold()
88
+
89
+ score = self.score
90
+
91
+ for offset, letter in enumerate(query):
92
+ last_index = len(candidate) - offset
93
+ positions: list[int] = []
94
+ letter_positions.append(positions)
95
+ index = position
96
+ while (location := candidate.find(letter, index)) != -1:
97
+ positions.append(location)
98
+ index = location + 1
99
+ if index >= last_index:
100
+ break
101
+ if not positions:
102
+ yield (0.0, ())
103
+ return
104
+ position = positions[0] + 1
105
+
106
+ possible_offsets: list[list[int]] = []
107
+ query_length = len(query)
108
+
109
+ def get_offsets(offsets: list[int], positions_index: int) -> None:
110
+ """Recursively match offsets.
111
+
112
+ Args:
113
+ offsets: A list of offsets.
114
+ positions_index: Index of query letter.
115
+
116
+ """
117
+ for offset in letter_positions[positions_index]:
118
+ if not offsets or offset > offsets[-1]:
119
+ new_offsets = [*offsets, offset]
120
+ if len(new_offsets) == query_length:
121
+ possible_offsets.append(new_offsets)
122
+ else:
123
+ get_offsets(new_offsets, positions_index + 1)
124
+
125
+ get_offsets([], 0)
126
+ for offsets in possible_offsets:
127
+ yield score(candidate, offsets), offsets
128
+
129
+
130
+ _fuzzy_search = PathFuzzySearch(case_sensitive=False)
131
+
132
+
133
+ def match_path(query_path: tuple[str, str]) -> tuple[float, Sequence[int], str]:
134
+ global _fuzzy_search
135
+ query, path = query_path
136
+ score, indices = _fuzzy_search.match(query, path)
137
+ return (
138
+ score,
139
+ tuple(indices),
140
+ path,
141
+ )
@@ -0,0 +1,9 @@
1
+ # Vendored libraries
2
+
3
+ Copied by `scripts/vendor.py`; don't edit them here. Change the ref in the script and run it again.
4
+ Each keeps its own license (`<name>/LICENSE`).
5
+
6
+ | Library | Repository | Ref | Commit |
7
+ |---|---|---|---|
8
+ | `termaid` | https://github.com/deftai/termaid | `0f2fb37f5cb65a75a778b298480d2fc2b306f634` | `0f2fb37f5cb6` |
9
+ | `pixcell` | https://github.com/visionik/pixcell | `v0.4.1` | `a0c5952cdd48` |
@@ -0,0 +1 @@
1
+ """Libraries Console bundles: see VENDORED.md."""
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pixcell contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,12 @@
1
+ """pixcell: images in terminal cells.
2
+
3
+ - :mod:`pixcell.kitty` — Kitty graphics protocol escape codes and Unicode placeholders.
4
+ - :mod:`pixcell.iterm2`, :mod:`pixcell.sixel` — iTerm2 inline images and Sixel.
5
+ - :mod:`pixcell.halfblock` — two pixels per cell for any 24-bit-colour terminal.
6
+ - :mod:`pixcell.frames` — the frames of animated pictures (GIF, WebP, APNG).
7
+ - :mod:`pixcell.probe` — find out which the terminal supports, and its cell size.
8
+ - :mod:`pixcell.textual` — an ``Image`` widget for Textual (``pip install pixcell[textual]``).
9
+ - :mod:`pixcell.live` — ``LiveImage``, a Textual widget you keep pushing frames into.
10
+ """
11
+
12
+ __version__ = "0.4.1"
@@ -0,0 +1,229 @@
1
+ """``python -m pixcell [image ...]``: show images in a scrolling Textual view.
2
+
3
+ Each image is drawn twice: with the protocol the probe found, and with half blocks.
4
+ Without arguments it draws a test gradient.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+
11
+
12
+ def _gradient():
13
+ from PIL import Image, ImageDraw
14
+
15
+ image = Image.new("RGB", (640, 360))
16
+ draw = ImageDraw.Draw(image)
17
+ for x in range(640):
18
+ draw.line([(x, 0), (x, 359)], fill=(x * 255 // 639, 120, 255 - x * 255 // 639))
19
+ draw.ellipse((220, 80, 420, 280), fill=(250, 250, 250), outline=(0, 0, 0), width=6)
20
+ draw.text((20, 20), "pixcell", fill=(255, 255, 255))
21
+ return image
22
+
23
+
24
+ def _bouncing(frames: int = 16):
25
+ """An animated GIF: a ball bouncing across a gradient, with a frame counter. Small
26
+ enough (320x120, 16 frames: ~2.4 MB decoded) to play natively within Rex's budget."""
27
+ import io
28
+
29
+ from PIL import Image, ImageDraw
30
+
31
+ pictures = []
32
+ for number in range(frames):
33
+ image = Image.new("RGB", (320, 120))
34
+ draw = ImageDraw.Draw(image)
35
+ for x in range(320):
36
+ draw.line([(x, 0), (x, 119)], fill=(40, 60 + x * 120 // 319, 140))
37
+ position = abs((number * 2 * 260 // frames) % 520 - 260)
38
+ draw.ellipse((position + 10, 40, position + 50, 80), fill=(255, 200, 40))
39
+ draw.text((10, 8), f"frame {number + 1}/{frames}", fill=(255, 255, 255))
40
+ pictures.append(image)
41
+ buffer = io.BytesIO()
42
+ pictures[0].save(buffer, format="GIF", save_all=True, append_images=pictures[1:], duration=80, loop=0)
43
+ buffer.seek(0)
44
+ return Image.open(buffer)
45
+
46
+
47
+ def _tall_page(width: int):
48
+ """A long, detailed page (headings, text, code, boxes) to scroll: like a web page."""
49
+ import random
50
+
51
+ from PIL import Image, ImageDraw, ImageFont
52
+
53
+ def font(size):
54
+ try:
55
+ return ImageFont.load_default(size=size)
56
+ except TypeError:
57
+ return ImageFont.load_default()
58
+
59
+ random.seed(7)
60
+ words = ("terminal pixels frame image kitty pixcell render scroll update browser page text code "
61
+ "placeholder diff zlib encode decode widget textual console agent stream click").split()
62
+ page = Image.new("RGB", (width, 3600), (250, 250, 247))
63
+ draw = ImageDraw.Draw(page)
64
+ y = 20
65
+ while y < page.height - 120:
66
+ kind = random.random()
67
+ if kind < 0.15:
68
+ draw.text((24, y), " ".join(random.choices(words, k=4)).title(), fill=(20, 30, 60), font=font(28))
69
+ y += 46
70
+ elif kind < 0.3:
71
+ draw.rectangle((24, y, width - 24, y + 90), fill=(30, 34, 44))
72
+ for line in range(5):
73
+ draw.text((36, y + 8 + line * 16), " " * random.randint(0, 3) + " ".join(random.choices(words, k=6)),
74
+ fill=random.choice([(152, 195, 121), (229, 192, 123), (97, 175, 239), (224, 108, 117)]), font=font(13))
75
+ y += 104
76
+ elif kind < 0.4:
77
+ colour = random.choice([(219, 234, 254), (220, 252, 231), (254, 243, 199), (252, 231, 243)])
78
+ draw.rounded_rectangle((24, y, width - 24, y + 60), radius=10, fill=colour)
79
+ draw.text((40, y + 20), " ".join(random.choices(words, k=9)), fill=(40, 40, 40), font=font(16))
80
+ y += 76
81
+ else:
82
+ for _ in range(random.randint(3, 6)):
83
+ draw.text((24, y), " ".join(random.choices(words, k=11)), fill=(55, 55, 55), font=font(15))
84
+ y += 21
85
+ y += 12
86
+ return page
87
+
88
+
89
+ def live_demo() -> None:
90
+ """``python -m pixcell live``: a generated 640x400 scene at 15 fps. Click to drop a
91
+ marker at that pixel; scroll to change the orbit's speed."""
92
+ import math
93
+ import os
94
+ import threading
95
+ import time
96
+
97
+ from PIL import Image, ImageDraw
98
+ from textual.app import App, ComposeResult
99
+ from textual.widgets import Footer, Static
100
+
101
+ from deft_console._vendor.pixcell import textual as ct
102
+ from deft_console._vendor.pixcell.live import LiveImage
103
+
104
+ found = ct.configure()
105
+ width, height = 640, 400
106
+ arguments = sys.argv[2:]
107
+ fps = float(arguments[0]) if arguments and arguments[0].replace(".", "").isdigit() else 15.0
108
+ full = "full" in arguments # every pixel changes (a flat colour: compresses well)
109
+ scrolling = "scroll" in arguments # real worst case: a detailed page scrolling
110
+ page = _tall_page(width) if scrolling else None
111
+ marks: list[tuple[int, int]] = []
112
+ speed = [1.0]
113
+
114
+ def scene(t: float) -> Image.Image:
115
+ if page is not None:
116
+ top = int(t * 60 * 6) % (page.height - height) # 6 pixels a frame at 60 fps
117
+ return page.crop((0, top, width, top + height))
118
+ return _scene(t)
119
+
120
+ def _scene(t: float) -> Image.Image:
121
+ shade = int(t * 40) % 40 if full else 0
122
+ picture = Image.new("RGB", (width, height), (18 + shade, 24, 38))
123
+ draw = ImageDraw.Draw(picture)
124
+ for x in range(0, width, 40):
125
+ draw.line([(x, 0), (x, height)], fill=(30, 40, 60))
126
+ for y in range(0, height, 40):
127
+ draw.line([(0, y), (width, y)], fill=(30, 40, 60))
128
+ angle = t * speed[0]
129
+ cx, cy = width / 2 + 180 * math.cos(angle), height / 2 + 120 * math.sin(angle)
130
+ draw.ellipse((cx - 22, cy - 22, cx + 22, cy + 22), fill=(80, 200, 255))
131
+ bar = int((math.sin(t * 2) + 1) / 2 * (width - 120))
132
+ draw.rectangle((bar, height - 30, bar + 120, height - 14), fill=(255, 170, 40))
133
+ draw.text((14, 12), time.strftime("%H:%M:%S") + f" t={t:6.2f}s", fill=(230, 230, 230))
134
+ for mx, my in marks[-5:]:
135
+ draw.line([(mx - 8, my), (mx + 8, my)], fill=(255, 60, 60), width=2)
136
+ draw.line([(mx, my - 8), (mx, my + 8)], fill=(255, 60, 60), width=2)
137
+ draw.text((mx + 10, my + 4), f"{mx},{my}", fill=(255, 120, 120))
138
+ return picture
139
+
140
+ class Live(App):
141
+ CSS = "LiveImage { width: 80; height: auto; } Static { color: $text-muted; }"
142
+ BINDINGS = [("q", "quit", "Quit")]
143
+
144
+ def compose(self) -> ComposeResult:
145
+ yield Static(f"{found.protocol} ({found.terminal} {found.version}); format {found.frame_format}; "
146
+ f"{'edits' if found.edits else 'whole frames'} — click to mark, scroll for speed")
147
+ yield LiveImage((width, height), fps=fps, id="live")
148
+ yield Static("", id="stats")
149
+ yield Footer()
150
+
151
+ def on_mount(self) -> None:
152
+ live = self.query_one(LiveImage)
153
+ started = time.monotonic()
154
+
155
+ def produce() -> None:
156
+ while self.is_running:
157
+ live.push(scene(time.monotonic() - started))
158
+ time.sleep(1 / (fps * 2)) # faster than shown: the newest frame wins
159
+
160
+ threading.Thread(target=produce, daemon=True).start()
161
+ self.started = time.monotonic()
162
+ self.set_interval(1, self.stats)
163
+
164
+ def stats(self) -> None:
165
+ live = self.query_one(LiveImage)
166
+ frames = max(1, live.frames_shown)
167
+ elapsed = time.monotonic() - self.started
168
+ line = (
169
+ f"target {fps:g} fps{' (whole frame changes)' if full else ''}{' (page scrolling)' if scrolling else ''}: {live.frames_shown / elapsed:.1f} fps shown, "
170
+ f"{live.bytes_sent / frames / 1024:.1f} KB/frame, {live.busy_seconds / frames * 1000:.1f} ms/frame main thread"
171
+ )
172
+ self.query_one("#stats", Static).update(line)
173
+ if path := os.environ.get("PIXCELL_LIVE_STATS"): # for terminals we can't read back
174
+ with open(path, "w") as out:
175
+ out.write(f"{found.protocol} {found.terminal} {found.version} {found.frame_format} "
176
+ f"{'edits' if found.edits else 'swap'}: {line}\n")
177
+
178
+ def on_live_image_clicked(self, message: LiveImage.Clicked) -> None:
179
+ marks.append((message.x, message.y))
180
+
181
+ def on_live_image_scrolled(self, message: LiveImage.Scrolled) -> None:
182
+ speed[0] = max(0.2, min(5.0, speed[0] * (0.8 if message.delta > 0 else 1.25)))
183
+
184
+ Live().run()
185
+
186
+
187
+ def main() -> None:
188
+ from PIL import Image as PILImage
189
+ from textual.app import App, ComposeResult
190
+ from textual.containers import VerticalScroll
191
+ from textual.widgets import Footer, Static
192
+
193
+ from deft_console._vendor.pixcell import textual as ct
194
+
195
+ found = ct.configure()
196
+ images = [(path, PILImage.open(path)) for path in sys.argv[1:]] or [
197
+ ("animated GIF", _bouncing()),
198
+ ("gradient", _gradient()),
199
+ ]
200
+
201
+ class Demo(App):
202
+ CSS = "Image { width: 40; height: auto; margin: 0 0 1 0; } Static { color: $text-muted; }"
203
+ BINDINGS = [("q", "quit", "Quit")]
204
+
205
+ def compose(self) -> ComposeResult:
206
+ with VerticalScroll():
207
+ yield Static(
208
+ f"{found.protocol}: {found.reason}; cell {found.cell_size}; terminal {found.terminal or '?'}; "
209
+ f"animation {'plays' if found.animation else 'stays still'}"
210
+ )
211
+ for name, image in images:
212
+ picture = ct.Image(image)
213
+ moving = f", {picture._animation} animation" if picture.animated else (
214
+ ", still" if getattr(image, "is_animated", False) else ""
215
+ )
216
+ yield Static(f"{name} ({picture.protocol}{moving})")
217
+ yield picture
218
+ yield Static(f"{name} (halfblock)")
219
+ yield ct.Image(image, protocol="halfblock")
220
+ yield Footer()
221
+
222
+ Demo().run()
223
+
224
+
225
+ if __name__ == "__main__":
226
+ if sys.argv[1:2] == ["live"]:
227
+ live_demo()
228
+ else:
229
+ main()
@@ -0,0 +1,43 @@
1
+ """Row/column diacritics for Kitty Unicode placeholders (generated by
2
+ tools/gen_diacritics.py from the Unicode 6.0.0 Character Database). Index = number."""
3
+
4
+ DIACRITICS: tuple[int, ...] = (
5
+ 0x0305, 0x030D, 0x030E, 0x0310, 0x0312, 0x033D, 0x033E, 0x033F,
6
+ 0x0346, 0x034A, 0x034B, 0x034C, 0x0350, 0x0351, 0x0352, 0x0357,
7
+ 0x035B, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, 0x0368, 0x0369,
8
+ 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, 0x0483, 0x0484,
9
+ 0x0485, 0x0486, 0x0487, 0x0592, 0x0593, 0x0594, 0x0595, 0x0597,
10
+ 0x0598, 0x0599, 0x059C, 0x059D, 0x059E, 0x059F, 0x05A0, 0x05A1,
11
+ 0x05A8, 0x05A9, 0x05AB, 0x05AC, 0x05AF, 0x05C4, 0x0610, 0x0611,
12
+ 0x0612, 0x0613, 0x0614, 0x0615, 0x0616, 0x0617, 0x0657, 0x0658,
13
+ 0x0659, 0x065A, 0x065B, 0x065D, 0x065E, 0x06D6, 0x06D7, 0x06D8,
14
+ 0x06D9, 0x06DA, 0x06DB, 0x06DC, 0x06DF, 0x06E0, 0x06E1, 0x06E2,
15
+ 0x06E4, 0x06E7, 0x06E8, 0x06EB, 0x06EC, 0x0730, 0x0732, 0x0733,
16
+ 0x0735, 0x0736, 0x073A, 0x073D, 0x073F, 0x0740, 0x0741, 0x0743,
17
+ 0x0745, 0x0747, 0x0749, 0x074A, 0x07EB, 0x07EC, 0x07ED, 0x07EE,
18
+ 0x07EF, 0x07F0, 0x07F1, 0x07F3, 0x0816, 0x0817, 0x0818, 0x0819,
19
+ 0x081B, 0x081C, 0x081D, 0x081E, 0x081F, 0x0820, 0x0821, 0x0822,
20
+ 0x0823, 0x0825, 0x0826, 0x0827, 0x0829, 0x082A, 0x082B, 0x082C,
21
+ 0x082D, 0x0951, 0x0953, 0x0954, 0x0F82, 0x0F83, 0x0F86, 0x0F87,
22
+ 0x135D, 0x135E, 0x135F, 0x17DD, 0x193A, 0x1A17, 0x1A75, 0x1A76,
23
+ 0x1A77, 0x1A78, 0x1A79, 0x1A7A, 0x1A7B, 0x1A7C, 0x1B6B, 0x1B6D,
24
+ 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73, 0x1CD0, 0x1CD1,
25
+ 0x1CD2, 0x1CDA, 0x1CDB, 0x1CE0, 0x1DC0, 0x1DC1, 0x1DC3, 0x1DC4,
26
+ 0x1DC5, 0x1DC6, 0x1DC7, 0x1DC8, 0x1DC9, 0x1DCB, 0x1DCC, 0x1DD1,
27
+ 0x1DD2, 0x1DD3, 0x1DD4, 0x1DD5, 0x1DD6, 0x1DD7, 0x1DD8, 0x1DD9,
28
+ 0x1DDA, 0x1DDB, 0x1DDC, 0x1DDD, 0x1DDE, 0x1DDF, 0x1DE0, 0x1DE1,
29
+ 0x1DE2, 0x1DE3, 0x1DE4, 0x1DE5, 0x1DE6, 0x1DFE, 0x20D0, 0x20D1,
30
+ 0x20D4, 0x20D5, 0x20D6, 0x20D7, 0x20DB, 0x20DC, 0x20E1, 0x20E7,
31
+ 0x20E9, 0x20F0, 0x2CEF, 0x2CF0, 0x2CF1, 0x2DE0, 0x2DE1, 0x2DE2,
32
+ 0x2DE3, 0x2DE4, 0x2DE5, 0x2DE6, 0x2DE7, 0x2DE8, 0x2DE9, 0x2DEA,
33
+ 0x2DEB, 0x2DEC, 0x2DED, 0x2DEE, 0x2DEF, 0x2DF0, 0x2DF1, 0x2DF2,
34
+ 0x2DF3, 0x2DF4, 0x2DF5, 0x2DF6, 0x2DF7, 0x2DF8, 0x2DF9, 0x2DFA,
35
+ 0x2DFB, 0x2DFC, 0x2DFD, 0x2DFE, 0x2DFF, 0xA66F, 0xA67C, 0xA67D,
36
+ 0xA6F0, 0xA6F1, 0xA8E0, 0xA8E1, 0xA8E2, 0xA8E3, 0xA8E4, 0xA8E5,
37
+ 0xA8E6, 0xA8E7, 0xA8E8, 0xA8E9, 0xA8EA, 0xA8EB, 0xA8EC, 0xA8ED,
38
+ 0xA8EE, 0xA8EF, 0xA8F0, 0xA8F1, 0xAAB0, 0xAAB2, 0xAAB3, 0xAAB7,
39
+ 0xAAB8, 0xAABE, 0xAABF, 0xAAC1, 0xFE20, 0xFE21, 0xFE22, 0xFE23,
40
+ 0xFE24, 0xFE25, 0xFE26, 0x10A0F, 0x10A38, 0x1D185, 0x1D186, 0x1D187,
41
+ 0x1D188, 0x1D189, 0x1D1AA, 0x1D1AB, 0x1D1AC, 0x1D1AD, 0x1D242, 0x1D243,
42
+ 0x1D244,
43
+ )