@celestia-island/hikari 0.4.4 → 0.4.5

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 (152) hide show
  1. package/README.md +1 -1
  2. package/package.json +16 -7
  3. package/src/DemoApp.vue +1 -1
  4. package/src/components/HkAdminHeader.tsx +205 -0
  5. package/src/components/HkAdminShell.tsx +85 -0
  6. package/src/components/HkAdminTablePage.tsx +70 -0
  7. package/src/components/HkAuthCard.tsx +36 -0
  8. package/src/components/HkAuthSubmitButton.scss +12 -0
  9. package/src/components/HkAuthSubmitButton.tsx +94 -0
  10. package/src/components/HkAvatar.scss +93 -0
  11. package/src/components/HkAvatar.tsx +1 -1
  12. package/src/components/HkBreadcrumb.scss +624 -0
  13. package/src/components/HkBreadcrumb.tsx +1 -1
  14. package/src/components/HkCard.scss +1 -1
  15. package/src/components/HkCheckbox.tsx +2 -1
  16. package/src/components/HkCollapse.scss +50 -0
  17. package/src/components/HkCollapse.tsx +1 -1
  18. package/src/components/HkColorSchemeDialog.scss +16 -0
  19. package/src/components/HkColorSchemeDialog.tsx +237 -0
  20. package/src/components/HkConnectionInfo.ts +80 -0
  21. package/src/components/HkConnectionStatus.tsx +133 -0
  22. package/src/components/HkCountdownDigit.tsx +97 -0
  23. package/src/components/HkDateTimePicker.tsx +7 -1
  24. package/src/components/HkErrorBoundary.tsx +4 -1
  25. package/src/components/HkIcon.scss +123 -0
  26. package/src/components/HkIcon.tsx +7 -23
  27. package/src/components/HkIconButton.scss +170 -0
  28. package/src/components/HkIconButton.tsx +2 -2
  29. package/src/components/HkIconButtonVars.scss +176 -0
  30. package/src/components/HkInput.scss +22 -3
  31. package/src/components/HkInput.tsx +41 -1
  32. package/src/components/HkKeywordSearchModal.tsx +10 -7
  33. package/src/components/HkLocalePicker.tsx +113 -0
  34. package/src/components/HkMediaControlBar.tsx +14 -7
  35. package/src/components/HkMinimap.tsx +10 -2
  36. package/src/components/HkModal.tsx +2 -23
  37. package/src/components/HkNavItem.tsx +2 -1
  38. package/src/components/HkNavSidebar.tsx +99 -0
  39. package/src/components/HkPageHeader.tsx +40 -0
  40. package/src/components/HkPasswordInput.scss +14 -12
  41. package/src/components/HkPasswordInput.test.ts +242 -0
  42. package/src/components/HkPasswordInput.tsx +236 -41
  43. package/src/components/HkPopupSelect.scss +3 -3
  44. package/src/components/HkPopupSelect.tsx +2 -1
  45. package/src/components/HkSearchInput.tsx +2 -2
  46. package/src/components/HkSecretRevealModal.scss +61 -0
  47. package/src/components/HkSecretRevealModal.tsx +91 -0
  48. package/src/components/HkSelect.scss +4 -4
  49. package/src/components/HkSelect.tsx +2 -0
  50. package/src/components/HkSelectionGrid.tsx +2 -1
  51. package/src/components/HkStatusBar.tsx +235 -0
  52. package/src/components/HkTag.tsx +2 -1
  53. package/src/components/HkThemeToggle.scss +130 -0
  54. package/src/components/HkThemeToggle.tsx +160 -0
  55. package/src/components/HkTimeline.tsx +2 -1
  56. package/src/components/HkToast.tsx +4 -1
  57. package/src/components/HkTrendChart.tsx +27 -16
  58. package/src/components/HkZoomToolbar.tsx +4 -1
  59. package/src/components/_hikari-vars.scss +4 -0
  60. package/src/composables/highlightLanguages.ts +109 -0
  61. package/src/composables/iconRegistry.ts +127 -0
  62. package/src/composables/messaging/browserTransport.ts +96 -0
  63. package/src/composables/messaging/index.ts +204 -0
  64. package/src/composables/messaging/messaging.test.ts +256 -0
  65. package/src/composables/messaging/toastTransport.ts +26 -0
  66. package/src/composables/messaging/types.ts +115 -0
  67. package/src/composables/useActionBar.ts +66 -0
  68. package/src/composables/useConnectionProbe.ts +126 -0
  69. package/src/composables/useDeferredTransition.ts +16 -0
  70. package/src/composables/useEngineHealth.ts +69 -0
  71. package/src/composables/useHighlight.ts +124 -0
  72. package/src/composables/usePageTitle.ts +37 -0
  73. package/src/composables/useReducedMotion.ts +34 -0
  74. package/src/composables/useResourceListModal.tsx +147 -0
  75. package/src/composables/useZoomPan.ts +192 -0
  76. package/src/i18n/locales/ar/components.json +12 -3
  77. package/src/i18n/locales/ar/components.toml +5 -0
  78. package/src/i18n/locales/ar/connection.json +25 -0
  79. package/src/i18n/locales/ar/platform.json +50 -0
  80. package/src/i18n/locales/de/components.json +12 -3
  81. package/src/i18n/locales/de/components.toml +5 -0
  82. package/src/i18n/locales/de/connection.json +25 -0
  83. package/src/i18n/locales/de/platform.json +50 -0
  84. package/src/i18n/locales/en/components.json +12 -3
  85. package/src/i18n/locales/en/components.toml +5 -0
  86. package/src/i18n/locales/en/connection.json +27 -0
  87. package/src/i18n/locales/en/platform.json +53 -0
  88. package/src/i18n/locales/es/components.json +12 -3
  89. package/src/i18n/locales/es/components.toml +5 -0
  90. package/src/i18n/locales/es/connection.json +25 -0
  91. package/src/i18n/locales/es/platform.json +50 -0
  92. package/src/i18n/locales/fr/components.json +12 -3
  93. package/src/i18n/locales/fr/components.toml +5 -0
  94. package/src/i18n/locales/fr/connection.json +25 -0
  95. package/src/i18n/locales/fr/platform.json +50 -0
  96. package/src/i18n/locales/ja/components.json +12 -3
  97. package/src/i18n/locales/ja/components.toml +5 -0
  98. package/src/i18n/locales/ja/connection.json +27 -0
  99. package/src/i18n/locales/ja/platform.json +53 -0
  100. package/src/i18n/locales/ko/components.json +12 -3
  101. package/src/i18n/locales/ko/components.toml +5 -0
  102. package/src/i18n/locales/ko/connection.json +27 -0
  103. package/src/i18n/locales/ko/platform.json +50 -0
  104. package/src/i18n/locales/pt/components.json +12 -3
  105. package/src/i18n/locales/pt/components.toml +5 -0
  106. package/src/i18n/locales/pt/connection.json +25 -0
  107. package/src/i18n/locales/pt/platform.json +50 -0
  108. package/src/i18n/locales/ru/components.json +12 -3
  109. package/src/i18n/locales/ru/components.toml +5 -0
  110. package/src/i18n/locales/ru/connection.json +25 -0
  111. package/src/i18n/locales/ru/platform.json +50 -0
  112. package/src/i18n/locales/zh-Hans/components.json +12 -3
  113. package/src/i18n/locales/zh-Hans/components.toml +5 -0
  114. package/src/i18n/locales/zh-Hans/connection.json +27 -0
  115. package/src/i18n/locales/zh-Hans/platform.json +53 -0
  116. package/src/i18n/locales/zh-Hant/components.json +12 -3
  117. package/src/i18n/locales/zh-Hant/components.toml +5 -0
  118. package/src/i18n/locales/zh-Hant/connection.json +27 -0
  119. package/src/i18n/locales/zh-Hant/platform.json +53 -0
  120. package/src/index.ts +67 -4
  121. package/src/plugins/bootScriptsPlugin.ts +47 -0
  122. package/src/plugins/buildHashPlugin.ts +67 -0
  123. package/src/plugins/index.ts +2 -0
  124. package/src/render.ts +1 -1
  125. package/src/runtime/env.ts +4 -0
  126. package/src/runtime/index.ts +0 -1
  127. package/src/runtime/useConfirm.ts +7 -1
  128. package/src/styles/admin-tokens.scss +634 -0
  129. package/src/styles/index.scss +1 -0
  130. package/src/theme/_field.scss +116 -0
  131. package/src/theme/index.ts +2 -0
  132. package/src/theme/presets.ts +11 -9
  133. package/src/theme/useTheme.ts +54 -5
  134. package/src/tokens.scss +76 -0
  135. package/src/utils/base64.ts +31 -0
  136. package/src/utils/connectivity.ts +51 -0
  137. package/src/utils/dom.ts +39 -0
  138. package/src/utils/download.ts +22 -0
  139. package/src/utils/fileType.ts +127 -0
  140. package/src/utils/fuzzy.ts +77 -0
  141. package/src/utils/fuzzySearch.test.ts +87 -0
  142. package/src/utils/objectPath.ts +59 -0
  143. package/src/utils/password.ts +53 -0
  144. package/src/utils/pow.test.ts +45 -0
  145. package/src/utils/pow.ts +226 -0
  146. package/src/utils/powNonce.ts +83 -0
  147. package/src/utils/validation.test.ts +78 -0
  148. package/src/composables/useBreakpoint.ts +0 -41
  149. package/src/composables/useClipboard.ts +0 -37
  150. package/src/composables/useConfirm.ts +0 -35
  151. package/src/composables/useToast.ts +0 -2
  152. package/src/runtime/useAsyncData.ts +0 -30
@@ -1,13 +1,6 @@
1
1
  import { defineComponent, type PropType } from "vue";
2
- import * as LucideIcons from "lucide-vue-next";
3
- import "../../../components/src/styles/components/icon.scss";
4
-
5
- function pascalCase(str: string): string {
6
- const camel = str.replace(/-([a-z])/g, (_: string, c: string) => c.toUpperCase());
7
- return camel.charAt(0).toUpperCase() + camel.slice(1);
8
- }
9
-
10
- const icons = LucideIcons as Record<string, any>;
2
+ import { iconByName } from "../composables/iconRegistry";
3
+ import "./HkIcon.scss";
11
4
 
12
5
  export default defineComponent({
13
6
  name: "HkIcon",
@@ -24,20 +17,11 @@ export default defineComponent({
24
17
  props.color ? `hk-icon-${props.color}` : "",
25
18
  ];
26
19
 
27
- const pName = pascalCase(props.name);
28
- const IconComp = icons[pName] || icons[props.name];
29
-
30
- if (IconComp) {
31
- return <span class={cls}><IconComp /></span>;
32
- }
33
-
34
- return (
35
- <span class={cls}>
36
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
37
- <circle cx="12" cy="12" r="10" />
38
- </svg>
39
- </span>
40
- );
20
+ // Resolve through the explicit registry: a wildcard import of
21
+ // lucide-vue-next here defeated tree-shaking and shipped the whole
22
+ // ~1500-icon library in the shared bundle of every consumer.
23
+ const IconComp = iconByName(props.name) as any;
24
+ return <span class={cls}><IconComp /></span>;
41
25
  };
42
26
  },
43
27
  });
@@ -0,0 +1,170 @@
1
+ // Use theme variables with namespace to avoid conflicts
2
+ @use "./hikari-vars" as vars;
3
+ @use "./HkIconButtonVars.scss";
4
+
5
+ // ------
6
+ // Hikari IconButton Component - FUI Styling
7
+ // Three-layer CSS variable system:
8
+ // - Layer1: Foundation variables (foundation.scss)
9
+ // - Layer2: Component variables (icon-button-vars.scss)
10
+ // - Custom: Runtime overrides via AnimationBuilder
11
+ //
12
+ // NOTE: Hover effects are handled by Glow wrapper, not here
13
+ // ------
14
+
15
+ .hk-icon-button {
16
+ // Layer2 variables are defined in icon-button-vars.scss
17
+
18
+ // Layout
19
+ display: inline-flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ flex-shrink: 0;
23
+ padding: 0;
24
+ line-height: 0;
25
+ gap: 0;
26
+
27
+ // Size - use CSS variables
28
+ width: var(--hi-icon-button-size);
29
+ height: var(--hi-icon-button-size);
30
+ min-width: var(--hi-icon-button-size);
31
+ max-width: var(--hi-icon-button-size);
32
+ min-height: var(--hi-icon-button-size);
33
+ max-height: var(--hi-icon-button-size);
34
+
35
+ // Allow glow effect to show
36
+ overflow: visible;
37
+
38
+ // Remove default button styling
39
+ appearance: none;
40
+ -webkit-appearance: none;
41
+ -moz-appearance: none;
42
+
43
+ // Background - use CSS variables
44
+ background: var(--hi-icon-button-bg);
45
+ border: none;
46
+ border-radius: var(--hi-icon-button-radius);
47
+
48
+ // Typography
49
+ font-family: vars.$hikari-font-family-sans;
50
+ font-size: vars.$hikari-font-size-sm;
51
+ font-weight: 400;
52
+ color: var(--hi-icon-button-icon-color);
53
+
54
+ // Transitions - use CSS variables
55
+ transition: var(--hi-icon-button-transition);
56
+
57
+ // No transform - controlled by AnimationBuilder via Glow wrapper
58
+
59
+ // Cursor
60
+ cursor: pointer;
61
+
62
+ // Focus state - only outline, no color changes
63
+ &:focus-visible {
64
+ outline: 2px solid var(--hi-icon-button-border-color-focus);
65
+ outline-offset: 2px;
66
+ color: var(--hi-icon-button-icon-color);
67
+
68
+ .hk-icon-button-icon {
69
+ color: var(--hi-icon-button-icon-color);
70
+ }
71
+ }
72
+
73
+ // Disabled state
74
+ &:disabled {
75
+ background: var(--hi-icon-button-bg-disabled);
76
+ color: var(--hi-icon-button-icon-color-disabled);
77
+ cursor: not-allowed;
78
+ pointer-events: none;
79
+ }
80
+
81
+ // No active state - controlled by AnimationBuilder via Glow wrapper
82
+
83
+ // ------
84
+ // Size Variants - Variables defined in icon-button-vars.scss
85
+ // ------
86
+
87
+ &.hk-icon-button-16,
88
+ &.hk-icon-button-24,
89
+ &.hk-icon-button-32,
90
+ &.hk-icon-button-36,
91
+ &.hk-icon-button-40 {
92
+ // Size variables are already set by the classes in icon-button-vars.scss
93
+ }
94
+
95
+ // ------
96
+ // Variant: Ghost (default - transparent)
97
+ // ------
98
+
99
+ &.hk-icon-button-ghost,
100
+ &.hk-icon-button-borderless {
101
+ // Variables defined in icon-button-vars.scss
102
+ }
103
+
104
+ // ------
105
+ // Variant: Primary (solid primary color)
106
+ // ------
107
+
108
+ &.hk-icon-button-primary {
109
+ // Variables defined in icon-button-vars.scss
110
+ }
111
+
112
+ // ------
113
+ // Variant: Secondary (solid secondary color)
114
+ // ------
115
+
116
+ &.hk-icon-button-secondary {
117
+ // Variables defined in icon-button-vars.scss
118
+ }
119
+
120
+ // ------
121
+ // Variant: Danger (solid danger color)
122
+ // ------
123
+
124
+ &.hk-icon-button-danger {
125
+ // Variables defined in icon-button-vars.scss
126
+ }
127
+
128
+ // ------
129
+ // Variant: Success (solid success color)
130
+ // ------
131
+
132
+ &.hk-icon-button-success {
133
+ // Variables defined in icon-button-vars.scss
134
+ }
135
+
136
+ // ------
137
+ // Icon Styles - Use CSS Variables
138
+ // ------
139
+
140
+ .hk-icon-button-icon {
141
+ width: var(--hi-icon-button-icon-size);
142
+ height: var(--hi-icon-button-icon-size);
143
+ display: inline-flex;
144
+ align-items: center;
145
+ justify-content: center;
146
+ line-height: 0;
147
+ color: var(--hi-icon-button-icon-color);
148
+
149
+ svg {
150
+ width: var(--hi-icon-button-icon-size);
151
+ height: var(--hi-icon-button-icon-size);
152
+ display: block;
153
+ fill: currentColor;
154
+ }
155
+ }
156
+
157
+ // Active state for icon
158
+ &:active:not(:disabled) .hk-icon-button-icon {
159
+ color: var(--hi-icon-button-icon-color-active);
160
+ }
161
+
162
+ // Disabled state for icon
163
+ &:disabled .hk-icon-button-icon {
164
+ color: var(--hi-icon-button-icon-color-disabled);
165
+ }
166
+
167
+ .hk-icon-button-disabled {
168
+ opacity: 0.3;
169
+ }
170
+ }
@@ -1,6 +1,6 @@
1
1
  import { computed, defineComponent, type PropType } from "vue";
2
- import "../../../components/src/styles/components/icon_button.scss";
3
- import "../../../components/src/styles/components/icon-button-vars.scss";
2
+ import "./HkIconButton.scss";
3
+ import "./HkIconButtonVars.scss";
4
4
 
5
5
  export default defineComponent({
6
6
  name: "HkIconButton",
@@ -0,0 +1,176 @@
1
+ // HkIconButtonVars.scss
2
+ // Layer2 - IconButton Component Variables
3
+ //
4
+ // This file defines component-level CSS variables for the IconButton component.
5
+ // These variables can be overridden at runtime via AnimationBuilder.
6
+ //
7
+ // NOTE: Hover effects are handled by Glow wrapper, not here.
8
+ // Only base, active, and disabled states are defined.
9
+
10
+ // ------
11
+ // Base IconButton Variables
12
+ // ------
13
+
14
+ .hk-icon-button {
15
+ // ------
16
+ // 1. Color System
17
+ // ------
18
+
19
+ // Icon Colors
20
+ --hi-icon-button-icon-color: var(--hi-icon-color);
21
+ --hi-icon-button-icon-color-active: var(--hi-icon-color);
22
+ --hi-icon-button-icon-color-disabled: rgba(128, 128, 128, 0.5);
23
+
24
+ // Background Colors
25
+ --hi-icon-button-bg: transparent;
26
+ --hi-icon-button-bg-active: var(--hi-bg-surface-dark);
27
+ --hi-icon-button-bg-disabled: rgba(128, 128, 128, 0.1);
28
+
29
+ // Border Colors
30
+ --hi-icon-button-border-color: transparent;
31
+ --hi-icon-button-border-color-focus: var(--hi-border-color-focus);
32
+
33
+ // Shadow & Glow (used by Glow wrapper)
34
+ --hi-icon-button-glow: var(--hi-glow-color);
35
+
36
+ // ------
37
+ // 2. Border Radius System
38
+ // ------
39
+
40
+ --hi-icon-button-radius: 4px;
41
+
42
+ // ------
43
+ // 3. Animation System
44
+ // ------
45
+
46
+ // Duration
47
+ --hi-icon-button-duration: var(--hi-duration-fast);
48
+ --hi-icon-button-duration-active: var(--hi-duration-instant);
49
+
50
+ // Easing
51
+ --hi-icon-button-easing: var(--hi-ease-default);
52
+
53
+ // Transition
54
+ --hi-icon-button-transition:
55
+ background-color var(--hi-icon-button-duration) var(--hi-icon-button-easing),
56
+ transform var(--hi-icon-button-duration-active) var(--hi-icon-button-easing);
57
+
58
+ // ------
59
+ // 4. Size System
60
+ // ------
61
+
62
+ --hi-icon-button-size: 40px;
63
+ --hi-icon-button-icon-size: var(--hi-icon-size-sm);
64
+
65
+ // ------
66
+ // 5. Transform System
67
+ // ------
68
+
69
+ --hi-icon-button-transform: none;
70
+ --hi-icon-button-transform-active: scale(0.95);
71
+ }
72
+
73
+ // ------
74
+ // Size Variants
75
+ // ------
76
+
77
+ .hk-icon-button-16 {
78
+ --hi-icon-button-size: 16px;
79
+ --hi-icon-button-icon-size: var(--hi-icon-size-xs);
80
+ }
81
+
82
+ .hk-icon-button-24 {
83
+ --hi-icon-button-size: 24px;
84
+ --hi-icon-button-icon-size: var(--hi-icon-size-xs);
85
+ }
86
+
87
+ .hk-icon-button-32 {
88
+ --hi-icon-button-size: 32px;
89
+ --hi-icon-button-icon-size: var(--hi-icon-size-sm);
90
+ }
91
+
92
+ .hk-icon-button-36 {
93
+ --hi-icon-button-size: 36px;
94
+ --hi-icon-button-icon-size: var(--hi-icon-size-sm);
95
+ }
96
+
97
+ .hk-icon-button-40 {
98
+ --hi-icon-button-size: 40px;
99
+ --hi-icon-button-icon-size: var(--hi-icon-size-sm);
100
+ }
101
+
102
+ // ------
103
+ // Ghost Variant Variables
104
+ // ------
105
+
106
+ .hk-icon-button-ghost,
107
+ .hk-icon-button-borderless {
108
+ --hi-icon-button-icon-color: var(--hi-color-text-secondary);
109
+ --hi-icon-button-icon-color-active: var(--hi-color-text-secondary);
110
+
111
+ --hi-icon-button-bg: transparent;
112
+ --hi-icon-button-bg-active: var(--hi-color-gray-20, rgba(128, 128, 128, 0.2));
113
+
114
+ --hi-icon-button-border-color: transparent;
115
+ --hi-icon-button-glow: var(--hi-glow-color);
116
+ }
117
+
118
+ // ------
119
+ // Primary Variant Variables
120
+ // ------
121
+
122
+ .hk-icon-button-primary {
123
+ --hi-icon-button-icon-color: var(--hi-color-text-on-primary);
124
+ --hi-icon-button-icon-color-active: var(--hi-color-text-on-primary);
125
+
126
+ --hi-icon-button-bg: var(--hi-color-primary);
127
+ --hi-icon-button-bg-active: var(--hi-color-primary-dark);
128
+
129
+ --hi-icon-button-border-color-focus: var(--hi-color-primary);
130
+ --hi-icon-button-glow: var(--hi-glow-color);
131
+ }
132
+
133
+ // ------
134
+ // Secondary Variant Variables
135
+ // ------
136
+
137
+ .hk-icon-button-secondary {
138
+ --hi-icon-button-icon-color: var(--hi-color-text-on-secondary);
139
+ --hi-icon-button-icon-color-active: var(--hi-color-text-on-secondary);
140
+
141
+ --hi-icon-button-bg: var(--hi-color-secondary);
142
+ --hi-icon-button-bg-active: var(--hi-color-secondary-dark);
143
+
144
+ --hi-icon-button-border-color-focus: var(--hi-color-secondary);
145
+ --hi-icon-button-glow: var(--hi-glow-color);
146
+ }
147
+
148
+ // ------
149
+ // Danger Variant Variables
150
+ // ------
151
+
152
+ .hk-icon-button-danger {
153
+ --hi-icon-button-icon-color: var(--hi-color-text-on-danger, #ffffff);
154
+ --hi-icon-button-icon-color-active: var(--hi-color-text-on-danger, #ffffff);
155
+
156
+ --hi-icon-button-bg: var(--hi-color-danger);
157
+ --hi-icon-button-bg-active: var(--hi-color-danger-dark);
158
+
159
+ --hi-icon-button-border-color-focus: var(--hi-color-danger);
160
+ --hi-icon-button-glow: var(--hi-glow-color);
161
+ }
162
+
163
+ // ------
164
+ // Success Variant Variables
165
+ // ------
166
+
167
+ .hk-icon-button-success {
168
+ --hi-icon-button-icon-color: var(--hi-color-text-on-success, #ffffff);
169
+ --hi-icon-button-icon-color-active: var(--hi-color-text-on-success, #ffffff);
170
+
171
+ --hi-icon-button-bg: var(--hi-color-success);
172
+ --hi-icon-button-bg-active: var(--hi-color-success-dark);
173
+
174
+ --hi-icon-button-border-color-focus: var(--hi-color-success);
175
+ --hi-icon-button-glow: var(--hi-glow-color);
176
+ }
@@ -25,9 +25,9 @@
25
25
  gap: var(--space-6);
26
26
  padding: var(--space-8) var(--space-12);
27
27
  min-height: 2.5rem;
28
- background: rgb(var(--color-surface) / var(--opacity-half));
29
- backdrop-filter: blur(var(--blur-sm));
30
- border: 1px solid var(--border-input);
28
+ background: color-mix(in srgb, rgb(var(--color-surface)) 55%, transparent);
29
+
30
+ border: 1px solid color-mix(in srgb, rgb(var(--color-text)) 14%, transparent);
31
31
  border-radius: var(--radius-sm);
32
32
  overflow: hidden;
33
33
  transition: background-color, border-color, color, box-shadow, opacity, transform, filter var(--duration-normal) var(--ease-standard);
@@ -72,6 +72,9 @@
72
72
  background: transparent;
73
73
  border: none;
74
74
  outline: none;
75
+ appearance: none;
76
+ -webkit-appearance: none;
77
+ border-radius: 0;
75
78
  font-family: inherit;
76
79
  font-size: var(--text-base);
77
80
  line-height: 1.5;
@@ -170,3 +173,19 @@
170
173
  -webkit-box-shadow: 0 0 0 1000px rgb(var(--color-surface)) inset !important;
171
174
  caret-color: rgb(var(--color-text));
172
175
  }
176
+
177
+ /* Password-variant visibility toggle */
178
+ .hk-input-password-toggle {
179
+ display: inline-flex;
180
+ align-items: center;
181
+ justify-content: center;
182
+ padding: 0;
183
+ border: none;
184
+ background: transparent;
185
+ color: var(--color-text-secondary, #888);
186
+ cursor: pointer;
187
+
188
+ &:hover {
189
+ color: var(--color-text-primary, #222);
190
+ }
191
+ }
@@ -1,4 +1,8 @@
1
+ import { Eye, EyeOff } from "lucide-vue-next";
1
2
  import { computed, defineComponent, nextTick, onBeforeUnmount, onMounted, ref, useAttrs, watch } from "vue";
3
+
4
+ import { useI18n } from "../i18n/context";
5
+
2
6
  import "./HkInput.scss";
3
7
 
4
8
  export default defineComponent({
@@ -19,6 +23,16 @@ export default defineComponent({
19
23
  rows: { type: Number, default: 3 },
20
24
  autoGrow: { type: Boolean, default: false },
21
25
  size: { type: String as () => "sm" | "md" | "lg", default: "md" },
26
+ /**
27
+ * Input variant. `password` renders a password field with a built-in
28
+ * visibility toggle (HPasswordInput layers strength / caps-lock /
29
+ * full-width extras on top of this base behavior). `number` maps to the
30
+ * numeric input type. Defaults to "text".
31
+ */
32
+ variant: {
33
+ type: String as () => "text" | "password" | "number",
34
+ default: "text",
35
+ },
22
36
  },
23
37
  emits: {
24
38
  "update:modelValue": (_value: string) => true,
@@ -27,9 +41,19 @@ export default defineComponent({
27
41
  keydown: (_e: KeyboardEvent) => true,
28
42
  },
29
43
  setup(props, { emit, slots }) {
44
+ const { t } = useI18n();
30
45
  const attrs = useAttrs();
31
46
  const inputRef = ref<HTMLElement>();
32
47
 
48
+ const revealing = ref(false);
49
+ const resolvedType = computed(() => {
50
+ if (props.variant === "password") {
51
+ return revealing.value ? "text" : "password";
52
+ }
53
+ if (props.variant === "number") return "number";
54
+ return props.type;
55
+ });
56
+
33
57
  const filteredAttrs = computed(() => {
34
58
  const { class: _, style: __, ...rest } = attrs as Record<string, unknown>;
35
59
  return rest;
@@ -113,7 +137,7 @@ export default defineComponent({
113
137
  {isText ? (
114
138
  <input
115
139
  ref={inputRef}
116
- type={props.type}
140
+ type={resolvedType.value}
117
141
  value={props.modelValue}
118
142
  placeholder={props.placeholder}
119
143
  disabled={props.disabled}
@@ -163,6 +187,22 @@ export default defineComponent({
163
187
  {slots.suffixIcon()}
164
188
  </span>
165
189
  )}
190
+ {props.variant === "password" && !slots.suffix && !slots.suffixIcon && (
191
+ <span class="hk-input-affix hk-input-suffix">
192
+ <button
193
+ type="button"
194
+ class="hk-input-password-toggle"
195
+ aria-label={
196
+ revealing.value
197
+ ? t("hikari::input.hidePassword", "Hide password")
198
+ : t("hikari::input.showPassword", "Show password")
199
+ }
200
+ onClick={() => { revealing.value = !revealing.value; }}
201
+ >
202
+ {revealing.value ? <EyeOff size={15} /> : <Eye size={15} />}
203
+ </button>
204
+ </span>
205
+ )}
166
206
  </div>
167
207
  {props.error ? (
168
208
  <p class="hk-input-error-msg">{props.error}</p>
@@ -1,5 +1,7 @@
1
1
  import { computed, defineComponent, ref, watch, type PropType } from "vue";
2
2
 
3
+ import { useI18n } from "../i18n/context";
4
+
3
5
  import "./HkKeywordSearchModal.scss";
4
6
  import HModal from "./HkModal";
5
7
  import { scheduleCronAfter, type CronHandle } from "../runtime/cronBus";
@@ -115,6 +117,7 @@ export default defineComponent({
115
117
  select: (_rec: unknown) => true,
116
118
  },
117
119
  setup(props, { slots, emit }) {
120
+ const { t } = useI18n();
118
121
  const query = ref("");
119
122
  let debounceTimer: CronHandle | null = null;
120
123
  const debouncedQuery = ref("");
@@ -190,7 +193,7 @@ export default defineComponent({
190
193
  <HModal
191
194
  modelValue={props.modelValue}
192
195
  onUpdate:modelValue={(v: boolean) => emit("update:modelValue", v)}
193
- title={props.title ?? "Search"}
196
+ title={props.title ?? t("hk.keywordSearch.title", "Search")}
194
197
  width="40rem"
195
198
  >
196
199
  <div class="hk-kw-search">
@@ -212,7 +215,7 @@ export default defineComponent({
212
215
  class="hk-kw-search-input"
213
216
  type="text"
214
217
  value={query.value}
215
- placeholder={props.placeholder ?? "Search…"}
218
+ placeholder={props.placeholder ?? t("hk.keywordSearch.placeholder", "Search…")}
216
219
  onInput={(e: Event) => { query.value = (e.target as HTMLInputElement).value; }}
217
220
  autofocus
218
221
  />
@@ -220,7 +223,7 @@ export default defineComponent({
220
223
  <button
221
224
  class="hk-kw-search-clear"
222
225
  type="button"
223
- aria-label="Clear"
226
+ aria-label={t("hk.keywordSearch.clear", "Clear")}
224
227
  onClick={() => {
225
228
  query.value = "";
226
229
  debouncedQuery.value = "";
@@ -247,13 +250,13 @@ export default defineComponent({
247
250
  {semanticActive.value ? (
248
251
  semanticLoading.value && semanticResults.value.length === 0 ? (
249
252
  <div class="hk-kw-search-empty">
250
- {props.searchingText ?? "Searching…"}
253
+ {props.searchingText ?? t("hk.keywordSearch.searching", "Searching…")}
251
254
  </div>
252
255
  ) : semanticResults.value.length === 0 ? (
253
256
  <div class="hk-kw-search-empty">
254
257
  {debouncedQuery.value
255
- ? (props.emptyText ?? "No matches")
256
- : (props.placeholder ?? "Search…")}
258
+ ? (props.emptyText ?? t("hk.keywordSearch.noMatches", "No matches"))
259
+ : (props.placeholder ?? t("hk.keywordSearch.placeholder", "Search…"))}
257
260
  </div>
258
261
  ) : (
259
262
  semanticResults.value.map((r) => (
@@ -286,7 +289,7 @@ export default defineComponent({
286
289
  )
287
290
  ) : results.value.length === 0 ? (
288
291
  <div class="hk-kw-search-empty">
289
- {props.emptyText ?? "No matches"}
292
+ {props.emptyText ?? t("hk.keywordSearch.noMatches", "No matches")}
290
293
  </div>
291
294
  ) : (
292
295
  results.value.map((r) => (
@@ -0,0 +1,113 @@
1
+ import { defineComponent, ref, type PropType } from "vue";
2
+
3
+ export interface LocaleOption {
4
+ code: string;
5
+ label: string;
6
+ }
7
+
8
+ export const HkLocalePicker = defineComponent({
9
+ name: "HkLocalePicker",
10
+ props: {
11
+ locales: {
12
+ type: Array as PropType<LocaleOption[]>,
13
+ default: () => [],
14
+ },
15
+ currentLocale: {
16
+ type: String,
17
+ default: "en",
18
+ },
19
+ },
20
+ emits: ["select"],
21
+ setup(props, { emit }) {
22
+ const open = ref(false);
23
+
24
+ function handleSelect(code: string) {
25
+ emit("select", code);
26
+ open.value = false;
27
+ }
28
+
29
+ return () => (
30
+ <div style={{ position: "relative" }}>
31
+ <button
32
+ type="button"
33
+ style={{
34
+ display: "inline-flex",
35
+ alignItems: "center",
36
+ gap: "0.25rem",
37
+ padding: "0.25rem 0.5rem",
38
+ border: "1px solid var(--border-faint, rgb(var(--color-border) / 10%))",
39
+ borderRadius: "var(--radius-sm, 4px)",
40
+ background: "transparent",
41
+ color: "rgb(var(--color-muted))",
42
+ fontSize: "0.75rem",
43
+ fontWeight: 500,
44
+ cursor: "pointer",
45
+ transition: "background 0.15s",
46
+ }}
47
+ onClick={() => (open.value = !open.value)}
48
+ onBlur={() => setTimeout(() => (open.value = false), 150)}
49
+ >
50
+ {props.currentLocale.toUpperCase()}
51
+ <svg width="10" height="10" viewBox="0 0 10 10" style={{ opacity: 0.5 }}>
52
+ <path d="M2 3.5l3 3 3-3" fill="none" stroke="currentColor" stroke-width="1.2" />
53
+ </svg>
54
+ </button>
55
+ {open.value && (
56
+ <div
57
+ style={{
58
+ position: "absolute",
59
+ top: "calc(100% + 4px)",
60
+ right: 0,
61
+ minWidth: "120px",
62
+ background: "rgb(var(--color-surface))",
63
+ border: "1px solid var(--border-faint, rgb(var(--color-border) / 10%))",
64
+ borderRadius: "var(--radius-md, 6px)",
65
+ boxShadow: "0 4px 16px rgb(0 0 0 / 12%)",
66
+ zIndex: 1000,
67
+ overflow: "hidden",
68
+ padding: "4px",
69
+ }}
70
+ >
71
+ {props.locales.map((loc) => (
72
+ <button
73
+ key={loc.code}
74
+ type="button"
75
+ style={{
76
+ display: "flex",
77
+ alignItems: "center",
78
+ width: "100%",
79
+ padding: "6px 10px",
80
+ border: "none",
81
+ borderRadius: "4px",
82
+ background: loc.code === props.currentLocale
83
+ ? "var(--c-primary-subtle, rgb(var(--color-primary) / 8%))"
84
+ : "transparent",
85
+ color: loc.code === props.currentLocale
86
+ ? "var(--c-primary, rgb(var(--color-primary)))"
87
+ : "rgb(var(--color-text))",
88
+ fontSize: "0.75rem",
89
+ cursor: "pointer",
90
+ textAlign: "left",
91
+ transition: "background 0.1s",
92
+ }}
93
+ onMouseenter={(e: MouseEvent) => {
94
+ if (loc.code !== props.currentLocale) {
95
+ (e.currentTarget as HTMLElement).style.background = "var(--c-primary-overlay, rgb(var(--color-primary) / 15%))";
96
+ }
97
+ }}
98
+ onMouseleave={(e: MouseEvent) => {
99
+ if (loc.code !== props.currentLocale) {
100
+ (e.currentTarget as HTMLElement).style.background = "transparent";
101
+ }
102
+ }}
103
+ onMousedown={() => handleSelect(loc.code)}
104
+ >
105
+ {loc.label}
106
+ </button>
107
+ ))}
108
+ </div>
109
+ )}
110
+ </div>
111
+ );
112
+ },
113
+ });