python-codex 0.2.0__tar.gz → 0.2.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 (121) hide show
  1. {python_codex-0.2.0 → python_codex-0.2.1}/PKG-INFO +1 -1
  2. {python_codex-0.2.0 → python_codex-0.2.1}/pyproject.toml +1 -1
  3. {python_codex-0.2.0 → python_codex-0.2.1}/workspace_server/workspace.html +228 -94
  4. {python_codex-0.2.0 → python_codex-0.2.1}/.github/workflows/publish.yml +0 -0
  5. {python_codex-0.2.0 → python_codex-0.2.1}/.github/workflows/test.yml +0 -0
  6. {python_codex-0.2.0 → python_codex-0.2.1}/.gitignore +0 -0
  7. {python_codex-0.2.0 → python_codex-0.2.1}/AGENTS.md +0 -0
  8. {python_codex-0.2.0 → python_codex-0.2.1}/LICENSE +0 -0
  9. {python_codex-0.2.0 → python_codex-0.2.1}/README.md +0 -0
  10. {python_codex-0.2.0 → python_codex-0.2.1}/README_ZH.md +0 -0
  11. {python_codex-0.2.0 → python_codex-0.2.1}/docs/ALIGNMENT.md +0 -0
  12. {python_codex-0.2.0 → python_codex-0.2.1}/docs/CONTEXT.md +0 -0
  13. {python_codex-0.2.0 → python_codex-0.2.1}/docs/responses_server/README.md +0 -0
  14. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/__init__.py +0 -0
  15. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/agent.py +0 -0
  16. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/cli.py +0 -0
  17. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/collaboration.py +0 -0
  18. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/compat.py +0 -0
  19. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/context.py +0 -0
  20. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/doctor.py +0 -0
  21. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/feishu_card.py +0 -0
  22. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/feishu_link.py +0 -0
  23. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/interactive_session.py +0 -0
  24. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/model.py +0 -0
  25. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/portable.py +0 -0
  26. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/portable_server.py +0 -0
  27. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/collaboration_default.md +0 -0
  28. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/collaboration_plan.md +0 -0
  29. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/default_base_instructions.md +0 -0
  30. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/models.json +0 -0
  31. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/permissions/approval_policy/never.md +0 -0
  32. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/permissions/approval_policy/on_failure.md +0 -0
  33. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/permissions/approval_policy/on_request.md +0 -0
  34. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/permissions/approval_policy/on_request_rule_request_permission.md +0 -0
  35. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/permissions/approval_policy/unless_trusted.md +0 -0
  36. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/permissions/sandbox_mode/danger_full_access.md +0 -0
  37. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/permissions/sandbox_mode/read_only.md +0 -0
  38. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/prompts/permissions/sandbox_mode/workspace_write.md +0 -0
  39. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/protocol.py +0 -0
  40. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/runtime.py +0 -0
  41. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/runtime_services.py +0 -0
  42. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/__init__.py +0 -0
  43. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/agent_tool_schemas.py +0 -0
  44. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/apply_patch_tool.py +0 -0
  45. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/base_tool.py +0 -0
  46. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/close_agent_tool.py +0 -0
  47. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/code_mode_manager.py +0 -0
  48. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/exec_command_tool.py +0 -0
  49. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/exec_runtime.js +0 -0
  50. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/exec_tool.py +0 -0
  51. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/grep_files_tool.py +0 -0
  52. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/ipython_tool.py +0 -0
  53. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/list_dir_tool.py +0 -0
  54. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/read_file_tool.py +0 -0
  55. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/request_permissions_tool.py +0 -0
  56. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/request_user_input_tool.py +0 -0
  57. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/resume_agent_tool.py +0 -0
  58. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/send_input_tool.py +0 -0
  59. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/shell_command_tool.py +0 -0
  60. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/shell_tool.py +0 -0
  61. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/spawn_agent_tool.py +0 -0
  62. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/unified_exec_manager.py +0 -0
  63. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/update_plan_tool.py +0 -0
  64. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/view_image_tool.py +0 -0
  65. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/wait_agent_tool.py +0 -0
  66. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/wait_tool.py +0 -0
  67. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/web_search_tool.py +0 -0
  68. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/tools/write_stdin_tool.py +0 -0
  69. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/utils/__init__.py +0 -0
  70. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/utils/async_bridge.py +0 -0
  71. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/utils/compactor.py +0 -0
  72. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/utils/debug.py +0 -0
  73. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/utils/dotenv.py +0 -0
  74. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/utils/get_env.py +0 -0
  75. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/utils/random_ids.py +0 -0
  76. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/utils/session_persist.py +0 -0
  77. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/utils/toolcall_visualize.py +0 -0
  78. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/utils/truncation.py +0 -0
  79. {python_codex-0.2.0 → python_codex-0.2.1}/pycodex/utils/visualize.py +0 -0
  80. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/__init__.py +0 -0
  81. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/__main__.py +0 -0
  82. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/app.py +0 -0
  83. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/config.py +0 -0
  84. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/messages_api.py +0 -0
  85. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/payload_processors.py +0 -0
  86. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/server.py +0 -0
  87. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/session_store.py +0 -0
  88. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/stream_router.py +0 -0
  89. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/tools/__init__.py +0 -0
  90. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/tools/custom_adapter.py +0 -0
  91. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/tools/web_search.py +0 -0
  92. {python_codex-0.2.0 → python_codex-0.2.1}/responses_server/trajectory_dump.py +0 -0
  93. {python_codex-0.2.0 → python_codex-0.2.1}/tests/TESTS.md +0 -0
  94. {python_codex-0.2.0 → python_codex-0.2.1}/tests/__init__.py +0 -0
  95. {python_codex-0.2.0 → python_codex-0.2.1}/tests/compare_request_user_input_roundtrip.py +0 -0
  96. {python_codex-0.2.0 → python_codex-0.2.1}/tests/compare_steer_request_bodies.py +0 -0
  97. {python_codex-0.2.0 → python_codex-0.2.1}/tests/compare_tool_schemas.py +0 -0
  98. {python_codex-0.2.0 → python_codex-0.2.1}/tests/fake_responses_server.py +0 -0
  99. {python_codex-0.2.0 → python_codex-0.2.1}/tests/fakes.py +0 -0
  100. {python_codex-0.2.0 → python_codex-0.2.1}/tests/responses_server/fake_chat_completions_server.py +0 -0
  101. {python_codex-0.2.0 → python_codex-0.2.1}/tests/responses_server/test_server.py +0 -0
  102. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_agent.py +0 -0
  103. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_async_bridge.py +0 -0
  104. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_builtin_tools.py +0 -0
  105. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_cli.py +0 -0
  106. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_compactor.py +0 -0
  107. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_context.py +0 -0
  108. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_doctor.py +0 -0
  109. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_fake_responses_server.py +0 -0
  110. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_feishu_card.py +0 -0
  111. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_feishu_link.py +0 -0
  112. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_ipython_tool.py +0 -0
  113. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_model.py +0 -0
  114. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_portable.py +0 -0
  115. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_py36_syntax.py +0 -0
  116. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_visualize.py +0 -0
  117. {python_codex-0.2.0 → python_codex-0.2.1}/tests/test_workspace_server.py +0 -0
  118. {python_codex-0.2.0 → python_codex-0.2.1}/tools/feishu_oauth.py +0 -0
  119. {python_codex-0.2.0 → python_codex-0.2.1}/workspace_server/__init__.py +0 -0
  120. {python_codex-0.2.0 → python_codex-0.2.1}/workspace_server/__main__.py +0 -0
  121. {python_codex-0.2.0 → python_codex-0.2.1}/workspace_server/app.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-codex
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: A minimal Python extraction of Codex's main agent loop
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.6.2
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "python-codex"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "A minimal Python extraction of Codex's main agent loop"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.6.2"
@@ -7,14 +7,31 @@
7
7
  <style>
8
8
  :root {
9
9
  color-scheme: light;
10
- --bg: #f5f5f2;
10
+ --bg: #f2f6f3;
11
11
  --panel: #ffffff;
12
- --ink: #1f2528;
13
- --muted: #667178;
14
- --line: #d8ddd8;
15
- --accent: #126b43;
12
+ --ink: #1e2529;
13
+ --muted: #5f6f75;
14
+ --line: #d2ddd5;
15
+ --accent: #0f8f55;
16
16
  --accent-ink: #ffffff;
17
- --code: #f6f8fa;
17
+ --warn-soft: #fff5cf;
18
+ --info: #1f6fa4;
19
+ --info-soft: #edf8ff;
20
+ --danger: #b23a3a;
21
+ --danger-soft: #fff1ef;
22
+ --violet: #6b4fb0;
23
+ --violet-soft: #f5f0ff;
24
+ --code: #f3f7f8;
25
+ --entry-shadow: 0 1px 2px rgba(25, 35, 31, 0.04);
26
+ --space-1: 2px;
27
+ --space-2: 4px;
28
+ --space-3: 6px;
29
+ --space-4: 8px;
30
+ --space-5: 10px;
31
+ --space-6: 12px;
32
+ --space-7: 14px;
33
+ --radius-sm: 4px;
34
+ --radius-md: 6px;
18
35
  --chat-width: min(42vw, 760px);
19
36
  --splitter-width: 8px;
20
37
  }
@@ -34,7 +51,7 @@
34
51
  min-width: 0;
35
52
  }
36
53
  .workspace.collapsed .splitter::after {
37
- background: #7e9187;
54
+ background: #4f6f60;
38
55
  }
39
56
  .board-pane {
40
57
  min-width: 0;
@@ -46,7 +63,7 @@
46
63
  .boardbar {
47
64
  min-width: 0;
48
65
  border-bottom: 1px solid var(--line);
49
- padding: 8px 12px;
66
+ padding: var(--space-4) var(--space-6);
50
67
  color: var(--muted);
51
68
  font-size: 12px;
52
69
  white-space: nowrap;
@@ -61,24 +78,36 @@
61
78
  background: #fff;
62
79
  }
63
80
  .splitter {
81
+ position: relative;
64
82
  width: 8px;
65
83
  height: 100dvh;
66
84
  border: 0;
67
- border-left: 1px solid var(--line);
68
- border-right: 1px solid var(--line);
69
- background: #eef1ed;
85
+ border-left: 1px solid #c1cec6;
86
+ border-right: 1px solid #c1cec6;
87
+ background: #dfe8e2;
70
88
  cursor: col-resize;
71
89
  padding: 0;
72
90
  touch-action: none;
73
91
  }
74
92
  .splitter::after {
75
93
  content: "";
76
- display: block;
77
- width: 2px;
94
+ position: absolute;
95
+ left: 50%;
96
+ top: 50%;
97
+ width: 3px;
78
98
  height: 42px;
79
- margin: calc(50dvh - 21px) auto 0;
80
99
  border-radius: 999px;
81
- background: #b9c1ba;
100
+ background: #7d9588;
101
+ transform: translate(-50%, -50%);
102
+ }
103
+ .splitter:hover,
104
+ .workspace.resizing .splitter {
105
+ background: #d3dfd7;
106
+ border-color: #aebeb4;
107
+ }
108
+ .splitter:hover::after,
109
+ .workspace.resizing .splitter::after {
110
+ background: #527465;
82
111
  }
83
112
  .workspace.resizing,
84
113
  .workspace.resizing * {
@@ -96,37 +125,38 @@
96
125
  }
97
126
  .topbar {
98
127
  min-width: 0;
99
- padding: 12px 14px;
128
+ padding: var(--space-4) var(--space-7);
100
129
  border-bottom: 1px solid var(--line);
101
130
  display: grid;
102
- gap: 10px;
131
+ gap: var(--space-4);
103
132
  }
104
133
  .tabbar {
105
134
  display: flex;
106
135
  align-items: center;
107
- gap: 6px;
136
+ gap: var(--space-2);
108
137
  min-width: 0;
109
138
  }
110
139
  .tabs {
111
140
  display: flex;
112
141
  align-items: center;
113
- gap: 6px;
142
+ gap: var(--space-1);
114
143
  min-width: 0;
115
144
  overflow-x: auto;
116
145
  scrollbar-width: thin;
117
146
  }
118
147
  .tab {
148
+ position: relative;
119
149
  display: inline-flex;
120
150
  align-items: center;
121
- gap: 6px;
151
+ gap: var(--space-2);
122
152
  min-width: 88px;
123
153
  max-width: 180px;
124
- height: 28px;
125
- border: 1px solid var(--line);
126
- border-radius: 6px;
127
- background: #f8faf8;
154
+ height: 31px;
155
+ border: 1px solid #d5ddd7;
156
+ border-radius: var(--radius-sm);
157
+ background: #f6f8f7;
128
158
  color: var(--ink);
129
- padding: 0 7px;
159
+ padding: 0 var(--space-3) 0 var(--space-4);
130
160
  font: inherit;
131
161
  cursor: pointer;
132
162
  user-select: none;
@@ -137,8 +167,24 @@
137
167
  outline-offset: 1px;
138
168
  }
139
169
  .tab.active {
140
- border-color: #9bb7a6;
141
- background: #edf6f0;
170
+ border-color: #afd6bd;
171
+ background: #eef7f1;
172
+ box-shadow: inset 0 -2px 0 var(--accent);
173
+ }
174
+ .tab.running::before {
175
+ content: "";
176
+ position: absolute;
177
+ left: 6px;
178
+ top: 50%;
179
+ width: 5px;
180
+ height: 5px;
181
+ margin-top: -2.5px;
182
+ border-radius: 999px;
183
+ background: var(--accent);
184
+ animation: spinner-pulse 1.2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
185
+ }
186
+ .tab.running .tab-label {
187
+ padding-left: var(--space-4);
142
188
  }
143
189
  .tab-label {
144
190
  min-width: 0;
@@ -150,11 +196,11 @@
150
196
  }
151
197
  .tab-close,
152
198
  .tab-new {
153
- width: 24px;
154
- height: 24px;
199
+ width: 31px;
200
+ height: 31px;
155
201
  border: 1px solid var(--line);
156
- border-radius: 6px;
157
- background: #fff;
202
+ border-radius: var(--radius-md);
203
+ background: #eef7f1;
158
204
  color: var(--muted);
159
205
  padding: 0;
160
206
  line-height: 1;
@@ -166,56 +212,79 @@
166
212
  height: 18px;
167
213
  border: 0;
168
214
  background: transparent;
215
+ opacity: 0.42;
216
+ font-size: 17px;
217
+ font-weight: 500;
218
+ line-height: 18px;
219
+ }
220
+ .tab-new {
221
+ font-size: 22px;
222
+ font-weight: 400;
223
+ line-height: 31px;
224
+ }
225
+ .tab:hover .tab-close,
226
+ .tab:focus-within .tab-close,
227
+ .tab.active .tab-close {
228
+ opacity: 0.85;
229
+ }
230
+ .tab-close:disabled {
231
+ opacity: 0.22;
232
+ cursor: default;
169
233
  }
170
234
  .tab-close:hover,
171
235
  .tab-new:hover {
172
236
  color: var(--ink);
173
- background: #f3f6f4;
237
+ background: #ffffff;
174
238
  }
175
239
  .log {
176
240
  min-height: 0;
177
241
  overflow-y: auto;
178
242
  overflow-x: hidden;
179
- padding: 14px;
243
+ padding: var(--space-6) var(--space-7);
180
244
  display: flex;
181
245
  flex-direction: column;
182
- gap: 10px;
246
+ gap: var(--space-4);
183
247
  background: #fbfcfa;
184
248
  }
185
249
  .entry {
186
250
  min-width: 0;
187
251
  border: 1px solid var(--line);
188
- border-radius: 6px;
189
- padding: 10px 11px;
252
+ border-radius: var(--radius-sm);
253
+ padding: var(--space-4) var(--space-5);
190
254
  background: #fff;
255
+ box-shadow: var(--entry-shadow);
191
256
  }
192
257
  .entry.user {
193
- border-color: #e6d790;
194
- background: #fff9d8;
258
+ border-color: #edd17a;
259
+ background: var(--warn-soft);
260
+ color: #5a4714;
195
261
  }
196
262
  .entry.response,
197
263
  .entry.assistant {
198
- border-color: #d9ded9;
264
+ border-color: #d0ddd5;
199
265
  background: #ffffff;
200
266
  }
201
267
  .entry.thinking {
202
- border-color: #b9d9c7;
203
- background: #f5fbf6;
268
+ border-color: #b9dcc5;
269
+ background: #f0fbf3;
270
+ color: #256b43;
271
+ animation: thinking-breathe 4.8s ease-in-out infinite;
204
272
  }
205
273
  .entry.control {
206
- border-color: #c8d2dc;
207
- background: #f7fafd;
274
+ border-color: #b9d8ea;
275
+ background: var(--info-soft);
276
+ color: var(--info);
208
277
  }
209
278
  .entry.tool {
210
- border-color: #d7d1e6;
211
- background: #faf8ff;
212
- color: #4c4261;
279
+ border-color: #d7c8f0;
280
+ background: var(--violet-soft);
281
+ color: var(--violet);
213
282
  font-size: 12px;
214
283
  }
215
284
  .entry.error {
216
- border-color: #e4b6b6;
217
- background: #fff7f7;
218
- color: #7a2c2c;
285
+ border-color: #efb4ad;
286
+ background: var(--danger-soft);
287
+ color: var(--danger);
219
288
  }
220
289
  .text {
221
290
  white-space: pre-wrap;
@@ -226,7 +295,7 @@
226
295
  overflow-wrap: anywhere;
227
296
  }
228
297
  .markdown p {
229
- margin: 0 0 8px;
298
+ margin: 0 0 var(--space-4);
230
299
  }
231
300
  .markdown p:last-child,
232
301
  .markdown ul:last-child,
@@ -237,17 +306,17 @@
237
306
  }
238
307
  .markdown ul,
239
308
  .markdown ol {
240
- margin: 0 0 8px 18px;
309
+ margin: 0 0 var(--space-4) 18px;
241
310
  padding: 0;
242
311
  }
243
312
  .markdown li {
244
313
  margin: 2px 0;
245
314
  }
246
315
  .markdown pre {
247
- margin: 0 0 8px;
248
- padding: 9px 10px;
316
+ margin: 0 0 var(--space-4);
317
+ padding: var(--space-4) var(--space-5);
249
318
  border: 1px solid var(--line);
250
- border-radius: 6px;
319
+ border-radius: var(--radius-sm);
251
320
  background: var(--code);
252
321
  overflow-x: auto;
253
322
  white-space: pre;
@@ -260,23 +329,23 @@
260
329
  }
261
330
  .markdown table {
262
331
  width: 100%;
263
- margin: 0 0 8px;
332
+ margin: 0 0 var(--space-4);
264
333
  border-collapse: collapse;
265
334
  font-size: 13px;
266
335
  }
267
336
  .markdown th,
268
337
  .markdown td {
269
338
  border: 1px solid var(--line);
270
- padding: 5px 7px;
339
+ padding: var(--space-3) var(--space-4);
271
340
  text-align: left;
272
341
  vertical-align: top;
273
342
  }
274
343
  .markdown th {
275
- background: #f3f6f4;
344
+ background: #eef7f1;
276
345
  font-weight: 650;
277
346
  }
278
347
  .markdown a {
279
- color: #0b6bcb;
348
+ color: #176fa8;
280
349
  text-decoration: none;
281
350
  }
282
351
  .markdown a:hover {
@@ -285,22 +354,31 @@
285
354
  .composer {
286
355
  min-width: 0;
287
356
  border-top: 1px solid var(--line);
288
- padding: 12px 12px 28px;
357
+ padding: var(--space-4) var(--space-6) var(--space-5);
289
358
  display: grid;
290
- gap: 8px;
359
+ gap: var(--space-3);
360
+ align-content: end;
291
361
  background: #fff;
292
362
  }
293
363
  textarea {
294
364
  min-width: 0;
295
365
  resize: vertical;
296
- min-height: 88px;
366
+ min-height: 40px;
297
367
  max-height: 35vh;
298
368
  width: 100%;
299
369
  border: 1px solid var(--line);
300
- border-radius: 6px;
301
- padding: 10px;
370
+ border-radius: var(--radius-sm);
371
+ padding: var(--space-4) var(--space-5);
372
+ background: #ffffff;
373
+ color: var(--ink);
302
374
  font: inherit;
303
375
  line-height: 1.4;
376
+ overflow-y: auto;
377
+ }
378
+ textarea:focus {
379
+ border-color: #65b782;
380
+ box-shadow: 0 0 0 3px rgba(15, 143, 85, 0.13);
381
+ outline: 0;
304
382
  }
305
383
  .actions {
306
384
  display: flex;
@@ -308,21 +386,79 @@
308
386
  align-items: center;
309
387
  }
310
388
  .spinner {
311
- position: absolute;
312
- left: 12px;
313
- right: 12px;
314
- bottom: 6px;
315
- min-height: 18px;
389
+ position: relative;
390
+ display: inline-flex;
391
+ align-items: center;
392
+ justify-self: start;
393
+ align-self: end;
394
+ max-width: 100%;
395
+ min-height: 20px;
316
396
  color: var(--muted);
317
397
  font-size: 12px;
318
398
  white-space: nowrap;
319
399
  overflow: hidden;
320
400
  text-overflow: ellipsis;
401
+ padding: var(--space-1) var(--space-4) var(--space-1) 18px;
402
+ border-radius: 999px;
403
+ background: rgba(240, 250, 244, 0.96);
404
+ border: 1px solid rgba(172, 216, 188, 0.9);
405
+ }
406
+ .spinner:empty {
407
+ display: none;
408
+ }
409
+ .spinner:not(:empty) {
410
+ margin-bottom: var(--space-1);
411
+ }
412
+ .spinner:not(:empty)::before {
413
+ content: "";
414
+ position: absolute;
415
+ left: 7px;
416
+ top: 50%;
417
+ width: 6px;
418
+ height: 6px;
419
+ margin-top: -3px;
420
+ border-radius: 999px;
421
+ background: var(--accent);
422
+ animation: spinner-pulse 1.2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
423
+ }
424
+ @keyframes spinner-pulse {
425
+ 0%, 100% {
426
+ opacity: 0.45;
427
+ transform: scale(0.82);
428
+ }
429
+ 50% {
430
+ opacity: 1;
431
+ transform: scale(1.08);
432
+ }
433
+ }
434
+ @keyframes thinking-breathe {
435
+ 0%, 100% {
436
+ border-color: #b9dcc5;
437
+ box-shadow: var(--entry-shadow);
438
+ }
439
+ 50% {
440
+ border-color: #72d494;
441
+ box-shadow:
442
+ 0 0 0 2px rgba(22, 168, 90, 0.16),
443
+ 0 2px 7px rgba(25, 35, 31, 0.08);
444
+ }
445
+ }
446
+ @media (prefers-reduced-motion: reduce) {
447
+ .entry.thinking,
448
+ .tab.running::before,
449
+ .spinner:not(:empty)::before {
450
+ animation: none;
451
+ }
452
+ .tab.running::before,
453
+ .spinner:not(:empty)::before {
454
+ opacity: 0.8;
455
+ transform: none;
456
+ }
321
457
  }
322
458
  code {
323
459
  background: var(--code);
324
460
  border: 1px solid var(--line);
325
- border-radius: 4px;
461
+ border-radius: var(--radius-sm);
326
462
  padding: 1px 4px;
327
463
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
328
464
  font-size: 12px;
@@ -343,15 +479,15 @@
343
479
  <section class="chat" aria-label="pycodex chat">
344
480
  <div class="topbar">
345
481
  <div class="tabbar">
346
- <div id="tabs" class="tabs"></div>
482
+ <div id="tabs" class="tabs" role="tablist"></div>
347
483
  <button id="newTab" class="tab-new" type="button" title="New session" aria-label="New session">+</button>
348
484
  </div>
349
485
  </div>
350
486
  <div id="log" class="log"></div>
351
487
  <form id="composer" class="composer">
488
+ <div id="spinner" class="spinner" role="status" aria-live="polite"></div>
352
489
  <textarea id="prompt" placeholder="Ask pycodex or type /help..."></textarea>
353
490
  </form>
354
- <div id="spinner" class="spinner"></div>
355
491
  </section>
356
492
  </div>
357
493
  <script src="https://cdn.jsdelivr.net/npm/markdown-it@14.1.0/dist/markdown-it.min.js"></script>
@@ -373,8 +509,6 @@
373
509
  let lastBoardSignature = "";
374
510
  let lastRenderedSignature = "";
375
511
  let resizeStart = null;
376
- let spinnerTimer = null;
377
- let spinnerFrameIndex = 0;
378
512
  let spinnerText = "";
379
513
  let activeSessionId = "";
380
514
  let sessions = [];
@@ -382,7 +516,6 @@
382
516
  const sessionState = new Map();
383
517
  let restoreScrollTop = null;
384
518
  let suppressScrollSave = false;
385
- const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
386
519
  const markdownRenderer = window.markdownit
387
520
  ? window.markdownit({html: false, linkify: true, breaks: false})
388
521
  : null;
@@ -496,25 +629,7 @@
496
629
 
497
630
  function setSpinner(text) {
498
631
  spinnerText = String(text || "").trim();
499
- spinnerFrameIndex = 0;
500
- renderSpinner();
501
- if (spinnerText && !spinnerTimer) {
502
- spinnerTimer = setInterval(renderSpinner, 120);
503
- }
504
- if (!spinnerText && spinnerTimer) {
505
- clearInterval(spinnerTimer);
506
- spinnerTimer = null;
507
- }
508
- }
509
-
510
- function renderSpinner() {
511
- if (!spinnerText) {
512
- spinner.textContent = "";
513
- return;
514
- }
515
- const frame = spinnerFrames[spinnerFrameIndex % spinnerFrames.length];
516
- spinnerFrameIndex += 1;
517
- spinner.textContent = `${frame} ${spinnerText}`;
632
+ spinner.textContent = spinnerText;
518
633
  }
519
634
 
520
635
  function isNearBottom() {
@@ -525,6 +640,14 @@
525
640
  log.scrollTop = log.scrollHeight;
526
641
  }
527
642
 
643
+ function resizePrompt() {
644
+ prompt.style.height = "auto";
645
+ const minHeight = 40;
646
+ const maxHeight = Math.max(minHeight, Math.round(window.innerHeight * 0.35));
647
+ const nextHeight = Math.max(minHeight, Math.min(prompt.scrollHeight, maxHeight));
648
+ prompt.style.height = `${nextHeight}px`;
649
+ }
650
+
528
651
  function stateForSession(sessionId) {
529
652
  const key = String(sessionId || "");
530
653
  if (!sessionState.has(key)) {
@@ -556,10 +679,14 @@
556
679
  sessions.forEach((session, index) => {
557
680
  const sessionId = session.id || "";
558
681
  const tab = document.createElement("div");
559
- tab.className = "tab" + (sessionId === activeSessionId ? " active" : "");
682
+ tab.className =
683
+ "tab" +
684
+ (sessionId === activeSessionId ? " active" : "") +
685
+ (session.running ? " running" : "");
560
686
  tab.title = session.title || `Session ${index + 1}`;
561
687
  tab.setAttribute("role", "tab");
562
688
  tab.setAttribute("aria-selected", sessionId === activeSessionId ? "true" : "false");
689
+ if (session.running) tab.setAttribute("aria-busy", "true");
563
690
  tab.addEventListener("click", () => switchSession(sessionId));
564
691
 
565
692
  const label = document.createElement("span");
@@ -758,6 +885,7 @@
758
885
  state.lastRenderedSignature = "";
759
886
  lastRenderedSignature = "";
760
887
  prompt.value = state.draft || "";
888
+ resizePrompt();
761
889
  suppressScrollSave = true;
762
890
  log.textContent = "";
763
891
  suppressScrollSave = false;
@@ -782,6 +910,7 @@
782
910
  state.lastRenderedSignature = "";
783
911
  lastRenderedSignature = "";
784
912
  prompt.value = "";
913
+ resizePrompt();
785
914
  restoreScrollTop = null;
786
915
  updateSessions(payload.sessions || []);
787
916
  renderSnapshot(payload.snapshot);
@@ -803,6 +932,7 @@
803
932
  activeSessionId = sessions.length ? sessions[0].id || "" : "";
804
933
  lastRenderedSignature = "";
805
934
  prompt.value = "";
935
+ resizePrompt();
806
936
  suppressScrollSave = true;
807
937
  log.textContent = "";
808
938
  suppressScrollSave = false;
@@ -817,12 +947,14 @@
817
947
  const text = prompt.value.trim();
818
948
  if (!text) return;
819
949
  prompt.value = "";
950
+ resizePrompt();
820
951
  stateForSession(activeSessionId).draft = "";
821
952
  submitPrompt(text);
822
953
  });
823
954
 
824
955
  prompt.addEventListener("input", () => {
825
956
  stateForSession(activeSessionId).draft = prompt.value;
957
+ resizePrompt();
826
958
  });
827
959
 
828
960
  prompt.addEventListener("compositionstart", () => {
@@ -856,9 +988,11 @@
856
988
  window.addEventListener("resize", () => {
857
989
  const current = Number(window.localStorage.getItem("pycodex.workspace.chatWidth") || 0);
858
990
  if (current > 0) setChatWidth(current);
991
+ resizePrompt();
859
992
  });
860
993
 
861
994
  loadChatWidth();
995
+ resizePrompt();
862
996
  startPolling();
863
997
  startBoardPolling();
864
998
  </script>
File without changes
File without changes
File without changes
File without changes
File without changes