@5even7/dlc-ui 0.2.18 → 0.2.20

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 (87) hide show
  1. package/CHANGELOG.md +158 -144
  2. package/EXTENDING.md +114 -0
  3. package/LICENSE +21 -21
  4. package/README.md +389 -338
  5. package/THIRD_PARTY_NOTICES.md +221 -0
  6. package/dist/avatar-creator/styles/humation.mjs +26 -0
  7. package/dist/avatar-creator/styles.mjs +76 -0
  8. package/dist/avatar-creator.cjs +7831 -0
  9. package/dist/avatar-creator.mjs +74 -0
  10. package/dist/capsule.cjs +185 -66
  11. package/dist/capsule.mjs +364 -1340
  12. package/dist/chunks/avatar-creator-DiAmhpRa.mjs +687 -0
  13. package/dist/chunks/color-DnmNceaf.mjs +350 -0
  14. package/dist/chunks/controls-B5_ctvvA.mjs +160 -0
  15. package/dist/chunks/copy-7J0XIj_0.mjs +13 -0
  16. package/dist/chunks/cosmic-fallback-v8G5_ibh.mjs +408 -0
  17. package/dist/chunks/emitter-CxLYMSbw.mjs +38 -0
  18. package/dist/chunks/emo-Dvahy5Qr.mjs +1140 -0
  19. package/dist/chunks/hash-BiO02UNb.mjs +38 -0
  20. package/dist/chunks/humation-B9OPUGL7.mjs +2987 -0
  21. package/dist/chunks/identicon-BUdMd-yf.mjs +3063 -0
  22. package/dist/chunks/index-BchW1tWE.mjs +478 -0
  23. package/dist/chunks/index-BtH8Lbra.mjs +296 -0
  24. package/dist/chunks/index-C02dvGLD.mjs +263 -0
  25. package/dist/chunks/index-CFybYBA1.mjs +278 -0
  26. package/dist/chunks/index-DgI872-7.mjs +241 -0
  27. package/dist/chunks/index-_sf_14Zu.mjs +455 -0
  28. package/dist/chunks/index-w5Pzdwhv.mjs +305 -0
  29. package/dist/chunks/motion-a8_f8_Ui.mjs +177 -0
  30. package/dist/chunks/number-BqWLX0iQ.mjs +231 -0
  31. package/dist/chunks/progress-KweS4Rx0.mjs +1836 -0
  32. package/dist/chunks/size-Bztk2ryJ.mjs +22 -0
  33. package/dist/chunks/wrapper-DXhy1E7Z.mjs +514 -0
  34. package/dist/color.cjs +186 -67
  35. package/dist/color.mjs +341 -1280
  36. package/dist/emo/miao/angry.mjs +4 -0
  37. package/dist/emo/miao/asleep.mjs +4 -0
  38. package/dist/emo/miao/badminton.mjs +4 -0
  39. package/dist/emo/miao/confident.mjs +4 -0
  40. package/dist/emo/miao/cry.mjs +4 -0
  41. package/dist/emo/miao/investigate.mjs +4 -0
  42. package/dist/emo/miao/laugh.mjs +4 -0
  43. package/dist/emo/miao/leisure.mjs +4 -0
  44. package/dist/emo/miao/mock.mjs +4 -0
  45. package/dist/emo/miao/music.mjs +4 -0
  46. package/dist/emo/miao/mute.mjs +4 -0
  47. package/dist/emo/miao/panic.mjs +4 -0
  48. package/dist/emo/miao/ponder.mjs +4 -0
  49. package/dist/emo/miao/question.mjs +4 -0
  50. package/dist/emo/miao/sad.mjs +4 -0
  51. package/dist/emo/miao/shocked.mjs +4 -0
  52. package/dist/emo/miao/shy.mjs +4 -0
  53. package/dist/emo/miao/sigh.mjs +4 -0
  54. package/dist/emo/miao/smile.mjs +4 -0
  55. package/dist/emo/miao/snigger.mjs +4 -0
  56. package/dist/emo/miao/static.mjs +4 -0
  57. package/dist/emo/miao/yawn.mjs +4 -0
  58. package/dist/emo/miao/yummy.mjs +4 -0
  59. package/dist/emo.cjs +27161 -25964
  60. package/dist/emo.mjs +8 -1421
  61. package/dist/index.cjs +32729 -24466
  62. package/dist/index.mjs +16 -4757
  63. package/dist/index.umd.js +33115 -24852
  64. package/dist/index.umd.min.js +3 -1
  65. package/dist/progress.cjs +195 -76
  66. package/dist/progress.mjs +7 -2448
  67. package/dist/vue2.cjs +33282 -25002
  68. package/dist/vue2.mjs +63 -5131
  69. package/dist/vue3.cjs +33282 -25002
  70. package/dist/vue3.mjs +53 -5122
  71. package/package.json +161 -136
  72. package/styles/avatar-creator.css +38 -0
  73. package/styles/base.css +32 -32
  74. package/styles/color.css +18 -18
  75. package/styles/emo.css +110 -89
  76. package/styles/progress.css +96 -96
  77. package/styles/theme.css +1 -0
  78. package/types/avatar-creator-styles.d.ts +10 -0
  79. package/types/avatar-creator.d.ts +143 -0
  80. package/types/capsule.d.ts +15 -15
  81. package/types/color.d.ts +15 -15
  82. package/types/emo-miao.d.ts +9 -0
  83. package/types/emo.d.ts +38 -37
  84. package/types/index.d.ts +593 -554
  85. package/types/progress.d.ts +16 -16
  86. package/types/vue2.d.ts +11 -10
  87. package/types/vue3.d.ts +169 -146
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Document-level shared rAF scheduler. Every component instance subscribes
3
+ * its own frame callback; the whole page runs ONE animation loop (like the
4
+ * original demo), which avoids jank from many competing rAF loops.
5
+ */
6
+ const subscribers = [];
7
+ let running = false;
8
+ let globallyPaused = false;
9
+ let rafId = 0;
10
+ let last = 0;
11
+
12
+ function tick(now) {
13
+ if (!running) return;
14
+ const activeItems = [];
15
+ if (!globallyPaused) {
16
+ for (const item of subscribers.slice()) {
17
+ try {
18
+ if (!item.isPaused()) activeItems.push(item);
19
+ } catch {}
20
+ }
21
+ }
22
+ if (activeItems.length === 0) {
23
+ running = false;
24
+ rafId = 0;
25
+ last = 0;
26
+ return;
27
+ }
28
+ const delta = last ? Math.min((now - last) / 1000, 0.05) : 0;
29
+ last = now;
30
+ // Schedule the next frame BEFORE running callbacks so one throwing
31
+ // subscriber can never kill the whole animation loop.
32
+ rafId = requestAnimationFrame(tick);
33
+ for (const item of activeItems) {
34
+ try {
35
+ item.onFrame(delta, now);
36
+ } catch (error) {
37
+ console.warn('[dlc-ui] frame error:', error);
38
+ }
39
+ }
40
+ if (subscribers.length === 0) {
41
+ cancelAnimationFrame(rafId);
42
+ running = false;
43
+ rafId = 0;
44
+ }
45
+ }
46
+
47
+ function start() {
48
+ if (running) return;
49
+ running = true;
50
+ last = 0;
51
+ rafId = requestAnimationFrame(tick);
52
+ }
53
+
54
+ function pauseAll() {
55
+ globallyPaused = true;
56
+ }
57
+
58
+ function resumeAll() {
59
+ globallyPaused = false;
60
+ start();
61
+ }
62
+
63
+ function wakeScheduler() {
64
+ start();
65
+ }
66
+
67
+ function subscribeScheduler(onFrame, isPaused) {
68
+ const item = { onFrame, isPaused };
69
+ subscribers.push(item);
70
+ start();
71
+ return () => {
72
+ const index = subscribers.indexOf(item);
73
+ if (index !== -1) subscribers.splice(index, 1);
74
+ if (subscribers.length === 0 && rafId) {
75
+ cancelAnimationFrame(rafId);
76
+ running = false;
77
+ rafId = 0;
78
+ }
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Gates drawing on "element intersects viewport AND the page tab is visible".
84
+ * Falls back to always-visible when IntersectionObserver is unavailable.
85
+ */
86
+ function createVisibilityGuard(element, onChange = null) {
87
+ let intersecting = true;
88
+ let pageVisible = typeof document === 'undefined' || !document.hidden;
89
+ let disposed = false;
90
+ let observer = null;
91
+
92
+ if (typeof IntersectionObserver !== 'undefined') {
93
+ observer = new IntersectionObserver(
94
+ (entries) => {
95
+ intersecting = entries.some((entry) => entry.isIntersecting);
96
+ if (typeof onChange === 'function') onChange();
97
+ },
98
+ { rootMargin: '180px' }
99
+ );
100
+ observer.observe(element);
101
+ }
102
+
103
+ const onVisibilityChange = () => {
104
+ pageVisible = typeof document !== 'undefined' && !document.hidden;
105
+ if (typeof onChange === 'function') onChange();
106
+ };
107
+ if (typeof document !== 'undefined') {
108
+ document.addEventListener('visibilitychange', onVisibilityChange);
109
+ }
110
+
111
+ return {
112
+ isVisible() {
113
+ return intersecting && pageVisible;
114
+ },
115
+ dispose() {
116
+ if (disposed) return;
117
+ disposed = true;
118
+ if (observer) observer.disconnect();
119
+ if (typeof document !== 'undefined') {
120
+ document.removeEventListener('visibilitychange', onVisibilityChange);
121
+ }
122
+ }
123
+ };
124
+ }
125
+
126
+ function normalizeFps(value, fallback = 60) {
127
+ const fps = Number(value);
128
+ if (!Number.isFinite(fps)) return fallback;
129
+ return Math.min(60, Math.max(1, fps));
130
+ }
131
+
132
+ function createFrameGate(initialFps = 60) {
133
+ let fps = normalizeFps(initialFps);
134
+ let elapsed = 0;
135
+ return {
136
+ shouldDraw(delta) {
137
+ elapsed += delta;
138
+ const interval = 1 / fps;
139
+ if (elapsed + 0.0001 < interval) return false;
140
+ elapsed %= interval;
141
+ return true;
142
+ },
143
+ setFps(value) {
144
+ fps = normalizeFps(value, fps);
145
+ elapsed = 0;
146
+ return fps;
147
+ },
148
+ getFps() {
149
+ return fps;
150
+ }
151
+ };
152
+ }
153
+
154
+ function createReducedMotionPreference(enabled, onChange) {
155
+ const query = enabled && typeof matchMedia !== 'undefined'
156
+ ? matchMedia('(prefers-reduced-motion: reduce)')
157
+ : null;
158
+ const notify = () => {
159
+ if (typeof onChange === 'function') onChange(Boolean(query && query.matches));
160
+ };
161
+ if (query) {
162
+ if (typeof query.addEventListener === 'function') query.addEventListener('change', notify);
163
+ else if (typeof query.addListener === 'function') query.addListener(notify);
164
+ }
165
+ return {
166
+ matches() {
167
+ return Boolean(query && query.matches);
168
+ },
169
+ dispose() {
170
+ if (!query) return;
171
+ if (typeof query.removeEventListener === 'function') query.removeEventListener('change', notify);
172
+ else if (typeof query.removeListener === 'function') query.removeListener(notify);
173
+ }
174
+ };
175
+ }
176
+
177
+ export { createVisibilityGuard as a, createReducedMotionPreference as b, createFrameGate as c, pauseAll as p, resumeAll as r, subscribeScheduler as s, wakeScheduler as w };
@@ -0,0 +1,231 @@
1
+ const QUALITY_TIERS = {
2
+ low: { dpr: 1 },
3
+ medium: { dpr: 1.5 },
4
+ high: { dpr: 2 }
5
+ };
6
+
7
+ function autoDprCap() {
8
+ const nav = typeof navigator !== 'undefined' ? navigator : null;
9
+ const memory = nav && typeof nav.deviceMemory === 'number' ? nav.deviceMemory : 8;
10
+ const cores = nav && typeof nav.hardwareConcurrency === 'number' ? nav.hardwareConcurrency : 8;
11
+ return memory <= 4 || cores <= 4 ? 1.5 : 2;
12
+ }
13
+
14
+ function dprCapFor(quality) {
15
+ if (quality === 'auto') return autoDprCap();
16
+ const tier = QUALITY_TIERS[quality] || QUALITY_TIERS.medium;
17
+ return tier.dpr;
18
+ }
19
+
20
+ function effectiveDprCap(quality, renderScale = 1) {
21
+ const scale = Number(renderScale);
22
+ const normalizedScale = Number.isFinite(scale) ? Math.min(1, Math.max(0.25, scale)) : 1;
23
+ return Math.max(0.5, dprCapFor(quality) * normalizedScale);
24
+ }
25
+
26
+ /**
27
+ * Merge defaults < preset < user. Unknown user keys are preserved so the
28
+ * component API can grow (new props, cssVars, callbacks) without a breaking
29
+ * change.
30
+ */
31
+ function normalizeOptions(defaults, preset, user) {
32
+ // undefined means "not provided" (e.g. Vue $props with unset props):
33
+ // drop those keys before merging so defaults/preset values survive.
34
+ const omitUndefined = (source) => {
35
+ const result = {};
36
+ for (const key of Object.keys(source)) {
37
+ if (source[key] !== undefined) result[key] = source[key];
38
+ }
39
+ return result;
40
+ };
41
+ const presetOptions = omitUndefined(preset && typeof preset === 'object' ? preset : {});
42
+ const userOptions = omitUndefined(user && typeof user === 'object' ? user : {});
43
+ return { ...defaults, ...presetOptions, ...userOptions };
44
+ }
45
+
46
+ /**
47
+ * 公共色板:NC-01~NC-06 的四色组(底色 / 主色 / 辅色 / 高光色)。
48
+ * Capsule 预置与 dlc-color 等组件共用,新增色板只改这里。
49
+ */
50
+ const PALETTES = {
51
+ original: ['#FFF3EA', '#F5B27A', '#F67BC6', '#A978E8'],
52
+ ocean: ['#EAF6FF', '#8FD0FF', '#3B87F6', '#6B58E9'],
53
+ klein: ['#EDF2FF', '#2F58D5', '#1B2040', '#E07A43'],
54
+ ultraviolet: ['#F2EEFF', '#B99AF1', '#8F74DB', '#D7D85C'],
55
+ chrome: ['#F5F6F8', '#B9C0CC', '#7F8793', '#4A4F59'],
56
+ plus: ['#FFF0E6', '#F6C26B', '#F98A64', '#E86D74']
57
+ };
58
+
59
+ /**
60
+ * Capsule 预置:仅星云材质 NC-01~NC-06(NC-07~09 aurora 已移除)。
61
+ * 颜色引用公共色板,seed/speed 决定形态与流速。
62
+ */
63
+ const CAPSULE_PRESETS = [
64
+ { id: 'original', code: 'NC-01', name: '初光', group: 'warm', seed: 1.7, speed: 0.5, colors: [...PALETTES.original] },
65
+ { id: 'ocean', code: 'NC-02', name: '沧溟', group: 'cold', seed: 8.2, speed: 0.48, colors: [...PALETTES.ocean] },
66
+ { id: 'klein', code: 'NC-03', name: '玄夜', group: 'cold', seed: 14.1, speed: 0.49, colors: [...PALETTES.klein] },
67
+ { id: 'ultraviolet', code: 'NC-04', name: '紫霄', group: 'cold', seed: 23.4, speed: 0.47, colors: [...PALETTES.ultraviolet] },
68
+ { id: 'chrome', code: 'NC-05', name: '银汉', group: 'cold', seed: 37.8, speed: 0.42, colors: [...PALETTES.chrome] },
69
+ { id: 'plus', code: 'NC-06', name: '熔金', group: 'warm', seed: 51.3, speed: 0.5, colors: [...PALETTES.plus] }
70
+ ];
71
+
72
+ const PROGRESS_PRESETS = [
73
+ {
74
+ id: 'model-training',
75
+ code: 'NC-10',
76
+ name: '星火',
77
+ subtitle: 'SHA 4.5 + 100 2026 TKN',
78
+ group: 'progress',
79
+ initialProgress: 43,
80
+ edgeStyle: 'flow',
81
+ colors: ['#2B1025', '#FF3F94', '#FF8A3D', '#FFF06A']
82
+ },
83
+ {
84
+ id: 'agent-migration',
85
+ code: 'NC-11',
86
+ name: '迁流',
87
+ subtitle: 'TRANSFERRING PROTOCOL',
88
+ group: 'progress',
89
+ initialProgress: 33,
90
+ edgeStyle: 'flow',
91
+ colors: ['#101C37', '#245BFF', '#00CFFF', '#5DFFE6']
92
+ },
93
+ {
94
+ id: 'visual-training',
95
+ code: 'NC-12',
96
+ name: '幻境',
97
+ subtitle: 'GENERATING POWER ++',
98
+ group: 'progress',
99
+ initialProgress: 58,
100
+ edgeStyle: 'flow',
101
+ colors: ['#21142D', '#7042FF', '#42F58D', '#C4FF8A']
102
+ },
103
+ {
104
+ id: 'tide',
105
+ code: 'NC-13',
106
+ name: '汐潮',
107
+ subtitle: 'MOON PULL / COAST',
108
+ group: 'progress',
109
+ initialProgress: 30,
110
+ edgeStyle: 'tide',
111
+ colors: ['#0A2239', '#2E9BFF', '#7FE3FF', '#EAF9FF']
112
+ },
113
+ {
114
+ id: 'botanic-lab',
115
+ code: 'NC-14',
116
+ name: '森息',
117
+ subtitle: 'MOSS CULTURE / ROOTING',
118
+ group: 'progress',
119
+ initialProgress: 46,
120
+ edgeStyle: 'flow',
121
+ colors: ['#102A23', '#287A58', '#9BD65B', '#F2FFC4']
122
+ }
123
+ ];
124
+
125
+ const PRESETS = CAPSULE_PRESETS;
126
+ const ALL_PRESETS = [...CAPSULE_PRESETS, ...PROGRESS_PRESETS];
127
+
128
+ /**
129
+ * Resolve a preset by its literary name (e.g. '沧溟').
130
+ * Matching is case-insensitive and ignores surrounding whitespace,
131
+ * so '沧溟' and ' 沧溟 ' both work.
132
+ */
133
+ function getPreset(kind, ref) {
134
+ const list = kind === 'capsule' ? CAPSULE_PRESETS : kind === 'progress' ? PROGRESS_PRESETS : null;
135
+ if (!list) throw new Error(`Unknown preset kind: ${kind} (use "capsule" or "progress")`);
136
+ const key = String(ref).trim().toLowerCase();
137
+ let found = null;
138
+ for (let index = 0; index < list.length; index += 1) {
139
+ if (list[index].name.toLowerCase() === key) {
140
+ found = list[index];
141
+ break;
142
+ }
143
+ }
144
+ if (!found) throw new Error(`Unknown ${kind} preset: ${ref}`);
145
+ return found;
146
+ }
147
+
148
+ const DEFAULTS = {
149
+ capsule: {
150
+ width: '100%',
151
+ height: 160,
152
+ quality: 'auto',
153
+ renderer: 'auto',
154
+ renderScale: 1,
155
+ powerPreference: 'high-performance',
156
+ fps: 60,
157
+ paused: false,
158
+ static: false,
159
+ respectReducedMotion: true,
160
+ mouseColor: true,
161
+ textRatio: 39
162
+ },
163
+ progress: {
164
+ width: 454,
165
+ height: 104,
166
+ min: 0,
167
+ max: 100,
168
+ step: 'any',
169
+ draggable: true,
170
+ keyboard: true,
171
+ disabled: false,
172
+ readonly: false,
173
+ direction: 'ltr',
174
+ precision: 0,
175
+ valueSuffix: '%',
176
+ edgeStyle: 'flow',
177
+ quality: 'auto',
178
+ renderer: 'auto',
179
+ renderScale: 1,
180
+ powerPreference: 'high-performance',
181
+ fps: 60,
182
+ paused: false,
183
+ static: false,
184
+ textRatio: 54,
185
+ showValue: true,
186
+ respectReducedMotion: true
187
+ },
188
+ emo: {
189
+ type: 'miao',
190
+ name: 'smile',
191
+ width: 160,
192
+ height: 160,
193
+ partColors: {},
194
+ speed: 1,
195
+ loop: true,
196
+ autoplay: true,
197
+ direction: 'forward',
198
+ opacity: 1,
199
+ fit: 'contain',
200
+ renderer: 'svg',
201
+ paused: false,
202
+ respectReducedMotion: true,
203
+ segment: null,
204
+ background: 'transparent'
205
+ }
206
+ };
207
+
208
+ /**
209
+ * Run a callback asynchronously, right after the current task and before the
210
+ * next paint. Used for mount-time events (ready / error) so listeners that
211
+ * are attached after create() still receive them. Falls back for legacy
212
+ * browsers that lack queueMicrotask / Promise.
213
+ */
214
+ function nextTick(fn) {
215
+ if (typeof queueMicrotask === 'function') {
216
+ queueMicrotask(fn);
217
+ } else if (typeof Promise !== 'undefined' && typeof Promise.resolve === 'function') {
218
+ Promise.resolve().then(fn);
219
+ } else {
220
+ setTimeout(fn, 0);
221
+ }
222
+ }
223
+
224
+ function toFiniteNumber(value) {
225
+ if (value == null || typeof value === 'boolean') return null;
226
+ if (typeof value === 'string' && value.trim() === '') return null;
227
+ const number = Number(value);
228
+ return Number.isFinite(number) ? number : null;
229
+ }
230
+
231
+ export { ALL_PRESETS as A, DEFAULTS as D, PRESETS as P, QUALITY_TIERS as Q, PROGRESS_PRESETS as a, nextTick as b, dprCapFor as d, effectiveDprCap as e, getPreset as g, normalizeOptions as n, toFiniteNumber as t };