python-codex 0.2.5__py3-none-any.whl → 0.2.6__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-codex
3
- Version: 0.2.5
3
+ Version: 0.2.6
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
@@ -23,8 +23,17 @@ Description-Content-Type: text/markdown
23
23
 
24
24
  English README. Chinese version: `README_ZH.md`
25
25
 
26
- PyPI distribution name: `python-codex`
27
- Import path and CLI command remain `pycodex`.
26
+ PyPI distributions:
27
+
28
+ - Primary package: `python-codex`
29
+ - Workspace install alias: `pycodex-ws`
30
+
31
+ The import path remains `pycodex`; the CLI commands are `pycodex` and
32
+ `pycodex-ws`.
33
+
34
+ `pycodex-ws` is a thin metapackage that depends on the exact matching
35
+ `python-codex` version. The implementation and console script remain owned by
36
+ `python-codex`, so the two distributions never install duplicate modules.
28
37
 
29
38
  This repository extracts the core Codex agent loop from upstream Codex
30
39
  (`https://github.com/openai/codex`) into a deliberately small Python version,
@@ -44,6 +53,13 @@ Relevant Rust reference points:
44
53
 
45
54
  ## Quick Start
46
55
 
56
+ Install the full package or the workspace-oriented alias:
57
+
58
+ ```bash
59
+ pip install python-codex
60
+ pip install pycodex-ws
61
+ ```
62
+
47
63
  Install dependencies first:
48
64
 
49
65
  ```bash
@@ -81,11 +81,11 @@ responses_server/tools/web_search.py,sha256=pm4ZUiHUfxc0bGY1kEvt-BCzDrZIyP24xzPU
81
81
  workspace_server/__init__.py,sha256=PRM4ONODb6hxjBUHkBD4TprE_pkTCIlT9sfftK5ic6M,866
82
82
  workspace_server/__main__.py,sha256=9SRp-Yw7ShGxc6DhSIXcDLKgGEdAVm3oBZ59rBOPjT0,62
83
83
  workspace_server/app.py,sha256=20lTENFBlercCzFmtAR4fIeMvqIZr8Gq_J2VotmNPck,56858
84
- workspace_server/workspace.html,sha256=xEcb5tVFM_5Ol6VFp1KzT3D95D8CBse5YyUkAxHugsM,42109
84
+ workspace_server/workspace.html,sha256=UqUqOfQFra7KV8btI4BUTB38jkkhYZvzzdN5OWpQwdQ,48521
85
85
  workspace_server/workspaces.html,sha256=GuYlFiOm1RJINrCKncClHt24D5i7XNABiP9C0xGxBA0,15679
86
86
  workspace_server/workspaces.py,sha256=Z-zoj0xgkOOP2CwbQl3vAI6TwLIDUInJKyobs0Jt3ps,19962
87
- python_codex-0.2.5.dist-info/METADATA,sha256=EbfCi71Or5nayWpWlbT5jnE1rEcmxmj-wvXxG6UOaZE,18852
88
- python_codex-0.2.5.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
89
- python_codex-0.2.5.dist-info/entry_points.txt,sha256=vkV2UWCtEKvQNMJuPNjt8HyBKiwp83JyqBatrBNGDp8,80
90
- python_codex-0.2.5.dist-info/licenses/LICENSE,sha256=0X8ifk312hYAORM4hlzg8wVSEXYKNmiPgWlB1YIy2Nw,10926
91
- python_codex-0.2.5.dist-info/RECORD,,
87
+ python_codex-0.2.6.dist-info/METADATA,sha256=AjjNd0oWIKS2og9c2chID0_K4Jz2ICk2z6JfE_npUoM,19288
88
+ python_codex-0.2.6.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
89
+ python_codex-0.2.6.dist-info/entry_points.txt,sha256=vkV2UWCtEKvQNMJuPNjt8HyBKiwp83JyqBatrBNGDp8,80
90
+ python_codex-0.2.6.dist-info/licenses/LICENSE,sha256=0X8ifk312hYAORM4hlzg8wVSEXYKNmiPgWlB1YIy2Nw,10926
91
+ python_codex-0.2.6.dist-info/RECORD,,
@@ -8,22 +8,33 @@
8
8
  <style>
9
9
  :root {
10
10
  color-scheme: light;
11
- --bg: #f2f6f3;
12
- --panel: #ffffff;
13
- --ink: #1e2529;
14
- --muted: #5f6f75;
15
- --line: #d2ddd5;
16
- --accent: #0f8f55;
11
+ --bg: #eef3f6;
12
+ --panel: #f8fbfd;
13
+ --panel-deep: #f2f6f8;
14
+ --panel-raised: #ffffff;
15
+ --panel-hover: #e8f1f5;
16
+ --board-chrome: #ffffff;
17
+ --ink: #172630;
18
+ --muted: #60727d;
19
+ --line: #cbd8df;
20
+ --line-strong: #aebfc9;
21
+ --accent: #087f9a;
17
22
  --accent-ink: #ffffff;
18
- --warn-soft: #fff5cf;
19
- --info: #1f6fa4;
20
- --info-soft: #edf8ff;
21
- --danger: #b23a3a;
22
- --danger-soft: #fff1ef;
23
- --violet: #6b4fb0;
24
- --violet-soft: #f5f0ff;
25
- --code: #f3f7f8;
26
- --entry-shadow: 0 1px 2px rgba(25, 35, 31, 0.04);
23
+ --accent-soft: #e6f6f8;
24
+ --warn: #b77908;
25
+ --warn-ink: #62480c;
26
+ --warn-soft: #fff7df;
27
+ --info: #2868b2;
28
+ --info-soft: #edf5ff;
29
+ --success: #247a52;
30
+ --success-line: #8bc9a8;
31
+ --success-soft: #edf9f2;
32
+ --danger: #b33d50;
33
+ --danger-soft: #fff0f2;
34
+ --violet: #6850a4;
35
+ --violet-soft: #f4f0ff;
36
+ --code: #f1f5f7;
37
+ --entry-shadow: 0 1px 2px rgba(27, 57, 72, 0.06);
27
38
  --space-1: 2px;
28
39
  --space-2: 4px;
29
40
  --space-3: 6px;
@@ -52,14 +63,14 @@
52
63
  min-width: 0;
53
64
  }
54
65
  .workspace.collapsed .splitter::after {
55
- background: #4f6f60;
66
+ background: var(--accent);
56
67
  }
57
68
  .board-pane {
58
69
  min-width: 0;
59
70
  height: 100dvh;
60
71
  display: grid;
61
72
  grid-template-rows: auto minmax(0, 1fr);
62
- background: #fff;
73
+ background: var(--board-chrome);
63
74
  }
64
75
  .boardbar {
65
76
  min-width: 0;
@@ -101,10 +112,10 @@
101
112
  text-decoration: none;
102
113
  }
103
114
  .workspace-back:hover {
104
- background: #eef7f1;
115
+ background: var(--accent-soft);
105
116
  }
106
117
  .workspace-back:focus {
107
- outline: 2px solid #9bb7a6;
118
+ outline: 2px solid var(--accent);
108
119
  outline-offset: 1px;
109
120
  }
110
121
  .workspace-back::before {
@@ -120,16 +131,16 @@
120
131
  min-width: 0;
121
132
  width: 100%;
122
133
  height: 100%;
123
- background: #fff;
134
+ background: var(--board-chrome);
124
135
  }
125
136
  .splitter {
126
137
  position: relative;
127
138
  width: 8px;
128
139
  height: 100dvh;
129
140
  border: 0;
130
- border-left: 1px solid #c1cec6;
131
- border-right: 1px solid #c1cec6;
132
- background: #dfe8e2;
141
+ border-left: 1px solid var(--line);
142
+ border-right: 1px solid var(--line);
143
+ background: var(--panel-hover);
133
144
  cursor: col-resize;
134
145
  padding: 0;
135
146
  touch-action: none;
@@ -142,17 +153,17 @@
142
153
  width: 3px;
143
154
  height: 42px;
144
155
  border-radius: 999px;
145
- background: #7d9588;
156
+ background: var(--muted);
146
157
  transform: translate(-50%, -50%);
147
158
  }
148
159
  .splitter:hover,
149
160
  .workspace.resizing .splitter {
150
- background: #d3dfd7;
151
- border-color: #aebeb4;
161
+ background: #dce9ee;
162
+ border-color: var(--line-strong);
152
163
  }
153
164
  .splitter:hover::after,
154
165
  .workspace.resizing .splitter::after {
155
- background: #527465;
166
+ background: var(--accent);
156
167
  }
157
168
  .workspace.resizing,
158
169
  .workspace.resizing * {
@@ -190,8 +201,8 @@
190
201
  width: var(--context-meter-size);
191
202
  height: var(--context-meter-size);
192
203
  border-radius: 999px;
193
- background: conic-gradient(#4ca56f 0 var(--context-remaining), #cfd8d2 0 100%);
194
- color: #335243;
204
+ background: conic-gradient(var(--accent) 0 var(--context-remaining), var(--line-strong) 0 100%);
205
+ color: var(--muted);
195
206
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
196
207
  font-size: 9px;
197
208
  line-height: 1;
@@ -205,7 +216,7 @@
205
216
  position: absolute;
206
217
  inset: 4px;
207
218
  border-radius: 999px;
208
- background: #fff;
219
+ background: var(--panel);
209
220
  }
210
221
  .context-meter-value {
211
222
  position: relative;
@@ -227,9 +238,9 @@
227
238
  min-width: 88px;
228
239
  max-width: 180px;
229
240
  height: 31px;
230
- border: 1px solid #d5ddd7;
241
+ border: 1px solid var(--line);
231
242
  border-radius: var(--radius-sm);
232
- background: #f6f8f7;
243
+ background: var(--panel-raised);
233
244
  color: var(--ink);
234
245
  padding: 0 var(--space-3) 0 var(--space-4);
235
246
  font: inherit;
@@ -238,12 +249,12 @@
238
249
  }
239
250
  .tab:focus-within,
240
251
  .tab-new:focus {
241
- outline: 2px solid #9bb7a6;
252
+ outline: 2px solid var(--accent);
242
253
  outline-offset: 1px;
243
254
  }
244
255
  .tab.active {
245
- border-color: #afd6bd;
246
- background: #eef7f1;
256
+ border-color: var(--accent);
257
+ background: var(--accent-soft);
247
258
  box-shadow: inset 0 -2px 0 var(--accent);
248
259
  }
249
260
  .tab-status {
@@ -281,7 +292,7 @@
281
292
  height: 31px;
282
293
  border: 1px solid var(--line);
283
294
  border-radius: var(--radius-md);
284
- background: #eef7f1;
295
+ background: var(--panel-raised);
285
296
  color: var(--muted);
286
297
  padding: 0;
287
298
  line-height: 1;
@@ -315,7 +326,7 @@
315
326
  .tab-close:hover,
316
327
  .tab-new:hover {
317
328
  color: var(--ink);
318
- background: #ffffff;
329
+ background: var(--panel-hover);
319
330
  }
320
331
  .log {
321
332
  min-height: 0;
@@ -325,45 +336,45 @@
325
336
  display: flex;
326
337
  flex-direction: column;
327
338
  gap: var(--space-4);
328
- background: #fbfcfa;
339
+ background: var(--bg);
329
340
  }
330
341
  .entry {
331
342
  min-width: 0;
332
343
  border: 1px solid var(--line);
333
344
  border-radius: var(--radius-sm);
334
345
  padding: var(--space-4) var(--space-5);
335
- background: #fff;
346
+ background: var(--panel-raised);
336
347
  box-shadow: var(--entry-shadow);
337
348
  }
338
349
  .entry.user {
339
- border-color: #edd17a;
350
+ border-color: var(--warn);
340
351
  background: var(--warn-soft);
341
- color: #5a4714;
352
+ color: var(--warn-ink);
342
353
  }
343
354
  .entry.response,
344
355
  .entry.assistant {
345
- border-color: #d0ddd5;
346
- background: #ffffff;
356
+ border-color: var(--line);
357
+ background: var(--panel-raised);
347
358
  }
348
359
  .entry.thinking {
349
- border-color: #b9dcc5;
350
- background: #f0fbf3;
351
- color: #256b43;
360
+ border-color: var(--success-line);
361
+ background: var(--success-soft);
362
+ color: var(--success);
352
363
  animation: thinking-breathe 4.8s ease-in-out infinite;
353
364
  }
354
365
  .entry.control {
355
- border-color: #b9d8ea;
366
+ border-color: #b7d3ee;
356
367
  background: var(--info-soft);
357
368
  color: var(--info);
358
369
  }
359
370
  .entry.tool {
360
- border-color: #d7c8f0;
371
+ border-color: #d8cdf1;
361
372
  background: var(--violet-soft);
362
373
  color: var(--violet);
363
374
  font-size: 12px;
364
375
  }
365
376
  .entry.error {
366
- border-color: #efb4ad;
377
+ border-color: #efb8c1;
367
378
  background: var(--danger-soft);
368
379
  color: var(--danger);
369
380
  }
@@ -422,11 +433,11 @@
422
433
  vertical-align: top;
423
434
  }
424
435
  .markdown th {
425
- background: #eef7f1;
436
+ background: var(--accent-soft);
426
437
  font-weight: 650;
427
438
  }
428
439
  .markdown a {
429
- color: #176fa8;
440
+ color: var(--info);
430
441
  text-decoration: none;
431
442
  }
432
443
  .markdown a:hover {
@@ -436,6 +447,17 @@
436
447
  margin: 0 0 var(--space-4);
437
448
  overflow-x: auto;
438
449
  overflow-y: hidden;
450
+ overflow-wrap: normal;
451
+ word-break: normal;
452
+ }
453
+ .markdown .katex {
454
+ overflow-wrap: normal;
455
+ word-break: normal;
456
+ }
457
+ .markdown .katex .text {
458
+ white-space: normal;
459
+ overflow-wrap: normal;
460
+ word-break: normal;
439
461
  }
440
462
  .markdown .katex-display:last-child {
441
463
  margin-bottom: 0;
@@ -447,7 +469,7 @@
447
469
  display: grid;
448
470
  gap: var(--space-3);
449
471
  align-content: end;
450
- background: #fff;
472
+ background: var(--panel);
451
473
  }
452
474
  textarea {
453
475
  min-width: 0;
@@ -457,15 +479,15 @@
457
479
  border: 1px solid var(--line);
458
480
  border-radius: var(--radius-sm);
459
481
  padding: var(--space-4) var(--space-5);
460
- background: #ffffff;
482
+ background: var(--panel-deep);
461
483
  color: var(--ink);
462
484
  font: inherit;
463
485
  line-height: 1.4;
464
486
  overflow-y: auto;
465
487
  }
466
488
  textarea:focus {
467
- border-color: #65b782;
468
- box-shadow: 0 0 0 3px rgba(15, 143, 85, 0.13);
489
+ border-color: var(--accent);
490
+ box-shadow: 0 0 0 3px rgba(8, 127, 154, 0.18);
469
491
  outline: 0;
470
492
  }
471
493
  .actions {
@@ -501,8 +523,8 @@
501
523
  text-overflow: ellipsis;
502
524
  padding: var(--space-1) var(--space-4) var(--space-1) 18px;
503
525
  border-radius: 999px;
504
- background: rgba(240, 250, 244, 0.96);
505
- border: 1px solid rgba(172, 216, 188, 0.9);
526
+ background: rgba(230, 246, 248, 0.96);
527
+ border: 1px solid rgba(8, 127, 154, 0.35);
506
528
  font: inherit;
507
529
  font-size: 12px;
508
530
  cursor: pointer;
@@ -514,8 +536,8 @@
514
536
  display: none;
515
537
  }
516
538
  .spinner:focus-visible {
517
- border-color: #65b782;
518
- box-shadow: 0 0 0 3px rgba(15, 143, 85, 0.13);
539
+ border-color: var(--accent);
540
+ box-shadow: 0 0 0 3px rgba(8, 127, 154, 0.18);
519
541
  outline: 0;
520
542
  }
521
543
  .spinner-wrap:has(.spinner:not(:empty)) {
@@ -534,12 +556,12 @@
534
556
  animation: spinner-pulse 1.2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
535
557
  }
536
558
  .spinner.notify-enabled {
537
- color: #0b6b43;
538
- background: #e6f4eb;
539
- border-color: #4ca56f;
559
+ color: var(--success);
560
+ background: var(--success-soft);
561
+ border-color: var(--success-line);
540
562
  box-shadow:
541
- inset 0 1px 2px rgba(9, 93, 55, 0.16),
542
- inset 0 0 0 1px rgba(15, 143, 85, 0.1);
563
+ inset 0 1px 2px rgba(27, 57, 72, 0.08),
564
+ inset 0 0 0 1px rgba(36, 122, 82, 0.1);
543
565
  }
544
566
  .toast {
545
567
  position: absolute;
@@ -548,11 +570,11 @@
548
570
  z-index: 5;
549
571
  width: max-content;
550
572
  max-width: min(320px, calc(100vw - var(--space-6) * 2));
551
- border: 1px solid #bfd6c8;
573
+ border: 1px solid var(--line-strong);
552
574
  border-radius: var(--radius-sm);
553
- background: #f6fbf8;
575
+ background: var(--panel-raised);
554
576
  color: var(--ink);
555
- box-shadow: 0 8px 20px rgba(25, 35, 31, 0.12);
577
+ box-shadow: 0 8px 20px rgba(27, 57, 72, 0.14);
556
578
  padding: var(--space-4) var(--space-6);
557
579
  font-size: 12px;
558
580
  opacity: 0;
@@ -577,14 +599,14 @@
577
599
  }
578
600
  @keyframes thinking-breathe {
579
601
  0%, 100% {
580
- border-color: #b9dcc5;
602
+ border-color: var(--success-line);
581
603
  box-shadow: var(--entry-shadow);
582
604
  }
583
605
  50% {
584
- border-color: #72d494;
606
+ border-color: var(--success);
585
607
  box-shadow:
586
- 0 0 0 2px rgba(22, 168, 90, 0.16),
587
- 0 2px 7px rgba(25, 35, 31, 0.08);
608
+ 0 0 0 2px rgba(36, 122, 82, 0.14),
609
+ 0 2px 7px rgba(27, 57, 72, 0.08);
588
610
  }
589
611
  }
590
612
  @media (prefers-reduced-motion: reduce) {
@@ -683,12 +705,14 @@
683
705
  const markdownRenderer = window.markdownit
684
706
  ? window.markdownit({html: false, linkify: true, breaks: false})
685
707
  : null;
686
- const mathDelimiterPlaceholders = [
687
- ["\\[", "\uE000"],
688
- ["\\]", "\uE001"],
689
- ["\\(", "\uE002"],
690
- ["\\)", "\uE003"],
708
+ const mathDelimiters = [
709
+ {left: "$$", right: "$$", display: true},
710
+ {left: "\\[", right: "\\]", display: true},
711
+ {left: "\\(", right: "\\)", display: false},
712
+ {left: "$", right: "$", display: false},
691
713
  ];
714
+ const mathTokenPrefix = "\uE000math";
715
+ const mathTokenSuffix = "\uE001";
692
716
 
693
717
  function relativeUrl(path) {
694
718
  return new URL(path, window.location.href).toString();
@@ -725,41 +749,211 @@
725
749
  root.textContent = source || "";
726
750
  return;
727
751
  }
728
- let markdownSource = String(source || "");
729
- mathDelimiterPlaceholders.forEach(([delimiter, placeholder]) => {
730
- markdownSource = markdownSource.split(delimiter).join(placeholder);
731
- });
732
- const dirtyHtml = markdownRenderer.render(markdownSource);
752
+ const protectedMath = protectMath(String(source || ""));
753
+ const dirtyHtml = markdownRenderer.render(protectedMath.source);
733
754
  root.innerHTML = window.DOMPurify.sanitize(dirtyHtml, {
734
755
  USE_PROFILES: {html: true},
735
756
  ADD_ATTR: ["target", "rel"],
736
757
  });
758
+ renderProtectedMath(root, protectedMath.expressions);
759
+ root.querySelectorAll("a").forEach((anchor) => {
760
+ anchor.target = "_blank";
761
+ anchor.rel = "noopener noreferrer";
762
+ });
763
+ }
764
+
765
+ function isEscapedAt(source, index) {
766
+ let slashCount = 0;
767
+ for (let cursor = index - 1; cursor >= 0 && source[cursor] === "\\"; cursor -= 1) {
768
+ slashCount += 1;
769
+ }
770
+ return slashCount % 2 === 1;
771
+ }
772
+
773
+ function countBackticks(source, index) {
774
+ let count = 0;
775
+ while (source[index + count] === "`") {
776
+ count += 1;
777
+ }
778
+ return count;
779
+ }
780
+
781
+ function findMathEnd(source, start, delimiter) {
782
+ let searchFrom = start + delimiter.left.length;
783
+ while (searchFrom < source.length) {
784
+ const end = source.indexOf(delimiter.right, searchFrom);
785
+ if (end < 0) {
786
+ return -1;
787
+ }
788
+ if (!isEscapedAt(source, end)) {
789
+ if (
790
+ delimiter.left === "$" &&
791
+ (source[end - 1] === "$" || source[end + 1] === "$")
792
+ ) {
793
+ searchFrom = end + delimiter.right.length;
794
+ continue;
795
+ }
796
+ return end;
797
+ }
798
+ searchFrom = end + delimiter.right.length;
799
+ }
800
+ return -1;
801
+ }
802
+
803
+ function protectMath(source) {
804
+ const expressions = [];
805
+ let protectedSource = "";
806
+ let index = 0;
807
+ while (index < source.length) {
808
+ if (source[index] === "`") {
809
+ const tickCount = countBackticks(source, index);
810
+ const fence = "`".repeat(tickCount);
811
+ const close = source.indexOf(fence, index + tickCount);
812
+ if (close >= 0) {
813
+ const end = close + tickCount;
814
+ protectedSource += source.slice(index, end);
815
+ index = end;
816
+ continue;
817
+ }
818
+ }
819
+
820
+ let matched = null;
821
+ if (!isEscapedAt(source, index)) {
822
+ for (const delimiter of mathDelimiters) {
823
+ if (
824
+ source.indexOf(delimiter.left, index) !== index ||
825
+ (delimiter.left === "$" && source[index + 1] === "$")
826
+ ) {
827
+ continue;
828
+ }
829
+ const end = findMathEnd(source, index, delimiter);
830
+ if (end > index + delimiter.left.length) {
831
+ matched = {
832
+ delimiter: delimiter,
833
+ start: index,
834
+ end: end,
835
+ };
836
+ break;
837
+ }
838
+ }
839
+ }
840
+
841
+ if (matched) {
842
+ const expressionIndex = expressions.length;
843
+ expressions.push({
844
+ body: source.slice(
845
+ matched.start + matched.delimiter.left.length,
846
+ matched.end,
847
+ ),
848
+ delimiter: matched.delimiter,
849
+ });
850
+ protectedSource += mathTokenPrefix + expressionIndex + mathTokenSuffix;
851
+ index = matched.end + matched.delimiter.right.length;
852
+ continue;
853
+ }
854
+
855
+ protectedSource += source[index];
856
+ index += 1;
857
+ }
858
+ return {
859
+ source: protectedSource,
860
+ expressions: expressions,
861
+ };
862
+ }
863
+
864
+ function renderProtectedMath(root, expressions) {
865
+ if (!expressions.length) {
866
+ return;
867
+ }
868
+ const expressionByToken = new Map();
869
+ expressions.forEach((expression, index) => {
870
+ expressionByToken.set(
871
+ mathTokenPrefix + index + mathTokenSuffix,
872
+ expression,
873
+ );
874
+ });
875
+ const textWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
876
+ const textNodes = [];
877
+ let textNode = textWalker.nextNode();
878
+ while (textNode) {
879
+ textNodes.push(textNode);
880
+ textNode = textWalker.nextNode();
881
+ }
882
+ for (const textNode of textNodes) {
883
+ let value = textNode.nodeValue || "";
884
+ let cursor = 0;
885
+ let tokenIndex = value.indexOf(mathTokenPrefix, cursor);
886
+ if (tokenIndex < 0) {
887
+ continue;
888
+ }
889
+ const fragment = document.createDocumentFragment();
890
+ while (tokenIndex >= 0) {
891
+ const tokenEnd = value.indexOf(mathTokenSuffix, tokenIndex);
892
+ if (tokenEnd < 0) {
893
+ break;
894
+ }
895
+ const token = value.slice(tokenIndex, tokenEnd + mathTokenSuffix.length);
896
+ const expression = expressionByToken.get(token);
897
+ if (!expression) {
898
+ break;
899
+ }
900
+ if (tokenIndex > cursor) {
901
+ fragment.appendChild(
902
+ document.createTextNode(value.slice(cursor, tokenIndex)),
903
+ );
904
+ }
905
+ const mathNode = document.createElement("span");
906
+ const sourceText =
907
+ expression.delimiter.left +
908
+ expression.body +
909
+ expression.delimiter.right;
910
+ if (window.renderMathInElement) {
911
+ mathNode.textContent = sourceText;
912
+ window.renderMathInElement(mathNode, {
913
+ delimiters: [
914
+ {
915
+ left: expression.delimiter.left,
916
+ right: expression.delimiter.right,
917
+ display: expression.delimiter.display,
918
+ },
919
+ ],
920
+ throwOnError: false,
921
+ trust: false,
922
+ });
923
+ } else {
924
+ mathNode.textContent = expression.body;
925
+ }
926
+ fragment.appendChild(mathNode);
927
+ cursor = tokenEnd + mathTokenSuffix.length;
928
+ tokenIndex = value.indexOf(mathTokenPrefix, cursor);
929
+ }
930
+ if (cursor > 0) {
931
+ if (cursor < value.length) {
932
+ fragment.appendChild(document.createTextNode(value.slice(cursor)));
933
+ }
934
+ if (textNode.parentNode) {
935
+ textNode.parentNode.replaceChild(fragment, textNode);
936
+ }
937
+ }
938
+ }
939
+ restoreUnrenderedMathTokens(root, expressionByToken);
940
+ }
941
+
942
+ function restoreUnrenderedMathTokens(root, expressionByToken) {
737
943
  const textWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
738
944
  let textNode = textWalker.nextNode();
739
945
  while (textNode) {
740
946
  let value = textNode.nodeValue || "";
741
- mathDelimiterPlaceholders.forEach(([delimiter, placeholder]) => {
742
- value = value.split(placeholder).join(delimiter);
947
+ expressionByToken.forEach((expression, token) => {
948
+ const sourceText =
949
+ expression.delimiter.left +
950
+ expression.body +
951
+ expression.delimiter.right;
952
+ value = value.split(token).join(sourceText);
743
953
  });
744
954
  textNode.nodeValue = value;
745
955
  textNode = textWalker.nextNode();
746
956
  }
747
- if (window.renderMathInElement) {
748
- window.renderMathInElement(root, {
749
- delimiters: [
750
- {left: "$$", right: "$$", display: true},
751
- {left: "\\[", right: "\\]", display: true},
752
- {left: "\\(", right: "\\)", display: false},
753
- {left: "$", right: "$", display: false},
754
- ],
755
- throwOnError: false,
756
- trust: false,
757
- });
758
- }
759
- root.querySelectorAll("a").forEach((anchor) => {
760
- anchor.target = "_blank";
761
- anchor.rel = "noopener noreferrer";
762
- });
763
957
  }
764
958
 
765
959
  function loadChatWidth() {