@dsh-workbench/shared 0.1.2 → 0.1.4

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 (99) hide show
  1. package/dist/api/host-write.d.ts +1 -0
  2. package/dist/api/host-write.js +7 -1
  3. package/dist/api/workbench-contract.d.ts +1 -1
  4. package/dist/components/ApprovalDetail.css +4 -0
  5. package/dist/components/ApprovalDetail.d.ts +1 -0
  6. package/dist/components/ApprovalDetail.js +5 -1
  7. package/dist/components/CardHead.css +92 -0
  8. package/dist/components/CardHead.d.ts +1 -0
  9. package/dist/components/CardHead.js +9 -7
  10. package/dist/components/ChoiceGroup.css +75 -0
  11. package/dist/components/ChoiceGroup.d.ts +1 -0
  12. package/dist/components/ChoiceGroup.js +1 -0
  13. package/dist/components/LongTaskRow.css +53 -0
  14. package/dist/components/LongTaskRow.d.ts +3 -0
  15. package/dist/components/LongTaskRow.js +11 -11
  16. package/dist/components/MockStrip.css +39 -0
  17. package/dist/components/MockStrip.d.ts +1 -0
  18. package/dist/components/MockStrip.js +1 -0
  19. package/dist/components/ResourceStates.css +111 -0
  20. package/dist/components/ResourceStates.d.ts +2 -1
  21. package/dist/components/ResourceStates.js +1 -0
  22. package/dist/components/SaveBar.css +7 -0
  23. package/dist/components/SaveBar.d.ts +1 -0
  24. package/dist/components/SaveBar.js +4 -2
  25. package/dist/components/TextField.css +22 -0
  26. package/dist/components/TextField.d.ts +6 -0
  27. package/dist/components/TextField.js +5 -0
  28. package/dist/components/icons.d.ts +0 -12
  29. package/dist/components/icons.js +0 -61
  30. package/dist/host/http.js +5 -29
  31. package/dist/host/provision.d.ts +18 -1
  32. package/dist/host/provision.js +73 -0
  33. package/dist/host.d.ts +8 -0
  34. package/dist/host.js +8 -0
  35. package/dist/i18n/en.js +126 -1
  36. package/dist/i18n/index.d.ts +129 -4
  37. package/dist/i18n/zh.d.ts +128 -3
  38. package/dist/i18n/zh.js +128 -3
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.js +1 -1
  41. package/package.json +5 -3
  42. package/src/api/__tests__/host-write.test.ts +20 -0
  43. package/src/api/host-write.ts +17 -3
  44. package/src/api/workbench-contract.ts +5 -1
  45. package/src/components/ApprovalDetail.css +4 -0
  46. package/src/components/ApprovalDetail.tsx +11 -1
  47. package/src/components/CardHead.css +92 -0
  48. package/src/components/CardHead.tsx +43 -31
  49. package/src/components/ChoiceGroup.css +75 -0
  50. package/src/components/ChoiceGroup.tsx +1 -0
  51. package/src/components/LongTaskRow.css +53 -0
  52. package/src/components/LongTaskRow.tsx +52 -13
  53. package/src/components/MockStrip.css +39 -0
  54. package/src/components/MockStrip.tsx +1 -0
  55. package/src/components/ResourceStates.css +111 -0
  56. package/src/components/ResourceStates.tsx +2 -1
  57. package/src/components/SaveBar.css +7 -0
  58. package/src/components/SaveBar.tsx +8 -5
  59. package/src/components/TextField.css +22 -0
  60. package/src/components/TextField.tsx +17 -0
  61. package/src/components/icons.tsx +0 -164
  62. package/src/host/__tests__/provision.test.ts +90 -0
  63. package/src/host/http.ts +5 -33
  64. package/src/host/provision.ts +87 -0
  65. package/src/host.ts +8 -0
  66. package/src/i18n/en.ts +127 -1
  67. package/src/i18n/zh.ts +128 -4
  68. package/src/index.ts +1 -1
  69. package/dist/components/StateDot.d.ts +0 -5
  70. package/dist/components/StateDot.js +0 -10
  71. package/dist/host/__tests__/fakes.d.ts +0 -41
  72. package/dist/host/__tests__/fakes.js +0 -76
  73. package/dist/host/__tests__/preflight-fixtures.d.ts +0 -19
  74. package/dist/host/__tests__/preflight-fixtures.js +0 -47
  75. package/dist/host/context-inject.d.ts +0 -24
  76. package/dist/host/context-inject.js +0 -74
  77. package/dist/host/events.d.ts +0 -9
  78. package/dist/host/events.js +0 -70
  79. package/dist/host/mount-units.d.ts +0 -52
  80. package/dist/host/mount-units.js +0 -186
  81. package/dist/host/preflight.d.ts +0 -58
  82. package/dist/host/preflight.js +0 -233
  83. package/dist/host/probe-cache.d.ts +0 -6
  84. package/dist/host/probe-cache.js +0 -19
  85. package/dist/host/probe-libs.d.ts +0 -45
  86. package/dist/host/probe-libs.js +0 -61
  87. package/dist/host/probe-units.d.ts +0 -17
  88. package/dist/host/probe-units.js +0 -102
  89. package/dist/host/probe-verdict.d.ts +0 -7
  90. package/dist/host/probe-verdict.js +0 -143
  91. package/dist/host/tool-scope.d.ts +0 -32
  92. package/dist/host/tool-scope.js +0 -84
  93. package/dist/host/unit-readiness.d.ts +0 -7
  94. package/dist/host/unit-readiness.js +0 -5
  95. package/dist/host/unit-registry.d.ts +0 -9
  96. package/dist/host/unit-registry.js +0 -33
  97. package/dist/host/version-range.d.ts +0 -2
  98. package/dist/host/version-range.js +0 -73
  99. package/src/components/StateDot.tsx +0 -31
@@ -1,7 +1,14 @@
1
1
  import type { ReactNode } from '../lib/react.js';
2
2
  import type { Translate } from '../api/dsh-client.js';
3
- import { IconRefresh, IconRightUp, IconSettings } from '../components/icons.js';
3
+ import {
4
+ Button,
5
+ IconRefreshOutlineRegular,
6
+ IconRightUpOutlineRegular,
7
+ IconSettingsOutlineRegular,
8
+ Tooltip,
9
+ } from '@deepseek-ai/dsh-client-ui-primitives';
4
10
  import { h } from '../lib/react.js';
11
+ import './CardHead.css';
5
12
 
6
13
  export interface CardHeadProps {
7
14
  readonly t: Translate;
@@ -36,38 +43,43 @@ export function CardHead({
36
43
  <h2 className="wb-cardTitle">{title}</h2>
37
44
  {meta === undefined ? null : <span className="wb-cardMeta">{meta}</span>}
38
45
  {platformHref === undefined || platform === undefined ? null : (
39
- <a
40
- className="wb-iconButton wb-cardOpen"
41
- href={platformHref}
42
- target="_blank"
43
- rel="noreferrer noopener"
44
- title={platformTitle ?? platform}
45
- aria-label={platform}
46
- >
47
- <IconRightUp size={14} />
48
- </a>
46
+ <Tooltip label={platformTitle ?? platform}>
47
+ <a
48
+ className="wb-iconButton wb-cardOpen"
49
+ href={platformHref}
50
+ target="_blank"
51
+ rel="noreferrer noopener"
52
+ aria-label={platform}
53
+ >
54
+ <IconRightUpOutlineRegular size={14} />
55
+ </a>
56
+ </Tooltip>
49
57
  )}
50
- <button
51
- type="button"
52
- className={`wb-iconButton wb-cardRefresh${refreshing ? ' wb-spinning' : ''}`}
53
- aria-label={refreshLabel}
54
- title={refreshLabel}
55
- disabled={refreshing}
56
- onClick={onRefresh}
57
- >
58
- <IconRefresh size={14} />
59
- </button>
60
- {gear === undefined || onToggleGear === undefined ? null : (
61
- <button
58
+ <Tooltip label={refreshLabel}>
59
+ <Button
62
60
  type="button"
63
- className={`wb-iconButton wb-cardGear${gearOpen === true ? ' wb-iconButtonActive' : ''}`}
64
- aria-label={gear}
65
- aria-expanded={gearOpen === true}
66
- title={gear}
67
- onClick={onToggleGear}
68
- >
69
- <IconSettings size={15} />
70
- </button>
61
+ size="sm"
62
+ variant="ghost"
63
+ className={`wb-cardRefresh${refreshing ? ' wb-spinning' : ''}`}
64
+ aria-label={refreshLabel}
65
+ disabled={refreshing}
66
+ icon={<IconRefreshOutlineRegular size={14} />}
67
+ onClick={onRefresh}
68
+ />
69
+ </Tooltip>
70
+ {gear === undefined || onToggleGear === undefined ? null : (
71
+ <Tooltip label={gear}>
72
+ <Button
73
+ type="button"
74
+ size="sm"
75
+ variant="ghost"
76
+ className={`wb-cardGear${gearOpen === true ? ' wb-iconButtonActive' : ''}`}
77
+ aria-label={gear}
78
+ aria-expanded={gearOpen === true}
79
+ icon={<IconSettingsOutlineRegular size={15} />}
80
+ onClick={onToggleGear}
81
+ />
82
+ </Tooltip>
71
83
  )}
72
84
  </header>
73
85
  );
@@ -0,0 +1,75 @@
1
+
2
+ .wb-choiceMenu {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: var(--wb-len-2);
6
+ padding: var(--wb-len-4);
7
+ border: 0.5px solid var(--dsw-alias-border-l3);
8
+ border-radius: var(--dsw-radius-md);
9
+ background: transparent;
10
+ }
11
+
12
+ .wb-choiceOption {
13
+ display: flex;
14
+ align-items: center;
15
+ gap: var(--wb-len-6);
16
+ min-height: var(--wb-len-30);
17
+ padding: var(--wb-len-6) var(--wb-len-8);
18
+ border-radius: var(--dsw-radius-md);
19
+ cursor: pointer;
20
+ font-size: var(--dsw-font-xs-13-font-size);
21
+ line-height: var(--wb-line-20);
22
+ color: var(--dsw-alias-label-primary);
23
+ }
24
+
25
+ .wb-choiceOption:hover {
26
+ background: var(--dsw-alias-interactive-bg-hover);
27
+ }
28
+
29
+ .wb-choiceLabel {
30
+ flex: 1;
31
+ min-width: 0;
32
+ overflow: hidden;
33
+ text-overflow: ellipsis;
34
+ white-space: nowrap;
35
+ }
36
+
37
+ .wb-radio {
38
+ appearance: none;
39
+ -webkit-appearance: none;
40
+ flex: none;
41
+ box-sizing: border-box;
42
+ width: var(--wb-len-12);
43
+ height: var(--wb-len-12);
44
+ margin: 0;
45
+ border: 1px solid var(--dsw-alias-border-l3);
46
+ border-radius: 50%;
47
+ corner-shape: round;
48
+ background-color: transparent;
49
+ cursor: pointer;
50
+ transition:
51
+ background-color var(--wb-dur-0-12) ease, border-color var(--wb-dur-0-12) ease;
52
+ }
53
+
54
+ .wb-radio:not(:checked):hover {
55
+ border-color: var(--dsw-alias-label-tertiary);
56
+ }
57
+
58
+ .wb-radio:checked {
59
+ border-color: var(--dsw-alias-brand-primary);
60
+ background-image: radial-gradient(
61
+ circle at center,
62
+ var(--dsw-alias-brand-primary) 0 3px,
63
+ transparent 3px
64
+ );
65
+ }
66
+
67
+ .wb-radio:disabled {
68
+ cursor: default;
69
+ opacity: 0.5;
70
+ }
71
+
72
+ .wb-radio:focus-visible {
73
+ outline: var(--dsw-focus-ring-width, var(--wb-len-2)) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
74
+ outline-offset: 2px;
75
+ }
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode } from '../lib/react.js';
2
2
  import { Fragment, h } from '../lib/react.js';
3
+ import './ChoiceGroup.css';
3
4
 
4
5
  export interface ChoiceGroupOption {
5
6
  readonly value: string;
@@ -0,0 +1,53 @@
1
+
2
+ .wb-longTask {
3
+ padding-top: var(--wb-len-12);
4
+ border-top: 0.5px solid var(--dsw-alias-border-l2);
5
+ display: flex;
6
+ flex-direction: column;
7
+ gap: var(--wb-len-2);
8
+ }
9
+
10
+ .wb-longTaskHead {
11
+ display: flex;
12
+ align-items: center;
13
+ gap: var(--wb-len-6);
14
+ margin: 0;
15
+ }
16
+
17
+ .wb-longTaskLink {
18
+ flex: none;
19
+ display: inline-flex;
20
+ align-items: center;
21
+ gap: var(--wb-len-2);
22
+ text-decoration: none;
23
+ white-space: nowrap;
24
+ }
25
+
26
+ .wb-longTaskHeadline {
27
+ margin: 0;
28
+ font-size: var(--dsw-font-xxs-12-font-size);
29
+ line-height: var(--wb-line-18);
30
+ white-space: pre-wrap;
31
+ overflow-wrap: anywhere;
32
+ color: var(--dsw-alias-label-secondary);
33
+ }
34
+
35
+ .wb-longTaskProblem {
36
+ margin: 0;
37
+ font-size: var(--dsw-font-xxxs-11-font-size);
38
+ line-height: var(--wb-line-16);
39
+ color: var(--dsw-alias-state-error-primary);
40
+ overflow-wrap: anywhere;
41
+ }
42
+
43
+ .wb-longTaskDump {
44
+ margin: var(--wb-len-2) 0 0;
45
+ font-size: var(--dsw-font-xxxs-11-font-size);
46
+ color: var(--dsw-alias-label-tertiary);
47
+ }
48
+
49
+ .wb-longTaskDump pre {
50
+ margin: var(--wb-len-4) 0 0;
51
+ max-height: var(--wb-len-220);
52
+ overflow: auto;
53
+ }
@@ -1,7 +1,14 @@
1
1
  import { longTaskDot, type LongTaskStatus } from '../api/long-task.js';
2
- import { Fragment, h } from '../lib/react.js';
3
- import { IconRightUp } from './icons.js';
4
- import { StateDot } from './StateDot.js';
2
+ import { Fragment, h, useState } from '../lib/react.js';
3
+ import {
4
+ CodeBlock,
5
+ DisclosureRow,
6
+ IconCodeOutlineRegular,
7
+ IconFlatListOutlineRegular,
8
+ IconRightUpOutlineRegular,
9
+ StateDot,
10
+ } from '@deepseek-ai/dsh-client-ui-primitives';
11
+ import './LongTaskRow.css';
5
12
 
6
13
  export interface LongTaskRowLink {
7
14
  readonly href: string;
@@ -17,18 +24,22 @@ export interface LongTaskRowProps {
17
24
  readonly raw?: string | undefined;
18
25
  readonly stepsTitle?: string | undefined;
19
26
  readonly rawTitle?: string | undefined;
27
+ readonly copyLabel?: string | undefined;
28
+ readonly copiedLabel?: string | undefined;
20
29
  }
21
30
 
22
31
  function RowLink({ href, text }: LongTaskRowLink) {
23
32
  return (
24
- <a className="wb-linkButton wb-longTaskLink" href={href} target="_blank" rel="noreferrer noopener">
33
+ <a className="wb-longTaskLink" href={href} target="_blank" rel="noreferrer noopener">
25
34
  {text}
26
- <IconRightUp size={12} />
35
+ <IconRightUpOutlineRegular size={12} />
27
36
  </a>
28
37
  );
29
38
  }
30
39
 
31
40
  export function LongTaskRow(props: LongTaskRowProps) {
41
+ const [stepsOpen, setStepsOpen] = useState(false);
42
+ const [rawOpen, setRawOpen] = useState(false);
32
43
  const failed = props.status === 'failed';
33
44
  const problem = props.problem ?? '';
34
45
  const raw = (props.raw ?? '').trim();
@@ -47,16 +58,44 @@ export function LongTaskRow(props: LongTaskRowProps) {
47
58
  {!failed ? null : (
48
59
  <Fragment>
49
60
  {steps.length === 0 || props.stepsTitle === undefined ? null : (
50
- <details className="wb-longTaskDump">
51
- <summary>{props.stepsTitle}</summary>
52
- <pre>{steps.join('\n')}</pre>
53
- </details>
61
+ <DisclosureRow
62
+ className="wb-longTaskDump"
63
+ icon={<IconFlatListOutlineRegular size={14} />}
64
+ title={props.stepsTitle}
65
+ open={stepsOpen}
66
+ expandable
67
+ expandOnRowClick
68
+ previewChevron
69
+ onToggle={() => setStepsOpen(!stepsOpen)}
70
+ >
71
+ <CodeBlock
72
+ code={steps.join('\n')}
73
+ className="wb-longTaskDump"
74
+ showHeader={props.copyLabel !== undefined}
75
+ copyLabel={props.copyLabel ?? ''}
76
+ copiedLabel={props.copiedLabel ?? ''}
77
+ />
78
+ </DisclosureRow>
54
79
  )}
55
80
  {raw === '' || props.rawTitle === undefined ? null : (
56
- <details className="wb-longTaskDump">
57
- <summary>{props.rawTitle}</summary>
58
- <pre>{raw}</pre>
59
- </details>
81
+ <DisclosureRow
82
+ className="wb-longTaskDump"
83
+ icon={<IconCodeOutlineRegular size={14} />}
84
+ title={props.rawTitle}
85
+ open={rawOpen}
86
+ expandable
87
+ expandOnRowClick
88
+ previewChevron
89
+ onToggle={() => setRawOpen(!rawOpen)}
90
+ >
91
+ <CodeBlock
92
+ code={raw}
93
+ className="wb-longTaskDump"
94
+ showHeader={props.copyLabel !== undefined}
95
+ copyLabel={props.copyLabel ?? ''}
96
+ copiedLabel={props.copiedLabel ?? ''}
97
+ />
98
+ </DisclosureRow>
60
99
  )}
61
100
  </Fragment>
62
101
  )}
@@ -0,0 +1,39 @@
1
+
2
+ .wb-mockStrip {
3
+ display: flex;
4
+ flex-wrap: wrap;
5
+ gap: var(--wb-len-4);
6
+ padding: var(--wb-len-8) var(--wb-len-12);
7
+ border-bottom: 0.5px solid var(--dsw-alias-border-l2);
8
+ flex: none;
9
+ }
10
+
11
+ .wb-mockNote {
12
+ flex: 1 0 100%;
13
+ font-size: var(--wb-font-10);
14
+ line-height: var(--wb-line-14);
15
+ color: var(--dsw-alias-label-tertiary);
16
+ }
17
+
18
+ .wb-mockChip {
19
+ flex: none;
20
+ padding: var(--wb-len-2) var(--wb-len-7);
21
+ border: 0.5px solid var(--dsw-alias-border-l2);
22
+ border-radius: var(--wb-radius-pill);
23
+ corner-shape: round;
24
+ background: transparent;
25
+ font-size: var(--wb-font-10);
26
+ line-height: var(--wb-line-14);
27
+ color: var(--dsw-alias-label-secondary);
28
+ cursor: pointer;
29
+ }
30
+
31
+ .wb-mockChip:hover {
32
+ color: var(--dsw-alias-label-primary);
33
+ }
34
+
35
+ .wb-mockChipOn {
36
+ border-color: transparent;
37
+ background: var(--dsw-alias-brand-primary);
38
+ color: var(--dsw-alias-label-primary-foreground);
39
+ }
@@ -1,4 +1,5 @@
1
1
  import { h } from '../lib/react.js';
2
+ import './MockStrip.css';
2
3
 
3
4
  export function MockStrip({
4
5
  mocks,
@@ -0,0 +1,111 @@
1
+
2
+ .wb-centered.wb-centeredCompact {
3
+ flex: none;
4
+ min-height: 0;
5
+ gap: var(--wb-len-8);
6
+ padding: var(--wb-len-20) var(--wb-len-16);
7
+ }
8
+
9
+ .wb-centered.wb-centeredCompact .wb-centeredHero {
10
+ margin-bottom: var(--wb-len-4);
11
+ }
12
+
13
+ .wb-skeleton {
14
+ display: flex;
15
+ flex-direction: column;
16
+ }
17
+
18
+ .wb-skeletonRow {
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: var(--wb-len-8);
22
+ padding: var(--wb-len-8);
23
+ border-radius: var(--dsw-radius-md);
24
+ }
25
+
26
+ .wb-skeletonRow + .wb-skeletonRow::before {
27
+ content: '';
28
+ align-self: stretch;
29
+ height: var(--wb-len-0-5);
30
+ margin: 0 var(--wb-len-8) var(--wb-len-neg-0-5);
31
+ background: var(--dsw-alias-border-l2);
32
+ }
33
+
34
+ .wb-skeletonBar {
35
+ display: block;
36
+ height: var(--wb-len-13);
37
+ border-radius: var(--dsw-radius-xs);
38
+ background: var(--dsw-alias-bg-skeleton);
39
+ animation: wb-skeleton-pulse var(--wb-dur-1-4) var(--ds-ease-in-out) infinite;
40
+ }
41
+
42
+ .wb-skeletonBarTitle {
43
+ width: 62%;
44
+ }
45
+
46
+ .wb-skeletonBarMeta {
47
+ width: 32%;
48
+ height: var(--wb-len-10);
49
+ }
50
+
51
+ .wb-skeletonRow:nth-child(2n) .wb-skeletonBarTitle {
52
+ width: 74%;
53
+ }
54
+
55
+ .wb-skeletonRow:nth-child(3n) .wb-skeletonBarTitle {
56
+ width: 52%;
57
+ }
58
+
59
+ @keyframes wb-skeleton-pulse {
60
+ 50% {
61
+ opacity: 0.45;
62
+ }
63
+ }
64
+
65
+ .wb-centered {
66
+ box-sizing: border-box;
67
+ display: flex;
68
+ flex-direction: column;
69
+ justify-content: center;
70
+ align-items: center;
71
+ gap: var(--wb-len-14);
72
+ flex: 1;
73
+ min-height: var(--wb-len-240);
74
+ padding: 0 var(--wb-len-24) var(--wb-len-24);
75
+ text-align: center;
76
+ }
77
+
78
+ .wb-centeredHero {
79
+ display: flex;
80
+ margin-bottom: var(--wb-len-16);
81
+ color: var(--dsw-alias-label-dimmed);
82
+ }
83
+
84
+ .wb-centeredTitle {
85
+ font-size: var(--dsw-font-s-14-font-size);
86
+ line-height: var(--wb-line-22);
87
+ color: var(--dsw-alias-label-primary);
88
+ }
89
+
90
+ .wb-centeredHint {
91
+ max-width: var(--wb-len-300);
92
+ font-size: var(--dsw-font-xxs-12-font-size);
93
+ line-height: var(--wb-line-18);
94
+ color: var(--dsw-alias-label-tertiary);
95
+ }
96
+
97
+ .wb-centeredDetail {
98
+ max-width: var(--wb-len-320);
99
+ font-family: var(--ds-font-family-code, monospace);
100
+ font-size: var(--dsw-font-xxxs-11-font-size);
101
+ line-height: var(--wb-line-16);
102
+ color: var(--dsw-alias-label-dimmed);
103
+ word-break: break-all;
104
+ }
105
+
106
+ .wb-centeredAction {
107
+ display: flex;
108
+ justify-content: center;
109
+ gap: var(--wb-len-8);
110
+ margin-top: var(--wb-len-2);
111
+ }
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import { h } from '../lib/react.js';
3
+ import './ResourceStates.css';
3
4
 
4
5
  export function SkeletonRows({ rows = 4 }: { rows?: number }) {
5
6
  return (
@@ -16,7 +17,7 @@ export function SkeletonRows({ rows = 4 }: { rows?: number }) {
16
17
 
17
18
  export interface CenteredStateProps {
18
19
  icon: ReactNode;
19
- title: string;
20
+ title: ReactNode;
20
21
  hint?: string;
21
22
  detail?: string;
22
23
  action?: ReactNode;
@@ -0,0 +1,7 @@
1
+
2
+ .wb-formActions {
3
+ display: flex;
4
+ align-items: center;
5
+ justify-content: flex-end;
6
+ gap: var(--wb-len-8);
7
+ }
@@ -1,4 +1,6 @@
1
+ import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
1
2
  import { h } from '../lib/react.js';
3
+ import './SaveBar.css';
2
4
 
3
5
  export interface SaveBarProps {
4
6
  readonly busy: boolean;
@@ -15,17 +17,18 @@ export interface SaveBarProps {
15
17
  export function SaveBar({ busy, disabled, labels, onCancel, onSave }: SaveBarProps) {
16
18
  return (
17
19
  <div className="wb-formActions">
18
- <button type="button" className="wb-linkButton" onClick={onCancel} disabled={busy}>
20
+ <Button type="button" size="sm" variant="ghost" onClick={onCancel} disabled={busy}>
19
21
  {labels.cancel}
20
- </button>
21
- <button
22
+ </Button>
23
+ <Button
22
24
  type="button"
23
- className="wb-primaryButton"
25
+ size="sm"
26
+ variant="primary"
24
27
  onClick={onSave}
25
28
  disabled={busy || disabled === true}
26
29
  >
27
30
  {busy ? labels.saving : labels.save}
28
- </button>
31
+ </Button>
29
32
  </div>
30
33
  );
31
34
  }
@@ -0,0 +1,22 @@
1
+ .wb-textField {
2
+ box-sizing: border-box;
3
+ border: 0.5px solid var(--dsw-alias-border-l4);
4
+ border-radius: var(--dsw-radius-md);
5
+ background: var(--dsw-alias-bg-layer-3);
6
+ font: var(--dsw-font-xs-13);
7
+ color: var(--dsw-alias-label-primary);
8
+ outline: none;
9
+ }
10
+
11
+ .wb-textField:focus {
12
+ border-color: var(--dsw-alias-state-business-primary);
13
+ box-shadow: inset 0 0 0 0.5px var(--dsw-alias-state-business-primary);
14
+ }
15
+
16
+ .wb-textField[aria-invalid='true'] {
17
+ border-color: var(--dsw-alias-state-error-primary);
18
+ }
19
+
20
+ .wb-textField[aria-invalid='true']:focus {
21
+ box-shadow: inset 0 0 0 0.5px var(--dsw-alias-state-error-primary);
22
+ }
@@ -0,0 +1,17 @@
1
+ import type { InputHTMLAttributes } from 'react';
2
+ import './TextField.css';
3
+ import { h } from '../lib/react.js';
4
+
5
+ export interface TextFieldProps extends InputHTMLAttributes<HTMLInputElement> {
6
+ readonly invalid?: boolean;
7
+ }
8
+
9
+ export function TextField({ invalid = false, className, ...rest }: TextFieldProps) {
10
+ return (
11
+ <input
12
+ {...rest}
13
+ className={className === undefined ? 'wb-textField' : `wb-textField ${className}`}
14
+ aria-invalid={invalid || undefined}
15
+ />
16
+ );
17
+ }