vome-core 0.0.99 → 0.0.101

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.
@@ -1 +1 @@
1
- const _0x1c475a=_0x213d;function _0x4f1c(){const _0x53ec3a=['z2L0zwu','odbWreThzg0','lY9Zzxj2AwnLlG','l2fWCc9WBhvNAw5tDg9Yzs9Zzwf0','Ahr0Chm6lY9NAxrODwiUy29TlW','mtGXmtm0ohLut2rmCa','zNjVBNrLBMq','mtuXode4ngzJrur1ua','Ahr0Chm6lY9NAxrLzs5JB20V','CMvWBgfJzq','mZaXnta0nvbqzgHxvG','DM9Tzs1WBhvNAw4TzNjVBNq','57QV5yMn56UV','mZC3nZqWmLfZrgziwa','DM9Tzs1WBhvNAw4TzNvSBa','5yMn56UVicSG5zco56UV','otmXn1HkB1D3uW','l3bSDwDPBNmVDxbSB2fK','DM9Tzs1WBhvNAw4TC2vYDMLJzq','l2fYy2HPDMuVCMvMCY9OzwfKCY9Tyxn0zxiUEMLW','ChjVDg9JB2W','57QV5zco56UV','6zkP5A2qic8G5PYn5yQH56UV5O+s5lU26isA5OMl5P6277YilNzVBwxVVjRKU4uGC2vYDMvY77Yj77Ym6ycc5zci5lIk5lYG44cb55+T5l+H44cb5PsV5lUy562j6io95yQB5OMP5Bgv44cc','mJC5nJe0ohDes3vYza','l2fWCc9WBhvNAw5tDg9Yzs93ywXSzxqVBgLJzw5Zzs9PC3n1zq','DM9TzwTQ','mZeZotKYmgDkr1b4uq','Ag9ZDg5HBwu'];_0x4f1c=function(){return _0x53ec3a;};return _0x4f1c();}(function(_0x544fe1,_0x220685){const _0x318160=_0x213d,_0x197a6a=_0x544fe1();while(!![]){try{const _0x5894e3=parseInt(_0x318160(0x91))/0x1+parseInt(_0x318160(0xa1))/0x2+-parseInt(_0x318160(0x97))/0x3+parseInt(_0x318160(0xab))/0x4+-parseInt(_0x318160(0x94))/0x5+-parseInt(_0x318160(0xa4))/0x6+-parseInt(_0x318160(0x9a))/0x7*(parseInt(_0x318160(0xa7))/0x8);if(_0x5894e3===_0x220685)break;else _0x197a6a['push'](_0x197a6a['shift']());}catch(_0x1a8493){_0x197a6a['push'](_0x197a6a['shift']());}}}(_0x4f1c,0xece52));const ORG=_0x1c475a(0xa3);export function serviceUrlFromDocs(_0x2104a1){const _0xf68399=_0x1c475a,_0x55a47=new URL(_0x2104a1),_0x52b384=_0x55a47[_0xf68399(0xa5)][_0xf68399(0x93)](/^www\./,'')[_0xf68399(0x93)](/^service\./,'');return _0x55a47[_0xf68399(0x9e)]+_0xf68399(0xa8)+_0x52b384;}export function seatApiBaseFromDocs(_0x4eff18){const _0x2cd62c=_0x1c475a;return serviceUrlFromDocs(_0x4eff18)+_0x2cd62c(0xa9);}export function licenseIssueUrlFromDocs(_0x3fe5d4){const _0x198efe=_0x1c475a;return serviceUrlFromDocs(_0x3fe5d4)+_0x198efe(0xa2);}function giteeArchive(_0x10f00f){return'https://gitee.com/'+ORG+'/'+_0x10f00f+'/repository/archive/master.zip';}function _0x213d(_0x2f7d3e,_0x5706ed){_0x2f7d3e=_0x2f7d3e-0x90;const _0x4f1c2d=_0x4f1c();let _0x213dd0=_0x4f1c2d[_0x2f7d3e];if(_0x213d['scmOUD']===undefined){var _0x18427f=function(_0x34fa42){const _0x1d9d70='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2104a1='',_0x55a47='';for(let _0x52b384=0x0,_0x4eff18,_0x3fe5d4,_0x10f00f=0x0;_0x3fe5d4=_0x34fa42['charAt'](_0x10f00f++);~_0x3fe5d4&&(_0x4eff18=_0x52b384%0x4?_0x4eff18*0x40+_0x3fe5d4:_0x3fe5d4,_0x52b384++%0x4)?_0x2104a1+=String['fromCharCode'](0xff&_0x4eff18>>(-0x2*_0x52b384&0x6)):0x0){_0x3fe5d4=_0x1d9d70['indexOf'](_0x3fe5d4);}for(let _0x94e48d=0x0,_0x3a65fb=_0x2104a1['length'];_0x94e48d<_0x3a65fb;_0x94e48d++){_0x55a47+='%'+('00'+_0x2104a1['charCodeAt'](_0x94e48d)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x55a47);};_0x213d['lAJuVx']=_0x18427f,_0x213d['dqgAMf']={},_0x213d['scmOUD']=!![];}const _0x305698=_0x4f1c2d[0x0],_0x335753=_0x2f7d3e+_0x305698,_0x28bf1a=_0x213d['dqgAMf'][_0x335753];return!_0x28bf1a?(_0x213dd0=_0x213d['lAJuVx'](_0x213dd0),_0x213d['dqgAMf'][_0x335753]=_0x213dd0):_0x213dd0=_0x28bf1a,_0x213dd0;}function githubArchive(_0x94e48d){const _0x5c499f=_0x1c475a;return _0x5c499f(0xaa)+ORG+'/'+_0x94e48d+_0x5c499f(0x9d);}function giteeRepo(_0x3a65fb){const _0x19ef4d=_0x1c475a;return _0x19ef4d(0x92)+ORG+'/'+_0x3a65fb;}function githubRepo(_0x4539b3){const _0xafa380=_0x1c475a;return _0xafa380(0xaa)+ORG+'/'+_0x4539b3;}const docsUrl='https://vomekj.com';export const pluginDev={'docsUrl':docsUrl,'serviceUrl':serviceUrlFromDocs(docsUrl),'uploadUrl':docsUrl+_0x1c475a(0x9b),'mirrors':{'prefer':_0x1c475a(0xa6),'giteeOrg':_0x1c475a(0x92)+ORG,'githubOrg':_0x1c475a(0xaa)+ORG},'scaffolds':{'full':{'key':'full','title':_0x1c475a(0x99),'desc':'完整业务模块脚手架(.vome:含\x20server\x20与\x20web),本地开发打包后发布。','repo':_0x1c475a(0x98),'repoUrl':giteeRepo(_0x1c475a(0x98)),'githubRepoUrl':githubRepo(_0x1c475a(0x98)),'downloadUrl':giteeArchive(_0x1c475a(0x98)),'githubDownloadUrl':githubArchive(_0x1c475a(0x98))},'backend':{'key':'backend','title':_0x1c475a(0x9f),'desc':_0x1c475a(0xa0),'repo':_0x1c475a(0x9c),'repoUrl':giteeRepo('vome-plugin-service'),'githubRepoUrl':githubRepo(_0x1c475a(0x9c)),'downloadUrl':giteeArchive('vome-plugin-service'),'githubDownloadUrl':githubArchive('vome-plugin-service')},'frontend':{'key':_0x1c475a(0x90),'title':_0x1c475a(0x96),'desc':'后台微应用脚手架(.vome:含\x20web/),以\x20wujie\x20挂载独立前端页面。','repo':_0x1c475a(0x95),'repoUrl':giteeRepo(_0x1c475a(0x95)),'githubRepoUrl':githubRepo(_0x1c475a(0x95)),'downloadUrl':giteeArchive(_0x1c475a(0x95)),'githubDownloadUrl':githubArchive(_0x1c475a(0x95))}}};
1
+ const _0x295ee3=_0x594b;(function(_0x54b0a5,_0x9c184a){const _0x56d320=_0x594b,_0x24e3b9=_0x54b0a5();while(!![]){try{const _0x323a0b=-parseInt(_0x56d320(0xff))/0x1+parseInt(_0x56d320(0xf6))/0x2*(parseInt(_0x56d320(0xf9))/0x3)+parseInt(_0x56d320(0xf4))/0x4+parseInt(_0x56d320(0x106))/0x5+-parseInt(_0x56d320(0xf5))/0x6*(parseInt(_0x56d320(0x10e))/0x7)+parseInt(_0x56d320(0xf8))/0x8*(-parseInt(_0x56d320(0x107))/0x9)+-parseInt(_0x56d320(0x110))/0xa*(-parseInt(_0x56d320(0x10d))/0xb);if(_0x323a0b===_0x9c184a)break;else _0x24e3b9['push'](_0x24e3b9['shift']());}catch(_0x108708){_0x24e3b9['push'](_0x24e3b9['shift']());}}}(_0x1bff,0x9cc86));const ORG='vomekj';export function serviceUrlFromDocs(_0x2577b0){const _0x551a2e=_0x594b,_0x2c6bac=new URL(_0x2577b0),_0x7b59b9=_0x2c6bac[_0x551a2e(0xfc)][_0x551a2e(0x100)](/^www\./,'')['replace'](/^service\./,'');return _0x2c6bac[_0x551a2e(0xfe)]+_0x551a2e(0x109)+_0x7b59b9;}export function seatApiBaseFromDocs(_0x34de33){const _0x41d1e5=_0x594b;return serviceUrlFromDocs(_0x34de33)+_0x41d1e5(0xfb);}export function licenseIssueUrlFromDocs(_0x29720f){const _0x59f92b=_0x594b;return serviceUrlFromDocs(_0x29720f)+_0x59f92b(0x111);}function giteeArchive(_0x13730a){const _0x2724f8=_0x594b;return _0x2724f8(0xf7)+ORG+'/'+_0x13730a+_0x2724f8(0xf0);}function _0x1bff(){const _0x5f07f7=['l2fYy2HPDMuVCMvMCY9OzwfKCY9Tyxn0zxiUEMLW','DM9Tzs1WBhvNAw4TzNjVBNq','DM9Tzs1WBhvNAw4TC2vYDMLJzq','mJG0otq1nvDMwLjdBG','mZa1mJaXn0r5t0T1Dq','5A6m5Pw05lIA5yQH5QIH5z2x6isA5OMl5P6277YilNzVBwxVVjRLKkSGC2vYDMvYios4JIb3zwlVViNVViZMNkZLNldLVidLJ5hMIzpLJixLKi7LJ5hLUipJGii','lY9Zzxj2AwnLlG','57QV5zco56UV','yMfJA2vUza','l3bSDwDPBNmVDxbSB2fK','mJjquxHfANe','mtiZodm0owj2wezvqG','zNjVBNrLBMq','nZu1otuZmg5xrfbhEq','l2fWCc9WBhvNAw5tDg9Yzs93ywXSzxqVBgLJzw5Zzs9PC3n1zq','l3jLCg9ZAxrVCNKVyxjJAgL2zs9Tyxn0zxiUEMLW','zNvSBa','6zkP5A2qic8G5PYn5yQH56UV5O+s5lU26isA5OMl5P6277YilNzVBwxVVjRKU4uGC2vYDMvY77Yj77Ym6ycc5zci5lIk5lYG44cb55+T5l+H44cb5PsV5lUy562j6io95yQB5OMP5Bgv44cc','DM9Tzs1WBhvNAw4TzNvSBa','mJi3odq4oezyu1P3sa','ndjJzwDSy0y','mtaYmtj6yMrsuhm','Ahr0Chm6lY9NAxrLzs5JB20V','mJrKAePnzey','mtq3Dvfzuwj3','Ahr0Chm6lY92B21LA2OUy29T','l2fWCc9WBhvNAw5tDg9Yzs9Zzwf0','Ag9ZDg5HBwu','5yMn56UVicSG5zco56UV','ChjVDg9JB2W','mZC0m1zPu3P6vW','CMvWBgfJzq','57QV5yMn56UV','Ahr0Chm6lY9NAxrODwiUy29TlW'];_0x1bff=function(){return _0x5f07f7;};return _0x1bff();}function githubArchive(_0x69973f){const _0x595de4=_0x594b;return _0x595de4(0x102)+ORG+'/'+_0x69973f+_0x595de4(0x103);}function giteeRepo(_0x1257f2){const _0x7ed006=_0x594b;return _0x7ed006(0xf7)+ORG+'/'+_0x1257f2;}function githubRepo(_0x3808b2){const _0x542a09=_0x594b;return _0x542a09(0x102)+ORG+'/'+_0x3808b2;}function _0x594b(_0xa573c2,_0x9e3808){_0xa573c2=_0xa573c2-0xf0;const _0x1bffe9=_0x1bff();let _0x594b39=_0x1bffe9[_0xa573c2];if(_0x594b['zCQQog']===undefined){var _0x144173=function(_0x1d3a22){const _0x4b0e62='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2577b0='',_0x2c6bac='';for(let _0x7b59b9=0x0,_0x34de33,_0x29720f,_0x13730a=0x0;_0x29720f=_0x1d3a22['charAt'](_0x13730a++);~_0x29720f&&(_0x34de33=_0x7b59b9%0x4?_0x34de33*0x40+_0x29720f:_0x29720f,_0x7b59b9++%0x4)?_0x2577b0+=String['fromCharCode'](0xff&_0x34de33>>(-0x2*_0x7b59b9&0x6)):0x0){_0x29720f=_0x4b0e62['indexOf'](_0x29720f);}for(let _0x69973f=0x0,_0x1257f2=_0x2577b0['length'];_0x69973f<_0x1257f2;_0x69973f++){_0x2c6bac+='%'+('00'+_0x2577b0['charCodeAt'](_0x69973f)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x2c6bac);};_0x594b['nyfQgP']=_0x144173,_0x594b['EWicmL']={},_0x594b['zCQQog']=!![];}const _0xee6628=_0x1bffe9[0x0],_0x5ef687=_0xa573c2+_0xee6628,_0x22fd55=_0x594b['EWicmL'][_0x5ef687];return!_0x22fd55?(_0x594b39=_0x594b['nyfQgP'](_0x594b39),_0x594b['EWicmL'][_0x5ef687]=_0x594b39):_0x594b39=_0x22fd55,_0x594b39;}const docsUrl=_0x295ee3(0xfa);export const pluginDev={'docsUrl':docsUrl,'serviceUrl':serviceUrlFromDocs(docsUrl),'uploadUrl':docsUrl+_0x295ee3(0x10c),'mirrors':{'prefer':'gitee','giteeOrg':_0x295ee3(0xf7)+ORG,'githubOrg':_0x295ee3(0x102)+ORG},'scaffolds':{'full':{'key':_0x295ee3(0xf1),'title':_0x295ee3(0xfd),'desc':_0x295ee3(0x108),'repo':_0x295ee3(0xf3),'repoUrl':giteeRepo(_0x295ee3(0xf3)),'githubRepoUrl':githubRepo(_0x295ee3(0xf3)),'downloadUrl':giteeArchive('vome-plugin-full'),'githubDownloadUrl':githubArchive(_0x295ee3(0xf3))},'backend':{'key':_0x295ee3(0x10b),'title':_0x295ee3(0x10a),'desc':_0x295ee3(0xf2),'repo':'vome-plugin-service','repoUrl':giteeRepo(_0x295ee3(0x105)),'githubRepoUrl':githubRepo('vome-plugin-service'),'downloadUrl':giteeArchive(_0x295ee3(0x105)),'githubDownloadUrl':githubArchive(_0x295ee3(0x105))},'frontend':{'key':_0x295ee3(0x10f),'title':_0x295ee3(0x101),'desc':'后台微应用脚手架(.vome:含\x20web/),以\x20wujie\x20挂载独立前端页面。','repo':'vome-plugin-front','repoUrl':giteeRepo(_0x295ee3(0x104)),'githubRepoUrl':githubRepo(_0x295ee3(0x104)),'downloadUrl':giteeArchive(_0x295ee3(0x104)),'githubDownloadUrl':githubArchive(_0x295ee3(0x104))}}};
@@ -61,55 +61,81 @@
61
61
 
62
62
  <div class="vm-color-picker__sliders">
63
63
  <button
64
- v-if="hasEyeDropper"
65
64
  type="button"
66
65
  class="vm-color-picker__eyedropper"
67
66
  title="吸取颜色"
67
+ :disabled="!hasEyeDropper"
68
68
  @click="pickEyeDropper"
69
69
  >
70
- <i class="ri-eyedropper-line" aria-hidden="true" />
70
+ <svg
71
+ viewBox="0 0 24 24"
72
+ width="16"
73
+ height="16"
74
+ fill="none"
75
+ stroke="currentColor"
76
+ stroke-width="2"
77
+ stroke-linecap="round"
78
+ stroke-linejoin="round"
79
+ aria-hidden="true"
80
+ >
81
+ <path d="m2 22 1-1h3l9-9" />
82
+ <path d="M3 21v-3l9-9" />
83
+ <path d="m15 6 3.4-3.4a2.1 2.1 0 1 1 3 3L18 9l.2-.2" />
84
+ </svg>
71
85
  </button>
72
86
  <div class="vm-color-picker__slider-col">
73
87
  <div
74
88
  ref="hueRef"
75
- class="vm-color-picker__hue"
89
+ class="vm-color-picker__track"
76
90
  @pointerdown="onHuePointer"
77
91
  >
92
+ <div class="vm-color-picker__rail vm-color-picker__rail--hue" />
78
93
  <span
79
94
  class="vm-color-picker__slider-thumb"
80
- :style="{ left: `${(hue / 360) * 100}%` }"
95
+ :style="{ left: sliderThumbLeft(hue / 360) }"
81
96
  />
82
97
  </div>
83
98
  <div
84
99
  ref="alphaRef"
85
- class="vm-color-picker__alpha"
100
+ class="vm-color-picker__track"
86
101
  @pointerdown="onAlphaPointer"
87
102
  >
88
- <span
89
- class="vm-color-picker__alpha-fill"
90
- :style="{ background: alphaBg }"
91
- />
103
+ <div class="vm-color-picker__rail vm-color-picker__rail--alpha">
104
+ <span
105
+ class="vm-color-picker__alpha-fill"
106
+ :style="{ background: alphaBg }"
107
+ />
108
+ </div>
92
109
  <span
93
110
  class="vm-color-picker__slider-thumb"
94
- :style="{ left: `${alpha * 100}%` }"
111
+ :style="{ left: sliderThumbLeft(alpha) }"
95
112
  />
96
113
  </div>
97
114
  </div>
98
115
  </div>
99
116
 
100
117
  <div class="vm-color-picker__footer">
101
- <span class="vm-color-picker__format" title="颜色格式">
102
- Hex
103
- <i class="ri-arrow-down-s-line" aria-hidden="true" />
104
- </span>
118
+ <select
119
+ class="vm-color-picker__format"
120
+ :value="colorFormat"
121
+ @change="onFormatChange"
122
+ >
123
+ <option
124
+ v-for="opt in FORMAT_OPTS"
125
+ :key="opt.id"
126
+ :value="opt.id"
127
+ >
128
+ {{ opt.label }}
129
+ </option>
130
+ </select>
105
131
  <input
106
132
  type="text"
107
- class="vm-color-picker__hex"
108
- :value="panelHex"
133
+ class="vm-color-picker__value"
134
+ :value="panelValue"
109
135
  spellcheck="false"
110
136
  autocomplete="off"
111
- @input="onPanelHexInput"
112
- @blur="onPanelHexBlur"
137
+ @input="onPanelValueInput"
138
+ @blur="onPanelValueBlur"
113
139
  @keydown.esc.stop="closePanel"
114
140
  />
115
141
  <input
@@ -155,6 +181,17 @@ const emit = defineEmits<{
155
181
 
156
182
  type Rgba = { r: number; g: number; b: number; a: number }
157
183
  type Hsv = { h: number; s: number; v: number }
184
+ type ColorFormat = 'hex' | 'rgb' | 'css' | 'hsl' | 'hsb'
185
+
186
+ const FORMAT_OPTS: { id: ColorFormat; label: string }[] = [
187
+ { id: 'hex', label: 'Hex' },
188
+ { id: 'rgb', label: 'RGB' },
189
+ { id: 'css', label: 'CSS' },
190
+ { id: 'hsl', label: 'HSL' },
191
+ { id: 'hsb', label: 'HSB' },
192
+ ]
193
+
194
+ const THUMB_PX = 14
158
195
 
159
196
  const rootRef = ref<HTMLElement | null>(null)
160
197
  const triggerRef = ref<HTMLElement | null>(null)
@@ -166,15 +203,16 @@ const alphaRef = ref<HTMLElement | null>(null)
166
203
  const open = ref(false)
167
204
  const suppressOpen = ref(false)
168
205
  const panelStyle = ref<Record<string, string>>({})
206
+ const colorFormat = ref<ColorFormat>('hex')
169
207
 
170
208
  const hue = ref(0)
171
209
  const sat = ref(0)
172
210
  const val = ref(0)
173
211
  const alpha = ref(1)
174
212
 
175
- /** 行内 / 面板 Hex 编辑中的草稿(不含 #) */
213
+ /** 行内 Hex(不含 #);面板按当前格式展示 */
176
214
  const inlineHex = ref('')
177
- const panelHex = ref('')
215
+ const panelValue = ref('')
178
216
  const alphaPctText = ref('100%')
179
217
 
180
218
  const hasEyeDropper = ref(false)
@@ -188,6 +226,21 @@ function clamp(n: number, min: number, max: number) {
188
226
  return Math.min(max, Math.max(min, n))
189
227
  }
190
228
 
229
+ /** 拇指左缘:0 → 0,1 → 100%-14px,整圆始终在轨道盒内 */
230
+ function sliderThumbLeft(ratio: number) {
231
+ const t = clamp(ratio, 0, 1)
232
+ return `calc(${t} * (100% - ${THUMB_PX}px))`
233
+ }
234
+
235
+ /** 指针映射:扣除两端半拇指宽度 */
236
+ function sliderRatio(el: HTMLElement, e: PointerEvent) {
237
+ const r = el.getBoundingClientRect()
238
+ const pad = THUMB_PX / 2
239
+ const usable = r.width - THUMB_PX
240
+ if (usable <= 0) return 0
241
+ return clamp((e.clientX - r.left - pad) / usable, 0, 1)
242
+ }
243
+
191
244
  function toHex2(n: number) {
192
245
  return clamp(Math.round(n), 0, 255).toString(16).padStart(2, '0')
193
246
  }
@@ -251,6 +304,61 @@ function rgbToHsv(r: number, g: number, b: number): Hsv {
251
304
  return { h, s, v: max }
252
305
  }
253
306
 
307
+ function rgbToHsl(r: number, g: number, b: number): { h: number; s: number; l: number } {
308
+ const rr = r / 255
309
+ const gg = g / 255
310
+ const bb = b / 255
311
+ const max = Math.max(rr, gg, bb)
312
+ const min = Math.min(rr, gg, bb)
313
+ const l = (max + min) / 2
314
+ const d = max - min
315
+ let h = 0
316
+ let s = 0
317
+ if (d !== 0) {
318
+ s = d / (1 - Math.abs(2 * l - 1))
319
+ if (max === rr) h = ((gg - bb) / d) % 6
320
+ else if (max === gg) h = (bb - rr) / d + 2
321
+ else h = (rr - gg) / d + 4
322
+ h *= 60
323
+ if (h < 0) h += 360
324
+ }
325
+ return { h, s, l }
326
+ }
327
+
328
+ function hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number } {
329
+ const hh = ((h % 360) + 360) % 360
330
+ const c = (1 - Math.abs(2 * l - 1)) * s
331
+ const x = c * (1 - Math.abs(((hh / 60) % 2) - 1))
332
+ const m = l - c / 2
333
+ let rp = 0
334
+ let gp = 0
335
+ let bp = 0
336
+ if (hh < 60) {
337
+ rp = c
338
+ gp = x
339
+ } else if (hh < 120) {
340
+ rp = x
341
+ gp = c
342
+ } else if (hh < 180) {
343
+ gp = c
344
+ bp = x
345
+ } else if (hh < 240) {
346
+ gp = x
347
+ bp = c
348
+ } else if (hh < 300) {
349
+ rp = x
350
+ bp = c
351
+ } else {
352
+ rp = c
353
+ bp = x
354
+ }
355
+ return {
356
+ r: (rp + m) * 255,
357
+ g: (gp + m) * 255,
358
+ b: (bp + m) * 255,
359
+ }
360
+ }
361
+
254
362
  /** 解析为 rgba;无效返回 null */
255
363
  function parseColor(raw: string): Rgba | null {
256
364
  const s = raw.trim()
@@ -283,9 +391,8 @@ function parseColor(raw: string): Rgba | null {
283
391
  return null
284
392
  }
285
393
 
286
- const bare = s.startsWith('#') ? s.slice(1) : s
287
- if (/^[0-9a-fA-F]{3}$/.test(bare) || /^[0-9a-fA-F]{6}$/.test(bare) || /^[0-9a-fA-F]{8}$/.test(bare)) {
288
- return parseColor(`#${bare}`)
394
+ if (/^[0-9a-fA-F]{3}$/.test(s) || /^[0-9a-fA-F]{6}$/.test(s) || /^[0-9a-fA-F]{8}$/.test(s)) {
395
+ return parseColor(`#${s}`)
289
396
  }
290
397
 
291
398
  const m = s.match(
@@ -299,6 +406,68 @@ function parseColor(raw: string): Rgba | null {
299
406
  a: m[4] != null ? clamp(Number(m[4]), 0, 1) : 1,
300
407
  }
301
408
  }
409
+
410
+ const hsl = s.match(
411
+ /^hsla?\(\s*([\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%(?:\s*,\s*([\d.]+))?\s*\)$/i,
412
+ )
413
+ if (hsl) {
414
+ const rgb = hslToRgb(Number(hsl[1]), Number(hsl[2]) / 100, Number(hsl[3]) / 100)
415
+ return {
416
+ r: rgb.r,
417
+ g: rgb.g,
418
+ b: rgb.b,
419
+ a: hsl[4] != null ? clamp(Number(hsl[4]), 0, 1) : 1,
420
+ }
421
+ }
422
+
423
+ return null
424
+ }
425
+
426
+ /** 按当前格式解析面板中间输入(不改变 alpha,除非 CSS/rgba 自带) */
427
+ function parsePanelValue(raw: string, keepAlpha: number): Rgba | null {
428
+ const s = raw.trim()
429
+ if (!s) return null
430
+ const fmt = colorFormat.value
431
+
432
+ if (fmt === 'hex' || fmt === 'css') {
433
+ const parsed = parseColor(s)
434
+ if (!parsed) return null
435
+ if (fmt === 'hex') parsed.a = keepAlpha
436
+ return parsed
437
+ }
438
+
439
+ if (fmt === 'rgb') {
440
+ const parts = s.split(/[,\s]+/).filter(Boolean)
441
+ if (parts.length < 3) return null
442
+ const r = Number(parts[0])
443
+ const g = Number(parts[1])
444
+ const b = Number(parts[2])
445
+ if (![r, g, b].every(Number.isFinite)) return null
446
+ return { r: clamp(r, 0, 255), g: clamp(g, 0, 255), b: clamp(b, 0, 255), a: keepAlpha }
447
+ }
448
+
449
+ if (fmt === 'hsl') {
450
+ const parts = s.replace(/%/g, '').split(/[,\s]+/).filter(Boolean)
451
+ if (parts.length < 3) return null
452
+ const h = Number(parts[0])
453
+ const satPct = Number(parts[1])
454
+ const lPct = Number(parts[2])
455
+ if (![h, satPct, lPct].every(Number.isFinite)) return null
456
+ const rgb = hslToRgb(h, clamp(satPct, 0, 100) / 100, clamp(lPct, 0, 100) / 100)
457
+ return { ...rgb, a: keepAlpha }
458
+ }
459
+
460
+ if (fmt === 'hsb') {
461
+ const parts = s.replace(/%/g, '').split(/[,\s]+/).filter(Boolean)
462
+ if (parts.length < 3) return null
463
+ const h = Number(parts[0])
464
+ const sPct = Number(parts[1])
465
+ const bPct = Number(parts[2])
466
+ if (![h, sPct, bPct].every(Number.isFinite)) return null
467
+ const rgb = hsvToRgb(h, clamp(sPct, 0, 100) / 100, clamp(bPct, 0, 100) / 100)
468
+ return { ...rgb, a: keepAlpha }
469
+ }
470
+
302
471
  return null
303
472
  }
304
473
 
@@ -311,6 +480,31 @@ function currentHex(): string {
311
480
  return rgbToHex(r, g, b, alpha.value)
312
481
  }
313
482
 
483
+ function formatPanelValue(): string {
484
+ const { r, g, b } = currentRgb()
485
+ const rr = Math.round(r)
486
+ const gg = Math.round(g)
487
+ const bb = Math.round(b)
488
+ const a = alpha.value
489
+ switch (colorFormat.value) {
490
+ case 'rgb':
491
+ return `${rr}, ${gg}, ${bb}`
492
+ case 'css':
493
+ return a < 1 - 1e-6
494
+ ? `rgba(${rr}, ${gg}, ${bb}, ${Number(a.toFixed(2))})`
495
+ : `#${toHex2(rr)}${toHex2(gg)}${toHex2(bb)}`
496
+ case 'hsl': {
497
+ const hsl = rgbToHsl(rr, gg, bb)
498
+ return `${Math.round(hsl.h)}, ${Math.round(hsl.s * 100)}%, ${Math.round(hsl.l * 100)}%`
499
+ }
500
+ case 'hsb':
501
+ return `${Math.round(hue.value)}, ${Math.round(sat.value * 100)}%, ${Math.round(val.value * 100)}%`
502
+ case 'hex':
503
+ default:
504
+ return `${toHex2(rr)}${toHex2(gg)}${toHex2(bb)}`
505
+ }
506
+ }
507
+
314
508
  const cssColor = computed(() => {
315
509
  const parsed = parseColor(props.modelValue ?? '')
316
510
  if (!parsed && !(props.modelValue ?? '').trim()) return 'transparent'
@@ -338,9 +532,8 @@ const svCursorStyle = computed(() => ({
338
532
  function syncTextsFromState() {
339
533
  const hex = currentHex()
340
534
  const bare = hex.startsWith('#') ? hex.slice(1) : hex
341
- // 行内只展示 RGB 六位;透明度在面板 % 里改
342
535
  inlineHex.value = bare.slice(0, 6)
343
- panelHex.value = bare.slice(0, 6)
536
+ panelValue.value = formatPanelValue()
344
537
  alphaPctText.value = `${Math.round(alpha.value * 100)}%`
345
538
  }
346
539
 
@@ -362,7 +555,7 @@ function loadFromModel(raw: string | null | undefined) {
362
555
  val.value = 0
363
556
  alpha.value = 1
364
557
  inlineHex.value = ''
365
- panelHex.value = ''
558
+ panelValue.value = ''
366
559
  alphaPctText.value = '100%'
367
560
  return
368
561
  }
@@ -383,7 +576,6 @@ watch(
383
576
  () => props.modelValue,
384
577
  (v) => {
385
578
  if (open.value) {
386
- // 面板打开时若外部写入且与当前一致则跳过抖动
387
579
  const cur = currentHex()
388
580
  const parsed = parseColor(v ?? '')
389
581
  if (parsed && rgbToHex(parsed.r, parsed.g, parsed.b, parsed.a) === cur) return
@@ -413,7 +605,7 @@ function onInlineInput(e: Event) {
413
605
  hue.value = hsv.h
414
606
  sat.value = hsv.s
415
607
  val.value = hsv.v
416
- panelHex.value = inlineHex.value.replace(/[^0-9a-fA-F]/g, '').slice(0, 6)
608
+ panelValue.value = formatPanelValue()
417
609
  commit(currentHex())
418
610
  } else {
419
611
  const raw = inlineHex.value.trim()
@@ -437,25 +629,25 @@ function onInlineBlur() {
437
629
  }
438
630
  }
439
631
 
440
- function onPanelHexInput(e: Event) {
441
- const v = (e.target as HTMLInputElement).value.replace(/^#/, '')
442
- panelHex.value = v
443
- const parsed = parseColor(v)
444
- if (parsed) {
445
- parsed.a = alpha.value
446
- const hsv = rgbToHsv(parsed.r, parsed.g, parsed.b)
447
- hue.value = hsv.h
448
- sat.value = hsv.s
449
- val.value = hsv.v
450
- inlineHex.value = v.replace(/[^0-9a-fA-F]/g, '').slice(0, 6)
451
- commit(currentHex())
452
- }
632
+ function onPanelValueInput(e: Event) {
633
+ panelValue.value = (e.target as HTMLInputElement).value
634
+ const parsed = parsePanelValue(panelValue.value, alpha.value)
635
+ if (!parsed) return
636
+ const hsv = rgbToHsv(parsed.r, parsed.g, parsed.b)
637
+ hue.value = hsv.h
638
+ sat.value = hsv.s
639
+ val.value = hsv.v
640
+ if (colorFormat.value === 'css') alpha.value = parsed.a
641
+ const hex = currentHex()
642
+ inlineHex.value = hex.replace(/^#/, '').slice(0, 6)
643
+ alphaPctText.value = `${Math.round(alpha.value * 100)}%`
644
+ commit(hex)
453
645
  }
454
646
 
455
- function onPanelHexBlur() {
456
- const parsed = parseColor(panelHex.value)
647
+ function onPanelValueBlur() {
648
+ const parsed = parsePanelValue(panelValue.value, alpha.value)
457
649
  if (parsed) {
458
- parsed.a = alpha.value
650
+ if (colorFormat.value !== 'css') parsed.a = alpha.value
459
651
  applyRgba(parsed, true)
460
652
  } else {
461
653
  syncTextsFromState()
@@ -479,6 +671,12 @@ function onAlphaPctBlur() {
479
671
  commit(currentHex())
480
672
  }
481
673
 
674
+ function onFormatChange(e: Event) {
675
+ const v = (e.target as HTMLSelectElement).value as ColorFormat
676
+ colorFormat.value = v
677
+ syncTextsFromState()
678
+ }
679
+
482
680
  function ratioFromEvent(el: HTMLElement, e: PointerEvent, axis: 'x' | 'y' | 'both') {
483
681
  const r = el.getBoundingClientRect()
484
682
  const x = clamp((e.clientX - r.left) / r.width, 0, 1)
@@ -523,8 +721,7 @@ function onHuePointer(e: PointerEvent) {
523
721
  const el = hueRef.value
524
722
  if (!el) return
525
723
  bindDrag(el, e, (ev) => {
526
- const { x } = ratioFromEvent(el, ev, 'x')
527
- hue.value = x * 360
724
+ hue.value = sliderRatio(el, ev) * 360
528
725
  commitFromState()
529
726
  })
530
727
  }
@@ -533,14 +730,17 @@ function onAlphaPointer(e: PointerEvent) {
533
730
  const el = alphaRef.value
534
731
  if (!el) return
535
732
  bindDrag(el, e, (ev) => {
536
- const { x } = ratioFromEvent(el, ev, 'x')
537
- alpha.value = x
733
+ alpha.value = sliderRatio(el, ev)
538
734
  commitFromState()
539
735
  })
540
736
  }
541
737
 
542
738
  async function pickEyeDropper() {
543
- const ED = (window as unknown as { EyeDropper?: new () => { open: () => Promise<{ sRGBHex: string }> } }).EyeDropper
739
+ const ED = (
740
+ window as unknown as {
741
+ EyeDropper?: new () => { open: () => Promise<{ sRGBHex: string }> }
742
+ }
743
+ ).EyeDropper
544
744
  if (!ED) return
545
745
  try {
546
746
  const result = await new ED().open()
@@ -564,7 +764,7 @@ function syncPanelPos() {
564
764
  left = Math.max(8, window.innerWidth - width - 8)
565
765
  }
566
766
  let top = r.bottom + 6
567
- const estH = 280
767
+ const estH = 320
568
768
  if (top + estH > window.innerHeight - 8) {
569
769
  top = Math.max(8, r.top - estH - 6)
570
770
  }
@@ -623,9 +823,7 @@ function onWinChange() {
623
823
  }
624
824
 
625
825
  onMounted(() => {
626
- hasEyeDropper.value =
627
- typeof window !== 'undefined' &&
628
- 'EyeDropper' in window
826
+ hasEyeDropper.value = typeof window !== 'undefined' && 'EyeDropper' in window
629
827
  document.addEventListener('pointerdown', onDocPointer, true)
630
828
  window.addEventListener('resize', onWinChange)
631
829
  window.addEventListener('scroll', onWinChange, true)
@@ -741,6 +939,7 @@ onUnmounted(() => {
741
939
  0 4px 6px -1px rgb(0 0 0 / 10%),
742
940
  0 10px 24px -4px rgb(0 0 0 / 12%);
743
941
  user-select: none;
942
+ overflow: visible;
744
943
  }
745
944
 
746
945
  .vm-color-picker__sv {
@@ -779,17 +978,26 @@ onUnmounted(() => {
779
978
  width: 28px;
780
979
  height: 28px;
781
980
  padding: 0;
782
- border: 1px solid var(--border);
981
+ border: 1px solid var(--border, #e5e5e5);
783
982
  border-radius: 6px;
784
- background: var(--muted);
785
- color: var(--foreground);
786
- font-size: 15px;
983
+ background: #fff;
984
+ color: #111;
787
985
  cursor: pointer;
788
986
 
789
- &:hover {
987
+ &:hover:not(:disabled) {
790
988
  border-color: var(--brand, #4e5dff);
791
989
  color: var(--brand, #4e5dff);
792
990
  }
991
+
992
+ &:disabled {
993
+ opacity: 0.35;
994
+ cursor: not-allowed;
995
+ }
996
+
997
+ svg {
998
+ display: block;
999
+ flex-shrink: 0;
1000
+ }
793
1001
  }
794
1002
 
795
1003
  .vm-color-picker__slider-col {
@@ -797,19 +1005,30 @@ onUnmounted(() => {
797
1005
  min-width: 0;
798
1006
  display: flex;
799
1007
  flex-direction: column;
800
- gap: 8px;
1008
+ gap: 10px;
1009
+ overflow: visible;
801
1010
  }
802
1011
 
803
- .vm-color-picker__hue,
804
- .vm-color-picker__alpha {
1012
+ .vm-color-picker__track {
805
1013
  position: relative;
806
- height: 12px;
807
- border-radius: 999px;
1014
+ width: 100%;
1015
+ height: 14px;
1016
+ overflow: visible;
808
1017
  cursor: pointer;
809
1018
  touch-action: none;
810
1019
  }
811
1020
 
812
- .vm-color-picker__hue {
1021
+ .vm-color-picker__rail {
1022
+ position: absolute;
1023
+ left: 7px;
1024
+ right: 7px;
1025
+ top: 1px;
1026
+ height: 12px;
1027
+ border-radius: 999px;
1028
+ pointer-events: none;
1029
+ }
1030
+
1031
+ .vm-color-picker__rail--hue {
813
1032
  background: linear-gradient(
814
1033
  to right,
815
1034
  #f00 0%,
@@ -822,7 +1041,7 @@ onUnmounted(() => {
822
1041
  );
823
1042
  }
824
1043
 
825
- .vm-color-picker__alpha {
1044
+ .vm-color-picker__rail--alpha {
826
1045
  background-image:
827
1046
  linear-gradient(45deg, #c8c8c8 25%, transparent 25%),
828
1047
  linear-gradient(-45deg, #c8c8c8 25%, transparent 25%),
@@ -839,23 +1058,23 @@ onUnmounted(() => {
839
1058
  }
840
1059
 
841
1060
  .vm-color-picker__alpha-fill {
842
- position: absolute;
843
- inset: 0;
1061
+ display: block;
1062
+ width: 100%;
1063
+ height: 100%;
844
1064
  border-radius: inherit;
845
- pointer-events: none;
846
1065
  }
847
1066
 
848
1067
  .vm-color-picker__slider-thumb {
849
1068
  position: absolute;
850
- top: 50%;
1069
+ top: 0;
851
1070
  width: 14px;
852
1071
  height: 14px;
853
- margin: -7px 0 0 -7px;
854
1072
  border: 2px solid #fff;
855
1073
  border-radius: 50%;
856
1074
  background: transparent;
857
1075
  box-shadow: 0 0 0 1px rgb(0 0 0 / 35%);
858
1076
  pointer-events: none;
1077
+ box-sizing: border-box;
859
1078
  }
860
1079
 
861
1080
  .vm-color-picker__footer {
@@ -867,33 +1086,31 @@ onUnmounted(() => {
867
1086
 
868
1087
  .vm-color-picker__format {
869
1088
  flex-shrink: 0;
870
- display: inline-flex;
871
- align-items: center;
872
- gap: 2px;
873
1089
  height: 28px;
1090
+ max-width: 72px;
874
1091
  padding: 0 6px;
875
- border: 1px solid var(--border);
1092
+ border: 1px solid var(--border, #e5e5e5);
876
1093
  border-radius: 6px;
877
- background: var(--muted);
878
- color: var(--muted-foreground, var(--foreground));
1094
+ background: var(--muted, #f5f5f5);
1095
+ color: var(--foreground, #111);
879
1096
  font-size: 12px;
880
- line-height: 1;
881
- cursor: default;
1097
+ cursor: pointer;
1098
+ appearance: auto;
882
1099
 
883
- i {
884
- font-size: 14px;
885
- opacity: 0.7;
1100
+ &:focus {
1101
+ border-color: var(--brand, #4e5dff);
1102
+ outline: none;
886
1103
  }
887
1104
  }
888
1105
 
889
- .vm-color-picker__hex,
1106
+ .vm-color-picker__value,
890
1107
  .vm-color-picker__alpha-pct {
891
1108
  height: 28px;
892
1109
  padding: 0 8px;
893
- border: 1px solid var(--border);
1110
+ border: 1px solid var(--border, #e5e5e5);
894
1111
  border-radius: 6px;
895
- background: var(--muted);
896
- color: var(--foreground);
1112
+ background: var(--muted, #f5f5f5);
1113
+ color: var(--foreground, #111);
897
1114
  font-size: 12px;
898
1115
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
899
1116
  outline: none;
@@ -903,7 +1120,7 @@ onUnmounted(() => {
903
1120
  }
904
1121
  }
905
1122
 
906
- .vm-color-picker__hex {
1123
+ .vm-color-picker__value {
907
1124
  flex: 1;
908
1125
  min-width: 0;
909
1126
  }