@aphrody/koharu-ui 0.0.0-stage → 0.1.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.
Files changed (123) hide show
  1. package/README.md +37 -2
  2. package/package.json +70 -4
  3. package/src/assets/NotoSansCJK-VF.otf +0 -0
  4. package/src/assets/OFL.txt +92 -0
  5. package/src/components/ClientOnly.tsx +21 -0
  6. package/src/components/ShenronExportPanel.tsx +213 -0
  7. package/src/components/app/AboutDialog.tsx +64 -0
  8. package/src/components/app/KoharuApp.tsx +58 -0
  9. package/src/components/app/StartupView.tsx +67 -0
  10. package/src/components/app/TitleBar.tsx +322 -0
  11. package/src/components/app/Updater.tsx +143 -0
  12. package/src/components/app/WindowChrome.tsx +137 -0
  13. package/src/components/controls/ColorSampling.tsx +70 -0
  14. package/src/components/controls/ColorWell.tsx +139 -0
  15. package/src/components/controls/CommitTextarea.tsx +63 -0
  16. package/src/components/controls/FontPicker.tsx +543 -0
  17. package/src/components/controls/ModelPicker.tsx +178 -0
  18. package/src/components/controls/OutputPicker.tsx +170 -0
  19. package/src/components/editor/ActivityCenter.tsx +234 -0
  20. package/src/components/editor/AgentPanel.tsx +352 -0
  21. package/src/components/editor/CanvasCommandBar.tsx +38 -0
  22. package/src/components/editor/CanvasOverlay.tsx +194 -0
  23. package/src/components/editor/CanvasWorkspace.tsx +947 -0
  24. package/src/components/editor/Editor.tsx +56 -0
  25. package/src/components/editor/InferenceControl.tsx +457 -0
  26. package/src/components/editor/Inspector.tsx +1073 -0
  27. package/src/components/editor/PageRail.tsx +581 -0
  28. package/src/components/editor/ResourceMonitor.tsx +151 -0
  29. package/src/components/editor/RightSidebar.tsx +50 -0
  30. package/src/components/editor/SelectionControls.tsx +184 -0
  31. package/src/components/editor/StatusBar.tsx +41 -0
  32. package/src/components/editor/ToolBar.tsx +182 -0
  33. package/src/components/editor/useCanvas.ts +194 -0
  34. package/src/components/preferences/GenerationPreferences.tsx +118 -0
  35. package/src/components/preferences/KoharuSettings.tsx +229 -0
  36. package/src/components/preferences/PipelinePreferences.tsx +173 -0
  37. package/src/components/preferences/PreferenceFields.tsx +149 -0
  38. package/src/components/preferences/ProviderPreferences.tsx +173 -0
  39. package/src/components/preferences/SettingsPage.tsx +336 -0
  40. package/src/components/preferences/TranslationPreferences.tsx +187 -0
  41. package/src/components/preferences/TypesettingPreferences.tsx +142 -0
  42. package/src/components/preferences/models.ts +103 -0
  43. package/src/components/start/StartView.tsx +277 -0
  44. package/src/index.ts +59 -0
  45. package/src/internal/components/accordion.tsx +72 -0
  46. package/src/internal/components/alert-dialog.tsx +189 -0
  47. package/src/internal/components/alert.tsx +76 -0
  48. package/src/internal/components/aspect-ratio.tsx +22 -0
  49. package/src/internal/components/attachment.tsx +207 -0
  50. package/src/internal/components/avatar.tsx +109 -0
  51. package/src/internal/components/badge.tsx +52 -0
  52. package/src/internal/components/breadcrumb.tsx +125 -0
  53. package/src/internal/components/bubble.tsx +128 -0
  54. package/src/internal/components/button-group.tsx +87 -0
  55. package/src/internal/components/button.tsx +58 -0
  56. package/src/internal/components/card.tsx +103 -0
  57. package/src/internal/components/checkbox.tsx +29 -0
  58. package/src/internal/components/collapsible.tsx +21 -0
  59. package/src/internal/components/combobox.tsx +298 -0
  60. package/src/internal/components/context-menu.tsx +272 -0
  61. package/src/internal/components/dialog.tsx +162 -0
  62. package/src/internal/components/direction.tsx +6 -0
  63. package/src/internal/components/drawer.tsx +230 -0
  64. package/src/internal/components/dropdown-menu.tsx +278 -0
  65. package/src/internal/components/empty.tsx +104 -0
  66. package/src/internal/components/field.tsx +238 -0
  67. package/src/internal/components/hover-card.tsx +52 -0
  68. package/src/internal/components/input-group.tsx +158 -0
  69. package/src/internal/components/input.tsx +20 -0
  70. package/src/internal/components/item.tsx +201 -0
  71. package/src/internal/components/kbd.tsx +26 -0
  72. package/src/internal/components/label.tsx +20 -0
  73. package/src/internal/components/marker.tsx +71 -0
  74. package/src/internal/components/menubar.tsx +280 -0
  75. package/src/internal/components/message.tsx +92 -0
  76. package/src/internal/components/native-select.tsx +61 -0
  77. package/src/internal/components/navigation-menu.tsx +168 -0
  78. package/src/internal/components/number-field.tsx +68 -0
  79. package/src/internal/components/pagination.tsx +130 -0
  80. package/src/internal/components/popover.tsx +91 -0
  81. package/src/internal/components/progress.tsx +83 -0
  82. package/src/internal/components/radio-group.tsx +38 -0
  83. package/src/internal/components/resizable.tsx +50 -0
  84. package/src/internal/components/scroll-area.tsx +88 -0
  85. package/src/internal/components/select.tsx +191 -0
  86. package/src/internal/components/separator.tsx +25 -0
  87. package/src/internal/components/sheet.tsx +140 -0
  88. package/src/internal/components/sidebar.tsx +723 -0
  89. package/src/internal/components/skeleton.tsx +13 -0
  90. package/src/internal/components/slider.tsx +56 -0
  91. package/src/internal/components/spinner.tsx +10 -0
  92. package/src/internal/components/switch.tsx +32 -0
  93. package/src/internal/components/table.tsx +116 -0
  94. package/src/internal/components/tabs.tsx +82 -0
  95. package/src/internal/components/textarea.tsx +18 -0
  96. package/src/internal/components/toast.tsx +235 -0
  97. package/src/internal/components/toggle-group.tsx +89 -0
  98. package/src/internal/components/toggle.tsx +45 -0
  99. package/src/internal/components/tooltip.tsx +67 -0
  100. package/src/internal/hooks/use-mobile.ts +19 -0
  101. package/src/internal/lib/utils.ts +6 -0
  102. package/src/internal/styles/globals.css +73 -0
  103. package/src/lib/backend.ts +85 -0
  104. package/src/lib/document.ts +46 -0
  105. package/src/lib/geometry.ts +272 -0
  106. package/src/lib/i18n.ts +54 -0
  107. package/src/lib/queries.ts +110 -0
  108. package/src/lib/store.ts +191 -0
  109. package/src/lib/translation.ts +35 -0
  110. package/src/locales/en-US/translation.json +536 -0
  111. package/src/locales/es-ES/translation.json +536 -0
  112. package/src/locales/ja-JP/translation.json +536 -0
  113. package/src/locales/ko-KR/translation.json +536 -0
  114. package/src/locales/pt-BR/translation.json +536 -0
  115. package/src/locales/ru-RU/translation.json +536 -0
  116. package/src/locales/tr-TR/translation.json +536 -0
  117. package/src/locales/zh-CN/translation.json +536 -0
  118. package/src/locales/zh-TW/translation.json +536 -0
  119. package/src/navigation.ts +28 -0
  120. package/src/providers.tsx +132 -0
  121. package/src/shenron-pipeline-env.d.ts +14 -0
  122. package/src/styles.css +246 -0
  123. package/tsconfig.json +21 -0
@@ -0,0 +1,139 @@
1
+ 'use client'
2
+
3
+ import { Pipette, SquareSlash } from 'lucide-react'
4
+ import { useEffect, useState } from 'react'
5
+ import { HexColorInput, HexColorPicker } from 'react-colorful'
6
+ import { useTranslation } from 'react-i18next'
7
+
8
+ import { useColorSampling } from '@aphrody/koharu-ui/components/controls/ColorSampling'
9
+ import { Button } from '../../internal/components/button'
10
+ import { Popover, PopoverContent, PopoverTrigger } from '../../internal/components/popover'
11
+ import { cn } from '../../internal/lib/utils'
12
+
13
+ type ColorWellProps = {
14
+ label?: string
15
+ size?: 'default' | 'sm'
16
+ disabled?: boolean
17
+ } & (
18
+ | {
19
+ value: string
20
+ allowTransparent?: false
21
+ onChange: (color: string) => void
22
+ }
23
+ | {
24
+ value: string | null
25
+ allowTransparent: true
26
+ onChange: (color: string | null) => void
27
+ }
28
+ )
29
+
30
+ export function ColorWell(props: ColorWellProps) {
31
+ const { t } = useTranslation()
32
+ const { value, label, size = 'default', disabled = false, allowTransparent = false } = props
33
+ const [draft, setDraft] = useState(value ?? '#000000')
34
+ const [transparent, setTransparent] = useState(value === null)
35
+ const [open, setOpen] = useState(false)
36
+ const sampling = useColorSampling()
37
+ const accessibleLabel = label ?? t('colorPicker.brushColor')
38
+
39
+ useEffect(() => {
40
+ if (value === null) {
41
+ setTransparent(true)
42
+ } else {
43
+ setDraft(value)
44
+ setTransparent(false)
45
+ }
46
+ }, [value])
47
+
48
+ const set = (color: string) => {
49
+ const normalized = normalize(color)
50
+ setDraft(normalized)
51
+ setTransparent(false)
52
+ if (props.allowTransparent) props.onChange(normalized)
53
+ else props.onChange(normalized)
54
+ }
55
+
56
+ const clear = () => {
57
+ if (!props.allowTransparent) return
58
+ setTransparent(true)
59
+ props.onChange(null)
60
+ }
61
+
62
+ return (
63
+ <Popover open={open} onOpenChange={setOpen}>
64
+ <PopoverTrigger
65
+ aria-label={accessibleLabel}
66
+ disabled={disabled}
67
+ className={cn(
68
+ 'grid place-items-center border border-input bg-background disabled:cursor-not-allowed disabled:opacity-40',
69
+ size === 'sm' ? 'size-6 rounded-md' : 'size-8 rounded-lg',
70
+ )}
71
+ >
72
+ {transparent ? (
73
+ <SquareSlash
74
+ aria-hidden='true'
75
+ className={cn('text-muted-foreground', size === 'sm' ? 'size-3.5' : 'size-4')}
76
+ />
77
+ ) : (
78
+ <span
79
+ className={cn(
80
+ 'rounded-[3px] ring-1 ring-black/15',
81
+ size === 'sm' ? 'size-3' : 'size-4',
82
+ )}
83
+ style={{ backgroundColor: draft }}
84
+ />
85
+ )}
86
+ </PopoverTrigger>
87
+ <PopoverContent side='right' align='start' className='w-60 rounded-xl p-3'>
88
+ <div>
89
+ <HexColorPicker
90
+ color={draft}
91
+ onChange={(color) => setDraft(normalize(color))}
92
+ onChangeEnd={set}
93
+ />
94
+ </div>
95
+ <div className='mt-3 flex gap-2'>
96
+ <HexColorInput
97
+ prefixed
98
+ color={draft}
99
+ aria-label={t('colorPicker.hexCode')}
100
+ className='h-8 min-w-0 flex-1 rounded-lg border border-input bg-background px-2 font-mono text-[11px] uppercase outline-none focus:border-ring'
101
+ onChange={set}
102
+ />
103
+ {sampling && (
104
+ <Button
105
+ size='icon'
106
+ variant='outline'
107
+ aria-label={t('colorPicker.sampleCanvas')}
108
+ onClick={() => {
109
+ setOpen(false)
110
+ sampling.request(set)
111
+ }}
112
+ >
113
+ <Pipette />
114
+ </Button>
115
+ )}
116
+ </div>
117
+ {allowTransparent && (
118
+ <Button
119
+ type='button'
120
+ size='sm'
121
+ variant={transparent ? 'secondary' : 'ghost'}
122
+ className='mt-2 w-full justify-start gap-2'
123
+ onClick={clear}
124
+ >
125
+ <SquareSlash aria-hidden='true' className='size-3.5 text-muted-foreground' />
126
+ {t('colorPicker.transparent')}
127
+ </Button>
128
+ )}
129
+ </PopoverContent>
130
+ </Popover>
131
+ )
132
+ }
133
+
134
+ function normalize(value: string): string {
135
+ const digits = value.startsWith('#') ? value.slice(1) : value
136
+ const expanded =
137
+ digits.length === 3 ? [...digits].map((digit) => digit.repeat(2)).join('') : digits
138
+ return `#${expanded}`.toUpperCase()
139
+ }
@@ -0,0 +1,63 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useRef, useState, type ComponentProps } from 'react'
4
+
5
+ import { Textarea } from '../../internal/components/textarea'
6
+
7
+ type CommitTextareaProps = Omit<ComponentProps<typeof Textarea>, 'value' | 'onChange'> & {
8
+ value: string
9
+ delay?: number
10
+ onCommit: (value: string) => void
11
+ }
12
+
13
+ export function CommitTextarea({ value, delay = 360, onCommit, ...props }: CommitTextareaProps) {
14
+ const [draft, setDraft] = useState(value)
15
+ const timer = useRef<number | null>(null)
16
+ const composing = useRef(false)
17
+ const external = useRef(value)
18
+
19
+ useEffect(() => {
20
+ external.current = value
21
+ if (!composing.current && timer.current === null) setDraft(value)
22
+ }, [value])
23
+
24
+ useEffect(
25
+ () => () => {
26
+ if (timer.current !== null) window.clearTimeout(timer.current)
27
+ },
28
+ [],
29
+ )
30
+
31
+ const commit = (next: string) => {
32
+ if (timer.current !== null) window.clearTimeout(timer.current)
33
+ timer.current = null
34
+ if (next !== external.current) onCommit(next)
35
+ }
36
+
37
+ const schedule = (next: string) => {
38
+ if (timer.current !== null) window.clearTimeout(timer.current)
39
+ timer.current = window.setTimeout(() => commit(next), delay)
40
+ }
41
+
42
+ return (
43
+ <Textarea
44
+ {...props}
45
+ value={draft}
46
+ onChange={(event) => {
47
+ const next = event.currentTarget.value
48
+ setDraft(next)
49
+ if (!composing.current) schedule(next)
50
+ }}
51
+ onCompositionStart={() => {
52
+ composing.current = true
53
+ }}
54
+ onCompositionEnd={(event) => {
55
+ composing.current = false
56
+ const next = event.currentTarget.value
57
+ setDraft(next)
58
+ schedule(next)
59
+ }}
60
+ onBlur={() => commit(draft)}
61
+ />
62
+ )
63
+ }