@refraction-ui/astro 0.11.1 → 0.13.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/dist/astro-audio-room/AudioRoom.astro +81 -0
- package/dist/astro-audio-room/index.ts +1 -0
- package/dist/astro-call-controls/CallControls.astro +57 -0
- package/dist/astro-call-controls/index.ts +1 -0
- package/dist/astro-checklist/Checklist.astro +76 -0
- package/dist/astro-checklist/index.ts +1 -0
- package/dist/astro-editor-status-bar/EditorStatusBar.astro +92 -0
- package/dist/astro-editor-status-bar/index.ts +1 -0
- package/dist/astro-editor-tabs/EditorTabs.astro +74 -0
- package/dist/astro-editor-tabs/index.ts +1 -0
- package/dist/astro-empty-state/EmptyState.astro +57 -0
- package/dist/astro-empty-state/index.ts +1 -0
- package/dist/astro-floating-reactions/FloatingReactions.astro +56 -0
- package/dist/astro-floating-reactions/index.ts +1 -0
- package/dist/astro-flow-editor/FlowEditor.astro +99 -0
- package/dist/astro-flow-editor/index.ts +3 -0
- package/dist/astro-graph-view/GraphView.astro +109 -0
- package/dist/astro-graph-view/index.ts +1 -0
- package/dist/astro-infinite-canvas/InfiniteCanvas.astro +43 -0
- package/dist/astro-infinite-canvas/index.ts +1 -0
- package/dist/astro-input/Input.astro +5 -1
- package/dist/astro-live-captions/LiveCaptions.astro +54 -0
- package/dist/astro-live-captions/index.ts +1 -0
- package/dist/astro-live-cursors/LiveCursors.astro +55 -0
- package/dist/astro-live-cursors/index.ts +1 -0
- package/dist/astro-live-transcript/LiveTranscript.astro +55 -0
- package/dist/astro-live-transcript/index.ts +1 -0
- package/dist/astro-mini-map/MiniMap.astro +74 -0
- package/dist/astro-mini-map/index.ts +1 -0
- package/dist/astro-password-input/PasswordInput.astro +102 -0
- package/dist/astro-password-input/index.ts +1 -0
- package/dist/astro-pre-call-lobby/PreCallLobby.astro +202 -0
- package/dist/astro-pre-call-lobby/index.ts +1 -0
- package/dist/astro-radial-gauge/RadialGauge.astro +144 -0
- package/dist/astro-radial-gauge/index.ts +1 -0
- package/dist/astro-rating-scale/RatingScale.astro +73 -0
- package/dist/astro-rating-scale/index.ts +1 -0
- package/dist/astro-segmented-control/SegmentedControl.astro +106 -0
- package/dist/astro-segmented-control/SegmentedControlItem.astro +32 -0
- package/dist/astro-segmented-control/index.ts +1 -0
- package/dist/astro-separator/Separator.astro +49 -0
- package/dist/astro-separator/index.ts +1 -0
- package/dist/astro-social-auth-button/SocialAuthButton.astro +110 -0
- package/dist/astro-social-auth-button/SocialAuthRow.astro +13 -0
- package/dist/astro-social-auth-button/index.ts +1 -0
- package/dist/astro-sticky-note/StickyNote.astro +55 -0
- package/dist/astro-sticky-note/index.ts +1 -0
- package/dist/astro-terminal/Terminal.astro +67 -0
- package/dist/astro-terminal/index.ts +1 -0
- package/dist/astro-test-results/TestResults.astro +76 -0
- package/dist/astro-test-results/index.ts +1 -0
- package/dist/astro-timeline/Timeline.astro +59 -0
- package/dist/astro-timeline/index.ts +1 -0
- package/dist/astro-video-grid/VideoGrid.astro +182 -0
- package/dist/astro-video-grid/index.ts +1 -0
- package/dist/astro-video-tile/VideoTile.astro +104 -0
- package/dist/astro-video-tile/index.ts +1 -0
- package/dist/astro-wizard/Wizard.astro +137 -0
- package/dist/astro-wizard/index.ts +1 -0
- package/dist/audio-room/audio-room.styles.ts +88 -0
- package/dist/audio-room/audio-room.ts +70 -0
- package/dist/audio-room/index.ts +16 -0
- package/dist/call-controls/call-controls.styles.ts +63 -0
- package/dist/call-controls/call-controls.ts +79 -0
- package/dist/call-controls/index.ts +15 -0
- package/dist/checklist/checklist.styles.ts +76 -0
- package/dist/checklist/checklist.ts +62 -0
- package/dist/checklist/index.ts +18 -0
- package/dist/editor-status-bar/editor-status-bar.styles.ts +40 -0
- package/dist/editor-status-bar/editor-status-bar.ts +78 -0
- package/dist/editor-status-bar/index.ts +15 -0
- package/dist/editor-tabs/editor-tabs.styles.ts +52 -0
- package/dist/editor-tabs/editor-tabs.ts +82 -0
- package/dist/editor-tabs/index.ts +15 -0
- package/dist/empty-state/empty-state.styles.ts +42 -0
- package/dist/empty-state/empty-state.ts +27 -0
- package/dist/empty-state/index.ts +14 -0
- package/dist/floating-reactions/floating-reactions.styles.ts +54 -0
- package/dist/floating-reactions/floating-reactions.ts +85 -0
- package/dist/floating-reactions/index.ts +14 -0
- package/dist/flow-editor/flow-editor.styles.ts +56 -0
- package/dist/flow-editor/flow-editor.ts +126 -0
- package/dist/flow-editor/index.ts +18 -0
- package/dist/graph-view/graph-view.styles.ts +66 -0
- package/dist/graph-view/graph-view.ts +130 -0
- package/dist/graph-view/index.ts +21 -0
- package/dist/index.ts +45 -17
- package/dist/infinite-canvas/index.ts +17 -0
- package/dist/infinite-canvas/infinite-canvas.styles.ts +49 -0
- package/dist/infinite-canvas/infinite-canvas.ts +97 -0
- package/dist/input/index.ts +2 -0
- package/dist/input/input.styles.ts +4 -0
- package/dist/input/input.ts +39 -3
- package/dist/live-captions/index.ts +13 -0
- package/dist/live-captions/live-captions.styles.ts +45 -0
- package/dist/live-captions/live-captions.ts +57 -0
- package/dist/live-cursors/index.ts +14 -0
- package/dist/live-cursors/live-cursors.styles.ts +41 -0
- package/dist/live-cursors/live-cursors.ts +73 -0
- package/dist/live-transcript/index.ts +16 -0
- package/dist/live-transcript/live-transcript.styles.ts +48 -0
- package/dist/live-transcript/live-transcript.ts +82 -0
- package/dist/mini-map/index.ts +16 -0
- package/dist/mini-map/mini-map.styles.ts +32 -0
- package/dist/mini-map/mini-map.ts +122 -0
- package/dist/password-input/index.ts +9 -0
- package/dist/password-input/password-input.styles.ts +10 -0
- package/dist/password-input/password-input.ts +45 -0
- package/dist/pre-call-lobby/index.ts +22 -0
- package/dist/pre-call-lobby/pre-call-lobby.styles.ts +108 -0
- package/dist/pre-call-lobby/pre-call-lobby.ts +61 -0
- package/dist/radial-gauge/index.ts +26 -0
- package/dist/radial-gauge/radial-gauge.styles.ts +75 -0
- package/dist/radial-gauge/radial-gauge.ts +183 -0
- package/dist/rating-scale/index.ts +16 -0
- package/dist/rating-scale/rating-scale.styles.ts +48 -0
- package/dist/rating-scale/rating-scale.ts +101 -0
- package/dist/segmented-control/index.ts +13 -0
- package/dist/segmented-control/segmented-control.styles.ts +42 -0
- package/dist/segmented-control/segmented-control.ts +80 -0
- package/dist/separator/index.ts +12 -0
- package/dist/separator/separator.styles.ts +34 -0
- package/dist/separator/separator.ts +53 -0
- package/dist/social-auth-button/index.ts +14 -0
- package/dist/social-auth-button/social-auth-button.styles.ts +16 -0
- package/dist/social-auth-button/social-auth-button.ts +51 -0
- package/dist/sticky-note/index.ts +14 -0
- package/dist/sticky-note/sticky-note.styles.ts +37 -0
- package/dist/sticky-note/sticky-note.ts +47 -0
- package/dist/terminal/index.ts +13 -0
- package/dist/terminal/terminal.styles.ts +56 -0
- package/dist/terminal/terminal.ts +60 -0
- package/dist/test-results/index.ts +19 -0
- package/dist/test-results/test-results.styles.ts +93 -0
- package/dist/test-results/test-results.ts +83 -0
- package/dist/timeline/index.ts +22 -0
- package/dist/timeline/timeline.styles.ts +98 -0
- package/dist/timeline/timeline.ts +86 -0
- package/dist/video-grid/index.ts +16 -0
- package/dist/video-grid/video-grid.styles.ts +38 -0
- package/dist/video-grid/video-grid.ts +93 -0
- package/dist/video-tile/index.ts +17 -0
- package/dist/video-tile/video-tile.styles.ts +59 -0
- package/dist/video-tile/video-tile.ts +74 -0
- package/dist/wizard/index.ts +28 -0
- package/dist/wizard/wizard.styles.ts +147 -0
- package/dist/wizard/wizard.ts +128 -0
- package/package.json +47 -19
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { AccessibilityProps } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
export type SegmentedControlSize = 'sm' | 'md'
|
|
4
|
+
|
|
5
|
+
/** Value identifying a single segment. */
|
|
6
|
+
export type SegmentedControlValue = string
|
|
7
|
+
|
|
8
|
+
export interface SegmentedControlProps {
|
|
9
|
+
/** Currently selected segment value (controlled). */
|
|
10
|
+
value?: SegmentedControlValue
|
|
11
|
+
/** Visual size of the control. */
|
|
12
|
+
size?: SegmentedControlSize
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SegmentedControlAPI {
|
|
16
|
+
/** ARIA attributes to spread on the group element. */
|
|
17
|
+
ariaProps: Partial<AccessibilityProps>
|
|
18
|
+
/** Data attributes for CSS styling hooks. */
|
|
19
|
+
dataAttributes: Record<string, string>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Build the framework-agnostic group props for a segmented control.
|
|
24
|
+
*
|
|
25
|
+
* Returns `role="radiogroup"` plus data attributes; adapters spread these
|
|
26
|
+
* onto their group element and handle their own rendering/state.
|
|
27
|
+
*/
|
|
28
|
+
export function createSegmentedControl(
|
|
29
|
+
props: SegmentedControlProps = {},
|
|
30
|
+
): SegmentedControlAPI {
|
|
31
|
+
const { value, size = 'md' } = props
|
|
32
|
+
|
|
33
|
+
const ariaProps: Partial<AccessibilityProps> = {
|
|
34
|
+
role: 'radiogroup',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const dataAttributes: Record<string, string> = {
|
|
38
|
+
'data-size': size,
|
|
39
|
+
}
|
|
40
|
+
if (value !== undefined) {
|
|
41
|
+
dataAttributes['data-value'] = value
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { ariaProps, dataAttributes }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Pure keyboard-navigation rule shared by every adapter.
|
|
49
|
+
*
|
|
50
|
+
* Given the current index, the pressed key, and the item count, returns the
|
|
51
|
+
* index that focus/selection should move to. Arrow keys wrap; Home/End jump
|
|
52
|
+
* to the ends. Unhandled keys (or an empty group) return `current` unchanged.
|
|
53
|
+
*
|
|
54
|
+
* - ArrowRight / ArrowDown → next (wraps to 0 past the end)
|
|
55
|
+
* - ArrowLeft / ArrowUp → previous (wraps to last before 0)
|
|
56
|
+
* - Home → first
|
|
57
|
+
* - End → last
|
|
58
|
+
*/
|
|
59
|
+
export function getNextSegmentIndex(
|
|
60
|
+
current: number,
|
|
61
|
+
key: string,
|
|
62
|
+
count: number,
|
|
63
|
+
): number {
|
|
64
|
+
if (count <= 0) return current
|
|
65
|
+
|
|
66
|
+
switch (key) {
|
|
67
|
+
case 'ArrowRight':
|
|
68
|
+
case 'ArrowDown':
|
|
69
|
+
return (current + 1) % count
|
|
70
|
+
case 'ArrowLeft':
|
|
71
|
+
case 'ArrowUp':
|
|
72
|
+
return (current - 1 + count) % count
|
|
73
|
+
case 'Home':
|
|
74
|
+
return 0
|
|
75
|
+
case 'End':
|
|
76
|
+
return count - 1
|
|
77
|
+
default:
|
|
78
|
+
return current
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Visual variants for the separator rule.
|
|
5
|
+
*
|
|
6
|
+
* - `orientation: horizontal` → a full-width 1px line.
|
|
7
|
+
* - `orientation: vertical` → a full-height 1px line.
|
|
8
|
+
* - `labeled` → flex layout used by the labeled-divider variant, which
|
|
9
|
+
* arranges a centered label between two flanking lines.
|
|
10
|
+
*/
|
|
11
|
+
export const separatorVariants = cva({
|
|
12
|
+
base: '',
|
|
13
|
+
variants: {
|
|
14
|
+
orientation: {
|
|
15
|
+
horizontal: 'h-px w-full bg-border',
|
|
16
|
+
vertical: 'w-px h-full bg-border',
|
|
17
|
+
},
|
|
18
|
+
labeled: {
|
|
19
|
+
true: 'flex items-center',
|
|
20
|
+
false: '',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
orientation: 'horizontal',
|
|
25
|
+
labeled: 'false',
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
/** Class for each flanking line in the labeled-divider variant. */
|
|
30
|
+
export const separatorLineClass = 'h-px flex-1 bg-border'
|
|
31
|
+
|
|
32
|
+
/** Class for the centered label in the labeled-divider variant. */
|
|
33
|
+
export const separatorLabelClass =
|
|
34
|
+
'px-3 text-xs uppercase tracking-wide text-muted-foreground'
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AccessibilityProps } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
export type SeparatorOrientation = 'horizontal' | 'vertical'
|
|
4
|
+
|
|
5
|
+
export interface SeparatorProps {
|
|
6
|
+
/** Orientation of the separator. Defaults to `'horizontal'`. */
|
|
7
|
+
orientation?: SeparatorOrientation
|
|
8
|
+
/**
|
|
9
|
+
* When `true` (the default) the separator is purely visual and is hidden
|
|
10
|
+
* from assistive tech (`role="none"`). When `false` it is exposed as a
|
|
11
|
+
* semantic `role="separator"` with the appropriate `aria-orientation`.
|
|
12
|
+
*/
|
|
13
|
+
decorative?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* ARIA attributes for a separator. Extends the shared base with
|
|
18
|
+
* `aria-orientation`, which is specific to the `separator` role.
|
|
19
|
+
*/
|
|
20
|
+
export interface SeparatorAriaProps extends Partial<AccessibilityProps> {
|
|
21
|
+
'aria-orientation'?: SeparatorOrientation
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SeparatorAPI {
|
|
25
|
+
/** ARIA attributes to spread on the element. */
|
|
26
|
+
ariaProps: SeparatorAriaProps
|
|
27
|
+
/** Data attributes for CSS styling hooks. */
|
|
28
|
+
dataAttributes: Record<string, string>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Headless separator core — computes ARIA semantics and data attributes for
|
|
33
|
+
* a separator rule. JSX-free so it can be shared across framework adapters.
|
|
34
|
+
*
|
|
35
|
+
* Per ARIA, a decorative separator must not be announced (`role="none"`),
|
|
36
|
+
* while a semantic one exposes `role="separator"` and `aria-orientation`.
|
|
37
|
+
*/
|
|
38
|
+
export function createSeparator(props: SeparatorProps = {}): SeparatorAPI {
|
|
39
|
+
const { orientation = 'horizontal', decorative = true } = props
|
|
40
|
+
|
|
41
|
+
const ariaProps: SeparatorAriaProps = decorative
|
|
42
|
+
? { role: 'none' }
|
|
43
|
+
: { role: 'separator', 'aria-orientation': orientation }
|
|
44
|
+
|
|
45
|
+
const dataAttributes: Record<string, string> = {
|
|
46
|
+
'data-orientation': orientation,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
ariaProps,
|
|
51
|
+
dataAttributes,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createSocialAuthButton,
|
|
3
|
+
PROVIDER_LABELS,
|
|
4
|
+
PROVIDER_MONO,
|
|
5
|
+
type SocialProvider,
|
|
6
|
+
type SocialAuthButtonProps,
|
|
7
|
+
type SocialAuthButtonAPI,
|
|
8
|
+
} from './social-auth-button.js'
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
socialAuthButtonVariants,
|
|
12
|
+
socialAuthRowVariants,
|
|
13
|
+
lastUsedBadgeVariants,
|
|
14
|
+
} from './social-auth-button.styles.js'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/** Wrapper around an individual social auth button (positions the lastUsed badge). */
|
|
4
|
+
export const socialAuthButtonVariants = cva({
|
|
5
|
+
base: 'relative',
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
/** Responsive grid wrapper: one column on mobile, two columns from `sm` up. */
|
|
9
|
+
export const socialAuthRowVariants = cva({
|
|
10
|
+
base: 'grid grid-cols-1 sm:grid-cols-2 gap-3',
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
/** Floating "Last used" badge anchored to the top-right of a button. */
|
|
14
|
+
export const lastUsedBadgeVariants = cva({
|
|
15
|
+
base: 'absolute -top-2 -right-2 rounded-full bg-primary text-primary-foreground text-[10px] px-1.5',
|
|
16
|
+
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type SocialProvider = 'google' | 'github' | 'microsoft' | 'apple'
|
|
2
|
+
|
|
3
|
+
/** "Continue with …" label shown on each provider's button. */
|
|
4
|
+
export const PROVIDER_LABELS: Record<SocialProvider, string> = {
|
|
5
|
+
google: 'Continue with Google',
|
|
6
|
+
github: 'Continue with GitHub',
|
|
7
|
+
microsoft: 'Continue with Microsoft',
|
|
8
|
+
apple: 'Continue with Apple',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Which providers render their brand mark monochrome (currentColor). GitHub and
|
|
13
|
+
* Apple are monochrome by design so they read correctly on dark/outline
|
|
14
|
+
* surfaces; Google and Microsoft keep their full brand color.
|
|
15
|
+
*/
|
|
16
|
+
export const PROVIDER_MONO: Record<SocialProvider, boolean> = {
|
|
17
|
+
google: false,
|
|
18
|
+
github: true,
|
|
19
|
+
microsoft: false,
|
|
20
|
+
apple: true,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SocialAuthButtonProps {
|
|
24
|
+
/** Identity provider this button authenticates against. */
|
|
25
|
+
provider: SocialProvider
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SocialAuthButtonAPI {
|
|
29
|
+
/** Data attributes for CSS styling hooks. */
|
|
30
|
+
dataAttributes: Record<string, string>
|
|
31
|
+
/** "Continue with …" label for the provider. */
|
|
32
|
+
label: string
|
|
33
|
+
/** Whether the brand mark should render monochrome. */
|
|
34
|
+
mono: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Framework-agnostic core for a social sign-in button. Returns the provider
|
|
39
|
+
* label, monochrome hint, and the `data-provider` styling hook. No React/JSX.
|
|
40
|
+
*/
|
|
41
|
+
export function createSocialAuthButton(
|
|
42
|
+
props: SocialAuthButtonProps,
|
|
43
|
+
): SocialAuthButtonAPI {
|
|
44
|
+
const { provider } = props
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
dataAttributes: { 'data-provider': provider },
|
|
48
|
+
label: PROVIDER_LABELS[provider],
|
|
49
|
+
mono: PROVIDER_MONO[provider],
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
STICKY_NOTE_COLORS,
|
|
3
|
+
nextStickyColor,
|
|
4
|
+
createStickyNote,
|
|
5
|
+
type StickyNoteColor,
|
|
6
|
+
type CreateStickyNoteOptions,
|
|
7
|
+
type StickyNoteAPI,
|
|
8
|
+
} from './sticky-note.js'
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
stickyNoteVariants,
|
|
12
|
+
stickyNoteTextClass,
|
|
13
|
+
stickyNoteAuthorClass,
|
|
14
|
+
} from './sticky-note.styles.js'
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Note container with color variant.
|
|
5
|
+
*
|
|
6
|
+
* Sticky notes are intrinsically colorful — an approved exception to the
|
|
7
|
+
* semantic-token-only rule. Each color maps to a soft tint background with a
|
|
8
|
+
* readable dark text.
|
|
9
|
+
*/
|
|
10
|
+
export const stickyNoteVariants = cva({
|
|
11
|
+
base: [
|
|
12
|
+
'relative inline-flex flex-col gap-2 rounded-md p-4',
|
|
13
|
+
'shadow-md',
|
|
14
|
+
'min-w-[160px] min-h-[120px]',
|
|
15
|
+
].join(' '),
|
|
16
|
+
variants: {
|
|
17
|
+
color: {
|
|
18
|
+
yellow: 'bg-yellow-100 text-yellow-900',
|
|
19
|
+
pink: 'bg-pink-100 text-pink-900',
|
|
20
|
+
blue: 'bg-blue-100 text-blue-900',
|
|
21
|
+
green: 'bg-green-100 text-green-900',
|
|
22
|
+
purple: 'bg-purple-100 text-purple-900',
|
|
23
|
+
orange: 'bg-orange-100 text-orange-900',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
color: 'yellow',
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
/** Class for the editable/static text area inside the note. */
|
|
32
|
+
export const stickyNoteTextClass =
|
|
33
|
+
'flex-1 w-full resize-none bg-transparent text-sm leading-relaxed placeholder:opacity-60 outline-none'
|
|
34
|
+
|
|
35
|
+
/** Class for the author chip rendered in the note footer. */
|
|
36
|
+
export const stickyNoteAuthorClass =
|
|
37
|
+
'mt-auto text-xs font-medium opacity-70 truncate'
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** The available color variants for a sticky note. */
|
|
2
|
+
export type StickyNoteColor = 'yellow' | 'pink' | 'blue' | 'green' | 'purple' | 'orange'
|
|
3
|
+
|
|
4
|
+
/** Ordered list of all sticky note colors — used for cycling and iteration. */
|
|
5
|
+
export const STICKY_NOTE_COLORS: StickyNoteColor[] = [
|
|
6
|
+
'yellow',
|
|
7
|
+
'pink',
|
|
8
|
+
'blue',
|
|
9
|
+
'green',
|
|
10
|
+
'purple',
|
|
11
|
+
'orange',
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Returns the next color in the cycle. Wraps from the last color back to the
|
|
16
|
+
* first. Useful for "add note" flows that auto-assign colors in sequence.
|
|
17
|
+
*/
|
|
18
|
+
export function nextStickyColor(color: StickyNoteColor): StickyNoteColor {
|
|
19
|
+
const index = STICKY_NOTE_COLORS.indexOf(color)
|
|
20
|
+
const next = (index + 1) % STICKY_NOTE_COLORS.length
|
|
21
|
+
return STICKY_NOTE_COLORS[next]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface CreateStickyNoteOptions {
|
|
25
|
+
/** Color of the note. */
|
|
26
|
+
color: StickyNoteColor
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface StickyNoteAPI {
|
|
30
|
+
/** Data attributes for styling hooks; spread onto the note container. */
|
|
31
|
+
dataAttributes: { 'data-color': StickyNoteColor }
|
|
32
|
+
/** ARIA props for the note container. */
|
|
33
|
+
ariaProps: { role: 'group' }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Build the framework-agnostic props for a sticky note container.
|
|
38
|
+
*
|
|
39
|
+
* Returns `role="group"` plus the `data-color` attribute that adapters use
|
|
40
|
+
* as a styling hook. Logic is pure and SSR-safe — no Math.random, no Date.
|
|
41
|
+
*/
|
|
42
|
+
export function createStickyNote(options: CreateStickyNoteOptions): StickyNoteAPI {
|
|
43
|
+
return {
|
|
44
|
+
dataAttributes: { 'data-color': options.color },
|
|
45
|
+
ariaProps: { role: 'group' },
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The outer scroll container — a dark monospaced surface that houses all
|
|
5
|
+
* terminal lines. Uses semantic card tokens so it inherits the active theme.
|
|
6
|
+
*/
|
|
7
|
+
export const terminalVariants = cva({
|
|
8
|
+
base: [
|
|
9
|
+
'relative rounded-lg border border-border bg-card font-mono text-sm',
|
|
10
|
+
'overflow-y-auto overflow-x-auto',
|
|
11
|
+
'p-4 space-y-0.5',
|
|
12
|
+
].join(' '),
|
|
13
|
+
variants: {
|
|
14
|
+
maxHeight: {
|
|
15
|
+
sm: 'max-h-48',
|
|
16
|
+
md: 'max-h-80',
|
|
17
|
+
lg: 'max-h-[32rem]',
|
|
18
|
+
none: '',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
maxHeight: 'md',
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A single terminal line. The `kind` variant maps each semantic category to
|
|
28
|
+
* the appropriate semantic token class — no raw color values inline.
|
|
29
|
+
*/
|
|
30
|
+
export const terminalLineVariants = cva({
|
|
31
|
+
base: 'block whitespace-pre-wrap break-all leading-relaxed',
|
|
32
|
+
variants: {
|
|
33
|
+
kind: {
|
|
34
|
+
/** The user-typed command: full foreground, bold weight. */
|
|
35
|
+
command: 'text-foreground font-semibold',
|
|
36
|
+
/** Normal program output. */
|
|
37
|
+
stdout: 'text-foreground',
|
|
38
|
+
/** Error / stderr output — uses the destructive semantic token. */
|
|
39
|
+
stderr: 'text-destructive',
|
|
40
|
+
/** Informational summary lines — muted, visually de-emphasised. */
|
|
41
|
+
info: 'text-muted-foreground',
|
|
42
|
+
/**
|
|
43
|
+
* Success / passing lines — green-family.
|
|
44
|
+
* Maps to `text-emerald-500` via the Tailwind semantic alias used
|
|
45
|
+
* across the design system; downstream themes can override this token.
|
|
46
|
+
*/
|
|
47
|
+
success: 'text-emerald-500 dark:text-emerald-400',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
defaultVariants: {
|
|
51
|
+
kind: 'stdout',
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
/** The prompt symbol (`$`, `❯`, …) preceding a command line. */
|
|
56
|
+
export const terminalPromptClass = 'select-none mr-2 text-muted-foreground'
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** The semantic kind of a terminal output line. */
|
|
2
|
+
export type TerminalLineKind = 'command' | 'stdout' | 'stderr' | 'info' | 'success'
|
|
3
|
+
|
|
4
|
+
/** A single line displayed in the terminal output panel. */
|
|
5
|
+
export interface TerminalLine {
|
|
6
|
+
/** Optional stable key for list rendering. */
|
|
7
|
+
id?: string
|
|
8
|
+
/** Semantic kind — drives styling and screen-reader announcements. */
|
|
9
|
+
kind: TerminalLineKind
|
|
10
|
+
/** Text content of the line. */
|
|
11
|
+
text: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface TerminalAPI {
|
|
15
|
+
/**
|
|
16
|
+
* ARIA attributes to spread on the terminal container element.
|
|
17
|
+
* - `role="log"` semantics: assistive technologies announce appended content.
|
|
18
|
+
* - `aria-live="polite"` so new output does not interrupt the user mid-action.
|
|
19
|
+
*/
|
|
20
|
+
ariaProps: {
|
|
21
|
+
role: 'log'
|
|
22
|
+
'aria-live': 'polite'
|
|
23
|
+
'aria-label'?: string
|
|
24
|
+
}
|
|
25
|
+
/** Data attributes for styling hooks. */
|
|
26
|
+
dataAttributes: Record<string, string>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Build the prompt string shown before a command line.
|
|
31
|
+
*
|
|
32
|
+
* @param promptSymbol - The shell prompt character(s), e.g. `'$'` or `'❯'`.
|
|
33
|
+
* @param command - The command text that follows the prompt.
|
|
34
|
+
* @returns The formatted prompt string, e.g. `'$ python solution.py'`.
|
|
35
|
+
*/
|
|
36
|
+
export function formatPrompt(promptSymbol: string, command: string): string {
|
|
37
|
+
return `${promptSymbol} ${command}`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Build the framework-agnostic ARIA and data props for a terminal output panel.
|
|
42
|
+
*
|
|
43
|
+
* Returns `role="log"` (live region semantics for streaming output) plus data
|
|
44
|
+
* attributes; adapters spread these onto their scroll container.
|
|
45
|
+
*/
|
|
46
|
+
export function createTerminal(options: { label?: string } = {}): TerminalAPI {
|
|
47
|
+
const ariaProps: TerminalAPI['ariaProps'] = {
|
|
48
|
+
role: 'log',
|
|
49
|
+
'aria-live': 'polite',
|
|
50
|
+
}
|
|
51
|
+
if (options.label) {
|
|
52
|
+
ariaProps['aria-label'] = options.label
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const dataAttributes: Record<string, string> = {
|
|
56
|
+
'data-component': 'terminal',
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { ariaProps, dataAttributes }
|
|
60
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export {
|
|
2
|
+
summarizeTests,
|
|
3
|
+
createTestResults,
|
|
4
|
+
type TestStatus,
|
|
5
|
+
type TestResultData,
|
|
6
|
+
type TestSummary,
|
|
7
|
+
type TestResultsAPI,
|
|
8
|
+
} from './test-results.js'
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
testResultsContainerVariants,
|
|
12
|
+
testResultsSummaryVariants,
|
|
13
|
+
testRowVariants,
|
|
14
|
+
testStatusBadgeVariants,
|
|
15
|
+
testNameClass,
|
|
16
|
+
testDurationClass,
|
|
17
|
+
testDiffBlockVariants,
|
|
18
|
+
testMessageClass,
|
|
19
|
+
} from './test-results.styles.js'
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/** Outer container that wraps the summary bar and the test row list. */
|
|
4
|
+
export const testResultsContainerVariants = cva({
|
|
5
|
+
base: 'flex flex-col gap-3 w-full',
|
|
6
|
+
variants: {},
|
|
7
|
+
defaultVariants: {},
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Summary bar ("2 / 3 passed").
|
|
12
|
+
*
|
|
13
|
+
* The `outcome` variant maps to a semantic color token so the bar turns green
|
|
14
|
+
* when everything passes, red when there are failures.
|
|
15
|
+
*/
|
|
16
|
+
export const testResultsSummaryVariants = cva({
|
|
17
|
+
base: [
|
|
18
|
+
'flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium',
|
|
19
|
+
'border',
|
|
20
|
+
].join(' '),
|
|
21
|
+
variants: {
|
|
22
|
+
outcome: {
|
|
23
|
+
pass: 'border-success/30 bg-success/10 text-success',
|
|
24
|
+
fail: 'border-destructive/30 bg-destructive/10 text-destructive',
|
|
25
|
+
skip: 'border-muted/30 bg-muted/10 text-muted-foreground',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
defaultVariants: {
|
|
29
|
+
outcome: 'pass',
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
/** A single test row in the list. */
|
|
34
|
+
export const testRowVariants = cva({
|
|
35
|
+
base: [
|
|
36
|
+
'flex flex-col gap-1.5 rounded-md border px-3 py-2.5 text-sm',
|
|
37
|
+
].join(' '),
|
|
38
|
+
variants: {
|
|
39
|
+
status: {
|
|
40
|
+
pass: 'border-success/20 bg-success/5',
|
|
41
|
+
fail: 'border-destructive/20 bg-destructive/5',
|
|
42
|
+
skip: 'border-muted/20 bg-muted/5',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
defaultVariants: {
|
|
46
|
+
status: 'pass',
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
/** Status icon / badge inside a test row (the PASS / FAIL / SKIP pill). */
|
|
51
|
+
export const testStatusBadgeVariants = cva({
|
|
52
|
+
base: 'inline-flex items-center rounded px-1.5 py-0.5 text-xs font-semibold uppercase tracking-wide',
|
|
53
|
+
variants: {
|
|
54
|
+
status: {
|
|
55
|
+
pass: 'bg-success/20 text-success',
|
|
56
|
+
fail: 'bg-destructive/20 text-destructive',
|
|
57
|
+
skip: 'bg-muted text-muted-foreground',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
defaultVariants: {
|
|
61
|
+
status: 'pass',
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
/** Test name text inside a row. */
|
|
66
|
+
export const testNameClass = 'font-medium text-foreground'
|
|
67
|
+
|
|
68
|
+
/** Duration text ("12 ms") inside a row. */
|
|
69
|
+
export const testDurationClass = 'text-xs text-muted-foreground'
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Expected / actual diff block shown beneath a failing test row.
|
|
73
|
+
*
|
|
74
|
+
* The `side` variant sets the left-border accent color to distinguish the two
|
|
75
|
+
* sides of the diff.
|
|
76
|
+
*/
|
|
77
|
+
export const testDiffBlockVariants = cva({
|
|
78
|
+
base: [
|
|
79
|
+
'rounded border-l-2 bg-muted/30 px-3 py-2 font-mono text-xs text-foreground',
|
|
80
|
+
].join(' '),
|
|
81
|
+
variants: {
|
|
82
|
+
side: {
|
|
83
|
+
expected: 'border-l-success text-success',
|
|
84
|
+
actual: 'border-l-destructive text-destructive',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
defaultVariants: {
|
|
88
|
+
side: 'actual',
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
/** Error / skip message text beneath a test row. */
|
|
93
|
+
export const testMessageClass = 'text-xs text-muted-foreground italic'
|