@dryui/feedback 0.0.2

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 (79) hide show
  1. package/dist/components/annotation-marker.svelte +163 -0
  2. package/dist/components/annotation-marker.svelte.d.ts +11 -0
  3. package/dist/components/annotation-popup.svelte +669 -0
  4. package/dist/components/annotation-popup.svelte.d.ts +42 -0
  5. package/dist/components/highlight-overlay.svelte +48 -0
  6. package/dist/components/highlight-overlay.svelte.d.ts +8 -0
  7. package/dist/components/settings-panel.svelte +446 -0
  8. package/dist/components/settings-panel.svelte.d.ts +24 -0
  9. package/dist/components/toolbar.svelte +1111 -0
  10. package/dist/components/toolbar.svelte.d.ts +46 -0
  11. package/dist/constants.d.ts +9 -0
  12. package/dist/constants.js +37 -0
  13. package/dist/feedback.svelte +2879 -0
  14. package/dist/feedback.svelte.d.ts +4 -0
  15. package/dist/index.d.ts +10 -0
  16. package/dist/index.js +7 -0
  17. package/dist/layout-mode/catalog.d.ts +16 -0
  18. package/dist/layout-mode/catalog.js +81 -0
  19. package/dist/layout-mode/component-actions.svelte +84 -0
  20. package/dist/layout-mode/component-actions.svelte.d.ts +18 -0
  21. package/dist/layout-mode/component-picker.svelte +73 -0
  22. package/dist/layout-mode/component-picker.svelte.d.ts +10 -0
  23. package/dist/layout-mode/design-mode.svelte +1115 -0
  24. package/dist/layout-mode/design-mode.svelte.d.ts +24 -0
  25. package/dist/layout-mode/design-palette.svelte +396 -0
  26. package/dist/layout-mode/design-palette.svelte.d.ts +20 -0
  27. package/dist/layout-mode/element-heuristics.d.ts +5 -0
  28. package/dist/layout-mode/element-heuristics.js +51 -0
  29. package/dist/layout-mode/freeze.d.ts +6 -0
  30. package/dist/layout-mode/freeze.js +163 -0
  31. package/dist/layout-mode/generated-library.d.ts +940 -0
  32. package/dist/layout-mode/generated-library.js +1445 -0
  33. package/dist/layout-mode/geometry.d.ts +38 -0
  34. package/dist/layout-mode/geometry.js +133 -0
  35. package/dist/layout-mode/history.d.ts +10 -0
  36. package/dist/layout-mode/history.js +45 -0
  37. package/dist/layout-mode/index.d.ts +23 -0
  38. package/dist/layout-mode/index.js +18 -0
  39. package/dist/layout-mode/live-mount.d.ts +20 -0
  40. package/dist/layout-mode/live-mount.js +70 -0
  41. package/dist/layout-mode/output.d.ts +26 -0
  42. package/dist/layout-mode/output.js +550 -0
  43. package/dist/layout-mode/placement-skeleton.d.ts +9 -0
  44. package/dist/layout-mode/placement-skeleton.js +535 -0
  45. package/dist/layout-mode/rearrange-overlay.svelte +1293 -0
  46. package/dist/layout-mode/rearrange-overlay.svelte.d.ts +18 -0
  47. package/dist/layout-mode/responsive-bar.svelte +39 -0
  48. package/dist/layout-mode/responsive-bar.svelte.d.ts +8 -0
  49. package/dist/layout-mode/route-creator.svelte +70 -0
  50. package/dist/layout-mode/route-creator.svelte.d.ts +8 -0
  51. package/dist/layout-mode/section-detection.d.ts +6 -0
  52. package/dist/layout-mode/section-detection.js +214 -0
  53. package/dist/layout-mode/spatial.d.ts +42 -0
  54. package/dist/layout-mode/spatial.js +156 -0
  55. package/dist/layout-mode/types.d.ts +144 -0
  56. package/dist/layout-mode/types.js +84 -0
  57. package/dist/types.d.ts +157 -0
  58. package/dist/types.js +1 -0
  59. package/dist/utils/dryui-detection.d.ts +1 -0
  60. package/dist/utils/dryui-detection.js +219 -0
  61. package/dist/utils/element-id.d.ts +12 -0
  62. package/dist/utils/element-id.js +333 -0
  63. package/dist/utils/freeze.d.ts +7 -0
  64. package/dist/utils/freeze.js +168 -0
  65. package/dist/utils/output.d.ts +15 -0
  66. package/dist/utils/output.js +245 -0
  67. package/dist/utils/selection.d.ts +22 -0
  68. package/dist/utils/selection.js +58 -0
  69. package/dist/utils/shadow-dom.d.ts +4 -0
  70. package/dist/utils/shadow-dom.js +39 -0
  71. package/dist/utils/storage.d.ts +30 -0
  72. package/dist/utils/storage.js +206 -0
  73. package/dist/utils/svelte-detection.d.ts +8 -0
  74. package/dist/utils/svelte-detection.js +86 -0
  75. package/dist/utils/svelte-meta.d.ts +6 -0
  76. package/dist/utils/svelte-meta.js +69 -0
  77. package/dist/utils/sync.d.ts +18 -0
  78. package/dist/utils/sync.js +62 -0
  79. package/package.json +65 -0
@@ -0,0 +1,535 @@
1
+ function escapeHtml(value) {
2
+ return value
3
+ .replaceAll('&', '&')
4
+ .replaceAll('<', '&lt;')
5
+ .replaceAll('>', '&gt;')
6
+ .replaceAll('"', '&quot;')
7
+ .replaceAll("'", '&#39;');
8
+ }
9
+ function clamp(value, min, max) {
10
+ return Math.max(min, Math.min(max, value));
11
+ }
12
+ function cssSize(value) {
13
+ return typeof value === 'number' ? `${Math.round(value)}px` : value;
14
+ }
15
+ function bar(width, height = 3, strong = false, extra = '') {
16
+ return `<div style="inline-size:${cssSize(width)};block-size:${cssSize(height)};border-radius:999px;background:${strong ? 'var(--agd-bar-strong)' : 'var(--agd-bar)'};flex-shrink:0;${extra}"></div>`;
17
+ }
18
+ function block(width, height, radius = 4, extra = '') {
19
+ return `<div style="inline-size:${cssSize(width)};block-size:${cssSize(height)};border-radius:${cssSize(radius)};border:1px dashed var(--agd-stroke);background:var(--agd-fill);flex-shrink:0;${extra}"></div>`;
20
+ }
21
+ function circle(size, extra = '') {
22
+ return `<div style="inline-size:${cssSize(size)};block-size:${cssSize(size)};border-radius:999px;border:1px dashed var(--agd-stroke);background:var(--agd-fill);flex-shrink:0;${extra}"></div>`;
23
+ }
24
+ function wrap(type, body) {
25
+ return `<div data-design-skeleton data-design-skeleton-type="${type}" style="position:relative;inline-size:100%;block-size:100%;overflow:hidden;display:flex;border-radius:inherit;--agd-fill:color-mix(in srgb, var(--dry-color-fill-brand, #7c3aed) 10%, transparent);--agd-fill-strong:color-mix(in srgb, var(--dry-color-fill-brand, #7c3aed) 16%, transparent);--agd-stroke:color-mix(in srgb, var(--dry-color-fill-brand, #7c3aed) 44%, transparent);--agd-bar:color-mix(in srgb, var(--dry-color-fill-brand, #7c3aed) 26%, transparent);--agd-bar-strong:color-mix(in srgb, var(--dry-color-fill-brand, #7c3aed) 50%, transparent);--agd-text:color-mix(in srgb, var(--dry-color-text-strong, #111827) 78%, transparent);">${body}</div>`;
26
+ }
27
+ function renderNavigation(width, height) {
28
+ const pad = clamp(height * 0.2, 8, 18);
29
+ return `
30
+ <div style="display:flex;align-items:center;inline-size:100%;block-size:100%;padding:0 ${cssSize(pad)};gap:${cssSize(width * 0.024)};">
31
+ ${block(clamp(height * 0.48, 20, 34), clamp(height * 0.42, 12, 24), 4)}
32
+ <div style="display:flex;flex:1;gap:${cssSize(width * 0.03)};margin-inline-start:${cssSize(width * 0.03)};">
33
+ ${bar(width * 0.08)}
34
+ ${bar(width * 0.09)}
35
+ ${bar(width * 0.07)}
36
+ ${bar(width * 0.08)}
37
+ </div>
38
+ ${block(width * 0.12, clamp(height * 0.44, 20, 28), 6)}
39
+ </div>
40
+ `;
41
+ }
42
+ function renderHero(width, height, text, actionCount = 1) {
43
+ const label = text ? escapeHtml(text) : '';
44
+ const buttons = Array.from({ length: actionCount }, () => block(clamp(width * 0.16, 72, 140), clamp(height * 0.12, 26, 38), 8)).join('');
45
+ return `
46
+ <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;inline-size:100%;block-size:100%;padding:${cssSize(clamp(height * 0.12, 12, 32))};gap:${cssSize(clamp(height * 0.04, 8, 18))};text-align:center;">
47
+ ${label
48
+ ? `<span style="font-size:${cssSize(clamp(height * 0.085, 14, 22))};font-weight:600;color:var(--agd-text);max-inline-size:80%;line-height:1.2;">${label}</span>`
49
+ : bar(width * 0.52, clamp(height * 0.035, 5, 10), true)}
50
+ ${bar(width * 0.62)}
51
+ ${bar(width * 0.42)}
52
+ <div style="display:flex;gap:${cssSize(8)};margin-top:${cssSize(clamp(height * 0.05, 8, 18))};">${buttons}</div>
53
+ </div>
54
+ `;
55
+ }
56
+ function renderSidebar(width, height) {
57
+ const items = clamp(Math.floor(height / 40), 4, 9);
58
+ const rows = Array.from({ length: items }, (_, index) => `
59
+ <div style="display:flex;align-items:center;gap:${cssSize(8)};">
60
+ ${block(12, 12, 3)}
61
+ ${bar(`${50 + ((index * 11) % 25)}%`)}
62
+ </div>
63
+ `).join('');
64
+ return `
65
+ <div style="display:flex;flex-direction:column;inline-size:100%;block-size:100%;padding:${cssSize(clamp(width * 0.08, 10, 18))};gap:${cssSize(clamp(height * 0.03, 6, 14))};">
66
+ ${bar('58%', 4, true)}
67
+ ${rows}
68
+ </div>
69
+ `;
70
+ }
71
+ function renderFooter(width, height) {
72
+ const columns = clamp(Math.floor(width / 180), 2, 4);
73
+ const blocks = Array.from({ length: columns }, () => `
74
+ <div style="flex:1;display:flex;flex-direction:column;gap:${cssSize(5)};">
75
+ ${bar('55%', 3, true)}
76
+ ${bar('78%', 2)}
77
+ ${bar('64%', 2)}
78
+ ${bar('70%', 2)}
79
+ </div>
80
+ `).join('');
81
+ return `<div style="display:flex;inline-size:100%;block-size:100%;padding:${cssSize(clamp(height * 0.12, 10, 24))} ${cssSize(clamp(width * 0.04, 10, 18))};gap:${cssSize(clamp(width * 0.04, 12, 28))};">${blocks}</div>`;
82
+ }
83
+ function renderPanel(width, height, footer = true) {
84
+ return `
85
+ <div style="display:flex;flex-direction:column;inline-size:100%;block-size:100%;">
86
+ <div style="display:flex;align-items:center;justify-content:space-between;padding:${cssSize(clamp(height * 0.05, 8, 14))} ${cssSize(clamp(width * 0.04, 10, 16))};border-bottom:1px solid var(--agd-stroke);">
87
+ ${bar(width * 0.34, 4, true)}
88
+ ${block(14, 14, 4)}
89
+ </div>
90
+ <div style="flex:1;display:flex;flex-direction:column;gap:${cssSize(6)};padding:${cssSize(clamp(width * 0.04, 10, 16))};">
91
+ ${bar('90%')}
92
+ ${bar('76%')}
93
+ ${bar('84%')}
94
+ </div>
95
+ ${footer
96
+ ? `<div style="display:flex;justify-content:flex-end;gap:${cssSize(8)};padding:${cssSize(clamp(height * 0.05, 8, 14))} ${cssSize(clamp(width * 0.04, 10, 16))};border-top:1px solid var(--agd-stroke);">
97
+ ${block(70, 28, 6)}
98
+ ${block(72, 28, 6, 'background:var(--agd-bar);')}
99
+ </div>`
100
+ : ''}
101
+ </div>
102
+ `;
103
+ }
104
+ function renderCard(width, height, text, imageRatio = 0.38) {
105
+ const label = text ? escapeHtml(text) : '';
106
+ return `
107
+ <div style="display:flex;flex-direction:column;inline-size:100%;block-size:100%;">
108
+ <div style="block-size:${cssSize(height * imageRatio)};background:var(--agd-fill);border-bottom:1px dashed var(--agd-stroke);"></div>
109
+ <div style="flex:1;display:flex;flex-direction:column;gap:${cssSize(6)};padding:${cssSize(clamp(width * 0.04, 8, 14))};">
110
+ ${label
111
+ ? `<span style="font-size:${cssSize(clamp(height * 0.09, 11, 16))};font-weight:600;color:var(--agd-text);line-height:1.2;">${label}</span>`
112
+ : bar('72%', 4, true)}
113
+ ${bar('94%', 2)}
114
+ ${bar('82%', 2)}
115
+ ${bar('56%', 2)}
116
+ </div>
117
+ </div>
118
+ `;
119
+ }
120
+ function renderText(width, height, text) {
121
+ if (text) {
122
+ return `
123
+ <div style="inline-size:100%;block-size:100%;padding:${cssSize(clamp(width * 0.04, 8, 12))};font-size:${cssSize(clamp(height * 0.16, 11, 16))};line-height:1.45;color:var(--agd-text);word-break:break-word;overflow:hidden;">
124
+ ${escapeHtml(text)}
125
+ </div>
126
+ `;
127
+ }
128
+ const lines = clamp(Math.floor(height / 24), 2, 6);
129
+ const rows = Array.from({ length: lines }, (_, index) => bar(`${68 + ((index * 13) % 26)}%`, index === 0 ? 5 : 2, index === 0)).join('');
130
+ return `<div style="display:flex;flex-direction:column;gap:${cssSize(6)};inline-size:100%;block-size:100%;padding:${cssSize(clamp(width * 0.04, 8, 12))};">${rows}</div>`;
131
+ }
132
+ function renderMedia(width, height, showControls = false) {
133
+ const controls = showControls
134
+ ? `<div style="position:absolute;inset-inline:0;inset-block-end:0;display:flex;align-items:center;gap:${cssSize(8)};padding:${cssSize(10)};background:linear-gradient(180deg, transparent, color-mix(in srgb, var(--agd-fill-strong) 80%, transparent));">
135
+ ${circle(10)}
136
+ ${bar('48%', 3, true)}
137
+ ${bar('18%', 3)}
138
+ </div>`
139
+ : '';
140
+ return `
141
+ <div style="position:relative;inline-size:100%;block-size:100%;">
142
+ <svg width="100%" height="100%" viewBox="0 0 ${Math.max(1, width)} ${Math.max(1, height)}" preserveAspectRatio="none" fill="none">
143
+ <rect x="0.5" y="0.5" width="${Math.max(0, width - 1)}" height="${Math.max(0, height - 1)}" rx="10" stroke="var(--agd-stroke)" stroke-dasharray="6 4" />
144
+ <path d="M0 ${height * 0.78} L${width * 0.32} ${height * 0.44} L${width * 0.54} ${height * 0.62} L${width} ${height * 0.24}" stroke="var(--agd-stroke)" stroke-width="1.4" />
145
+ <circle cx="${width * 0.24}" cy="${height * 0.28}" r="${Math.max(4, Math.min(width, height) * 0.06)}" fill="var(--agd-fill-strong)" stroke="var(--agd-stroke)" />
146
+ </svg>
147
+ ${controls}
148
+ </div>
149
+ `;
150
+ }
151
+ function renderTable(width, height) {
152
+ const columns = clamp(Math.floor(width / 110), 2, 5);
153
+ const rows = clamp(Math.floor(height / 36), 3, 6);
154
+ const header = Array.from({ length: columns }, () => `<div style="flex:1;padding:0 ${cssSize(8)};">${bar('70%', 3, true)}</div>`).join('');
155
+ const body = Array.from({ length: rows }, (_, rowIndex) => `
156
+ <div style="display:flex;border-bottom:1px solid color-mix(in srgb, var(--agd-stroke) 55%, transparent);padding:${cssSize(6)} 0;">
157
+ ${Array.from({ length: columns }, (_, columnIndex) => `<div style="flex:1;padding:0 ${cssSize(8)};">${bar(`${48 + ((rowIndex * 9 + columnIndex * 11) % 35)}%`, 2)}</div>`).join('')}
158
+ </div>
159
+ `).join('');
160
+ return `
161
+ <div style="display:flex;flex-direction:column;inline-size:100%;block-size:100%;padding:${cssSize(clamp(width * 0.03, 8, 14))};">
162
+ <div style="display:flex;border-bottom:1px solid var(--agd-stroke);padding:${cssSize(8)} 0;">${header}</div>
163
+ ${body}
164
+ </div>
165
+ `;
166
+ }
167
+ function renderGrid(width, height, minColumns = 2) {
168
+ const columns = clamp(Math.floor(width / 160), minColumns, 4);
169
+ const rows = clamp(Math.floor(height / 120), 2, 4);
170
+ const cells = Array.from({ length: rows * columns }, () => block('100%', '100%', 8)).join('');
171
+ return `
172
+ <div style="display:grid;grid-template-columns:repeat(${columns}, minmax(0, 1fr));grid-auto-rows:minmax(0, 1fr);gap:${cssSize(clamp(width * 0.02, 8, 18))};inline-size:100%;block-size:100%;padding:${cssSize(clamp(width * 0.04, 10, 16))};">
173
+ ${cells}
174
+ </div>
175
+ `;
176
+ }
177
+ function renderList(height, kind = 'bullet') {
178
+ const items = clamp(Math.floor(height / 34), 3, 7);
179
+ const rows = Array.from({ length: items }, (_, index) => {
180
+ if (kind === 'timeline') {
181
+ return `
182
+ <div style="display:flex;align-items:flex-start;gap:${cssSize(10)};">
183
+ <div style="display:flex;flex-direction:column;align-items:center;gap:${cssSize(2)};">
184
+ ${circle(10)}
185
+ ${index < items - 1 ? `<div style="inline-size:2px;block-size:${cssSize(clamp(height * 0.1, 14, 26))};background:var(--agd-stroke);"></div>` : ''}
186
+ </div>
187
+ <div style="flex:1;display:flex;flex-direction:column;gap:${cssSize(4)};padding-block-start:${cssSize(2)};">
188
+ ${bar('38%', 3, true)}
189
+ ${bar('78%', 2)}
190
+ </div>
191
+ </div>
192
+ `;
193
+ }
194
+ return `
195
+ <div style="display:flex;align-items:${kind === 'faq' ? 'center' : 'flex-start'};gap:${cssSize(8)};">
196
+ ${kind === 'faq' ? block(12, 12, 3) : circle(8)}
197
+ <div style="flex:1;display:flex;flex-direction:column;gap:${cssSize(4)};">
198
+ ${bar(`${kind === 'faq' ? 70 : 58}%`, kind === 'faq' ? 3 : 2, kind === 'faq')}
199
+ ${kind === 'faq' ? '' : bar(`${72 + ((index * 9) % 18)}%`, 2)}
200
+ </div>
201
+ </div>
202
+ `;
203
+ }).join('');
204
+ return `<div style="display:flex;flex-direction:column;gap:${cssSize(8)};inline-size:100%;block-size:100%;padding:${cssSize(10)};">${rows}</div>`;
205
+ }
206
+ function renderTabs(width, height) {
207
+ const count = clamp(Math.floor(width / 120), 3, 5);
208
+ const tabs = Array.from({ length: count }, (_, index) => bar(`${60 + ((index * 17) % 18)}px`, 3, index === 0)).join('');
209
+ return `
210
+ <div style="display:flex;flex-direction:column;inline-size:100%;block-size:100%;">
211
+ <div style="display:flex;align-items:center;gap:${cssSize(14)};padding:${cssSize(10)} ${cssSize(12)};border-bottom:1px solid var(--agd-stroke);">${tabs}</div>
212
+ <div style="flex:1;display:flex;flex-direction:column;gap:${cssSize(6)};padding:${cssSize(12)};">
213
+ ${bar('42%', 4, true)}
214
+ ${bar('92%', 2)}
215
+ ${bar('80%', 2)}
216
+ </div>
217
+ </div>
218
+ `;
219
+ }
220
+ function renderPill(text, rounded = 999) {
221
+ return `
222
+ <div style="display:flex;align-items:center;justify-content:center;inline-size:100%;block-size:100%;padding-inline:${cssSize(10)};border-radius:${cssSize(rounded)};border:1px solid var(--agd-stroke);background:var(--agd-fill);">
223
+ ${text ? `<span style="font-size:${cssSize(12)};font-weight:600;color:var(--agd-text);line-height:1;">${escapeHtml(text)}</span>` : bar('58%', 3, true)}
224
+ </div>
225
+ `;
226
+ }
227
+ function renderField(width, height, withIcon = false) {
228
+ return `
229
+ <div style="display:flex;flex-direction:column;justify-content:center;gap:${cssSize(6)};inline-size:100%;block-size:100%;padding:${cssSize(clamp(width * 0.04, 8, 12))};">
230
+ ${bar(clamp(width * 0.28, 56, 92), 2)}
231
+ <div style="display:flex;align-items:center;gap:${cssSize(8)};block-size:${cssSize(clamp(height * 0.56, 24, 36))};padding-inline:${cssSize(10)};border-radius:${cssSize(6)};border:1px dashed var(--agd-stroke);background:var(--agd-fill);">
232
+ ${withIcon ? circle(12) : ''}
233
+ ${bar(withIcon ? '46%' : '58%', 3, true)}
234
+ </div>
235
+ </div>
236
+ `;
237
+ }
238
+ function renderForm(width, height) {
239
+ const fields = clamp(Math.floor(height / 72), 3, 5);
240
+ const rows = Array.from({ length: fields }, (_, index) => renderField(width, clamp(height * 0.18, 42, 58), index === 0)).join('');
241
+ return `
242
+ <div style="display:flex;flex-direction:column;inline-size:100%;block-size:100%;padding:${cssSize(clamp(width * 0.05, 12, 18))};gap:${cssSize(10)};">
243
+ ${rows}
244
+ <div style="margin-top:auto;display:flex;justify-content:flex-end;gap:${cssSize(8)};">
245
+ ${block(88, 30, 8)}
246
+ </div>
247
+ </div>
248
+ `;
249
+ }
250
+ function renderPricing(width, height) {
251
+ return `
252
+ <div style="display:flex;flex-direction:column;inline-size:100%;block-size:100%;padding:${cssSize(clamp(width * 0.05, 12, 18))};gap:${cssSize(8)};">
253
+ ${bar('40%', 3, true)}
254
+ ${bar('24%', clamp(height * 0.03, 8, 14), true)}
255
+ ${bar('54%', 2)}
256
+ <div style="display:flex;flex-direction:column;gap:${cssSize(6)};margin-block:${cssSize(10)};">
257
+ ${Array.from({ length: 4 }, () => `<div style="display:flex;align-items:center;gap:${cssSize(8)};">${circle(8)}${bar('72%', 2)}</div>`).join('')}
258
+ </div>
259
+ <div style="margin-top:auto;">${block('100%', 34, 8, 'background:var(--agd-bar);')}</div>
260
+ </div>
261
+ `;
262
+ }
263
+ function renderAlert(width, height) {
264
+ return `
265
+ <div style="display:flex;align-items:center;inline-size:100%;block-size:100%;padding:${cssSize(clamp(height * 0.18, 8, 14))};gap:${cssSize(10)};">
266
+ ${circle(clamp(height * 0.28, 14, 20))}
267
+ <div style="display:flex;flex-direction:column;gap:${cssSize(4)};flex:1;">
268
+ ${bar('48%', 3, true)}
269
+ ${bar('78%', 2)}
270
+ </div>
271
+ </div>
272
+ `;
273
+ }
274
+ function renderStat(width, height) {
275
+ return `
276
+ <div style="display:flex;flex-direction:column;justify-content:center;inline-size:100%;block-size:100%;padding:${cssSize(clamp(width * 0.08, 12, 18))};gap:${cssSize(8)};">
277
+ ${bar('34%', 2)}
278
+ ${bar('44%', clamp(height * 0.08, 12, 20), true)}
279
+ ${bar('60%', 2)}
280
+ </div>
281
+ `;
282
+ }
283
+ function renderCalendar(width, height) {
284
+ const rows = 5;
285
+ const cells = Array.from({ length: rows * 7 }, () => block('100%', '100%', 4)).join('');
286
+ return `
287
+ <div style="display:flex;flex-direction:column;inline-size:100%;block-size:100%;padding:${cssSize(10)};gap:${cssSize(8)};">
288
+ <div style="display:flex;align-items:center;justify-content:space-between;">${bar('30%', 3, true)}${bar('18%', 2)}</div>
289
+ <div style="display:grid;grid-template-columns:repeat(7, minmax(0, 1fr));grid-auto-rows:minmax(0, 1fr);gap:${cssSize(6)};flex:1;">
290
+ ${cells}
291
+ </div>
292
+ </div>
293
+ `;
294
+ }
295
+ function renderFileUpload(width, height) {
296
+ return `
297
+ <div style="display:flex;align-items:center;justify-content:center;inline-size:100%;block-size:100%;padding:${cssSize(12)};">
298
+ <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:${cssSize(8)};inline-size:100%;block-size:100%;border:1.5px dashed var(--agd-stroke);border-radius:${cssSize(10)};background:color-mix(in srgb, var(--agd-fill) 70%, transparent);">
299
+ ${block(20, 20, 6)}
300
+ ${bar('42%', 3, true)}
301
+ ${bar('26%', 2)}
302
+ </div>
303
+ </div>
304
+ `;
305
+ }
306
+ function renderMap(width, height) {
307
+ return `
308
+ <div style="position:relative;inline-size:100%;block-size:100%;background:
309
+ linear-gradient(90deg, color-mix(in srgb, var(--agd-stroke) 28%, transparent) 1px, transparent 1px),
310
+ linear-gradient(color-mix(in srgb, var(--agd-stroke) 28%, transparent) 1px, transparent 1px),
311
+ var(--agd-fill);
312
+ background-size:${cssSize(clamp(width * 0.08, 18, 36))} ${cssSize(clamp(height * 0.12, 18, 36))};
313
+ ">
314
+ <div style="position:absolute;inset-inline-start:${cssSize(width * 0.26)};inset-block-start:${cssSize(height * 0.22)};inline-size:${cssSize(clamp(width * 0.1, 14, 24))};block-size:${cssSize(clamp(width * 0.1, 14, 24))};border-radius:999px;background:var(--agd-fill-strong);border:1px solid var(--agd-stroke);"></div>
315
+ <div style="position:absolute;inset-inline-start:${cssSize(width * 0.56)};inset-block-start:${cssSize(height * 0.48)};inline-size:${cssSize(clamp(width * 0.12, 16, 26))};block-size:${cssSize(clamp(width * 0.12, 16, 26))};border-radius:999px;background:var(--agd-bar-strong);border:1px solid var(--agd-stroke);"></div>
316
+ </div>
317
+ `;
318
+ }
319
+ function renderRange(width, height, withThumbs = 1) {
320
+ const thumbSize = clamp(height * 0.35, 10, 14);
321
+ return `
322
+ <div style="display:flex;align-items:center;inline-size:100%;block-size:100%;padding-inline:${cssSize(clamp(width * 0.06, 10, 16))};">
323
+ <div style="position:relative;inline-size:100%;block-size:${cssSize(Math.max(4, height * 0.16))};border-radius:999px;background:var(--agd-fill-strong);">
324
+ ${Array.from({ length: withThumbs }, (_, index) => `<div style="position:absolute;inset-block-start:50%;inset-inline-start:${index === 0 ? '28%' : '70%'};inline-size:${cssSize(thumbSize)};block-size:${cssSize(thumbSize)};border-radius:999px;background:var(--agd-bar-strong);border:1px solid var(--agd-stroke);transform:translate(-50%, -50%);"></div>`).join('')}
325
+ </div>
326
+ </div>
327
+ `;
328
+ }
329
+ function renderBoolean(kind) {
330
+ if (kind === 'toggle') {
331
+ return `
332
+ <div style="display:flex;align-items:center;justify-content:center;inline-size:100%;block-size:100%;">
333
+ <div style="position:relative;inline-size:100%;block-size:100%;border-radius:999px;background:var(--agd-fill-strong);border:1px solid var(--agd-stroke);">
334
+ <div style="position:absolute;inset-block-start:50%;inset-inline-end:2px;inline-size:calc(50% - 2px);block-size:calc(100% - 4px);border-radius:999px;background:#fff;transform:translateY(-50%);"></div>
335
+ </div>
336
+ </div>
337
+ `;
338
+ }
339
+ return `
340
+ <div style="display:flex;align-items:center;justify-content:center;inline-size:100%;block-size:100%;">
341
+ <div style="inline-size:100%;block-size:100%;border-radius:${kind === 'radio' ? '999px' : cssSize(5)};border:1px solid var(--agd-stroke);background:var(--agd-fill);"></div>
342
+ </div>
343
+ `;
344
+ }
345
+ function renderSpinner(width, height) {
346
+ const size = clamp(Math.min(width, height) * 0.72, 14, 28);
347
+ return `
348
+ <div style="display:flex;align-items:center;justify-content:center;inline-size:100%;block-size:100%;">
349
+ <div style="inline-size:${cssSize(size)};block-size:${cssSize(size)};border-radius:999px;border:${cssSize(Math.max(2, size * 0.12))} solid color-mix(in srgb, var(--agd-fill-strong) 85%, transparent);border-top-color:var(--agd-bar-strong);"></div>
350
+ </div>
351
+ `;
352
+ }
353
+ function renderDivider(width, height) {
354
+ return `<div style="display:flex;align-items:center;inline-size:100%;block-size:100%;">${block('100%', Math.max(1, height), 999, 'border:none;background:var(--agd-stroke);')}</div>`;
355
+ }
356
+ function renderDatePicker(width, height) {
357
+ return `
358
+ <div style="display:flex;flex-direction:column;inline-size:100%;block-size:100%;padding:${cssSize(10)};gap:${cssSize(8)};">
359
+ ${renderField(width, 44, false)}
360
+ <div style="display:grid;grid-template-columns:repeat(7, minmax(0, 1fr));grid-auto-rows:minmax(0, 1fr);gap:${cssSize(4)};flex:1;">
361
+ ${Array.from({ length: 28 }, () => block('100%', '100%', 4)).join('')}
362
+ </div>
363
+ </div>
364
+ `;
365
+ }
366
+ function renderDropdown(width, height) {
367
+ const rows = Array.from({ length: clamp(Math.floor(height / 34), 4, 6) }, () => bar('72%', 2)).join('');
368
+ return `
369
+ <div style="display:flex;flex-direction:column;inline-size:100%;block-size:100%;padding:${cssSize(10)};gap:${cssSize(10)};">
370
+ <div style="display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--agd-stroke);padding-bottom:${cssSize(6)};">
371
+ ${bar('42%', 3, true)}
372
+ ${bar('12%', 2)}
373
+ </div>
374
+ <div style="display:flex;flex-direction:column;gap:${cssSize(8)};">${rows}</div>
375
+ </div>
376
+ `;
377
+ }
378
+ function renderRating(width) {
379
+ const stars = Array.from({ length: 5 }, () => block(clamp(width * 0.12, 14, 20), clamp(width * 0.12, 14, 20), 4)).join('');
380
+ return `<div style="display:flex;align-items:center;justify-content:center;gap:${cssSize(6)};inline-size:100%;block-size:100%;">${stars}</div>`;
381
+ }
382
+ function renderLogo(width, height) {
383
+ return `
384
+ <div style="display:flex;align-items:center;justify-content:center;inline-size:100%;block-size:100%;gap:${cssSize(8)};">
385
+ ${block(clamp(height * 0.56, 14, 22), clamp(height * 0.56, 14, 22), 4)}
386
+ ${bar(clamp(width * 0.42, 28, 56), 4, true)}
387
+ </div>
388
+ `;
389
+ }
390
+ function renderChart(width, height) {
391
+ const columns = 5;
392
+ const bars = Array.from({ length: columns }, (_, index) => `<div style="flex:1;display:flex;align-items:flex-end;">${block('70%', `${38 + ((index * 17) % 42)}%`, 4, 'align-self:flex-end;')}</div>`).join('');
393
+ return `
394
+ <div style="display:flex;flex-direction:column;inline-size:100%;block-size:100%;padding:${cssSize(12)};gap:${cssSize(8)};">
395
+ ${bar('32%', 3, true)}
396
+ <div style="display:flex;align-items:flex-end;gap:${cssSize(8)};flex:1;">${bars}</div>
397
+ </div>
398
+ `;
399
+ }
400
+ function renderButton(width, height, text) {
401
+ return `
402
+ <div style="display:flex;align-items:center;justify-content:center;inline-size:100%;block-size:100%;border-radius:${cssSize(clamp(height / 3, 8, 14))};border:1px solid var(--agd-stroke);background:var(--agd-fill);">
403
+ ${text ? `<span style="font-size:${cssSize(clamp(height * 0.36, 11, 14))};font-weight:600;color:var(--agd-text);line-height:1;">${escapeHtml(text)}</span>` : bar(clamp(width * 0.46, 26, 64), 3, true)}
404
+ </div>
405
+ `;
406
+ }
407
+ function renderPlacementBody({ type, width, height, text }) {
408
+ switch (type) {
409
+ case 'navigation':
410
+ case 'header':
411
+ return renderNavigation(width, height);
412
+ case 'hero':
413
+ return renderHero(width, height, text, 1);
414
+ case 'cta':
415
+ return renderHero(width, height, text, 2);
416
+ case 'sidebar':
417
+ return renderSidebar(width, height);
418
+ case 'footer':
419
+ return renderFooter(width, height);
420
+ case 'modal':
421
+ return renderPanel(width, height, true);
422
+ case 'drawer':
423
+ case 'popover':
424
+ return renderPanel(width, height, false);
425
+ case 'card':
426
+ case 'testimonial':
427
+ case 'feature':
428
+ return renderCard(width, height, text, 0.34);
429
+ case 'productCard':
430
+ return renderCard(width, height, text, 0.46);
431
+ case 'profile':
432
+ return `
433
+ <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;inline-size:100%;block-size:100%;padding:${cssSize(14)};gap:${cssSize(10)};">
434
+ ${circle(clamp(Math.min(width, height) * 0.24, 24, 48))}
435
+ ${text ? `<span style="font-size:${cssSize(14)};font-weight:600;color:var(--agd-text);">${escapeHtml(text)}</span>` : bar('40%', 4, true)}
436
+ ${bar('64%', 2)}
437
+ ${bar('52%', 2)}
438
+ </div>
439
+ `;
440
+ case 'pricing':
441
+ return renderPricing(width, height);
442
+ case 'button':
443
+ return renderButton(width, height, text);
444
+ case 'badge':
445
+ case 'tag':
446
+ case 'chip':
447
+ return renderPill(text, 999);
448
+ case 'input':
449
+ return renderField(width, height, false);
450
+ case 'search':
451
+ return renderField(width, height, true);
452
+ case 'form':
453
+ case 'login':
454
+ case 'contact':
455
+ return renderForm(width, height);
456
+ case 'text':
457
+ case 'codeBlock':
458
+ return renderText(width, height, text);
459
+ case 'list':
460
+ return renderList(height);
461
+ case 'faq':
462
+ return renderList(height, 'faq');
463
+ case 'timeline':
464
+ return renderList(height, 'timeline');
465
+ case 'image':
466
+ case 'gallery':
467
+ case 'map':
468
+ return type === 'map' ? renderMap(width, height) : type === 'gallery' ? renderGrid(width, height, 3) : renderMedia(width, height, false);
469
+ case 'video':
470
+ case 'carousel':
471
+ return renderMedia(width, height, true);
472
+ case 'table':
473
+ return renderTable(width, height);
474
+ case 'calendar':
475
+ return renderCalendar(width, height);
476
+ case 'grid':
477
+ case 'section':
478
+ case 'team':
479
+ return renderGrid(width, height);
480
+ case 'tabs':
481
+ return renderTabs(width, height);
482
+ case 'dropdown':
483
+ return renderDropdown(width, height);
484
+ case 'breadcrumb':
485
+ return `<div style="display:flex;align-items:center;inline-size:100%;block-size:100%;gap:${cssSize(8)};padding-inline:${cssSize(8)};">${bar('18%', 2, true)}${bar('6%', 2)}${bar('22%', 2)}${bar('6%', 2)}${bar('20%', 2)}</div>`;
486
+ case 'pagination':
487
+ return `<div style="display:flex;align-items:center;justify-content:center;gap:${cssSize(8)};inline-size:100%;block-size:100%;">${Array.from({ length: 5 }, (_, index) => block(clamp(height * 0.9, 18, 28), clamp(height * 0.9, 18, 28), 6, index === 2 ? 'background:var(--agd-bar-strong);' : '')).join('')}</div>`;
488
+ case 'progress':
489
+ return renderRange(width, height, 0);
490
+ case 'slider':
491
+ return renderRange(width, height, 1);
492
+ case 'stepper':
493
+ return `<div style="display:flex;align-items:center;justify-content:center;gap:${cssSize(8)};inline-size:100%;block-size:100%;">${Array.from({ length: 4 }, (_, index) => `<div style="display:flex;align-items:center;gap:${cssSize(8)};">${circle(14)}${index < 3 ? bar(40, 2) : ''}</div>`).join('')}</div>`;
494
+ case 'toggle':
495
+ return renderBoolean('toggle');
496
+ case 'checkbox':
497
+ return renderBoolean('checkbox');
498
+ case 'radio':
499
+ return renderBoolean('radio');
500
+ case 'avatar':
501
+ return `<div style="display:flex;align-items:center;justify-content:center;inline-size:100%;block-size:100%;">${circle(clamp(Math.min(width, height) * 0.88, 18, 44))}</div>`;
502
+ case 'tooltip':
503
+ return renderAlert(width, height);
504
+ case 'toast':
505
+ case 'notification':
506
+ case 'alert':
507
+ case 'banner':
508
+ return renderAlert(width, height);
509
+ case 'stat':
510
+ return renderStat(width, height);
511
+ case 'rating':
512
+ return renderRating(width);
513
+ case 'fileUpload':
514
+ return renderFileUpload(width, height);
515
+ case 'datePicker':
516
+ return renderDatePicker(width, height);
517
+ case 'skeleton':
518
+ return renderText(width, height);
519
+ case 'icon':
520
+ return `<div style="display:flex;align-items:center;justify-content:center;inline-size:100%;block-size:100%;">${block(clamp(Math.min(width, height) * 0.78, 12, 22), clamp(Math.min(width, height) * 0.78, 12, 22), 4)}</div>`;
521
+ case 'spinner':
522
+ return renderSpinner(width, height);
523
+ case 'logo':
524
+ return renderLogo(width, height);
525
+ case 'chart':
526
+ return renderChart(width, height);
527
+ case 'divider':
528
+ return renderDivider(width, height);
529
+ default:
530
+ return renderCard(width, height, text, 0.38);
531
+ }
532
+ }
533
+ export function renderPlacementSkeleton(options) {
534
+ return wrap(options.type, renderPlacementBody(options));
535
+ }