textual-code 0.0.2__tar.gz → 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. textual_code-0.1.1/.devcontainer/Dockerfile +85 -0
  2. textual_code-0.1.1/.devcontainer/devcontainer.json +26 -0
  3. textual_code-0.1.1/.devcontainer/docker-compose.yml +23 -0
  4. textual_code-0.1.1/.devcontainer/ensure-ai-symlinks.sh +51 -0
  5. textual_code-0.1.1/.devcontainer/qmd-config/index.yml +7 -0
  6. textual_code-0.1.1/.dockerignore +34 -0
  7. textual_code-0.1.1/.editorconfig +17 -0
  8. textual_code-0.1.1/.github/workflows/release.yml +49 -0
  9. textual_code-0.1.1/.gitignore +29 -0
  10. {textual_code-0.0.2 → textual_code-0.1.1}/.pre-commit-config.yaml +2 -0
  11. textual_code-0.1.1/.textual-code.toml +6 -0
  12. textual_code-0.1.1/CHANGELOG.md +47 -0
  13. textual_code-0.1.1/CLAUDE.md +56 -0
  14. textual_code-0.1.1/PKG-INFO +117 -0
  15. textual_code-0.1.1/README.md +92 -0
  16. textual_code-0.1.1/docs/features/config.md +391 -0
  17. textual_code-0.1.1/docs/features/editor.md +287 -0
  18. textual_code-0.1.1/docs/features/index.md +33 -0
  19. textual_code-0.1.1/docs/features/internals.md +124 -0
  20. textual_code-0.1.1/docs/features/ui.md +249 -0
  21. textual_code-0.1.1/docs/features/workspace.md +341 -0
  22. textual_code-0.1.1/docs/features.md +5 -0
  23. textual_code-0.1.1/docs/settings-guide.md +116 -0
  24. textual_code-0.1.1/docs/testing-guide.md +212 -0
  25. {textual_code-0.0.2 → textual_code-0.1.1}/pyproject.toml +21 -1
  26. textual_code-0.1.1/scripts/build-devcontainer.sh +89 -0
  27. textual_code-0.1.1/scripts/check-language.sh +134 -0
  28. textual_code-0.1.1/scripts/check-licenses.sh +127 -0
  29. textual_code-0.1.1/scripts/download-libs-docs.sh +156 -0
  30. textual_code-0.1.1/scripts/kill-devconatiner.sh +73 -0
  31. textual_code-0.1.1/scripts/run-devcontainer.sh +271 -0
  32. textual_code-0.1.1/src/textual_code/__init__.py +82 -0
  33. textual_code-0.1.1/src/textual_code/app.py +1557 -0
  34. textual_code-0.1.1/src/textual_code/commands.py +160 -0
  35. textual_code-0.1.1/src/textual_code/config.py +151 -0
  36. textual_code-0.1.1/src/textual_code/grammars/c.scm +85 -0
  37. textual_code-0.1.1/src/textual_code/grammars/cpp.scm +81 -0
  38. textual_code-0.1.1/src/textual_code/grammars/dockerfile.scm +60 -0
  39. textual_code-0.1.1/src/textual_code/grammars/kotlin.scm +203 -0
  40. textual_code-0.1.1/src/textual_code/grammars/lua.scm +105 -0
  41. textual_code-0.1.1/src/textual_code/grammars/make.scm +109 -0
  42. textual_code-0.1.1/src/textual_code/grammars/php.scm +128 -0
  43. textual_code-0.1.1/src/textual_code/grammars/ruby.scm +158 -0
  44. textual_code-0.1.1/src/textual_code/grammars/tsx.scm +39 -0
  45. textual_code-0.1.1/src/textual_code/grammars/typescript.scm +39 -0
  46. textual_code-0.1.1/src/textual_code/modals.py +1178 -0
  47. textual_code-0.1.1/src/textual_code/search.py +225 -0
  48. textual_code-0.1.1/src/textual_code/style.tcss +901 -0
  49. textual_code-0.1.1/src/textual_code/utils.py +15 -0
  50. textual_code-0.1.1/src/textual_code/widgets/code_editor.py +1925 -0
  51. textual_code-0.1.1/src/textual_code/widgets/draggable_tabs_content.py +597 -0
  52. textual_code-0.1.1/src/textual_code/widgets/explorer.py +684 -0
  53. textual_code-0.1.1/src/textual_code/widgets/find_replace_bar.py +163 -0
  54. textual_code-0.1.1/src/textual_code/widgets/main_view.py +1353 -0
  55. textual_code-0.1.1/src/textual_code/widgets/markdown_preview.py +77 -0
  56. textual_code-0.1.1/src/textual_code/widgets/multi_cursor_text_area.py +965 -0
  57. textual_code-0.1.1/src/textual_code/widgets/sidebar.py +140 -0
  58. textual_code-0.1.1/src/textual_code/widgets/split_container.py +45 -0
  59. textual_code-0.1.1/src/textual_code/widgets/split_resize_handle.py +107 -0
  60. textual_code-0.1.1/src/textual_code/widgets/split_tree.py +252 -0
  61. textual_code-0.1.1/src/textual_code/widgets/workspace_search.py +239 -0
  62. textual_code-0.1.1/tests/__init__.py +0 -0
  63. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_app_with_file.svg +229 -0
  64. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_change_language_modal.svg +239 -0
  65. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_change_syntax_theme_modal.svg +236 -0
  66. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_change_ui_theme_modal.svg +236 -0
  67. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_change_word_wrap_modal.svg +236 -0
  68. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_delete_modal.svg +237 -0
  69. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_discard_and_reload_modal.svg +236 -0
  70. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_dockerfile_highlighting.svg +229 -0
  71. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_drop_target_edge_highlight.svg +234 -0
  72. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_drop_target_highlight.svg +234 -0
  73. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_empty_app.svg +223 -0
  74. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_encoding_modal_with_save_level.svg +236 -0
  75. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_explorer_dim_hidden_files.svg +224 -0
  76. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_explorer_git_status.svg +226 -0
  77. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_explorer_hidden_files_visible.svg +224 -0
  78. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_find_bar_open.svg +235 -0
  79. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_footer_encoding_modal_no_save_level.svg +239 -0
  80. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_footer_indent_modal_no_save_level.svg +240 -0
  81. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_footer_line_ending_modal_no_save_level.svg +239 -0
  82. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_footer_path_truncation.svg +226 -0
  83. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_goto_line_modal.svg +239 -0
  84. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_indent_modal_with_save_level.svg +234 -0
  85. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_line_ending_modal_with_save_level.svg +236 -0
  86. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_markdown_preview_open.svg +224 -0
  87. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_multi_cursor.svg +229 -0
  88. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_multiple_tabs.svg +230 -0
  89. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_narrow_sidebar_icon_only.svg +233 -0
  90. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_new_editor_tab.svg +225 -0
  91. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_overwrite_confirm_modal.svg +235 -0
  92. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_readme_preview.svg +235 -0
  93. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_rebind_key_screen.svg +232 -0
  94. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_replace_bar_open.svg +236 -0
  95. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_save_as_modal.svg +239 -0
  96. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_show_shortcuts_screen.svg +230 -0
  97. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_sidebar_custom_width.svg +223 -0
  98. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_sidebar_resize_modal.svg +234 -0
  99. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_sidebar_search_tab.svg +233 -0
  100. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_split_left_view_open.svg +230 -0
  101. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_split_resize_modal.svg +240 -0
  102. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_split_view_open.svg +230 -0
  103. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_tab_dragging_highlight.svg +231 -0
  104. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_tab_reorder_active_indicator.svg +230 -0
  105. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_tab_reorder_right_indicator.svg +229 -0
  106. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_unsaved_change_modal.svg +239 -0
  107. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_unsaved_marker.svg +229 -0
  108. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_unsaved_quit_modal.svg +236 -0
  109. textual_code-0.1.1/tests/__snapshots__/test_snapshots/test_snapshot_workspace_search_results.svg +239 -0
  110. textual_code-0.1.1/tests/__snapshots__/test_vertical_split/test_vertical_split_snapshot.svg +230 -0
  111. textual_code-0.1.1/tests/conftest.py +179 -0
  112. textual_code-0.1.1/tests/test_app.py +373 -0
  113. textual_code-0.1.1/tests/test_binary_file.py +116 -0
  114. textual_code-0.1.1/tests/test_cli.py +47 -0
  115. textual_code-0.1.1/tests/test_click_selection.py +253 -0
  116. textual_code-0.1.1/tests/test_clipboard.py +135 -0
  117. textual_code-0.1.1/tests/test_code_editor.py +796 -0
  118. textual_code-0.1.1/tests/test_command_shortcuts.py +173 -0
  119. textual_code-0.1.1/tests/test_copy_path.py +183 -0
  120. textual_code-0.1.1/tests/test_cross_split_drag.py +1122 -0
  121. textual_code-0.1.1/tests/test_cursor_btn.py +165 -0
  122. textual_code-0.1.1/tests/test_delete_with_palette.py +164 -0
  123. textual_code-0.1.1/tests/test_dim_gitignored.py +333 -0
  124. textual_code-0.1.1/tests/test_dim_hidden_files.py +155 -0
  125. textual_code-0.1.1/tests/test_directional_leaf.py +253 -0
  126. textual_code-0.1.1/tests/test_draggable_tabs_content.py +546 -0
  127. textual_code-0.1.1/tests/test_editor_defaults.py +311 -0
  128. textual_code-0.1.1/tests/test_editorconfig.py +953 -0
  129. textual_code-0.1.1/tests/test_encoding.py +563 -0
  130. textual_code-0.1.1/tests/test_explorer_auto_refresh.py +430 -0
  131. textual_code-0.1.1/tests/test_explorer_delete.py +187 -0
  132. textual_code-0.1.1/tests/test_explorer_highlight.py +196 -0
  133. textual_code-0.1.1/tests/test_file_watcher.py +623 -0
  134. textual_code-0.1.1/tests/test_find.py +708 -0
  135. textual_code-0.1.1/tests/test_find_replace_bar.py +397 -0
  136. textual_code-0.1.1/tests/test_git_status.py +340 -0
  137. textual_code-0.1.1/tests/test_global_footer.py +292 -0
  138. textual_code-0.1.1/tests/test_hidden_files.py +180 -0
  139. textual_code-0.1.1/tests/test_indent.py +741 -0
  140. textual_code-0.1.1/tests/test_language_extensions.py +117 -0
  141. textual_code-0.1.1/tests/test_light_app.py +60 -0
  142. textual_code-0.1.1/tests/test_line_ending.py +505 -0
  143. textual_code-0.1.1/tests/test_main_view.py +877 -0
  144. textual_code-0.1.1/tests/test_markdown_preview.py +512 -0
  145. textual_code-0.1.1/tests/test_modals.py +843 -0
  146. textual_code-0.1.1/tests/test_move_line.py +374 -0
  147. textual_code-0.1.1/tests/test_move_tab_directional.py +269 -0
  148. textual_code-0.1.1/tests/test_multi_cursor.py +1318 -0
  149. textual_code-0.1.1/tests/test_no_italic_extension.py +132 -0
  150. textual_code-0.1.1/tests/test_open_file_command.py +68 -0
  151. textual_code-0.1.1/tests/test_open_settings.py +89 -0
  152. textual_code-0.1.1/tests/test_panel_focus.py +79 -0
  153. textual_code-0.1.1/tests/test_path_display_mode.py +169 -0
  154. textual_code-0.1.1/tests/test_recursive_split.py +463 -0
  155. textual_code-0.1.1/tests/test_redo.py +59 -0
  156. textual_code-0.1.1/tests/test_regex_search.py +447 -0
  157. textual_code-0.1.1/tests/test_reorder_tab.py +251 -0
  158. textual_code-0.1.1/tests/test_replace.py +675 -0
  159. textual_code-0.1.1/tests/test_responsive_labels.py +127 -0
  160. textual_code-0.1.1/tests/test_select_all_from_bar.py +289 -0
  161. textual_code-0.1.1/tests/test_select_all_occurrences.py +386 -0
  162. textual_code-0.1.1/tests/test_select_next_occurrence.py +349 -0
  163. textual_code-0.1.1/tests/test_shortcuts_panel.py +273 -0
  164. textual_code-0.1.1/tests/test_sidebar_drag_resize.py +151 -0
  165. textual_code-0.1.1/tests/test_sidebar_resize.py +273 -0
  166. textual_code-0.1.1/tests/test_sidebar_width.py +220 -0
  167. textual_code-0.1.1/tests/test_snapshots.py +942 -0
  168. textual_code-0.1.1/tests/test_split_container.py +131 -0
  169. textual_code-0.1.1/tests/test_split_drag_resize.py +172 -0
  170. textual_code-0.1.1/tests/test_split_resize.py +305 -0
  171. textual_code-0.1.1/tests/test_split_tree.py +402 -0
  172. textual_code-0.1.1/tests/test_split_view.py +1075 -0
  173. textual_code-0.1.1/tests/test_syntax_theme.py +287 -0
  174. textual_code-0.1.1/tests/test_tab_drag.py +288 -0
  175. textual_code-0.1.1/tests/test_ui_theme.py +234 -0
  176. textual_code-0.1.1/tests/test_vertical_split.py +118 -0
  177. textual_code-0.1.1/tests/test_word_wrap.py +180 -0
  178. textual_code-0.1.1/tests/test_workspace_replace.py +228 -0
  179. textual_code-0.1.1/tests/test_workspace_search.py +672 -0
  180. textual_code-0.1.1/uv.lock +1547 -0
  181. textual_code-0.0.2/.devcontainer.json +0 -19
  182. textual_code-0.0.2/.dockerignore +0 -2
  183. textual_code-0.0.2/.github/workflows/release.yml +0 -21
  184. textual_code-0.0.2/.gitignore +0 -2
  185. textual_code-0.0.2/CHANGELOG.md +0 -17
  186. textual_code-0.0.2/Dockerfile.dev +0 -21
  187. textual_code-0.0.2/PKG-INFO +0 -175
  188. textual_code-0.0.2/README.md +0 -154
  189. textual_code-0.0.2/docker-compose.dev.yml +0 -9
  190. textual_code-0.0.2/docs/preview.svg +0 -285
  191. textual_code-0.0.2/src/textual_code/__init__.py +0 -10
  192. textual_code-0.0.2/src/textual_code/app.py +0 -719
  193. textual_code-0.0.2/src/textual_code/modals.py +0 -123
  194. textual_code-0.0.2/src/textual_code/style.tcss +0 -160
  195. textual_code-0.0.2/src/textual_code/utils.py +0 -21
  196. textual_code-0.0.2/uv.lock +0 -938
  197. {textual_code-0.0.2 → textual_code-0.1.1}/.python-version +0 -0
  198. {textual_code-0.0.2 → textual_code-0.1.1}/.vscode/settings.json +0 -0
  199. {textual_code-0.0.2 → textual_code-0.1.1}/Dockerfile +0 -0
  200. {textual_code-0.0.2 → textual_code-0.1.1}/docker-compose.yml +0 -0
@@ -0,0 +1,85 @@
1
+ FROM ubuntu:24.04 AS base
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+
5
+ # install basic packages
6
+ RUN apt-get update && apt-get upgrade -y && apt-get install -y --fix-broken && apt-get install -y --no-install-recommends \
7
+ build-essential \
8
+ ca-certificates \
9
+ curl \
10
+ git \
11
+ locales \
12
+ ssh \
13
+ sudo \
14
+ vim \
15
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
16
+
17
+ RUN locale-gen en_US.UTF-8
18
+
19
+ ARG USER_UID=1000
20
+ ARG USER_GID=1000
21
+
22
+ RUN (groupadd --gid $USER_GID appuser || true) \
23
+ && (useradd --uid $USER_UID --gid $USER_GID -m appuser -s /bin/bash || true) \
24
+ && export USERNAME=$(getent passwd $USER_UID | cut -d: -f1) \
25
+ && echo "$USERNAME ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/$USERNAME \
26
+ && chmod 0440 /etc/sudoers.d/$USERNAME
27
+
28
+ RUN mkdir -p /project \
29
+ && chown $USER_UID:$USER_GID /project
30
+
31
+ # set LANG env variable
32
+ USER $USER_UID
33
+ RUN echo 'export LANG="en_US.UTF-8"' >> ~/.bashrc \
34
+ && echo 'export LC_ALL="en_US.UTF-8"' >> ~/.bashrc \
35
+ && echo 'export NODE_LLAMA_CPP_CMAKE_OPTION_GGML_CUDA=OFF' >> ~/.bashrc
36
+
37
+ FROM base AS tools
38
+
39
+ # install lazygit
40
+ USER root
41
+ RUN export LAZYGIT_VERSION=$(curl -s "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | \grep -Po '"tag_name": *"v\K[^"]*') \
42
+ && export ARCH=$(dpkg --print-architecture) \
43
+ && if [ "$ARCH" = "amd64" ]; then export LAZYGIT_ARCH="x86_64"; elif [ "$ARCH" = "arm64" ]; then export LAZYGIT_ARCH="arm64"; else export LAZYGIT_ARCH="$ARCH"; fi \
44
+ && curl -Lo lazygit.tar.gz "https://github.com/jesseduffield/lazygit/releases/download/v${LAZYGIT_VERSION}/lazygit_${LAZYGIT_VERSION}_Linux_${LAZYGIT_ARCH}.tar.gz" \
45
+ && tar xf lazygit.tar.gz lazygit \
46
+ && install lazygit -D -t /usr/local/bin/ \
47
+ && rm -rf lazygit.tar.gz lazygit
48
+
49
+ FROM tools AS dev
50
+
51
+ ### PROJECT-SPECIFIC SETUP BEGIN
52
+
53
+ # install dependencies
54
+ USER root
55
+ # shellcheck is for linting shell scripts
56
+ RUN apt-get update && apt-get install -y --no-install-recommends \
57
+ shellcheck \
58
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
59
+
60
+ # install uv
61
+ USER $USER_UID
62
+ COPY --from=ghcr.io/astral-sh/uv:0.10.2 /uv /uvx /bin/
63
+ ENV UV_COMPILE_BYTECODE=1
64
+ ENV UV_LINK_MODE=copy
65
+ ENV UV_CACHE_DIR=/project/.devcontainer/uv/cache
66
+ ENV UV_PYTHON_INSTALL_DIR=/project/.devcontainer/uv/python
67
+ ENV UV_PYTHON_BIN_DIR=/project/.devcontainer/uv/python-bin
68
+
69
+ # install python using uv
70
+ USER $USER_UID
71
+ RUN uv python install 3.12 --default \
72
+ && echo 'export PATH="/project/.devcontainer/uv/python-bin:$PATH"' >> ~/.bashrc \
73
+ && export PATH="/project/.devcontainer/uv/python-bin:$PATH"
74
+
75
+ # set TERM and COLORTERM env variable for correct color rendering in textual
76
+ USER $USER_UID
77
+ RUN echo 'export TERM="xterm-256color"' >> ~/.bashrc \
78
+ && echo 'export COLORTERM="truecolor"' >> ~/.bashrc \
79
+ && export TERM="xterm-256color" \
80
+ && export COLORTERM="truecolor"
81
+
82
+ ### PROJECT-SPECIFIC SETUP END
83
+
84
+ USER $USER_UID
85
+ WORKDIR /project
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "textual-code-devcontainer",
3
+ "dockerComposeFile": "docker-compose.yml",
4
+ "service": "textual-code-devcontainer-app",
5
+ "workspaceFolder": "/project",
6
+ "remoteUser": "1000", // uid of the user in the container, typically a non-root user
7
+ "postCreateCommand": "/project/.devcontainer/ensure-ai-symlinks.sh",
8
+ "customizations": {
9
+ "vscode": {
10
+ "settings": {
11
+ "window.autoDetectColorScheme": true,
12
+ "workbench.preferredDarkColorTheme": "One Dark Pro",
13
+ "workbench.preferredLightColorTheme": "Visual Studio Light"
14
+ },
15
+ "extensions": [
16
+ "Anthropic.claude-code",
17
+ "astral-sh.ty",
18
+ "charliermarsh.ruff",
19
+ "GitHub.copilot",
20
+ "mhutchie.git-graph",
21
+ "ms-python.python",
22
+ "Textualize.textual-syntax-highlighter"
23
+ ]
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,23 @@
1
+ name: textual-code-devcontainer
2
+
3
+ services:
4
+ textual-code-devcontainer-app:
5
+ build:
6
+ context: .
7
+ dockerfile: Dockerfile
8
+ restart: "no"
9
+ command: sleep infinity
10
+ init: true
11
+ volumes:
12
+ - ../:/project
13
+ environment:
14
+ - CLAUDE_CONFIG_DIR=/home/appuser/.claude
15
+ env_file:
16
+ - path: ../.env
17
+ required: false
18
+ logging:
19
+ driver: "json-file"
20
+ options:
21
+ max-size: "10m"
22
+ max-file: "100"
23
+ compress: "true"
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Ensure AI tool directories and files exist in devcontainer
4
+ # This script is called during devcontainer post-create phase
5
+
6
+ set -eu
7
+
8
+ # Define base paths
9
+ DEVCONTAINER_DIR="/project/.devcontainer"
10
+ HOME_DIR="/home/$(whoami)"
11
+
12
+ # Create directories if they don't exist
13
+ mkdir -p "${DEVCONTAINER_DIR}/.claude"
14
+ mkdir -p "${DEVCONTAINER_DIR}/qmd-config"
15
+ mkdir -p "${DEVCONTAINER_DIR}/qmd-cache"
16
+
17
+ # Create files if they don't exist
18
+ if [ ! -f "${DEVCONTAINER_DIR}/.claude.json" ]; then
19
+ echo "{}" > "${DEVCONTAINER_DIR}/.claude.json"
20
+ fi
21
+ if [ ! -f "${DEVCONTAINER_DIR}/.claude.json.backup" ]; then
22
+ echo "{}" > "${DEVCONTAINER_DIR}/.claude.json.backup"
23
+ fi
24
+
25
+ # Ensure proper ownership
26
+ chown -R $(whoami):$(whoami) "${DEVCONTAINER_DIR}/.claude"
27
+ chown -R $(whoami):$(whoami) "${DEVCONTAINER_DIR}/qmd-config"
28
+ chown -R $(whoami):$(whoami) "${DEVCONTAINER_DIR}/qmd-cache"
29
+ chown $(whoami):$(whoami) "${DEVCONTAINER_DIR}/.claude.json"
30
+ chown $(whoami):$(whoami) "${DEVCONTAINER_DIR}/.claude.json.backup"
31
+
32
+ # Create symlinks if they don't exist
33
+ if [ ! -L "${HOME_DIR}/.claude" ]; then
34
+ ln -sf "${DEVCONTAINER_DIR}/.claude" "${HOME_DIR}/"
35
+ fi
36
+
37
+ if [ ! -L "${HOME_DIR}/.config/qmd" ]; then
38
+ ln -sf "${DEVCONTAINER_DIR}/qmd-config" "${HOME_DIR}/.config/qmd"
39
+ fi
40
+
41
+ if [ ! -L "${HOME_DIR}/.cache/qmd" ]; then
42
+ ln -sf "${DEVCONTAINER_DIR}/qmd-cache" "${HOME_DIR}/.cache/qmd"
43
+ fi
44
+
45
+ if [ ! -L "${HOME_DIR}/.claude.json" ]; then
46
+ ln -sf "${DEVCONTAINER_DIR}/.claude.json" "${HOME_DIR}/.claude.json"
47
+ fi
48
+
49
+ if [ ! -L "${HOME_DIR}/.claude.json.backup" ]; then
50
+ ln -sf "${DEVCONTAINER_DIR}/.claude.json.backup" "${HOME_DIR}/.claude.json.backup"
51
+ fi
@@ -0,0 +1,7 @@
1
+ collections:
2
+ docs:
3
+ path: /project/docs
4
+ pattern: "**/*.md"
5
+ root:
6
+ path: /project
7
+ pattern: "*.md"
@@ -0,0 +1,34 @@
1
+ # devcontainer
2
+ !.devcontainer/.claude
3
+ .devcontainer/.claude/*
4
+ .devcontainer/.claude.json
5
+ .devcontainer/.claude.json.backup
6
+ .devcontainer/qmd-cache/
7
+ .devcontainer/data
8
+ .devcontainer/uv
9
+ .devcontainer/local
10
+
11
+ # Python
12
+ __pycache__/
13
+ *.py[cod]
14
+ *.egg-info/
15
+ .venv/
16
+
17
+ # claude
18
+ .claude/settings.local.json
19
+ CLAUDE.local.md
20
+
21
+ # Test artifacts
22
+ snapshot_report.html
23
+
24
+ # Environment
25
+ .env
26
+ .env.local
27
+
28
+ # Downloaded library docs
29
+ docs/libs/
30
+
31
+ # --- gitignore end ---
32
+
33
+ dist/
34
+ .devcontainer/qmd-config/
@@ -0,0 +1,17 @@
1
+ root = true
2
+
3
+ [*]
4
+ # end_of_line = lf
5
+
6
+ [*.py]
7
+ indent_style = space
8
+ indent_size = 4
9
+ insert_final_newline = true
10
+ charset = utf-8
11
+
12
+ [*.{yml,yaml}]
13
+ indent_style = space
14
+ indent_size = 2
15
+ trim_trailing_whitespace = true
16
+ insert_final_newline = true
17
+ charset = utf-8
@@ -0,0 +1,49 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ # Publish on any tag starting with a `v`, e.g. v1.2.3
7
+ - v*
8
+
9
+ jobs:
10
+ pypi:
11
+ name: Publish to PyPI
12
+ runs-on: ubuntu-latest
13
+ environment:
14
+ name: release
15
+ permissions:
16
+ id-token: write
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: astral-sh/setup-uv@v3
20
+ - run: uv build
21
+ - run: uv publish --trusted-publishing always
22
+
23
+ github-release:
24
+ name: Create GitHub Release
25
+ runs-on: ubuntu-latest
26
+ permissions:
27
+ contents: write
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - name: Extract changelog for this version
31
+ id: changelog
32
+ run: |
33
+ version="${GITHUB_REF_NAME#v}"
34
+ # Extract the section for this version from CHANGELOG.md
35
+ body=$(awk -v ver="$version" '
36
+ /^## \[/ {
37
+ if (found) exit
38
+ if (index($0, "[" ver "]")) found=1
39
+ next
40
+ }
41
+ found { print }
42
+ ' CHANGELOG.md)
43
+ # Write to file to preserve newlines
44
+ echo "$body" > release_body.md
45
+ - name: Create GitHub Release
46
+ uses: softprops/action-gh-release@v2
47
+ with:
48
+ body_path: release_body.md
49
+ generate_release_notes: false
@@ -0,0 +1,29 @@
1
+ # devcontainer
2
+ !.devcontainer/.claude
3
+ .devcontainer/.claude/*
4
+ .devcontainer/.claude.json
5
+ .devcontainer/.claude.json.backup
6
+ .devcontainer/qmd-cache/
7
+ .devcontainer/data
8
+ .devcontainer/uv
9
+ .devcontainer/local
10
+
11
+ # Python
12
+ __pycache__/
13
+ *.py[cod]
14
+ *.egg-info/
15
+ .venv/
16
+
17
+ # claude
18
+ .claude/
19
+ CLAUDE.local.md
20
+
21
+ # Test artifacts
22
+ snapshot_report.html
23
+
24
+ # Environment
25
+ .env
26
+ .env.local
27
+
28
+ # Downloaded library docs
29
+ docs/libs/
@@ -6,8 +6,10 @@ repos:
6
6
  entry: ruff check --force-exclude --fix
7
7
  language: python
8
8
  types_or: [python, pyi, jupyter]
9
+ additional_dependencies: [ruff]
9
10
  - id: ruff-format
10
11
  name: ruff format
11
12
  entry: ruff format --force-exclude
12
13
  language: python
13
14
  types_or: [python, pyi, jupyter]
15
+ additional_dependencies: [ruff]
@@ -0,0 +1,6 @@
1
+ [editor]
2
+ indent_type = "spaces"
3
+ indent_size = 4
4
+ line_ending = "lf"
5
+ encoding = "utf-8"
6
+ word_wrap = true
@@ -0,0 +1,47 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.1] - 2026-03-18
11
+
12
+ ### Fixed
13
+
14
+ - Wheel build only included `.scm` grammar files, excluding all Python source — caused `ModuleNotFoundError` when installed via `pip install` or `uv tool install`
15
+
16
+ ## [0.1.0] - 2026-03-18
17
+
18
+ ### Added
19
+
20
+ - **Editor**: multiple cursors with Ctrl+Alt+Up/Down, add next occurrence (Ctrl+D), select all occurrences (Ctrl+Shift+L); multi-cursor movement, selection, typing, and editing all work simultaneously; move line up/down (Alt+Up/Down), scroll viewport (Ctrl+Up/Down), select all (Ctrl+A), double/triple click word/line selection, Ctrl+C/X copies/cuts current line when no selection, Tab/Shift+Tab indent/dedent, Ctrl+Shift+Z redo
21
+ - **Find & Replace**: inline find/replace bar (Ctrl+F/Ctrl+H) with regex, case-sensitivity, and Select All matches for multi-cursor editing; workspace-wide search (Ctrl+Shift+F) and Replace All with gitignore, file include/exclude filters, case-sensitivity toggle, and background search with loading indicator
22
+ - **Split view**: unlimited nested horizontal/vertical splits via recursive tree structure; split in any direction (Ctrl+\\, command palette); close split (Ctrl+Shift+\\); drag-and-drop tabs between splits with 4-direction edge zones and visual drop hints; drag resize handles; toggle split orientation; directional tab move commands; move tab to other split (Ctrl+Alt+\\); live text sync between split editors viewing the same file; focus cycling with F6/Shift+F6
23
+ - **Explorer**: create file/directory (Ctrl+N/Ctrl+D in sidebar), delete file/folder (Delete key); toggle hidden files, dim gitignored files, dim hidden files, git status highlighting (modified in yellow, untracked in green with folder inheritance); auto-refresh on workspace file changes and git status changes; cursor sync with active editor tab; responsive emoji icons that collapse at narrow widths
24
+ - **File handling**: new file (Ctrl+N), Save As, reload file from disk; expanded encoding auto-detection (40+ encodings including CJK via charset-normalizer); binary file detection; EditorConfig support with auto-reload on `.editorconfig` changes; `trim_trailing_whitespace` and `insert_final_newline` applied at save time; configurable line endings; free-form indentation size input; external file change detection with auto-reload or overwrite confirmation
25
+ - **Tabs & navigation**: command palette (Ctrl+Shift+P) for quick access to all commands; tab reorder by drag, cross-split tab drag, Goto Line (Ctrl+G), copy relative/absolute path, close all (Ctrl+Shift+W), save all (Ctrl+Shift+S), toggle sidebar (Ctrl+B), sidebar drag resize, resize sidebar/split via command palette
26
+ - **Markdown**: live preview in tab (Ctrl+Shift+M) with debounced auto-updates, GFM support, keyboard scrolling; preview closes with source editor
27
+ - **Themes**: UI theme selection from 20 built-in themes (nord, gruvbox, dracula, etc.); syntax highlighting theme selection (monokai, dracula, github_light, vscode_dark, css)
28
+ - **Keyboard shortcuts**: view and customize all key bindings (F1), shortcut hints in command palette, custom bindings saved to keybindings.toml
29
+ - **Configuration**: user-level and project-level settings persistence (TOML) with project taking priority; open settings commands; save-level selector (User/Project) in theme and default settings dialogs; `sidebar_width`, `path_display_mode`, `warn_line_ending`, word wrap toggle and default settings; `--workspace` / `-w` CLI option for overriding sidebar root directory
30
+ - **Language support**: syntax highlighting for 10 additional languages (TypeScript, TSX, C, C++, Ruby, Kotlin, Lua, PHP, Dockerfile, Makefile) via tree-sitter-language-pack; extended file type detection for dotfiles and additional extensions
31
+ - **Footer**: clickable file path copies to clipboard; path display mode toggle (absolute/relative); cursor position with multi-cursor count; clickable language, encoding, line ending, and indentation indicators; dynamic column sizing
32
+
33
+ ### Changed
34
+
35
+ - Default word wrap changed to enabled
36
+ - Footer path truncation indicator visually distinct from actual path characters
37
+
38
+ ## [0.0.2] - 2025-01-07
39
+
40
+ ### Added
41
+
42
+ - Add basic text editing features
43
+
44
+ [unreleased]: https://github.com/rishubil/textual-code/compare/v0.1.1...HEAD
45
+ [0.1.1]: https://github.com/rishubil/textual-code/compare/v0.1.0...v0.1.1
46
+ [0.1.0]: https://github.com/rishubil/textual-code/compare/v0.0.2...v0.1.0
47
+ [0.0.2]: https://github.com/rishubil/textual-code/releases/tag/v0.0.2
@@ -0,0 +1,56 @@
1
+ # Agent Instructions
2
+
3
+ **CRITICAL RULES:**
4
+ - Put the truth and the correct answer above all else. Feel free to criticize user's opinion, and do not use false empathy with the user. Keep a dry and realistic perspective.
5
+ - Use qmd to check documentation on every task to maintain consistency
6
+ - **Always run Python with `uv`**: never call `python` or `pip` directly; always use `uv run python`, `uv run pytest`, `uv run ruff`, etc.
7
+ - **Avoid `$()` in Bash tool calls**: `$()` command substitution triggers extra user confirmation in Claude Code. Split into separate Bash tool calls instead:
8
+ ```
9
+ # BAD — single Bash call with nested $() requires extra confirmation
10
+ uv run pytest tests/ -n $(( $(nproc) * 2 )) -m "not serial"
11
+
12
+ # GOOD — two separate Bash tool calls
13
+ Call 1: nproc → returns e.g. "4"
14
+ Call 2: uv run pytest tests/ -n 8 -m "not serial"
15
+ ```
16
+ Read the output of the first call and substitute the value directly into the second call.
17
+ - Use WebFetch proactively. Always check the latest development docs and search for anything unclear.
18
+ - All code comments, docstrings, and documentation (including files in `docs/`) must be written in **English**.
19
+
20
+ ## Test Strategy: Red-Green TDD
21
+
22
+ > See `docs/testing-guide.md` for test patterns, best practices, and gotchas (`make_app(light=True)`, `pilot.pause()` rules, snapshot conventions, etc.)
23
+
24
+ **Before starting work, check and run existing tests:**
25
+
26
+ 1. **Find test files**: use Glob/Grep to find test files related to the code you are modifying
27
+ 2. **Run existing tests**: run tests before starting work to understand the current state (pass/fail)
28
+ 3. **Establish a baseline**: know which tests pass before your changes so you can detect regressions
29
+
30
+ **Apply Red-Green TDD to modification tasks:**
31
+
32
+ - **Red**: first write or identify a test that verifies the behaviour to be changed (failing state)
33
+ - **Green**: implement the minimum code needed to make the test pass
34
+ - **Verify**: confirm that all pre-existing tests still pass
35
+
36
+ **When modifying code with no tests:**
37
+ - Add tests before modifying if possible
38
+ - If adding tests is out of scope, state it explicitly: "This change has no tests"
39
+
40
+ **When adding new UI (modal / widget / screen):**
41
+ - A snapshot test is **mandatory**. Add it to `tests/test_snapshots.py` before the implementation is merged.
42
+ - Use `_open_editor_modal` helper for editor-triggered modals; use `_open_app_modal` for app-level modals.
43
+ - Run `uv run pytest tests/test_snapshots.py --snapshot-update` after adding the test to generate the SVG.
44
+
45
+ ## Textual Official Documentation
46
+
47
+ When Textual framework behaviour (API, Widget, Screen, Worker, reactive, etc.) is uncertain, **always search the local docs before implementing**.
48
+
49
+ - **Local docs**: `docs/libs/textual/` (MkDocs format, mirrored from textual.textualize.io)
50
+ - Key topics: App · ModalScreen · push_screen · dismiss · Message · on_* · reactive · watch_* · Worker · @work · BINDINGS · Binding · action_* · CommandPalette · Provider · TCSS · TabbedContent · TabPane · DirectoryTree · TextArea
51
+
52
+ **No guessing**: if behaviour is unclear, search the local docs first.
53
+
54
+ ---
55
+
56
+ See @README.md for project overview
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.4
2
+ Name: textual-code
3
+ Version: 0.1.1
4
+ Summary: Code editor for who don't know how to use vi
5
+ Project-URL: Repository, https://github.com/rishubil/textual-code.git
6
+ Project-URL: Changelog, https://github.com/rishubil/textual-code/blob/master/CHANGELOG.md
7
+ Author-email: Nesswit <rishubil@gmail.com>
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 10
14
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 11
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Text Editors
18
+ Requires-Python: >=3.12
19
+ Requires-Dist: charset-normalizer>=3.0.0
20
+ Requires-Dist: pathspec>=1.0.4
21
+ Requires-Dist: textual[syntax]>=1.0.0
22
+ Requires-Dist: tree-sitter-language-pack>=0.13.0
23
+ Requires-Dist: typer>=0.15.1
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Textual Code
27
+
28
+ Code editor for who don't know how to use vi
29
+
30
+ ![Screenshot](tests/__snapshots__/test_snapshots/test_snapshot_readme_preview.svg)
31
+
32
+ > [!WARNING]
33
+ > This project is in the early stages of development.
34
+ > It is not ready for use yet.
35
+
36
+ ## What is Textual Code?
37
+
38
+ Textual Code is a TUI-based code editor that feels familiar right from the start.
39
+
40
+ You’ve probably had to SSH into a server at some point just to tweak a few lines of code.
41
+ However, vi or Emacs can be overkill for quick fixes, requiring you to remember a whole host of commands for even the simplest changes.
42
+ Furthermore, nano doesn’t always provide enough features for comfortable coding, and setting up a GUI editor on a remote server can be a real hassle.
43
+
44
+ That’s why Textual Code was created.
45
+ You likely use a GUI editor like VS Code or Sublime Text in your day-to-day work, and Textual Code offers a similar experience with no learning curve.
46
+ It behaves much like any other code editor you’re used to.
47
+
48
+ We’re not asking you to switch to Textual Code as your main editor.
49
+ Just remember it’s there when you need to jump onto a server and make a few quick edits.
50
+ It’s that simple.
51
+
52
+ ## Features
53
+
54
+ > [!WARNING]
55
+ > This project is in the early stages of development.
56
+ > the features listed below are not yet implemented or are only partially implemented.
57
+
58
+ - Commonly used shortcuts, such as `Ctrl+S` to save and `Ctrl+F` to search
59
+ - Command palette for quick access to all features, and no need to remember shortcuts
60
+ - Multiple cursors
61
+ - Mouse support
62
+ - Find and replace from workspace
63
+ - Explore files in the sidebar
64
+ - Open files to tabs
65
+ - Syntax highlighting
66
+
67
+ ## Installation
68
+
69
+ ```bash
70
+ pip install textual-code
71
+ ```
72
+
73
+ ## Usage
74
+
75
+ To open the textual code, run the following command in your workspace:
76
+
77
+ ```bash
78
+ textual-code
79
+ ```
80
+
81
+ ## Development
82
+
83
+ (You need to use devcontainer to run the code)
84
+
85
+ To run the development version directly:
86
+
87
+ ```bash
88
+ uv run textual-code
89
+ ```
90
+
91
+ To run with the Textual dev console (shows logs and events), open two terminals:
92
+
93
+ ```bash
94
+ # Terminal 1: start the console
95
+ uv run textual console
96
+
97
+ # Terminal 2: run the app in dev mode
98
+ uv run textual run --dev textual_code:main
99
+ ```
100
+
101
+ ### Running Tests
102
+
103
+ See [docs/testing-guide.md](docs/testing-guide.md) for patterns, best practices, and gotchas.
104
+
105
+ ```bash
106
+ # Unit/integration tests — parallel (~2.5 min)
107
+ uv run pytest tests/ -n $(( $(nproc) * 2 )) -m "not serial"
108
+
109
+ # Snapshot tests — must run serially
110
+ uv run pytest tests/ -m serial
111
+
112
+ # Update snapshots after UI changes
113
+ uv run pytest tests/test_snapshots.py --snapshot-update
114
+
115
+ # Single file
116
+ uv run pytest tests/test_code_editor.py
117
+ ```
@@ -0,0 +1,92 @@
1
+ # Textual Code
2
+
3
+ Code editor for who don't know how to use vi
4
+
5
+ ![Screenshot](tests/__snapshots__/test_snapshots/test_snapshot_readme_preview.svg)
6
+
7
+ > [!WARNING]
8
+ > This project is in the early stages of development.
9
+ > It is not ready for use yet.
10
+
11
+ ## What is Textual Code?
12
+
13
+ Textual Code is a TUI-based code editor that feels familiar right from the start.
14
+
15
+ You’ve probably had to SSH into a server at some point just to tweak a few lines of code.
16
+ However, vi or Emacs can be overkill for quick fixes, requiring you to remember a whole host of commands for even the simplest changes.
17
+ Furthermore, nano doesn’t always provide enough features for comfortable coding, and setting up a GUI editor on a remote server can be a real hassle.
18
+
19
+ That’s why Textual Code was created.
20
+ You likely use a GUI editor like VS Code or Sublime Text in your day-to-day work, and Textual Code offers a similar experience with no learning curve.
21
+ It behaves much like any other code editor you’re used to.
22
+
23
+ We’re not asking you to switch to Textual Code as your main editor.
24
+ Just remember it’s there when you need to jump onto a server and make a few quick edits.
25
+ It’s that simple.
26
+
27
+ ## Features
28
+
29
+ > [!WARNING]
30
+ > This project is in the early stages of development.
31
+ > the features listed below are not yet implemented or are only partially implemented.
32
+
33
+ - Commonly used shortcuts, such as `Ctrl+S` to save and `Ctrl+F` to search
34
+ - Command palette for quick access to all features, and no need to remember shortcuts
35
+ - Multiple cursors
36
+ - Mouse support
37
+ - Find and replace from workspace
38
+ - Explore files in the sidebar
39
+ - Open files to tabs
40
+ - Syntax highlighting
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install textual-code
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ To open the textual code, run the following command in your workspace:
51
+
52
+ ```bash
53
+ textual-code
54
+ ```
55
+
56
+ ## Development
57
+
58
+ (You need to use devcontainer to run the code)
59
+
60
+ To run the development version directly:
61
+
62
+ ```bash
63
+ uv run textual-code
64
+ ```
65
+
66
+ To run with the Textual dev console (shows logs and events), open two terminals:
67
+
68
+ ```bash
69
+ # Terminal 1: start the console
70
+ uv run textual console
71
+
72
+ # Terminal 2: run the app in dev mode
73
+ uv run textual run --dev textual_code:main
74
+ ```
75
+
76
+ ### Running Tests
77
+
78
+ See [docs/testing-guide.md](docs/testing-guide.md) for patterns, best practices, and gotchas.
79
+
80
+ ```bash
81
+ # Unit/integration tests — parallel (~2.5 min)
82
+ uv run pytest tests/ -n $(( $(nproc) * 2 )) -m "not serial"
83
+
84
+ # Snapshot tests — must run serially
85
+ uv run pytest tests/ -m serial
86
+
87
+ # Update snapshots after UI changes
88
+ uv run pytest tests/test_snapshots.py --snapshot-update
89
+
90
+ # Single file
91
+ uv run pytest tests/test_code_editor.py
92
+ ```