@linxin666/dsh-pet 0.1.11 → 0.1.13
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.i18n.yaml +6 -0
- package/README.md +57 -56
- package/README.zh.md +109 -0
- package/lib/client.js +72 -54
- package/lib/client.js.map +1 -1
- package/lib/index.js +56 -35
- package/lib/invariant.js +1 -1
- package/lib/{state-IyVnKymD.js → state-C9EyycwI.js} +1 -1
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +3 -2
- package/lib/types/client/WhalePet.d.ts.map +1 -1
- package/lib/types/client/WhalePet.js +6 -0
- package/lib/types/client/index.d.ts +13 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +2 -1
- package/lib/types/service.d.ts +12 -8
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +48 -32
- package/lib/types/state.d.ts +7 -7
- package/lib/types/state.d.ts.map +1 -1
- package/lib/types/state.js +5 -5
- package/lib/types/treats.d.ts +6 -2
- package/lib/types/treats.d.ts.map +1 -1
- package/lib/types/treats.js +14 -3
- package/package.json +2 -12
- package/src/client/PluginSettingsCard.tsx +30 -5
- package/src/client/WhalePet.test.tsx +138 -0
- package/src/client/WhalePet.tsx +5 -0
- package/src/client/index.ts +15 -2
- package/src/client/settings-card.module.css +141 -127
- package/src/service.ts +48 -36
- package/src/state.ts +7 -7
- package/src/treats.test.ts +25 -2
- package/src/treats.ts +15 -3
|
@@ -41,13 +41,14 @@ export function PluginSettingsCard(props: PluginSettingsCardProps) {
|
|
|
41
41
|
if (!state.available) return null
|
|
42
42
|
const title = props.t(props.titleKey)
|
|
43
43
|
const blocked = !state.dirty || state.invalid || state.saving
|
|
44
|
+
const cardClass = open ? `${css.cardOpen} ${css.card}` : css.card
|
|
44
45
|
// The namespace exists but the Host does not serve it to this client (the
|
|
45
46
|
// official settings allowlist omits third-party namespaces): show a card
|
|
46
47
|
// that explains the gap instead of vanishing, so a missing card never
|
|
47
48
|
// reads as a missing plugin.
|
|
48
49
|
if (!state.exposed) {
|
|
49
50
|
return (
|
|
50
|
-
<li className={
|
|
51
|
+
<li className={cardClass}>
|
|
51
52
|
<button
|
|
52
53
|
type="button"
|
|
53
54
|
className={css.header}
|
|
@@ -59,7 +60,19 @@ export function PluginSettingsCard(props: PluginSettingsCardProps) {
|
|
|
59
60
|
<span className={css.name}>{title}</span>
|
|
60
61
|
<span className={css.description}>{props.t(props.descriptionKey)}</span>
|
|
61
62
|
</span>
|
|
62
|
-
<
|
|
63
|
+
<svg
|
|
64
|
+
width="14"
|
|
65
|
+
height="14"
|
|
66
|
+
viewBox="0 0 14 14"
|
|
67
|
+
fill="none"
|
|
68
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
69
|
+
className={open ? `${css.chevron} ${css.chevronOpen}` : css.chevron}
|
|
70
|
+
>
|
|
71
|
+
<path
|
|
72
|
+
d="M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z"
|
|
73
|
+
fill="currentColor"
|
|
74
|
+
/>
|
|
75
|
+
</svg>
|
|
63
76
|
</button>
|
|
64
77
|
{open
|
|
65
78
|
? (
|
|
@@ -72,7 +85,7 @@ export function PluginSettingsCard(props: PluginSettingsCardProps) {
|
|
|
72
85
|
)
|
|
73
86
|
}
|
|
74
87
|
return (
|
|
75
|
-
<li className={
|
|
88
|
+
<li className={cardClass}>
|
|
76
89
|
<button
|
|
77
90
|
type="button"
|
|
78
91
|
className={css.header}
|
|
@@ -85,7 +98,19 @@ export function PluginSettingsCard(props: PluginSettingsCardProps) {
|
|
|
85
98
|
<span className={css.description}>{props.t(props.descriptionKey)}</span>
|
|
86
99
|
</span>
|
|
87
100
|
{state.dirty ? <span className={css.pending}>{props.t('settings.unsaved')}</span> : null}
|
|
88
|
-
<
|
|
101
|
+
<svg
|
|
102
|
+
width="14"
|
|
103
|
+
height="14"
|
|
104
|
+
viewBox="0 0 14 14"
|
|
105
|
+
fill="none"
|
|
106
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
107
|
+
className={open ? `${css.chevron} ${css.chevronOpen}` : css.chevron}
|
|
108
|
+
>
|
|
109
|
+
<path
|
|
110
|
+
d="M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z"
|
|
111
|
+
fill="currentColor"
|
|
112
|
+
/>
|
|
113
|
+
</svg>
|
|
89
114
|
</button>
|
|
90
115
|
{open
|
|
91
116
|
? (
|
|
@@ -234,4 +259,4 @@ export function BooleanField(props: FieldProps & {
|
|
|
234
259
|
<p className={css.hint}>{props.hint}</p>
|
|
235
260
|
</div>
|
|
236
261
|
)
|
|
237
|
-
}
|
|
262
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
/**
|
|
3
|
+
* WhalePet rename-box keyboard handling. The rename input must treat
|
|
4
|
+
* Enter/Escape keydowns that arrive during IME composition (candidate
|
|
5
|
+
* selection) as composition input, never as submit/cancel (issue #89).
|
|
6
|
+
*/
|
|
7
|
+
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
|
|
8
|
+
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
|
|
9
|
+
import { WhalePet, type WhalePetProps } from './WhalePet.tsx'
|
|
10
|
+
import { t } from './locales.ts'
|
|
11
|
+
import type { PetStateView } from '../service.ts'
|
|
12
|
+
|
|
13
|
+
/** Snapshot fixture: idle whale girl named 泡泡. */
|
|
14
|
+
const snapshot: PetStateView = {
|
|
15
|
+
animation: 'idle',
|
|
16
|
+
phase: 'idle',
|
|
17
|
+
sessionActive: true,
|
|
18
|
+
affinity: {
|
|
19
|
+
points: 0,
|
|
20
|
+
rank: '幼鲸',
|
|
21
|
+
rankEmoji: '*',
|
|
22
|
+
pets: 0,
|
|
23
|
+
feeds: 0,
|
|
24
|
+
turns: 0,
|
|
25
|
+
petCooldown: false,
|
|
26
|
+
feedCooldown: false,
|
|
27
|
+
},
|
|
28
|
+
display: { visible: true, size: 160, right: 24, bottom: 20 },
|
|
29
|
+
name: '泡泡',
|
|
30
|
+
treats: { stocked: 3, max: 5 },
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
beforeAll(() => {
|
|
34
|
+
// Deterministic zh copy for button labels.
|
|
35
|
+
document.documentElement.lang = 'zh'
|
|
36
|
+
// Prefer-reduced-motion matches: the sprite loop then never schedules
|
|
37
|
+
// requestAnimationFrame, keeping the test free of animation timers.
|
|
38
|
+
Object.defineProperty(window, 'matchMedia', {
|
|
39
|
+
configurable: true,
|
|
40
|
+
value: (query: string) => ({
|
|
41
|
+
matches: query.includes('prefers-reduced-motion'),
|
|
42
|
+
media: query,
|
|
43
|
+
onchange: null,
|
|
44
|
+
addEventListener: () => {},
|
|
45
|
+
removeEventListener: () => {},
|
|
46
|
+
addListener: () => {},
|
|
47
|
+
removeListener: () => {},
|
|
48
|
+
dispatchEvent: () => false,
|
|
49
|
+
}),
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
cleanup()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
/** Render the pet with mocked callbacks; returns the rename spy. */
|
|
58
|
+
function renderPet(): { onRename: ReturnType<typeof vi.fn> } {
|
|
59
|
+
const onRename = vi.fn()
|
|
60
|
+
const props: WhalePetProps = {
|
|
61
|
+
snapshot,
|
|
62
|
+
display: snapshot.display,
|
|
63
|
+
feedback: null,
|
|
64
|
+
onPet: vi.fn(),
|
|
65
|
+
onFeed: vi.fn(),
|
|
66
|
+
onHide: vi.fn(),
|
|
67
|
+
onDragEnd: vi.fn(),
|
|
68
|
+
onRename,
|
|
69
|
+
onFeedbackDone: vi.fn(),
|
|
70
|
+
t,
|
|
71
|
+
}
|
|
72
|
+
render(<WhalePet {...props} />)
|
|
73
|
+
return { onRename }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Hover the sprite to open the panel, then click the rename button. */
|
|
77
|
+
function openRename(): HTMLInputElement {
|
|
78
|
+
fireEvent.pointerOver(screen.getByRole('button', { name: 'whale girl' }))
|
|
79
|
+
fireEvent.click(screen.getByText('改名'))
|
|
80
|
+
return screen.getByPlaceholderText('输入新名字') as HTMLInputElement
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Fire a keydown whose native event reports an active IME composition, the
|
|
85
|
+
* way Chromium marks Enter/Escape pressed to select or dismiss a candidate.
|
|
86
|
+
*/
|
|
87
|
+
function fireComposingKeydown(target: Element, key: string): void {
|
|
88
|
+
fireEvent.compositionStart(target)
|
|
89
|
+
const native = new window.KeyboardEvent('keydown', {
|
|
90
|
+
key,
|
|
91
|
+
bubbles: true,
|
|
92
|
+
cancelable: true,
|
|
93
|
+
isComposing: true,
|
|
94
|
+
})
|
|
95
|
+
// jsdom does not implement KeyboardEvent.isComposing, so pin the flag on
|
|
96
|
+
// the dispatched native event exactly as the browser would report it.
|
|
97
|
+
Object.defineProperty(native, 'isComposing', { value: true })
|
|
98
|
+
fireEvent(target, native)
|
|
99
|
+
fireEvent.compositionEnd(target)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
describe('WhalePet rename input', () => {
|
|
103
|
+
it('submits the draft on Enter outside composition', () => {
|
|
104
|
+
const { onRename } = renderPet()
|
|
105
|
+
const input = openRename()
|
|
106
|
+
fireEvent.change(input, { target: { value: ' 小鲸 ' } })
|
|
107
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
108
|
+
expect(onRename).toHaveBeenCalledTimes(1)
|
|
109
|
+
expect(onRename).toHaveBeenCalledWith('小鲸')
|
|
110
|
+
expect(screen.queryByPlaceholderText('输入新名字')).toBeNull()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('ignores Enter while an IME composition is active', () => {
|
|
114
|
+
const { onRename } = renderPet()
|
|
115
|
+
const input = openRename()
|
|
116
|
+
fireEvent.change(input, { target: { value: '泡泡酱' } })
|
|
117
|
+
fireComposingKeydown(input, 'Enter')
|
|
118
|
+
expect(onRename).not.toHaveBeenCalled()
|
|
119
|
+
expect(screen.getByPlaceholderText('输入新名字')).toBe(input)
|
|
120
|
+
expect(input.value).toBe('泡泡酱')
|
|
121
|
+
// Once the composition is over, Enter submits normally.
|
|
122
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
123
|
+
expect(onRename).toHaveBeenCalledWith('泡泡酱')
|
|
124
|
+
expect(screen.queryByPlaceholderText('输入新名字')).toBeNull()
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('ignores Escape while an IME composition is active', () => {
|
|
128
|
+
const { onRename } = renderPet()
|
|
129
|
+
const input = openRename()
|
|
130
|
+
fireEvent.change(input, { target: { value: 'abc' } })
|
|
131
|
+
fireComposingKeydown(input, 'Escape')
|
|
132
|
+
expect(screen.getByPlaceholderText('输入新名字')).toBe(input)
|
|
133
|
+
// A real Escape outside composition closes the box without renaming.
|
|
134
|
+
fireEvent.keyDown(input, { key: 'Escape' })
|
|
135
|
+
expect(onRename).not.toHaveBeenCalled()
|
|
136
|
+
expect(screen.queryByPlaceholderText('输入新名字')).toBeNull()
|
|
137
|
+
})
|
|
138
|
+
})
|
package/src/client/WhalePet.tsx
CHANGED
|
@@ -301,6 +301,11 @@ export function WhalePet(props: WhalePetProps): ReactPortal {
|
|
|
301
301
|
autoFocus
|
|
302
302
|
onChange={(e) => setNameDraft(e.target.value)}
|
|
303
303
|
onKeyDown={(e) => {
|
|
304
|
+
// While an IME composition is active (e.g. selecting a
|
|
305
|
+
// Chinese candidate), Enter/Escape keydowns belong to the
|
|
306
|
+
// input method: ignore them so candidate selection can
|
|
307
|
+
// neither submit the draft nor close the rename box.
|
|
308
|
+
if (e.nativeEvent.isComposing) return
|
|
304
309
|
if (e.key === 'Enter') {
|
|
305
310
|
const trimmed = nameDraft.trim()
|
|
306
311
|
if (trimmed !== '') {
|
package/src/client/index.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* @module @linxin666/dsh-pet/client
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
|
|
13
|
+
import type { ClientContext, SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-runtime/client'
|
|
14
14
|
// Type-only: pulls the locale plugin's Context merge (ctx.locale).
|
|
15
15
|
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
|
16
16
|
// Type-only: pulls the settings-surface Context merge (ctx.settingsScope).
|
|
@@ -92,6 +92,18 @@ export interface SettingsPluginItemOwnerProps {
|
|
|
92
92
|
children?: never
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
declare module '@deepseek-ai/cordis' {
|
|
96
|
+
interface Context {
|
|
97
|
+
/**
|
|
98
|
+
* Optional rc.6 compatibility binder provided by dsh-web-ui-settings;
|
|
99
|
+
* absent when that group plugin is not installed, so callers fall back to
|
|
100
|
+
* the official settings scope.
|
|
101
|
+
*/
|
|
102
|
+
webUiSettings?: { bind<S>(spec: SettingsScopeSpec<S>): SettingsScope<S> }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
95
107
|
/**
|
|
96
108
|
* Client plugin body: register dictionaries, mount the global pet entry and
|
|
97
109
|
* poll loop while the plugin is enabled, and seat the settings card in the
|
|
@@ -101,7 +113,8 @@ export interface SettingsPluginItemOwnerProps {
|
|
|
101
113
|
export function apply(ctx: ClientContext): void {
|
|
102
114
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'pet: dictionaries')
|
|
103
115
|
|
|
104
|
-
const
|
|
116
|
+
const binder = ctx.get('webUiSettings') ?? ctx.settingsScope
|
|
117
|
+
const settingsScope = binder.bind<PetSettings>({ namespace: PET_SETTINGS_NS })
|
|
105
118
|
const enabled = (): boolean => {
|
|
106
119
|
const snapshot = settingsScope.getSnapshot()
|
|
107
120
|
return snapshot.status === 'ready'
|
|
@@ -1,71 +1,83 @@
|
|
|
1
|
+
/* Plugin settings card chrome + staged form fields.
|
|
2
|
+
* Aligned with the official ui-settings-plugins PluginCard / fields CSS:
|
|
3
|
+
* same semantic tokens, radius, typography and states so family cards read
|
|
4
|
+
* as siblings of the built-in Shell / Agent loop / Web search cards. */
|
|
5
|
+
|
|
1
6
|
.card {
|
|
2
|
-
list-style: none;
|
|
3
7
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
4
|
-
border-radius: 8px;
|
|
5
8
|
background: var(--dsw-alias-bg-layer-3);
|
|
6
|
-
|
|
9
|
+
border-radius: 12px;
|
|
10
|
+
list-style: none;
|
|
7
11
|
transition: border-color 0.16s, background 0.16s;
|
|
8
12
|
}
|
|
9
13
|
|
|
14
|
+
.card:hover {
|
|
15
|
+
border-color: var(--dsw-alias-label-dimmed);
|
|
16
|
+
}
|
|
17
|
+
|
|
10
18
|
.cardOpen {
|
|
11
19
|
background: var(--dsw-alias-bg-layer-2);
|
|
12
20
|
border-color: var(--dsw-alias-label-dimmed);
|
|
13
21
|
}
|
|
14
22
|
|
|
15
23
|
.header {
|
|
16
|
-
|
|
17
|
-
align-items: center;
|
|
18
|
-
gap: 8px;
|
|
24
|
+
appearance: none;
|
|
19
25
|
width: 100%;
|
|
20
|
-
padding: 10px 14px;
|
|
21
|
-
border: 0;
|
|
22
|
-
background: transparent;
|
|
23
|
-
cursor: pointer;
|
|
24
|
-
text-align: left;
|
|
25
26
|
font: inherit;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
color: inherit;
|
|
28
|
+
text-align: left;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
background: transparent;
|
|
31
|
+
border: 0;
|
|
32
|
+
border-radius: 12px;
|
|
33
|
+
align-items: center;
|
|
34
|
+
gap: 12px;
|
|
35
|
+
padding: 14px 16px;
|
|
36
|
+
display: flex;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
.header:focus-visible {
|
|
38
|
-
outline:
|
|
39
|
-
|
|
40
|
-
box-shadow: inset 0 0 0 2px var(--dsw-alias-button-info-fill);
|
|
40
|
+
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
41
|
+
outline-offset: -2px;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
.headText {
|
|
44
|
-
display: flex;
|
|
45
45
|
flex-direction: column;
|
|
46
|
-
gap: 2px;
|
|
47
46
|
flex: 1;
|
|
47
|
+
gap: 4px;
|
|
48
48
|
min-width: 0;
|
|
49
|
+
display: flex;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
.name {
|
|
52
|
-
font-weight: 600;
|
|
53
53
|
color: var(--dsw-alias-label-primary);
|
|
54
|
+
font-size: 15px;
|
|
55
|
+
font-weight: 600;
|
|
56
|
+
line-height: 1.4;
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
.description {
|
|
57
|
-
font-size: 12px;
|
|
58
60
|
color: var(--dsw-alias-label-tertiary);
|
|
61
|
+
font-size: 13px;
|
|
62
|
+
line-height: 1.5;
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
.pending {
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
white-space: nowrap;
|
|
67
|
+
background: var(--dsw-alias-bg-module-platform);
|
|
68
|
+
color: var(--dsw-alias-label-secondary);
|
|
69
|
+
border-radius: 999px;
|
|
70
|
+
flex: none;
|
|
71
|
+
padding: 1px 8px;
|
|
72
|
+
font-size: 11px;
|
|
73
|
+
font-weight: 500;
|
|
74
|
+
line-height: 17px;
|
|
64
75
|
}
|
|
65
76
|
|
|
66
77
|
.chevron {
|
|
67
|
-
transition: transform 120ms ease;
|
|
68
78
|
color: var(--dsw-alias-label-tertiary);
|
|
79
|
+
flex: none;
|
|
80
|
+
transition: transform 0.16s;
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
.chevronOpen {
|
|
@@ -73,53 +85,59 @@
|
|
|
73
85
|
}
|
|
74
86
|
|
|
75
87
|
.body {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
gap: 14px;
|
|
88
|
+
border-top: 1px solid var(--dsw-alias-border-l2);
|
|
89
|
+
margin: 0 16px;
|
|
90
|
+
padding-bottom: 8px;
|
|
80
91
|
}
|
|
81
92
|
|
|
82
93
|
.readOnly {
|
|
83
|
-
margin: 0;
|
|
84
|
-
font-size: 12px;
|
|
85
94
|
color: var(--dsw-alias-label-tertiary);
|
|
95
|
+
margin: 12px 0 0;
|
|
96
|
+
font-size: 12px;
|
|
97
|
+
line-height: 1.5;
|
|
86
98
|
}
|
|
87
99
|
|
|
88
100
|
.notExposed {
|
|
89
|
-
|
|
101
|
+
color: var(--dsw-alias-state-warn-primary);
|
|
102
|
+
margin: 12px 0 0;
|
|
90
103
|
font-size: 12px;
|
|
91
104
|
line-height: 1.5;
|
|
92
|
-
color: var(--dsw-alias-state-warn-primary);
|
|
93
105
|
}
|
|
94
106
|
|
|
95
107
|
.footer {
|
|
96
|
-
|
|
97
|
-
align-items: center;
|
|
108
|
+
border-top: 1px solid var(--dsw-alias-border-l2);
|
|
98
109
|
justify-content: flex-end;
|
|
110
|
+
align-items: center;
|
|
99
111
|
gap: 8px;
|
|
112
|
+
padding: 12px 0 4px;
|
|
113
|
+
display: flex;
|
|
100
114
|
}
|
|
101
115
|
|
|
102
116
|
.failed {
|
|
103
|
-
|
|
117
|
+
min-width: 0;
|
|
118
|
+
color: var(--dsw-alias-label-error);
|
|
119
|
+
flex: 1;
|
|
120
|
+
margin: 0;
|
|
104
121
|
font-size: 12px;
|
|
105
|
-
|
|
122
|
+
line-height: 1.5;
|
|
106
123
|
}
|
|
107
124
|
|
|
108
125
|
.discard,
|
|
109
126
|
.save {
|
|
110
|
-
|
|
111
|
-
border-radius: 6px;
|
|
127
|
+
appearance: none;
|
|
112
128
|
font: inherit;
|
|
113
|
-
font-size: 13px;
|
|
114
129
|
cursor: pointer;
|
|
115
|
-
|
|
116
|
-
|
|
130
|
+
border: 1px solid transparent;
|
|
131
|
+
border-radius: 8px;
|
|
132
|
+
padding: 5px 14px;
|
|
133
|
+
font-size: 13px;
|
|
134
|
+
line-height: 1.5;
|
|
117
135
|
}
|
|
118
136
|
|
|
119
137
|
.discard {
|
|
120
|
-
border:
|
|
121
|
-
background: transparent;
|
|
138
|
+
border-color: var(--dsw-alias-border-l2);
|
|
122
139
|
color: var(--dsw-alias-label-secondary);
|
|
140
|
+
background: transparent;
|
|
123
141
|
}
|
|
124
142
|
|
|
125
143
|
.discard:hover:not(:disabled) {
|
|
@@ -127,156 +145,152 @@
|
|
|
127
145
|
border-color: var(--dsw-alias-label-dimmed);
|
|
128
146
|
}
|
|
129
147
|
|
|
130
|
-
.discard:active:not(:disabled) {
|
|
131
|
-
background: var(--dsw-alias-interactive-bg-hover);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
148
|
.save {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
color: var(--dsw-alias-label-primary-foreground);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.save:hover:not(:disabled) {
|
|
141
|
-
border-color: var(--dsw-alias-button-info-hover);
|
|
142
|
-
background: var(--dsw-alias-button-info-hover);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.save:active:not(:disabled) {
|
|
146
|
-
filter: brightness(0.94);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
.discard:focus-visible:not(:disabled),
|
|
150
|
-
.save:focus-visible:not(:disabled) {
|
|
151
|
-
outline: none;
|
|
152
|
-
box-shadow: 0 0 0 2px var(--dsw-alias-button-info-fill);
|
|
149
|
+
background: var(--dsw-alias-label-primary);
|
|
150
|
+
color: var(--dsw-alias-bg-layer-3);
|
|
153
151
|
}
|
|
154
152
|
|
|
155
153
|
.discard:disabled,
|
|
156
154
|
.save:disabled {
|
|
157
|
-
opacity: 0.
|
|
155
|
+
opacity: 0.4;
|
|
158
156
|
cursor: default;
|
|
159
157
|
}
|
|
160
158
|
|
|
159
|
+
.discard:focus-visible,
|
|
160
|
+
.save:focus-visible {
|
|
161
|
+
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
162
|
+
outline-offset: 1px;
|
|
163
|
+
}
|
|
164
|
+
|
|
161
165
|
.field {
|
|
162
|
-
display: flex;
|
|
163
166
|
flex-direction: column;
|
|
164
|
-
gap:
|
|
167
|
+
gap: 6px;
|
|
168
|
+
padding: 12px 0;
|
|
169
|
+
display: flex;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.field + .field {
|
|
173
|
+
border-top: 1px solid var(--dsw-alias-border-l2);
|
|
165
174
|
}
|
|
166
175
|
|
|
167
176
|
.head {
|
|
168
|
-
display: flex;
|
|
169
177
|
align-items: center;
|
|
170
178
|
gap: 8px;
|
|
179
|
+
display: flex;
|
|
171
180
|
}
|
|
172
181
|
|
|
173
182
|
.label {
|
|
183
|
+
min-width: 0;
|
|
184
|
+
color: var(--dsw-alias-label-primary);
|
|
185
|
+
flex: 1;
|
|
174
186
|
font-size: 13px;
|
|
175
187
|
font-weight: 500;
|
|
176
|
-
|
|
188
|
+
line-height: 1.5;
|
|
177
189
|
}
|
|
178
190
|
|
|
179
191
|
.badges {
|
|
180
|
-
display: flex;
|
|
181
192
|
align-items: center;
|
|
182
|
-
gap:
|
|
193
|
+
gap: 8px;
|
|
194
|
+
display: inline-flex;
|
|
183
195
|
}
|
|
184
196
|
|
|
185
197
|
.badge {
|
|
186
|
-
|
|
187
|
-
|
|
198
|
+
white-space: nowrap;
|
|
199
|
+
background: var(--dsw-alias-bg-module-platform);
|
|
200
|
+
color: var(--dsw-alias-label-secondary);
|
|
188
201
|
border-radius: 999px;
|
|
189
|
-
|
|
190
|
-
|
|
202
|
+
padding: 1px 8px;
|
|
203
|
+
font-size: 11px;
|
|
204
|
+
font-weight: 500;
|
|
205
|
+
line-height: 17px;
|
|
191
206
|
}
|
|
192
207
|
|
|
193
208
|
.reset {
|
|
194
|
-
font
|
|
195
|
-
|
|
196
|
-
background: transparent;
|
|
197
|
-
color: var(--dsw-alias-state-business-primary);
|
|
209
|
+
font: inherit;
|
|
210
|
+
color: var(--dsw-alias-label-secondary);
|
|
198
211
|
cursor: pointer;
|
|
212
|
+
background: transparent;
|
|
213
|
+
border: none;
|
|
199
214
|
padding: 0;
|
|
200
|
-
|
|
201
|
-
|
|
215
|
+
font-size: 12px;
|
|
216
|
+
line-height: 1.5;
|
|
202
217
|
}
|
|
203
218
|
|
|
204
219
|
.reset:hover:not(:disabled) {
|
|
205
|
-
color: var(--dsw-alias-label-primary
|
|
206
|
-
text-decoration: underline;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.reset:active:not(:disabled) {
|
|
210
|
-
color: var(--dsw-alias-state-business-primary);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.reset:focus-visible:not(:disabled) {
|
|
214
|
-
outline: none;
|
|
215
|
-
box-shadow: 0 0 0 2px var(--dsw-alias-button-info-fill);
|
|
220
|
+
color: var(--dsw-alias-label-primary);
|
|
216
221
|
}
|
|
217
222
|
|
|
218
223
|
.reset:disabled {
|
|
219
|
-
opacity: 0.5;
|
|
220
224
|
cursor: default;
|
|
221
225
|
}
|
|
222
226
|
|
|
227
|
+
.reset:focus-visible {
|
|
228
|
+
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
229
|
+
outline-offset: 2px;
|
|
230
|
+
}
|
|
231
|
+
|
|
223
232
|
.input,
|
|
224
233
|
.select {
|
|
225
|
-
padding: 6px 8px;
|
|
226
234
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
227
|
-
|
|
235
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
236
|
+
height: 34px;
|
|
228
237
|
font: inherit;
|
|
229
|
-
font-size: 13px;
|
|
230
238
|
color: var(--dsw-alias-label-primary);
|
|
231
|
-
|
|
239
|
+
border-radius: 8px;
|
|
240
|
+
padding: 0 12px;
|
|
241
|
+
font-size: 13px;
|
|
242
|
+
line-height: 1.5;
|
|
232
243
|
}
|
|
233
244
|
|
|
234
|
-
.
|
|
235
|
-
|
|
236
|
-
border:
|
|
237
|
-
|
|
238
|
-
font: inherit;
|
|
239
|
-
font-size: 13px;
|
|
240
|
-
color: var(--dsw-alias-label-primary);
|
|
245
|
+
.input:focus-visible,
|
|
246
|
+
.select:focus-visible {
|
|
247
|
+
border-color: var(--dsw-alias-brand-primary);
|
|
248
|
+
outline: none;
|
|
241
249
|
}
|
|
242
250
|
|
|
243
251
|
.input:disabled,
|
|
244
252
|
.select:disabled {
|
|
245
|
-
|
|
253
|
+
color: var(--dsw-alias-label-tertiary);
|
|
254
|
+
cursor: default;
|
|
246
255
|
}
|
|
247
256
|
|
|
248
|
-
.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
257
|
+
.inputInvalid {
|
|
258
|
+
border: 1px solid var(--dsw-alias-label-error);
|
|
259
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
260
|
+
height: 34px;
|
|
261
|
+
font: inherit;
|
|
262
|
+
color: var(--dsw-alias-label-primary);
|
|
263
|
+
border-radius: 8px;
|
|
264
|
+
padding: 0 12px;
|
|
265
|
+
font-size: 13px;
|
|
266
|
+
line-height: 1.5;
|
|
253
267
|
}
|
|
254
268
|
|
|
255
|
-
.inputInvalid:focus {
|
|
256
|
-
outline:
|
|
257
|
-
|
|
269
|
+
.inputInvalid:focus-visible {
|
|
270
|
+
outline: 2px solid var(--dsw-alias-label-error);
|
|
271
|
+
outline-offset: 1px;
|
|
272
|
+
border-color: var(--dsw-alias-label-error);
|
|
258
273
|
}
|
|
259
274
|
|
|
260
|
-
.
|
|
275
|
+
.invalid {
|
|
276
|
+
color: var(--dsw-alias-label-error);
|
|
261
277
|
margin: 0;
|
|
262
278
|
font-size: 12px;
|
|
263
|
-
|
|
279
|
+
line-height: 1.5;
|
|
264
280
|
}
|
|
265
281
|
|
|
266
|
-
.
|
|
282
|
+
.hint {
|
|
283
|
+
color: var(--dsw-alias-label-tertiary);
|
|
267
284
|
margin: 0;
|
|
268
285
|
font-size: 12px;
|
|
269
|
-
|
|
286
|
+
line-height: 1.5;
|
|
270
287
|
}
|
|
271
288
|
|
|
272
|
-
/* Reduced motion: drop the card/button transitions. Token colors and the
|
|
273
|
-
focus rings stay, so interactive states remain visible without motion. */
|
|
274
289
|
@media (prefers-reduced-motion: reduce) {
|
|
275
290
|
.card,
|
|
276
291
|
.header,
|
|
277
292
|
.chevron,
|
|
278
293
|
.chevronOpen,
|
|
279
|
-
.reset,
|
|
280
294
|
.discard,
|
|
281
295
|
.save {
|
|
282
296
|
transition: none;
|