@opencode-ai/ui 1.18.12 → 1.18.14

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 (93) hide show
  1. package/dist/context/i18n.d.ts +10 -1
  2. package/dist/context/marked-parser.d.ts +2 -0
  3. package/dist/context/marked-theme-register.d.ts +1 -0
  4. package/dist/context/marked-theme.d.ts +2 -0
  5. package/dist/context/marked.d.ts +2 -9
  6. package/dist/i18n/ar.d.ts +38 -0
  7. package/dist/i18n/az.d.ts +1 -0
  8. package/dist/i18n/br.d.ts +26 -0
  9. package/dist/i18n/bs.d.ts +26 -0
  10. package/dist/i18n/da.d.ts +22 -0
  11. package/dist/i18n/de.d.ts +22 -0
  12. package/dist/i18n/es.d.ts +26 -0
  13. package/dist/i18n/fi.d.ts +1 -0
  14. package/dist/i18n/fr.d.ts +26 -0
  15. package/dist/i18n/hi.d.ts +1 -0
  16. package/dist/i18n/id.d.ts +1 -0
  17. package/dist/i18n/it.d.ts +1 -0
  18. package/dist/i18n/ja.d.ts +22 -0
  19. package/dist/i18n/ko.d.ts +22 -0
  20. package/dist/i18n/nl.d.ts +1 -0
  21. package/dist/i18n/pa.d.ts +1 -0
  22. package/dist/i18n/pl.d.ts +30 -0
  23. package/dist/i18n/ru.d.ts +30 -0
  24. package/dist/i18n/sv.d.ts +1 -0
  25. package/dist/i18n/th.d.ts +22 -0
  26. package/dist/i18n/tr.d.ts +22 -0
  27. package/dist/i18n/ur.d.ts +1 -0
  28. package/dist/i18n/vi.d.ts +1 -0
  29. package/dist/i18n/zh.d.ts +22 -0
  30. package/dist/i18n/zht.d.ts +22 -0
  31. package/package.json +2 -3
  32. package/src/components/diff-changes.css +2 -0
  33. package/src/components/dropdown-menu.css +2 -2
  34. package/src/components/icon.css +5 -0
  35. package/src/components/icon.tsx +12 -1
  36. package/src/components/resize-handle.css +14 -2
  37. package/src/components/resize-handle.tsx +5 -1
  38. package/src/components/scroll-view.css +1 -1
  39. package/src/components/select.css +5 -3
  40. package/src/components/switch.css +4 -0
  41. package/src/components/tabs.css +54 -26
  42. package/src/components/tabs.tsx +1 -0
  43. package/src/components/text-field.css +2 -2
  44. package/src/components/toast.css +1 -1
  45. package/src/context/i18n.tsx +38 -2
  46. package/src/context/marked-parser.tsx +68 -0
  47. package/src/context/marked-theme-register.tsx +10 -0
  48. package/src/context/marked-theme.tsx +372 -0
  49. package/src/context/marked.tsx +20 -562
  50. package/src/i18n/ar.ts +62 -22
  51. package/src/i18n/az.ts +197 -0
  52. package/src/i18n/br.ts +34 -6
  53. package/src/i18n/bs.ts +51 -23
  54. package/src/i18n/da.ts +36 -12
  55. package/src/i18n/de.ts +58 -36
  56. package/src/i18n/en.ts +24 -0
  57. package/src/i18n/es.ts +45 -17
  58. package/src/i18n/fi.ts +197 -0
  59. package/src/i18n/fr.ts +51 -21
  60. package/src/i18n/hi.ts +199 -0
  61. package/src/i18n/id.ts +219 -0
  62. package/src/i18n/it.ts +202 -0
  63. package/src/i18n/ja.ts +29 -5
  64. package/src/i18n/ko.ts +36 -12
  65. package/src/i18n/nl.ts +197 -0
  66. package/src/i18n/no.ts +47 -23
  67. package/src/i18n/pa.ts +198 -0
  68. package/src/i18n/pl.ts +57 -24
  69. package/src/i18n/ru.ts +55 -23
  70. package/src/i18n/sv.ts +197 -0
  71. package/src/i18n/th.ts +44 -21
  72. package/src/i18n/tr.ts +49 -25
  73. package/src/i18n/uk.ts +47 -15
  74. package/src/i18n/ur.ts +198 -0
  75. package/src/i18n/vi.ts +197 -0
  76. package/src/i18n/zh.ts +52 -29
  77. package/src/i18n/zht.ts +45 -22
  78. package/src/v2/components/dialog-v2.css +2 -2
  79. package/src/v2/components/dialog-v2.tsx +3 -1
  80. package/src/v2/components/diff-changes-v2.css +2 -0
  81. package/src/v2/components/file-tree-v2.css +6 -2
  82. package/src/v2/components/inline-input-v2.tsx +3 -1
  83. package/src/v2/components/line-comment-v2.css +1 -1
  84. package/src/v2/components/line-comment-v2.tsx +6 -4
  85. package/src/v2/components/select-v2.css +16 -5
  86. package/src/v2/components/switch-v2.css +8 -0
  87. package/src/v2/components/tabs-v2.css +4 -4
  88. package/src/v2/components/tabs-v2.tsx +4 -2
  89. package/src/v2/components/text-input-v2.css +7 -5
  90. package/src/v2/components/text-input-v2.tsx +7 -1
  91. package/src/v2/components/toast-v2.tsx +8 -4
  92. package/dist/context/marked-code-span.d.ts +0 -10
  93. package/src/context/marked-code-span.ts +0 -17
@@ -61,7 +61,7 @@
61
61
  flex-shrink: 0;
62
62
  max-width: 280px;
63
63
  border-bottom: 1px solid var(--border-weak-base);
64
- border-right: 1px solid var(--border-weak-base);
64
+ border-inline-end: 1px solid var(--border-weak-base);
65
65
  background-color: var(--background-base);
66
66
 
67
67
  [data-slot="tabs-trigger"] {
@@ -111,10 +111,10 @@
111
111
  color: var(--text-strong);
112
112
  }
113
113
  &:has([data-slot="tabs-trigger-close-button"]) {
114
- padding-right: 12px;
114
+ padding-inline-end: 12px;
115
115
 
116
116
  [data-slot="tabs-trigger"] {
117
- padding-right: 0;
117
+ padding-inline-end: 0;
118
118
  }
119
119
  }
120
120
  }
@@ -141,8 +141,8 @@
141
141
 
142
142
  [data-slot="tabs-list"] {
143
143
  height: var(--tabs-bar-height);
144
- padding-left: 12px;
145
- padding-right: 0;
144
+ padding-inline-start: 12px;
145
+ padding-inline-end: 0;
146
146
  --tabs-review-gap: 16px;
147
147
  --tabs-review-fade: 16px;
148
148
  gap: var(--tabs-review-gap);
@@ -162,11 +162,15 @@
162
162
  position: absolute;
163
163
  top: 0;
164
164
  bottom: 0;
165
- left: calc(var(--tabs-review-fade) * -1);
165
+ inset-inline-end: 100%;
166
166
  width: var(--tabs-review-fade);
167
167
  pointer-events: none;
168
168
  background: linear-gradient(90deg, transparent, var(--background-stronger));
169
169
  }
170
+
171
+ &:dir(rtl)::before {
172
+ background: linear-gradient(270deg, transparent, var(--background-stronger));
173
+ }
170
174
  }
171
175
  }
172
176
 
@@ -213,9 +217,9 @@
213
217
  }
214
218
 
215
219
  &:has([data-slot="tabs-trigger-close-button"]) {
216
- padding-right: 5px;
220
+ padding-inline-end: 5px;
217
221
  [data-slot="tabs-trigger"] {
218
- padding-right: 0 !important;
222
+ padding-inline-end: 0 !important;
219
223
  }
220
224
  }
221
225
 
@@ -271,7 +275,7 @@
271
275
  #terminal-panel &[data-variant="normal"][data-orientation="horizontal"] {
272
276
  [data-slot="tabs-list"] {
273
277
  height: 52px;
274
- padding-right: 12px;
278
+ padding-inline-end: 12px;
275
279
  gap: 8px;
276
280
  }
277
281
 
@@ -291,7 +295,7 @@
291
295
  }
292
296
 
293
297
  &:has([data-slot="tabs-trigger-close-button"]) {
294
- padding-right: 8px;
298
+ padding-inline-end: 8px;
295
299
  }
296
300
 
297
301
  &:has([data-selected]) {
@@ -378,9 +382,9 @@
378
382
  }
379
383
 
380
384
  &:has([data-slot="tabs-trigger-close-button"]) {
381
- padding-right: 0;
385
+ padding-inline-end: 0;
382
386
  [data-slot="tabs-trigger"] {
383
- padding-right: 0;
387
+ padding-inline-end: 0;
384
388
  }
385
389
  }
386
390
  }
@@ -490,7 +494,7 @@
490
494
  padding: 8px;
491
495
  gap: 4px;
492
496
  background-color: var(--background-base);
493
- border-right: 1px solid var(--border-weak-base);
497
+ border-inline-end: 1px solid var(--border-weak-base);
494
498
 
495
499
  &::after {
496
500
  display: none;
@@ -572,7 +576,7 @@
572
576
  padding: 12px;
573
577
  gap: 0;
574
578
  background-color: var(--background-base);
575
- border-right: 1px solid var(--border-weak-base);
579
+ border-inline-end: 1px solid var(--border-weak-base);
576
580
 
577
581
  &::after {
578
582
  display: none;
@@ -581,7 +585,8 @@
581
585
 
582
586
  [data-slot="tabs-section-title"] {
583
587
  width: 100%;
584
- padding: 0 0 0 4px;
588
+ padding: 0;
589
+ padding-inline-start: 4px;
585
590
  font-family: var(--font-family-sans);
586
591
  font-size: var(--font-size-small);
587
592
  font-weight: var(--font-weight-medium);
@@ -695,6 +700,10 @@ body[data-new-layout] #terminal-panel [data-component="tabs"][data-variant="norm
695
700
  &::before {
696
701
  background: linear-gradient(90deg, transparent, var(--v2-background-bg-base));
697
702
  }
703
+
704
+ &:dir(rtl)::before {
705
+ background: linear-gradient(270deg, transparent, var(--v2-background-bg-base));
706
+ }
698
707
  }
699
708
  }
700
709
  }
@@ -702,7 +711,7 @@ body[data-new-layout] #terminal-panel [data-component="tabs"][data-variant="norm
702
711
  body[data-new-layout] #review-panel [data-component="tabs"][data-variant="normal"][data-orientation="horizontal"] {
703
712
  [data-slot="tabs-list"] {
704
713
  height: 52px;
705
- padding-right: 12px;
714
+ padding-inline-end: 12px;
706
715
  gap: 8px;
707
716
  --tabs-review-fade: 8px;
708
717
  }
@@ -723,10 +732,10 @@ body[data-new-layout] #review-panel [data-component="tabs"][data-variant="normal
723
732
  }
724
733
 
725
734
  &:has([data-slot="tabs-trigger-close-button"]) {
726
- padding-right: 8px;
735
+ padding-inline-end: 8px;
727
736
 
728
737
  [data-slot="tabs-trigger-close-button"] {
729
- margin-left: 2px;
738
+ margin-inline-start: 2px;
730
739
  }
731
740
  }
732
741
 
@@ -754,7 +763,7 @@ body[data-new-layout] #review-panel [data-component="tabs"][data-variant="normal
754
763
  .tab-fileicon-color,
755
764
  .tab-fileicon-mono,
756
765
  [data-slot="icon-svg"] {
757
- margin-left: -2px;
766
+ margin-inline-start: -2px;
758
767
  }
759
768
  }
760
769
 
@@ -792,7 +801,7 @@ body[data-new-layout]
792
801
  .session-review-v2-tabs-bar
793
802
  [data-slot="tabs-list"]
794
803
  > .sticky {
795
- padding-right: 0;
804
+ padding-inline-end: 0;
796
805
 
797
806
  [data-component="icon-button-v2"] {
798
807
  position: relative;
@@ -800,7 +809,7 @@ body[data-new-layout]
800
809
  &::after {
801
810
  content: "";
802
811
  position: absolute;
803
- left: 100%;
812
+ inset-inline-start: 100%;
804
813
  top: 50%;
805
814
  transform: translateY(-50%);
806
815
  width: 20px;
@@ -811,21 +820,35 @@ body[data-new-layout]
811
820
  }
812
821
  }
813
822
 
823
+ body[data-new-layout]
824
+ #review-panel
825
+ [data-component="tabs"]
826
+ .session-review-v2-tabs-bar
827
+ [data-slot="tabs-list"]
828
+ > .sticky:not(.session-review-v2-sidebar-toggle-slot) {
829
+ left: auto;
830
+ right: auto;
831
+ inset-inline-end: 0;
832
+ }
833
+
814
834
  body[data-new-layout]
815
835
  #review-panel
816
836
  [data-component="tabs"]
817
837
  .session-review-v2-tabs-bar
818
838
  [data-slot="tabs-list"]
819
839
  > .session-review-v2-sidebar-toggle-slot.sticky {
820
- padding-right: 0;
840
+ left: auto;
841
+ right: auto;
842
+ inset-inline-start: 0;
843
+ padding-inline-end: 0;
821
844
 
822
845
  &::before {
823
846
  content: "";
824
847
  position: absolute;
825
848
  top: 50%;
826
849
  bottom: auto;
827
- left: auto;
828
- right: 100%;
850
+ inset-inline-start: auto;
851
+ inset-inline-end: 100%;
829
852
  transform: translateY(-50%);
830
853
  width: 12px;
831
854
  height: 28px;
@@ -838,7 +861,8 @@ body[data-new-layout]
838
861
  content: "";
839
862
  position: absolute;
840
863
  top: 50%;
841
- left: 100%;
864
+ inset-inline-start: 100%;
865
+ inset-inline-end: auto;
842
866
  transform: translateY(-50%);
843
867
  width: 8px;
844
868
  height: 28px;
@@ -846,6 +870,10 @@ body[data-new-layout]
846
870
  background: linear-gradient(90deg, var(--v2-background-bg-base), transparent);
847
871
  }
848
872
 
873
+ &:dir(rtl)::after {
874
+ background: linear-gradient(270deg, var(--v2-background-bg-base), transparent);
875
+ }
876
+
849
877
  [data-component="icon-button-v2"]::after {
850
878
  display: none;
851
879
  }
@@ -853,7 +881,7 @@ body[data-new-layout]
853
881
 
854
882
  body[data-new-layout] #review-panel [data-component="tabs"] .session-review-v2-open-in-app-slot {
855
883
  flex-shrink: 0;
856
- margin-left: auto;
884
+ margin-inline-start: auto;
857
885
  }
858
886
 
859
887
  body[data-new-layout] #review-panel [data-component="tabs"] .session-review-v2-open-in-app {
@@ -80,6 +80,7 @@ function TabsTrigger(props: ParentProps<TabsTriggerProps>) {
80
80
  >
81
81
  <Kobalte.Trigger
82
82
  {...rest}
83
+ dir={rest.dir ?? "auto"}
83
84
  data-slot="tabs-trigger"
84
85
  data-value={props.value}
85
86
  classList={{ [split.classes?.button ?? ""]: split.classes?.button }}
@@ -45,7 +45,7 @@
45
45
  align-items: start;
46
46
  justify-content: space-between;
47
47
  width: 100%;
48
- padding-right: 4px;
48
+ padding-inline-end: 4px;
49
49
 
50
50
  border-radius: var(--radius-md);
51
51
  border: 1px solid var(--border-weak-base);
@@ -68,7 +68,7 @@
68
68
  }
69
69
 
70
70
  &:not(:has([data-slot="input-copy-button"])) {
71
- padding-right: 0;
71
+ padding-inline-end: 0;
72
72
  }
73
73
  }
74
74
 
@@ -1,7 +1,7 @@
1
1
  [data-component="toast-region"] {
2
2
  position: fixed;
3
3
  bottom: 48px;
4
- right: 32px;
4
+ inset-inline-end: 32px;
5
5
  z-index: 1000;
6
6
  display: flex;
7
7
  flex-direction: column;
@@ -1,13 +1,41 @@
1
1
  import { createContext, useContext, type Accessor, type ParentProps } from "solid-js"
2
+ import { I18nProvider } from "@kobalte/core/i18n"
2
3
  import { dict as en } from "../i18n/en"
3
4
 
4
5
  export type UiI18nKey = keyof typeof en
5
6
 
7
+ export const UI_PLURAL_KEYS = [
8
+ "ui.sessionTurn.diffs.changed",
9
+ "ui.messagePart.context.read",
10
+ "ui.messagePart.context.search",
11
+ "ui.messagePart.context.list",
12
+ ] as const
13
+ export type UiI18nPluralKey = (typeof UI_PLURAL_KEYS)[number]
14
+ export type UiPluralCategory = "zero" | "one" | "two" | "few" | "many" | "other"
15
+ export type UiI18nPluralLookupKey = `${UiI18nPluralKey}.${UiPluralCategory}`
16
+
6
17
  export type UiI18nParams = Record<string, string | number | boolean>
7
18
 
8
19
  export type UiI18n = {
9
20
  locale: Accessor<string>
21
+ layoutLocale?: Accessor<string>
10
22
  t: (key: UiI18nKey, params?: UiI18nParams) => string
23
+ plural: (key: UiI18nPluralKey, count: number, params?: UiI18nParams) => string
24
+ }
25
+
26
+ const rules = new Map<string, Intl.PluralRules>()
27
+
28
+ export function pluralCategory(locale: string, count: number): UiPluralCategory {
29
+ const cached = rules.get(locale)
30
+ if (cached) return cached.select(count)
31
+ const next = new Intl.PluralRules(locale)
32
+ if (rules.size >= 32) rules.delete(rules.keys().next().value!)
33
+ rules.set(locale, next)
34
+ return next.select(count)
35
+ }
36
+
37
+ export function pluralKey(key: UiI18nPluralKey, category: UiPluralCategory) {
38
+ return `${key}.${category}` as UiI18nPluralLookupKey
11
39
  }
12
40
 
13
41
  function resolveTemplate(text: string, params?: UiI18nParams) {
@@ -25,14 +53,22 @@ const fallback: UiI18n = {
25
53
  const value = en[key] ?? String(key)
26
54
  return resolveTemplate(value, params)
27
55
  },
56
+ plural: (key, count, params) =>
57
+ fallback.t(pluralKey(key, pluralCategory(fallback.locale(), count)), { ...params, count }),
28
58
  }
29
59
 
30
60
  const Context = createContext<UiI18n>(fallback)
31
61
 
32
- export function I18nProvider(props: ParentProps<{ value: UiI18n }>) {
33
- return <Context.Provider value={props.value}>{props.children}</Context.Provider>
62
+ function UiI18nProvider(props: ParentProps<{ value: UiI18n }>) {
63
+ return (
64
+ <I18nProvider locale={(props.value.layoutLocale ?? props.value.locale)()}>
65
+ <Context.Provider value={props.value}>{props.children}</Context.Provider>
66
+ </I18nProvider>
67
+ )
34
68
  }
35
69
 
70
+ export { UiI18nProvider as I18nProvider }
71
+
36
72
  export function useI18n() {
37
73
  return useContext(Context)
38
74
  }
@@ -0,0 +1,68 @@
1
+ import katex from "katex"
2
+ import { Marked, type MarkedExtension, type Tokens } from "marked"
3
+ import markedShiki from "marked-shiki"
4
+
5
+ export function createMarkdownParser(highlight: (code: string, language: string) => string | Promise<string>) {
6
+ return new Marked(
7
+ {
8
+ renderer: {
9
+ link({ href, title, text }) {
10
+ const titleAttr = title ? ` title="${title}"` : ""
11
+ return `<a href="${href}"${titleAttr} class="external-link" target="_blank" rel="noopener noreferrer">${text}</a>`
12
+ },
13
+ },
14
+ },
15
+ katexExtension,
16
+ markedShiki({ highlight }),
17
+ )
18
+ }
19
+
20
+ const inlineMathRegex = /^\\\(((?:\\.|[^\\\n])*?)\\\)/
21
+ const blockMathRegex = /^\$\$\n([\s\S]+?)\n\$\$(?:\n|$)/
22
+
23
+ const katexExtension: MarkedExtension = {
24
+ extensions: [
25
+ {
26
+ name: "inlineKatex",
27
+ level: "inline",
28
+ start(src) {
29
+ const index = src.indexOf("\\(")
30
+ if (index === -1) return
31
+ return index
32
+ },
33
+ tokenizer(src) {
34
+ const match = src.match(inlineMathRegex)
35
+ if (!match) return
36
+ return {
37
+ type: "inlineKatex",
38
+ raw: match[0],
39
+ text: match[1].trim(),
40
+ displayMode: false,
41
+ }
42
+ },
43
+ renderer: renderKatexToken,
44
+ },
45
+ {
46
+ name: "blockKatex",
47
+ level: "block",
48
+ tokenizer(src) {
49
+ const match = src.match(blockMathRegex)
50
+ if (!match) return
51
+ return {
52
+ type: "blockKatex",
53
+ raw: match[0],
54
+ text: match[1].trim(),
55
+ displayMode: true,
56
+ }
57
+ },
58
+ renderer: renderKatexToken,
59
+ },
60
+ ],
61
+ }
62
+
63
+ function renderKatexToken(token: Tokens.Generic) {
64
+ return katex.renderToString(typeof token.text === "string" ? token.text : "", {
65
+ displayMode: token.displayMode === true,
66
+ throwOnError: false,
67
+ })
68
+ }
@@ -0,0 +1,10 @@
1
+ import { registerCustomTheme } from "@pierre/diffs"
2
+ import { OpenCodeTheme } from "./marked-theme"
3
+
4
+ let registered = false
5
+
6
+ export function registerOpenCodeTheme() {
7
+ if (registered) return
8
+ registered = true
9
+ registerCustomTheme("OpenCode", () => Promise.resolve(OpenCodeTheme))
10
+ }