@pgcorp/ui-kit 0.8.1 → 0.10.0
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.
- package/README.md +332 -9
- package/docs/getting-started.md +135 -0
- package/docs/public-api.md +53 -4
- package/docs/rich-document.md +216 -0
- package/docs/scroll-viewport.md +167 -0
- package/docs/theming.md +17 -0
- package/package.json +108 -50
- package/src/components/layout/SAppShell.vue +3 -1
- package/src/components/layout/SDockRegion.vue +5 -3
- package/src/components/layout/SResizeHandle.vue +45 -5
- package/src/components/layout/SWorkbenchPanelShell.vue +4 -2
- package/src/components/shared/_internal/SDialogSurface.css +23 -0
- package/src/components/shared/_internal/SDialogSurface.vue +9 -2
- package/src/components/shared/_internal/SToastItem.css +4 -0
- package/src/components/shared/_internal/SToastItem.vue +44 -8
- package/src/components/shared/_internal/STreeNode.vue +24 -11
- package/src/components/shared/_internal/dialogSurface.ts +7 -1
- package/src/components/shared/complex/SKanbanLane.vue +4 -2
- package/src/components/shared/containers/SCloudChromeRow.vue +3 -2
- package/src/components/shared/containers/SDrawer.css +11 -2
- package/src/components/shared/containers/SDrawer.vue +48 -9
- package/src/components/shared/containers/SModal.vue +5 -2
- package/src/components/shared/containers/SPanel.vue +4 -2
- package/src/components/shared/containers/SPopover.vue +7 -3
- package/src/components/shared/containers/SUiKitLocaleProvider.vue +57 -0
- package/src/components/shared/controls/SButton.css +14 -10
- package/src/components/shared/controls/SCombobox.vue +8 -6
- package/src/components/shared/controls/SComboboxTrigger.css +7 -4
- package/src/components/shared/controls/SComboboxTrigger.vue +5 -2
- package/src/components/shared/controls/SContextToggleButton.vue +6 -4
- package/src/components/shared/controls/SDragHandle.vue +8 -4
- package/src/components/shared/controls/SDropdownMenu.vue +7 -4
- package/src/components/shared/controls/SInputText.css +5 -4
- package/src/components/shared/controls/SListbox.vue +4 -2
- package/src/components/shared/controls/SSelect.vue +4 -2
- package/src/components/shared/controls/STextarea.ts +13 -0
- package/src/components/shared/controls/STextarea.vue +391 -37
- package/src/components/shared/controls/_internal/SInlineTokenSurface.vue +4 -2
- package/src/components/shared/data-display/SChart.vue +7 -5
- package/src/components/shared/data-display/SChip.vue +3 -1
- package/src/components/shared/data-display/SCodeBlock.css +4 -0
- package/src/components/shared/data-display/SCodeBlock.vue +46 -22
- package/src/components/shared/data-display/SCodeEditor.css +17 -0
- package/src/components/shared/data-display/SCodeEditor.vue +173 -8
- package/src/components/shared/data-display/SCodeSearchPanel.vue +48 -33
- package/src/components/shared/data-display/SCodeWorkspaceEditor.css +13 -0
- package/src/components/shared/data-display/SCodeWorkspaceEditor.vue +355 -0
- package/src/components/shared/data-display/SControlledToastViewport.css +89 -0
- package/src/components/shared/data-display/SControlledToastViewport.vue +205 -0
- package/src/components/shared/data-display/SCopyField.vue +12 -10
- package/src/components/shared/data-display/SDiffViewer.css +122 -0
- package/src/components/shared/data-display/SDiffViewer.vue +499 -0
- package/src/components/shared/data-display/SDocBlock.vue +99 -71
- package/src/components/shared/data-display/SExpandableText.vue +10 -8
- package/src/components/shared/data-display/SJsonTree.vue +4 -2
- package/src/components/shared/data-display/SLinkedSystemsList.vue +4 -2
- package/src/components/shared/data-display/SMessage.vue +6 -6
- package/src/components/shared/data-display/SRichDocument.css +8 -0
- package/src/components/shared/data-display/SRichDocument.vue +65 -0
- package/src/components/shared/data-display/SScrollViewport.css +53 -0
- package/src/components/shared/data-display/SScrollViewport.vue +470 -0
- package/src/components/shared/data-display/STable.vue +38 -19
- package/src/components/shared/data-display/SToastContainer.vue +22 -17
- package/src/components/shared/data-display/SVirtualList.ts +33 -0
- package/src/components/shared/data-display/SVirtualList.vue +321 -36
- package/src/components/shared/data-display/_internal/SCodeTokenLine.css +72 -0
- package/src/components/shared/data-display/_internal/SCodeTokenLine.vue +79 -0
- package/src/components/shared/data-display/_internal/SRichBlockNode.css +85 -0
- package/src/components/shared/data-display/_internal/SRichBlockNode.vue +282 -0
- package/src/components/shared/data-display/_internal/SRichInlineNode.css +6 -0
- package/src/components/shared/data-display/_internal/SRichInlineNode.vue +76 -0
- package/src/components/shared/data-display/_internal/SRichMath.css +42 -0
- package/src/components/shared/data-display/_internal/SRichMath.vue +102 -0
- package/src/components/shared/data-display/codeEditor.ts +200 -0
- package/src/components/shared/data-display/codeWorkspace.ts +233 -0
- package/src/components/shared/data-display/diff.ts +541 -0
- package/src/components/shared/data-display/richDocument.ts +876 -0
- package/src/components/shared/data-display/scrollViewport.ts +47 -0
- package/src/components/shared/data-display/toast.ts +194 -0
- package/src/components/shared/database/SDataGrid.vue +7 -4
- package/src/components/shared/database/SSqlEditor.vue +39 -39
- package/src/components/shared/feedback/SAsyncState.vue +3 -1
- package/src/components/shared/graph/SGraphViewport.vue +11 -9
- package/src/components/shared/navigation/SBreadcrumbs.vue +8 -5
- package/src/components/shared/navigation/SCatalogNavigator.vue +26 -20
- package/src/components/shared/navigation/STabList.vue +10 -6
- package/src/components/shared/navigation/STabs.vue +15 -11
- package/src/components/shared/navigation/SWizardSteps.vue +4 -2
- package/src/components/shared/persona/SPersonaProjectPicker.vue +31 -27
- package/src/components/shared/persona/SPersonaRunWorkbench.vue +39 -33
- package/src/components/shared/persona/_internal/SPersonaRunConsole.vue +8 -6
- package/src/components/shared/persona/_internal/labels.ts +66 -57
- package/src/components/shared/persona/presentation.ts +140 -110
- package/src/composables/useFloatingPosition.ts +39 -2
- package/src/composables/useUiKitLocale.ts +17 -0
- package/src/internal/codeHighlight.ts +110 -0
- package/src/internal/diffEngine.ts +464 -0
- package/src/internal/diffRuntime.ts +189 -0
- package/src/internal/feedbackPresentation.ts +33 -14
- package/src/internal/localeContext.ts +22 -0
- package/src/internal/ownedAttrs.ts +8 -0
- package/src/internal/runtimeContract.ts +11 -9
- package/src/internal/semanticSizing.ts +13 -0
- package/src/locales/catalog.ts +634 -0
- package/src/locales/locale.ts +472 -0
- package/src/locales/messages.generated.ts +1826 -0
- package/src/locales/messages.ts +150 -0
- package/src/stores/useNotifierStore.ts +61 -44
- package/src/styles/tokens.css +23 -2
- package/src/theme.ts +59 -32
- package/src/workers/diff.worker.ts +73 -0
- package/src/workers/diffProtocol.ts +155 -0
- package/src/components/shared/data-display/SToastContainer.css +0 -53
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
UiKitLocale,
|
|
3
|
+
UiKitMessageKey,
|
|
4
|
+
UiKitMessageParameters,
|
|
5
|
+
UiKitMessages,
|
|
6
|
+
UiKitMessagesOverride,
|
|
7
|
+
UiKitParameterizedMessageKey,
|
|
8
|
+
UiKitStaticMessageKey,
|
|
9
|
+
UiKitTranslate,
|
|
10
|
+
} from './locale'
|
|
11
|
+
import { UI_KIT_MESSAGE_SCHEMA, UI_KIT_MESSAGE_VALUES } from './messages.generated'
|
|
12
|
+
import { hasOwn } from '../internal/es2020'
|
|
13
|
+
|
|
14
|
+
type MessageRecord = Readonly<Record<string, unknown>>
|
|
15
|
+
|
|
16
|
+
function isRecord(value: unknown): value is MessageRecord {
|
|
17
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function deepFreeze<T>(value: T): Readonly<T> {
|
|
21
|
+
if (!isRecord(value)) return value
|
|
22
|
+
for (const child of Object.values(value)) deepFreeze(child)
|
|
23
|
+
return Object.freeze(value)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function buildMessages(locale: UiKitLocale, values: readonly string[]): Readonly<UiKitMessages> {
|
|
27
|
+
const bundle: Record<string, Record<string, string>> = {}
|
|
28
|
+
let index = 0
|
|
29
|
+
for (const [groupName, messageNames] of UI_KIT_MESSAGE_SCHEMA) {
|
|
30
|
+
const group: Record<string, string> = {}
|
|
31
|
+
for (const messageName of messageNames) {
|
|
32
|
+
const message = values[index]
|
|
33
|
+
if (typeof message !== 'string' || message.trim().length === 0) {
|
|
34
|
+
throw new TypeError(`UI-kit locale ${locale}: message ${groupName}.${messageName} отсутствует.`)
|
|
35
|
+
}
|
|
36
|
+
group[messageName] = message
|
|
37
|
+
index += 1
|
|
38
|
+
}
|
|
39
|
+
bundle[groupName] = group
|
|
40
|
+
}
|
|
41
|
+
if (index !== values.length) {
|
|
42
|
+
throw new TypeError(`UI-kit locale ${locale}: compact catalog содержит лишние значения.`)
|
|
43
|
+
}
|
|
44
|
+
return deepFreeze(bundle) as Readonly<UiKitMessages>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function messageAtPath(messages: unknown, key: string): string {
|
|
48
|
+
let current: unknown = messages
|
|
49
|
+
for (const segment of key.split('.')) {
|
|
50
|
+
if (!isRecord(current) || !hasOwn(current, segment)) {
|
|
51
|
+
throw new TypeError(`UI-kit locale: неизвестный message key ${JSON.stringify(key)}.`)
|
|
52
|
+
}
|
|
53
|
+
current = current[segment]
|
|
54
|
+
}
|
|
55
|
+
if (typeof current !== 'string') {
|
|
56
|
+
throw new TypeError(`UI-kit locale: message key ${JSON.stringify(key)} не является строкой.`)
|
|
57
|
+
}
|
|
58
|
+
return current
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function placeholders(message: string): readonly string[] {
|
|
62
|
+
return [...message.matchAll(/\{([A-Za-z][A-Za-z0-9]*)\}/gu)]
|
|
63
|
+
.map((match) => match[1] ?? '')
|
|
64
|
+
.filter((value, index, values) => value.length > 0 && values.indexOf(value) === index)
|
|
65
|
+
.sort()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const schemaKeys = Object.freeze(UI_KIT_MESSAGE_SCHEMA.flatMap(([groupName, messageNames]) => (
|
|
69
|
+
messageNames.map((messageName) => `${groupName}.${messageName}` as UiKitMessageKey)
|
|
70
|
+
)))
|
|
71
|
+
const schemaKeySet = new Set<string>(schemaKeys)
|
|
72
|
+
|
|
73
|
+
/** Complete immutable message bundles for every supported locale. */
|
|
74
|
+
export const UI_KIT_MESSAGES: Readonly<Record<UiKitLocale, Readonly<UiKitMessages>>> = deepFreeze({
|
|
75
|
+
en: buildMessages('en', UI_KIT_MESSAGE_VALUES.en),
|
|
76
|
+
ru: buildMessages('ru', UI_KIT_MESSAGE_VALUES.ru),
|
|
77
|
+
'zh-CN': buildMessages('zh-CN', UI_KIT_MESSAGE_VALUES['zh-CN']),
|
|
78
|
+
'zh-TW': buildMessages('zh-TW', UI_KIT_MESSAGE_VALUES['zh-TW']),
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
function mergeRecord(base: MessageRecord, override: unknown, coordinate: string): MessageRecord {
|
|
82
|
+
if (!isRecord(override)) {
|
|
83
|
+
throw new TypeError(`${coordinate} должен быть object.`)
|
|
84
|
+
}
|
|
85
|
+
const unexpected = Object.keys(override).filter((key) => !hasOwn(base, key))
|
|
86
|
+
if (unexpected.length > 0) {
|
|
87
|
+
throw new TypeError(`${coordinate}: неизвестные поля ${unexpected.join(', ')}.`)
|
|
88
|
+
}
|
|
89
|
+
const merged: Record<string, unknown> = { ...base }
|
|
90
|
+
for (const [key, value] of Object.entries(override)) {
|
|
91
|
+
const path = `${coordinate}.${key}`
|
|
92
|
+
const baseValue = base[key]
|
|
93
|
+
if (typeof baseValue === 'string') {
|
|
94
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
95
|
+
throw new TypeError(`${path} должен быть непустой строкой.`)
|
|
96
|
+
}
|
|
97
|
+
if (placeholders(value).join('\n') !== placeholders(baseValue).join('\n')) {
|
|
98
|
+
throw new TypeError(`${path} должен сохранять placeholders ${placeholders(baseValue).join(', ')}.`)
|
|
99
|
+
}
|
|
100
|
+
merged[key] = value
|
|
101
|
+
} else if (isRecord(baseValue)) {
|
|
102
|
+
merged[key] = mergeRecord(baseValue, value, path)
|
|
103
|
+
} else {
|
|
104
|
+
throw new TypeError(`${path}: base schema повреждена.`)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return merged
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Validates and immutably applies a provider-level exact deep override. */
|
|
111
|
+
export function mergeUiKitMessages(
|
|
112
|
+
base: Readonly<UiKitMessages>,
|
|
113
|
+
override: UiKitMessagesOverride | undefined,
|
|
114
|
+
): Readonly<UiKitMessages> {
|
|
115
|
+
if (override === undefined) return base
|
|
116
|
+
return deepFreeze(mergeRecord(base, override, 'SUiKitLocaleProvider.messages')) as Readonly<UiKitMessages>
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function translate(
|
|
120
|
+
messages: Readonly<UiKitMessages>,
|
|
121
|
+
key: UiKitMessageKey,
|
|
122
|
+
parameters?: Readonly<Record<string, string | number>>,
|
|
123
|
+
): string {
|
|
124
|
+
if (!schemaKeySet.has(key)) throw new TypeError(`UI-kit locale: неизвестный message key ${JSON.stringify(key)}.`)
|
|
125
|
+
const template = messageAtPath(messages, key)
|
|
126
|
+
const required = placeholders(template)
|
|
127
|
+
const received = parameters === undefined ? [] : Object.keys(parameters).sort()
|
|
128
|
+
if (required.join('\n') !== received.join('\n')) {
|
|
129
|
+
throw new TypeError(
|
|
130
|
+
`UI-kit locale: ${key} ожидает параметры [${required.join(', ')}], получено [${received.join(', ')}].`,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
return template.replace(/\{([A-Za-z][A-Za-z0-9]*)\}/gu, (_match, name: string) => String(parameters?.[name]))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Creates a strict translator bound to a reactive message accessor. */
|
|
137
|
+
export function createUiKitTranslate(messages: () => Readonly<UiKitMessages>): UiKitTranslate {
|
|
138
|
+
function t<Key extends UiKitStaticMessageKey>(key: Key): string
|
|
139
|
+
function t<Key extends UiKitParameterizedMessageKey>(key: Key, parameters: UiKitMessageParameters[Key]): string
|
|
140
|
+
function t(
|
|
141
|
+
key: UiKitMessageKey,
|
|
142
|
+
parameters?: Readonly<Record<string, string | number>>,
|
|
143
|
+
): string {
|
|
144
|
+
return translate(messages(), key, parameters)
|
|
145
|
+
}
|
|
146
|
+
return t
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Stable complete key inventory used by package and locale parity proofs. */
|
|
150
|
+
export const UI_KIT_MESSAGE_KEYS: readonly UiKitMessageKey[] = schemaKeys
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
|
+
import { onScopeDispose, ref } from 'vue'
|
|
2
3
|
import { v4 as uuidv4 } from 'uuid'
|
|
3
4
|
import type { FeedbackSeverity } from '../internal/feedbackPresentation'
|
|
4
5
|
|
|
@@ -16,54 +17,70 @@ export interface Notification {
|
|
|
16
17
|
|
|
17
18
|
export type NotificationInput = Omit<Notification, 'id' | 'createdAt'>
|
|
18
19
|
|
|
19
|
-
interface NotifierState {
|
|
20
|
-
notifications: Notification[]
|
|
21
|
-
history: Notification[]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
20
|
const DEFAULT_NOTIFICATION_DURATION_MS = 2800
|
|
25
|
-
|
|
21
|
+
/** Maximum number of records retained by the autonomous notification history. */
|
|
22
|
+
export const NOTIFIER_HISTORY_CAPACITY = 100
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
export const useNotifierStore = defineStore('notifier', () => {
|
|
25
|
+
const notifications = ref<Notification[]>([])
|
|
26
|
+
const history = ref<Notification[]>([])
|
|
27
|
+
const removalTimers = new Map<string, ReturnType<typeof setTimeout>>()
|
|
28
|
+
|
|
29
|
+
function stopRemovalTimer(id: string): void {
|
|
30
|
+
const timer = removalTimers.get(id)
|
|
31
|
+
if (!timer) return
|
|
32
|
+
clearTimeout(timer)
|
|
33
|
+
removalTimers.delete(id)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function clearRemovalTimers(): void {
|
|
37
|
+
for (const timer of removalTimers.values()) clearTimeout(timer)
|
|
38
|
+
removalTimers.clear()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function remove(id: string): void {
|
|
42
|
+
stopRemovalTimer(id)
|
|
43
|
+
notifications.value = notifications.value.filter((notification) => notification.id !== id)
|
|
31
44
|
}
|
|
32
|
-
clearTimeout(timer)
|
|
33
|
-
removalTimers.delete(id)
|
|
34
|
-
}
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const newNotification: Notification = {
|
|
45
|
-
id,
|
|
46
|
-
createdAt: new Date().toISOString(),
|
|
47
|
-
duration: DEFAULT_NOTIFICATION_DURATION_MS,
|
|
48
|
-
...notification,
|
|
49
|
-
}
|
|
46
|
+
function add(notification: NotificationInput): void {
|
|
47
|
+
const id = uuidv4()
|
|
48
|
+
const newNotification: Notification = {
|
|
49
|
+
id,
|
|
50
|
+
createdAt: new Date().toISOString(),
|
|
51
|
+
duration: DEFAULT_NOTIFICATION_DURATION_MS,
|
|
52
|
+
...notification,
|
|
53
|
+
}
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
history.value.unshift(newNotification)
|
|
56
|
+
if (history.value.length > NOTIFIER_HISTORY_CAPACITY) {
|
|
57
|
+
history.value.splice(NOTIFIER_HISTORY_CAPACITY)
|
|
58
|
+
}
|
|
59
|
+
notifications.value.push(newNotification)
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
if (newNotification.duration) {
|
|
62
|
+
removalTimers.set(id, setTimeout(() => remove(id), newNotification.duration))
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function clearHistory(): void {
|
|
67
|
+
history.value = []
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function $reset(): void {
|
|
71
|
+
clearRemovalTimers()
|
|
72
|
+
notifications.value = []
|
|
73
|
+
history.value = []
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
onScopeDispose(clearRemovalTimers)
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
notifications,
|
|
80
|
+
history,
|
|
81
|
+
add,
|
|
82
|
+
remove,
|
|
83
|
+
clearHistory,
|
|
84
|
+
$reset,
|
|
85
|
+
}
|
|
69
86
|
})
|
package/src/styles/tokens.css
CHANGED
|
@@ -111,11 +111,15 @@
|
|
|
111
111
|
--safe-area-left: env(safe-area-inset-left, 0px);
|
|
112
112
|
|
|
113
113
|
/* Semantic interaction geometry */
|
|
114
|
-
--s-control-hitbox-
|
|
115
|
-
--s-control-hitbox-
|
|
114
|
+
--s-control-hitbox-2xs: 1.5rem;
|
|
115
|
+
--s-control-hitbox-xs: 2rem;
|
|
116
|
+
--s-control-hitbox-sm: 2.25rem;
|
|
116
117
|
--s-control-hitbox-md: 2.5rem;
|
|
117
118
|
--s-control-hitbox-lg: 3rem;
|
|
118
119
|
--s-control-indicator-size: 1.25rem;
|
|
120
|
+
--s-resize-handle-hitbox-compact: 0.375rem;
|
|
121
|
+
--s-resize-handle-hitbox-standard: 0.75rem;
|
|
122
|
+
--s-resize-handle-hitbox-spacious: 2rem;
|
|
119
123
|
--s-icon-size-2xs: 0.75rem;
|
|
120
124
|
--s-icon-size-xs: 1rem;
|
|
121
125
|
--s-icon-size-sm: 1.25rem;
|
|
@@ -264,9 +268,26 @@
|
|
|
264
268
|
--s-code-editor-viewport-compact: 12rem;
|
|
265
269
|
--s-code-editor-viewport-standard: 20rem;
|
|
266
270
|
--s-code-editor-viewport-expanded: 32rem;
|
|
271
|
+
--s-diff-row-block-size: 2rem;
|
|
272
|
+
--s-diff-surface: var(--color-surface);
|
|
273
|
+
--s-diff-gutter-surface: color-mix(in srgb, var(--color-surface-muted) 78%, transparent);
|
|
274
|
+
--s-diff-added-surface: color-mix(in srgb, var(--color-success-500) 13%, transparent);
|
|
275
|
+
--s-diff-added-accent: var(--color-success-600);
|
|
276
|
+
--s-diff-removed-surface: color-mix(in srgb, var(--color-danger-500) 12%, transparent);
|
|
277
|
+
--s-diff-removed-accent: var(--color-danger-600);
|
|
278
|
+
--s-diff-context-surface: color-mix(in srgb, var(--color-info-500) 7%, var(--color-surface));
|
|
279
|
+
--s-code-token-comment: var(--color-text-muted);
|
|
280
|
+
--s-code-token-keyword: var(--color-danger-600);
|
|
281
|
+
--s-code-token-title: var(--color-primary-600);
|
|
282
|
+
--s-code-token-literal: var(--color-warn-700);
|
|
283
|
+
--s-code-token-string: var(--color-success-700);
|
|
284
|
+
--s-code-token-meta: var(--color-info-700);
|
|
267
285
|
--s-code-editor-search-match-background: rgb(99 102 241 / 22%);
|
|
268
286
|
--s-code-editor-search-match-selected-background: rgb(16 185 129 / 35%);
|
|
269
287
|
--s-code-editor-search-match-selected-outline: rgb(16 185 129 / 55%);
|
|
288
|
+
--s-code-editor-diagnostic-error: var(--color-danger-500);
|
|
289
|
+
--s-code-editor-diagnostic-warning: var(--color-warn-500);
|
|
290
|
+
--s-code-editor-diagnostic-info: var(--color-info-500);
|
|
270
291
|
|
|
271
292
|
/* Semantic chart palette and geometry surfaces */
|
|
272
293
|
--s-chart-series-1: var(--color-primary-600);
|
package/src/theme.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { UiKitTranslate } from './locales/locale';
|
|
2
|
+
import { createUiKitTranslate, UI_KIT_MESSAGES } from './locales/messages';
|
|
3
|
+
|
|
1
4
|
export const themeFamilies = ['classic', 'pink', 'green'] as const;
|
|
2
5
|
export type ThemeFamily = (typeof themeFamilies)[number];
|
|
3
6
|
|
|
@@ -28,6 +31,37 @@ export interface ThemeVariant {
|
|
|
28
31
|
|
|
29
32
|
export const defaultThemeId: ThemeId = 'classic-light';
|
|
30
33
|
|
|
34
|
+
const defaultThemeTranslate = createUiKitTranslate(() => UI_KIT_MESSAGES.ru);
|
|
35
|
+
|
|
36
|
+
function themeFamilyMessageKey(family: ThemeFamily): 'theme.classic' | 'theme.pink' | 'theme.green' {
|
|
37
|
+
if (family === 'classic') return 'theme.classic';
|
|
38
|
+
if (family === 'pink') return 'theme.pink';
|
|
39
|
+
if (family === 'green') return 'theme.green';
|
|
40
|
+
throw new Error(`Неизвестное семейство темы: ${String(family)}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function themeModeMessageKey(mode: ThemeMode): 'theme.light' | 'theme.dark' {
|
|
44
|
+
if (mode === 'light') return 'theme.light';
|
|
45
|
+
if (mode === 'dark') return 'theme.dark';
|
|
46
|
+
throw new Error(`Неизвестный режим темы: ${String(mode)}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function localizeThemeVariant(
|
|
50
|
+
family: ThemeFamily,
|
|
51
|
+
mode: ThemeMode,
|
|
52
|
+
translate: UiKitTranslate,
|
|
53
|
+
): Pick<ThemeVariant, 'label' | 'familyLabel' | 'modeLabel'> {
|
|
54
|
+
const familyLabel = translate(themeFamilyMessageKey(family));
|
|
55
|
+
const modeLabel = translate(themeModeMessageKey(mode));
|
|
56
|
+
return {
|
|
57
|
+
familyLabel,
|
|
58
|
+
modeLabel,
|
|
59
|
+
label: translate(mode === 'light' ? 'theme.variantLight' : 'theme.variantDark', {
|
|
60
|
+
family: familyLabel,
|
|
61
|
+
}),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
31
65
|
export const themeVariants = [
|
|
32
66
|
{
|
|
33
67
|
id: 'classic-light',
|
|
@@ -36,9 +70,7 @@ export const themeVariants = [
|
|
|
36
70
|
dataTheme: 'light',
|
|
37
71
|
browserThemeColor: '#f8fafc',
|
|
38
72
|
statusBarStyle: 'default',
|
|
39
|
-
|
|
40
|
-
familyLabel: 'Классическая',
|
|
41
|
-
modeLabel: 'Светлая',
|
|
73
|
+
...localizeThemeVariant('classic', 'light', defaultThemeTranslate),
|
|
42
74
|
},
|
|
43
75
|
{
|
|
44
76
|
id: 'classic-dark',
|
|
@@ -47,9 +79,7 @@ export const themeVariants = [
|
|
|
47
79
|
dataTheme: 'dark',
|
|
48
80
|
browserThemeColor: '#020617',
|
|
49
81
|
statusBarStyle: 'black-translucent',
|
|
50
|
-
|
|
51
|
-
familyLabel: 'Классическая',
|
|
52
|
-
modeLabel: 'Тёмная',
|
|
82
|
+
...localizeThemeVariant('classic', 'dark', defaultThemeTranslate),
|
|
53
83
|
},
|
|
54
84
|
{
|
|
55
85
|
id: 'pink-light',
|
|
@@ -58,9 +88,7 @@ export const themeVariants = [
|
|
|
58
88
|
dataTheme: 'pink-light',
|
|
59
89
|
browserThemeColor: '#fff6fb',
|
|
60
90
|
statusBarStyle: 'default',
|
|
61
|
-
|
|
62
|
-
familyLabel: 'Розовая',
|
|
63
|
-
modeLabel: 'Светлая',
|
|
91
|
+
...localizeThemeVariant('pink', 'light', defaultThemeTranslate),
|
|
64
92
|
},
|
|
65
93
|
{
|
|
66
94
|
id: 'pink-dark',
|
|
@@ -69,9 +97,7 @@ export const themeVariants = [
|
|
|
69
97
|
dataTheme: 'pink-dark',
|
|
70
98
|
browserThemeColor: '#020617',
|
|
71
99
|
statusBarStyle: 'black-translucent',
|
|
72
|
-
|
|
73
|
-
familyLabel: 'Розовая',
|
|
74
|
-
modeLabel: 'Тёмная',
|
|
100
|
+
...localizeThemeVariant('pink', 'dark', defaultThemeTranslate),
|
|
75
101
|
},
|
|
76
102
|
{
|
|
77
103
|
id: 'green-light',
|
|
@@ -80,9 +106,7 @@ export const themeVariants = [
|
|
|
80
106
|
dataTheme: 'green-light',
|
|
81
107
|
browserThemeColor: '#f5fbf6',
|
|
82
108
|
statusBarStyle: 'default',
|
|
83
|
-
|
|
84
|
-
familyLabel: 'Зелёная',
|
|
85
|
-
modeLabel: 'Светлая',
|
|
109
|
+
...localizeThemeVariant('green', 'light', defaultThemeTranslate),
|
|
86
110
|
},
|
|
87
111
|
{
|
|
88
112
|
id: 'green-dark',
|
|
@@ -91,9 +115,7 @@ export const themeVariants = [
|
|
|
91
115
|
dataTheme: 'green-dark',
|
|
92
116
|
browserThemeColor: '#04120b',
|
|
93
117
|
statusBarStyle: 'black-translucent',
|
|
94
|
-
|
|
95
|
-
familyLabel: 'Зелёная',
|
|
96
|
-
modeLabel: 'Тёмная',
|
|
118
|
+
...localizeThemeVariant('green', 'dark', defaultThemeTranslate),
|
|
97
119
|
},
|
|
98
120
|
] as const satisfies readonly ThemeVariant[];
|
|
99
121
|
|
|
@@ -417,22 +439,27 @@ export function resolveThemePreference(themeId: ThemeId): ThemePreference {
|
|
|
417
439
|
return getThemeVariant(themeId).dataTheme;
|
|
418
440
|
}
|
|
419
441
|
|
|
420
|
-
export function getThemeFamilyLabel(
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
return variant.familyLabel;
|
|
442
|
+
export function getThemeFamilyLabel(
|
|
443
|
+
family: ThemeFamily,
|
|
444
|
+
translate: UiKitTranslate = defaultThemeTranslate,
|
|
445
|
+
): string {
|
|
446
|
+
return translate(themeFamilyMessageKey(family));
|
|
426
447
|
}
|
|
427
448
|
|
|
428
|
-
export function getThemeModeLabel(
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
449
|
+
export function getThemeModeLabel(
|
|
450
|
+
mode: ThemeMode,
|
|
451
|
+
translate: UiKitTranslate = defaultThemeTranslate,
|
|
452
|
+
): string {
|
|
453
|
+
return translate(themeModeMessageKey(mode));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** Returns a locale-scoped display label without coupling theme state to one application locale. */
|
|
457
|
+
export function getThemeVariantLabel(
|
|
458
|
+
themeId: ThemeId,
|
|
459
|
+
translate: UiKitTranslate = defaultThemeTranslate,
|
|
460
|
+
): string {
|
|
461
|
+
const variant = getThemeVariant(themeId);
|
|
462
|
+
return localizeThemeVariant(variant.family, variant.mode, translate).label;
|
|
436
463
|
}
|
|
437
464
|
|
|
438
465
|
export function resolveLegacyThemeId(value: unknown): ThemeId | null {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { computeDiffModel, DiffEngineCancelledError } from '../internal/diffEngine'
|
|
2
|
+
import {
|
|
3
|
+
S_DIFF_WORKER_PROTOCOL_VERSION,
|
|
4
|
+
validateDiffWorkerRequest,
|
|
5
|
+
type SDiffWorkerResponse,
|
|
6
|
+
} from './diffProtocol'
|
|
7
|
+
|
|
8
|
+
interface DiffWorkerScope {
|
|
9
|
+
addEventListener(type: 'message', listener: (event: MessageEvent<unknown>) => void): void
|
|
10
|
+
postMessage(message: SDiffWorkerResponse): void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const workerScope = self as unknown as DiffWorkerScope
|
|
14
|
+
const cancelledRequestIds = new Set<string>()
|
|
15
|
+
|
|
16
|
+
function errorResponse(requestId: string, cause: unknown): SDiffWorkerResponse {
|
|
17
|
+
const error = cause instanceof Error ? cause : new Error(String(cause))
|
|
18
|
+
return {
|
|
19
|
+
version: S_DIFF_WORKER_PROTOCOL_VERSION,
|
|
20
|
+
type: 'error',
|
|
21
|
+
requestId,
|
|
22
|
+
error: { name: error.name, message: error.message },
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
workerScope.addEventListener('message', (event) => {
|
|
27
|
+
let requestId = 'invalid-request'
|
|
28
|
+
try {
|
|
29
|
+
const request = validateDiffWorkerRequest(event.data)
|
|
30
|
+
requestId = request.requestId
|
|
31
|
+
if (request.type === 'cancel') {
|
|
32
|
+
cancelledRequestIds.add(request.requestId)
|
|
33
|
+
workerScope.postMessage({
|
|
34
|
+
version: S_DIFF_WORKER_PROTOCOL_VERSION,
|
|
35
|
+
type: 'cancelled',
|
|
36
|
+
requestId: request.requestId,
|
|
37
|
+
})
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
const result = computeDiffModel(
|
|
41
|
+
request.before,
|
|
42
|
+
request.after,
|
|
43
|
+
request.options,
|
|
44
|
+
() => cancelledRequestIds.has(request.requestId),
|
|
45
|
+
)
|
|
46
|
+
if (cancelledRequestIds.delete(request.requestId)) {
|
|
47
|
+
workerScope.postMessage({
|
|
48
|
+
version: S_DIFF_WORKER_PROTOCOL_VERSION,
|
|
49
|
+
type: 'cancelled',
|
|
50
|
+
requestId: request.requestId,
|
|
51
|
+
})
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
workerScope.postMessage({
|
|
55
|
+
version: S_DIFF_WORKER_PROTOCOL_VERSION,
|
|
56
|
+
type: 'result',
|
|
57
|
+
requestId: request.requestId,
|
|
58
|
+
result,
|
|
59
|
+
})
|
|
60
|
+
} catch (cause) {
|
|
61
|
+
if (cause instanceof DiffEngineCancelledError) {
|
|
62
|
+
workerScope.postMessage({
|
|
63
|
+
version: S_DIFF_WORKER_PROTOCOL_VERSION,
|
|
64
|
+
type: 'cancelled',
|
|
65
|
+
requestId,
|
|
66
|
+
})
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
workerScope.postMessage(errorResponse(requestId, cause))
|
|
70
|
+
} finally {
|
|
71
|
+
cancelledRequestIds.delete(requestId)
|
|
72
|
+
}
|
|
73
|
+
})
|