agentcss 0.16.1 → 0.24.1

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 (93) hide show
  1. package/01-tokens/focus-ring-demo.html +1 -1
  2. package/01-tokens/page.css +32 -0
  3. package/01-tokens/responsive-demo.html +1 -1
  4. package/01-tokens/scrollbar-demo.html +1 -1
  5. package/02-primitives/badge/demo.html +1 -1
  6. package/02-primitives/button/demo.html +1 -1
  7. package/02-primitives/card/demo.html +1 -1
  8. package/02-primitives/choice/choice.css +31 -2
  9. package/02-primitives/choice/demo.html +1 -1
  10. package/02-primitives/combobox/demo.html +1 -1
  11. package/02-primitives/date-range/demo.html +1 -1
  12. package/02-primitives/delta/demo.html +1 -1
  13. package/02-primitives/input/demo.html +1 -1
  14. package/02-primitives/input/input.css +14 -0
  15. package/02-primitives/metric/demo.html +1 -1
  16. package/02-primitives/popover/demo.html +1 -1
  17. package/02-primitives/progress/demo.html +1 -1
  18. package/02-primitives/progress/progress.css +56 -0
  19. package/02-primitives/select/demo.html +1 -1
  20. package/02-primitives/separator/demo.html +1 -1
  21. package/02-primitives/skeleton/demo.html +1 -1
  22. package/02-primitives/skeleton/skeleton.css +18 -0
  23. package/02-primitives/switch/demo.html +1 -1
  24. package/02-primitives/switch/switch.css +61 -2
  25. package/02-primitives/timeline/README.md +70 -0
  26. package/02-primitives/timeline/demo.html +161 -0
  27. package/02-primitives/timeline/timeline.css +152 -0
  28. package/02-primitives/upload/README.md +142 -0
  29. package/02-primitives/upload/demo.html +318 -0
  30. package/02-primitives/upload/upload.css +490 -0
  31. package/02-primitives/upload/upload.js +676 -0
  32. package/03-patterns/accordion/demo.html +1 -1
  33. package/03-patterns/content/demo.html +1 -1
  34. package/03-patterns/drawer/demo.html +1 -1
  35. package/03-patterns/drawer/drawer.js +30 -5
  36. package/03-patterns/dropdown/demo.html +1 -1
  37. package/03-patterns/form-validation/demo.html +1 -1
  38. package/03-patterns/list/demo.html +1 -1
  39. package/03-patterns/nav/demo.html +1 -1
  40. package/03-patterns/overlay/demo.html +1 -1
  41. package/03-patterns/pagination/demo.html +1 -1
  42. package/03-patterns/states/demo.html +1 -1
  43. package/03-patterns/tabs/demo.html +1 -1
  44. package/03-patterns/tooltip/demo.html +1 -1
  45. package/03-patterns/tree/demo.html +1 -1
  46. package/04-recipes/app-shell/README.md +272 -0
  47. package/04-recipes/app-shell/app-shell.css +588 -0
  48. package/04-recipes/app-shell/demo.html +326 -0
  49. package/04-recipes/app-shell/shell-nav.js +288 -0
  50. package/04-recipes/table/table.css +22 -0
  51. package/09-assets/bar/demo.html +1 -1
  52. package/09-assets/echarts-adapter/demo.html +1 -1
  53. package/09-assets/model-viewer/demo.html +1 -1
  54. package/09-assets/sparkline/demo.html +1 -1
  55. package/AGENTS.md +8 -0
  56. package/API.md +4 -0
  57. package/CHANGELOG.md +795 -0
  58. package/CONTRIBUTING.md +22 -1
  59. package/README.md +17 -5
  60. package/README.zh-CN.md +16 -6
  61. package/START-HERE.md +12 -7
  62. package/ai/START-HERE.md +37 -3
  63. package/ai/behaviors.json +32 -0
  64. package/ai/cli.js +183 -25
  65. package/ai/components.json +185 -7
  66. package/ai/components.meta.json +13 -0
  67. package/ai/pitfalls-writing.json +119 -0
  68. package/ai/pitfalls.json +193 -0
  69. package/dist/01-tokens/page.css +1 -1
  70. package/dist/02-primitives/choice/choice.css +1 -1
  71. package/dist/02-primitives/input/input.css +1 -1
  72. package/dist/02-primitives/progress/progress.css +1 -1
  73. package/dist/02-primitives/skeleton/skeleton.css +1 -1
  74. package/dist/02-primitives/switch/switch.css +1 -1
  75. package/dist/02-primitives/timeline/timeline.css +1 -0
  76. package/dist/02-primitives/upload/upload.css +1 -0
  77. package/dist/02-primitives/upload/upload.js +515 -0
  78. package/dist/03-patterns/drawer/drawer.js +14 -4
  79. package/dist/04-recipes/app-shell/app-shell.css +1 -0
  80. package/dist/04-recipes/app-shell/shell-nav.js +173 -0
  81. package/dist/04-recipes/table/table.css +1 -1
  82. package/dist/COSTS.md +12 -10
  83. package/docs/ACCESSIBILITY.md +20 -7
  84. package/docs/AI-CONTRACT.md +2 -0
  85. package/docs/BUDGET.md +179 -0
  86. package/docs/CONVENTIONS.md +3 -0
  87. package/docs/STABILITY.md +126 -0
  88. package/esm/index.mjs +2 -0
  89. package/esm/upload.mjs +7 -0
  90. package/index.html +5 -2
  91. package/llms-full.txt +153 -0
  92. package/llms.txt +55 -0
  93. package/package.json +4 -1
@@ -0,0 +1,318 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <link rel="icon" href="data:,">
7
+ <title>文件上传 · upload</title>
8
+ <link rel="stylesheet" href="../../01-tokens/tokens.css">
9
+ <link rel="stylesheet" href="../../01-tokens/typography.css">
10
+ <link rel="stylesheet" href="../../01-tokens/page.css">
11
+ <link rel="stylesheet" href="../input/input.css">
12
+ <link rel="stylesheet" href="../progress/progress.css">
13
+ <link rel="stylesheet" href="upload.css">
14
+ <script src="upload.js"></script>
15
+ <style>
16
+ .demo {
17
+ background: var(--surface);
18
+ border: 1px solid var(--border-decor);
19
+ border-radius: var(--r-md);
20
+ padding: var(--sp-4);
21
+ margin-bottom: var(--sp-3);
22
+ }
23
+ .demo + .note { margin-top: 0; }
24
+ /* 演示用:窄槽 */
25
+ .cq-narrow {
26
+ max-width: 240px;
27
+ padding: var(--sp-3);
28
+ border: 1px dashed var(--border-decor);
29
+ border-radius: var(--r-md);
30
+ }
31
+ /* 演示用:把"上传中"的进度定住,视觉基线才不会拍到动画中间帧 */
32
+ .frozen { margin-top: var(--sp-2); }
33
+ </style>
34
+ </head>
35
+ <body class="page">
36
+ <main class="wrap">
37
+
38
+ <h1>文件上传 · upload</h1>
39
+ <p class="lede">上传是「导入 CSV / 附一份财报 / 传凭证」的必经入口。这个组件的核心不在拖放区好看,
40
+ 而在于:<b>JS 没加载时它仍然是能用的</b>,以及文件有没有真的进到表单里。</p>
41
+
42
+ <h2>1 · 基础形态:零 JS 也能用</h2>
43
+ <div class="demo">
44
+ <div class="field">
45
+ <label class="field__label" for="u-basic">导入对账单</label>
46
+ <input class="upload__input" type="file" id="u-basic" name="statement"
47
+ accept=".csv,.xlsx" aria-describedby="u-basic-hint">
48
+ <p class="field__hint" id="u-basic-hint">支持 CSV / XLSX,单个不超过 20 MB</p>
49
+ </div>
50
+ </div>
51
+ <p class="note">这一块<b>没有任何 JS</b>:浏览器原生的"选择文件"按钮经
52
+ <code>::file-selector-button</code> 美化,选完立刻在按钮右侧回显文件名 ——
53
+ 不需要脚本读 <code>files</code> 再写进 DOM。表单提交、<code>FormData</code>、
54
+ <code>accept</code> 过滤全走原生路径。</p>
55
+
56
+ <h2>2 · 增强形态:拖放区 + 文件列表</h2>
57
+ <div class="demo">
58
+ <div class="field">
59
+ <label class="field__label" for="u-drop">上传审计底稿</label>
60
+ <div class="upload upload--drop" id="u-drop-wrap" data-upload data-max-size="20971520">
61
+ <input class="upload__input" type="file" id="u-drop" name="workpapers" multiple
62
+ accept=".pdf,.xlsx,.csv" aria-describedby="u-drop-hint">
63
+ <label class="upload__dropzone" for="u-drop">
64
+ <svg class="upload__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor"
65
+ stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
66
+ <path d="M8 11V2.6M4.4 6.2L8 2.6l3.6 3.6M2.6 11.4v1.2a.8.8 0 0 0 .8.8h9.2a.8.8 0 0 0 .8-.8v-1.2"/>
67
+ </svg>
68
+ <span class="upload__title">拖入文件,或 <span class="upload__browse">选择文件</span></span>
69
+ <span class="upload__hint">PDF / XLSX / CSV,单个不超过 20 MB</span>
70
+ </label>
71
+ <ul class="upload__list"></ul>
72
+ <span class="upload__status" role="status" aria-live="polite"></span>
73
+ </div>
74
+ <p class="field__hint" id="u-drop-hint">可以一次选多个;再次拖入会<b>替换</b>整个列表</p>
75
+ </div>
76
+ </div>
77
+ <p class="note">拖放区是一个 <code>&lt;label for&gt;</code>:点它 = 原生打开选择器,
78
+ 这一步不依赖 JS。脚本只负责三件事 —— 把拖入的 <code>File</code> 塞进
79
+ <code>input.files</code>、重画列表、派发 <code>fl-upload</code>。
80
+ 所以 <code>&lt;form&gt;</code> 拿到的永远是真实文件,不是"组件自己记的一份清单"。</p>
81
+
82
+ <h2>3 · 上传中:进度既是图形也是文字</h2>
83
+ <div class="demo">
84
+ <ul class="upload__list">
85
+ <li class="upload__item" data-state="pending">
86
+ <span class="upload__mark">
87
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M8 1.7a6.3 6.3 0 1 0 0 12.6A6.3 6.3 0 0 0 8 1.7zM8 4.6V8l2.3 1.5"/></svg>
88
+ <span class="upload__status">待上传</span>
89
+ </span>
90
+ <span class="upload__name">2025年度审计报告.pdf</span>
91
+ <span class="upload__meta num">上传中 42%</span>
92
+ <span class="upload__progress">
93
+ <span class="progress"><span class="progress__bar" style="--progress:42%"></span></span>
94
+ </span>
95
+ <button class="upload__remove" type="button" aria-label="移除 2025年度审计报告.pdf">
96
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M4.2 4.2l7.6 7.6M11.8 4.2l-7.6 7.6"/></svg>
97
+ </button>
98
+ </li>
99
+ <li class="upload__item" data-state="done">
100
+ <span class="upload__mark">
101
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3.3 8.4l3.1 3.1 6.3-6.9"/></svg>
102
+ <span class="upload__status">已完成</span>
103
+ </span>
104
+ <span class="upload__name">应付账款明细.xlsx</span>
105
+ <span class="upload__meta num">2.4 MB</span>
106
+ <button class="upload__remove" type="button" aria-label="移除 应付账款明细.xlsx">
107
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M4.2 4.2l7.6 7.6M11.8 4.2l-7.6 7.6"/></svg>
108
+ </button>
109
+ </li>
110
+ <li class="upload__item" data-state="error">
111
+ <span class="upload__mark">
112
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M8 1.7a6.3 6.3 0 1 0 0 12.6A6.3 6.3 0 0 0 8 1.7zM8 4.9v3.4M8 11.1v.5"/></svg>
113
+ <span class="upload__status">失败</span>
114
+ </span>
115
+ <span class="upload__name">往来余额扫描件.pdf</span>
116
+ <span class="upload__meta num">超过 20 MB</span>
117
+ </li>
118
+ </ul>
119
+ </div>
120
+ <p class="note">三个状态的差别<b>不只是颜色</b>:左侧图形(空心圆 / 勾 / 叹号)、
121
+ 中间文件名的灰度、以及右侧那行文字("上传中 42%" / "2.4 MB" / "超过 20 MB")。
122
+ 把这一块截图转成灰度打印,信息一条都不丢。</p>
123
+
124
+ <h2>4 · 校验拒绝:一个都没收进来</h2>
125
+ <div class="demo">
126
+ <div class="field">
127
+ <label class="field__label" for="u-reject">上传凭证</label>
128
+ <div class="upload upload--drop" id="u-reject-wrap" data-upload
129
+ data-max-size="20971520" data-max-files="3">
130
+ <input class="upload__input" type="file" id="u-reject" name="proofs" multiple
131
+ accept=".pdf,.jpg,.png" aria-describedby="u-reject-hint">
132
+ <label class="upload__dropzone" for="u-reject">
133
+ <svg class="upload__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor"
134
+ stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
135
+ <path d="M8 11V2.6M4.4 6.2L8 2.6l3.6 3.6M2.6 11.4v1.2a.8.8 0 0 0 .8.8h9.2a.8.8 0 0 0 .8-.8v-1.2"/>
136
+ </svg>
137
+ <span class="upload__title">拖入文件,或 <span class="upload__browse">选择文件</span></span>
138
+ <span class="upload__hint">PDF / JPG / PNG,单个不超过 20 MB,最多 3 个</span>
139
+ </label>
140
+ <ul class="upload__list"></ul>
141
+ <span class="upload__status" role="status" aria-live="polite"></span>
142
+ </div>
143
+ <p class="field__hint" id="u-reject-hint">下面这一组是脚本真实拖入 3 个不合规文件的结果</p>
144
+ </div>
145
+ </div>
146
+ <p class="note">被拒的文件<b>不会进入 <code>input.files</code></b> ——
147
+ 它们根本没被接收,所以提交时不会悄悄带上一个 30 MB 的扫描件。
148
+ 拒绝原因写在文件名的位置,不是只把边框变红。</p>
149
+
150
+ <h2>5 · 紧凑档(侧栏 / 抽屉)</h2>
151
+ <div class="demo">
152
+ <div class="upload upload--drop upload--compact" data-upload>
153
+ <input class="upload__input" type="file" id="u-compact" name="att" multiple>
154
+ <label class="upload__dropzone" for="u-compact" aria-label="添加附件">
155
+ <svg class="upload__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor"
156
+ stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
157
+ <path d="M8 11V2.6M4.4 6.2L8 2.6l3.6 3.6M2.6 11.4v1.2a.8.8 0 0 0 .8.8h9.2a.8.8 0 0 0 .8-.8v-1.2"/>
158
+ </svg>
159
+ <span class="upload__title">添加附件</span>
160
+ </label>
161
+ <ul class="upload__list"></ul>
162
+ <span class="upload__status" role="status" aria-live="polite"></span>
163
+ </div>
164
+ </div>
165
+
166
+ <h2>6 · 槽宽适配(看槽宽,不看视口)</h2>
167
+ <div class="demo">
168
+ <div class="cq-narrow" data-cq>
169
+ <div class="upload upload--drop" data-upload>
170
+ <input class="upload__input" type="file" id="u-cq" name="cq" multiple>
171
+ <label class="upload__dropzone" for="u-cq" aria-label="窄槽里的上传">
172
+ <svg class="upload__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor"
173
+ stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
174
+ <path d="M8 11V2.6M4.4 6.2L8 2.6l3.6 3.6M2.6 11.4v1.2a.8.8 0 0 0 .8.8h9.2a.8.8 0 0 0 .8-.8v-1.2"/>
175
+ </svg>
176
+ <span class="upload__title">拖入文件,或 <span class="upload__browse">选择文件</span></span>
177
+ <span class="upload__hint">PDF / XLSX</span>
178
+ </label>
179
+ <ul class="upload__list"></ul>
180
+ <span class="upload__status" role="status" aria-live="polite"></span>
181
+ </div>
182
+ </div>
183
+ </div>
184
+ <p class="note">左侧这块是 240px 宽的槽(外层加了 <code>data-cq</code>)。
185
+ 它比右边窄,但没有"响应式跳跃":内边距与图标各降一档,限制说明<b>没有</b>被藏起来 ——
186
+ 藏掉它,用户就不知道该选什么文件了。</p>
187
+
188
+ <h2>7 · 禁用</h2>
189
+ <div class="demo">
190
+ <div class="field">
191
+ <label class="field__label" for="u-dis">上传补充材料</label>
192
+ <div class="upload upload--drop" data-upload>
193
+ <input class="upload__input" type="file" id="u-dis" name="extra" disabled>
194
+ <label class="upload__dropzone" for="u-dis">
195
+ <svg class="upload__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor"
196
+ stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
197
+ <path d="M8 11V2.6M4.4 6.2L8 2.6l3.6 3.6M2.6 11.4v1.2a.8.8 0 0 0 .8.8h9.2a.8.8 0 0 0 .8-.8v-1.2"/>
198
+ </svg>
199
+ <span class="upload__title">拖入文件,或 <span class="upload__browse">选择文件</span></span>
200
+ </label>
201
+ <ul class="upload__list"></ul>
202
+ </div>
203
+ <p class="field__hint">先在"基础信息"里选好主体类型,这里才能上传</p>
204
+ </div>
205
+ </div>
206
+ <p class="note">禁用不用 <code>opacity</code>:半透明会让文字对比度随底色漂移,
207
+ 暗色下直接不达标。这里换的是<b>明确色值</b>,边框同时从虚线变实线 ——
208
+ 不用颜色也能看出"这一档不一样"。</p>
209
+
210
+ <h2 class="rule-bottom">对比:两种"拖放区"</h2>
211
+ <div class="cmp">
212
+ <div class="cmp__pane cmp__bad" data-bad=".upload__input|focus|=0">
213
+ <h3>❌ 常见写法:拖放区是唯一入口</h3>
214
+ <div class="upload upload--drop">
215
+ <input class="upload__input" type="file" id="u-bad" aria-label="上传文件(反例)" style="display:none">
216
+ <label class="upload__dropzone" for="u-bad">
217
+ <svg class="upload__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor"
218
+ stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
219
+ <path d="M8 11V2.6M4.4 6.2L8 2.6l3.6 3.6M2.6 11.4v1.2a.8.8 0 0 0 .8.8h9.2a.8.8 0 0 0 .8-.8v-1.2"/>
220
+ </svg>
221
+ <span class="upload__title">拖入文件,或 <span class="upload__browse">选择文件</span></span>
222
+ </label>
223
+ </div>
224
+ <p class="cmp__why">把原生 <code>&lt;input&gt;</code> 设成 <code>display:none</code> 图省事 —— 两栏<b>看起来一模一样</b>,差别在键盘:
225
+ 后果是键盘 <kbd>Tab</kbd> 永远聚焦不到它(不可聚焦元素进不了 Tab 序列),
226
+ 读屏也拿不到可访问名字 —— 鼠标用户能用,键盘用户<b>完全无法上传</b>。</p>
227
+ </div>
228
+ <div class="cmp__pane cmp__good" data-good=".upload__input|focus|=1">
229
+ <h3>✅ 本库:隐藏的是像素,不是控件</h3>
230
+ <div class="upload upload--drop">
231
+ <input class="upload__input" type="file" id="u-good" aria-label="上传文件">
232
+ <label class="upload__dropzone" for="u-good">
233
+ <svg class="upload__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor"
234
+ stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
235
+ <path d="M8 11V2.6M4.4 6.2L8 2.6l3.6 3.6M2.6 11.4v1.2a.8.8 0 0 0 .8.8h9.2a.8.8 0 0 0 .8-.8v-1.2"/>
236
+ </svg>
237
+ <span class="upload__title">拖入文件,或 <span class="upload__browse">选择文件</span></span>
238
+ </label>
239
+ </div>
240
+ <p class="cmp__why">同样看不见原生控件,但它只是 1px + <code>opacity:0</code>:
241
+ 仍在 Tab 序列里、仍在表单里、仍能被 <code>label</code> 触发。
242
+ 聚焦时焦点环画在拖放区上,用户看到的是"这块被选中了"。</p>
243
+ </div>
244
+ </div>
245
+
246
+ <script>
247
+ /* ============================================================================
248
+ demo 脚本:只做两件事 —— 造出"真的文件",以及冻结进度
249
+ ============================================================================
250
+ ⚠️ 不写动画。视觉基线是逐像素比对的,一个 setInterval 就能让每次截图都不一样,
251
+ 然后"基线过期"会被误读成"组件改坏了"。
252
+ ========================================================================== */
253
+ (function () {
254
+ 'use strict';
255
+
256
+ /* 1 字节的真实 File + 覆盖 size。
257
+ ⚠️ 不要为了"看起来真实"去 new Uint8Array(30MB):那是真分配内存,
258
+ demo 页会多花几十毫秒,也会让无头浏览器在低配 CI 上偶发慢加载。
259
+ 而 size 只是 `_validate` 读的一个数字 —— 覆盖它不影响任何被验证的逻辑。 */
260
+ function fakeFile(name, size, type) {
261
+ var f = new File([new Uint8Array(1)], name, { type: type || '' });
262
+ try { Object.defineProperty(f, 'size', { value: size }); } catch (e) { /* 老引擎不支持就算了 */ }
263
+ return f;
264
+ }
265
+
266
+ function seed(rootId, list) {
267
+ var root = document.getElementById(rootId);
268
+ var inst = root && root.uploadInstance;
269
+ if (!inst) return;
270
+ var files = [];
271
+ for (var i = 0; i < list.length; i++) {
272
+ files.push(fakeFile(list[i][0], list[i][1], list[i][2]));
273
+ }
274
+ inst.add(files, 0);
275
+ }
276
+
277
+ function run() {
278
+ /* 2 · 增强形态:预置两个已经收下的文件 */
279
+ seed('u-drop-wrap', [
280
+ ['应付账款明细.xlsx', 2457600, 'application/vnd.ms-excel'],
281
+ ['银行存款余额调节表.pdf', 962560, 'application/pdf']
282
+ ]);
283
+
284
+ /* 4 · 校验拒绝:五个各自触发一条规则(类型 / 大小 / 数量) */
285
+ var rejRoot = document.getElementById('u-reject-wrap');
286
+ if (rejRoot && rejRoot.uploadInstance) {
287
+ rejRoot.uploadInstance.add([
288
+ fakeFile('会议纪要.docx', 120000, 'application/msword'), /* 类型不符 */
289
+ fakeFile('往来余额扫描件.pdf', 30 * 1024 * 1024, 'application/pdf'), /* 超过 20 MB */
290
+ fakeFile('a.pdf', 1024, 'application/pdf'),
291
+ fakeFile('b.pdf', 2048, 'application/pdf'),
292
+ fakeFile('c.pdf', 4096, 'application/pdf'),
293
+ fakeFile('d.pdf', 8192, 'application/pdf') /* 超过 3 个 */
294
+ ], 1);
295
+ }
296
+
297
+ /* 5 · 紧凑档:一个文件,让"有文件时"的样子也在 */
298
+ var compact = document.querySelector('.upload--compact');
299
+ if (compact && compact.uploadInstance) {
300
+ compact.uploadInstance.add([fakeFile('回函.pdf', 358400, 'application/pdf')], 0);
301
+ }
302
+ }
303
+
304
+ /* upload.js 在 <head> 里加载,它的 init 挂在 DOMContentLoaded 上;
305
+ 本脚本在 body 末尾,此时 readyState 还是 'loading' ⇒ 同样挂事件,
306
+ 且**注册在后** ⇒ 一定在 init 之后跑。若脚本被动态注入(readyState 已就绪)
307
+ 则直接跑 —— upload.js 那边也走"立即 init"分支,顺序依然成立。 */
308
+ if (document.readyState === 'loading') {
309
+ document.addEventListener('DOMContentLoaded', run);
310
+ } else {
311
+ run();
312
+ }
313
+ })();
314
+ </script>
315
+
316
+ </main>
317
+ </body>
318
+ </html>