@skrillex1224/chrome-article-publish-extension 1.0.8 → 1.0.10

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.
package/README.md CHANGED
@@ -1,71 +1,69 @@
1
1
  # @skrillex1224/chrome-article-publish-extension
2
2
 
3
- Direct article publishing adapters for Chrome extensions. The package runs in a Manifest V3 extension context and reuses the user's logged-in browser sessions.
3
+ Chrome extension runtime package for publishing articles through platform adapters.
4
4
 
5
- ## What It Exposes
5
+ This repository contains two separate parts:
6
6
 
7
- - `checkAuth(request)`: check whether the current extension browser profile is logged in to a platform.
8
- - `publish(request)`: publish or submit an article through the platform's public frontend API behavior.
9
- - `getStatus(request)`: query the platform's official management/detail/status source with the `statusRef` returned by `publish()`.
10
- - `createExtensionRuntime(config?)`: Chrome extension runtime implementation used by the three methods.
7
+ - `src/`: the reusable npm package. It exposes only `checkAuth`, `publish`, `getStatus`, and `createExtensionRuntime`.
8
+ - `extension-app/`: a standalone Chrome extension UI that imports the npm package and provides article storage, editing, import/export, publishing, retry, and status pages.
11
9
 
12
- The host extension owns UI, local task state, retry buttons, storage, and polling cadence.
10
+ The npm package is the kernel. The extension UI is one host application. Other Chrome extensions can install the package and call the same kernel APIs directly.
13
11
 
14
- ## Included Chrome Extension UI
12
+ ## Current Scope
15
13
 
16
- This repository also includes a standalone host extension UI under `extension-app/`.
14
+ The package is designed for low-frequency publishing from a user's own logged-in Chrome profile. It follows the public frontend behavior of each platform and uses platform cookies/session state from the current browser profile.
17
15
 
18
- Boundary:
16
+ It is not a Node CLI package and should not be used from a server to copy or reuse user cookies.
19
17
 
20
- - `src/` is the reusable npm kernel.
21
- - `extension-app/` is only a Chrome extension UI that imports the kernel package at build time.
22
- - The extension app does not import adapter internals.
23
- - The extension app never loads executable adapter code from a CDN.
18
+ ## Supported Platforms
24
19
 
25
- Build the side panel extension:
20
+ | Platform ID | UI Name | Publish Type | Main Inputs |
21
+ |---|---|---|---|
22
+ | `baijiahao` | 百家号 | Article | `title`, `markdown`, `description`, `cover`, `tags` |
23
+ | `bilibili` | 哔哩哔哩 | Opus/article | `title`, `markdown`, `description`, `cover`, `tags` |
24
+ | `cnblogs` | 博客园 | Article | `title`, `markdown`, `tags` |
25
+ | `csdn` | CSDN | Article | `title`, `markdown`, `description`, `cover`, `tags` |
26
+ | `51cto` | 51CTO | Article | `title`, `markdown`, `description`, `cover`, `tags` |
27
+ | `douyin` | 抖音图文 | Image-text note | `title`, `description`, `tags`, `images` |
28
+ | `douyin` | 抖音视频 | Video | `title`, `description`, `tags`, `video`, `cover` |
29
+ | `juejin` | 掘金 | Article | `title`, `markdown`, `description`, `cover`, `tags` |
30
+ | `netease` | 网易号 | Article | `title`, `markdown`, `description`, `cover` |
31
+ | `sohu` | 搜狐号 | Article | `title`, `markdown`, `description`, `cover` |
32
+ | `tencent` | 腾讯号 | Article | `title`, `markdown`, `description`, `cover` |
33
+ | `toutiao` | 今日头条 | Article | `title`, `markdown`, `description`, `cover` |
34
+ | `weixin` | 微信公众号 | Article | `title`, `markdown`, `description`, `cover` |
35
+
36
+ Notes:
37
+
38
+ - `douyin` uses one package adapter for both image-text and video. The extension UI exposes them as two entries: `douyin` and `douyin-video`.
39
+ - Platform policy can still put a post into review, reject it, require security verification, or block it because of account limits.
40
+ - `publish()` success means the platform accepted the publish/submit request and returned a queryable `statusRef`. Public visibility must be checked through `getStatus()`.
41
+
42
+ ## Install The Npm Package
26
43
 
27
44
  ```bash
28
- pnpm build:extension-app
29
- pnpm zip:extension-app
30
- ```
31
-
32
- The zip is generated as:
33
-
34
- ```text
35
- chrome-article-publish-extension-ui-1.0.2.zip
45
+ npm install @skrillex1224/chrome-article-publish-extension
36
46
  ```
37
47
 
38
- The UI uses Chrome Side Panel as the primary entry. Article creation happens in a full extension tab, while the side panel handles saved article selection, platform auth checks, publish tasks, retries, and status polling.
39
-
40
- ## Supported Platforms
41
-
42
- | id | Platform | Notes |
43
- |---|---|---|
44
- | `bilibili` | Bilibili Opus | Supports cover and structured Opus payload. Tables degrade to readable text. |
45
- | `weixin` | WeChat Official Account | Supports cover, body images, summary, and security QR events. |
46
- | `douyin` | Douyin | Requires exactly one of `article.images` URL list for image-text notes or `article.video.url` for video posts. |
47
- | `cnblogs` | Cnblogs | Supports article publish, tags, cover metadata. |
48
- | `juejin` | Juejin | Supports cover and real tag ids when the account is eligible. |
49
- | `baijiahao` | Baijiahao | Resolves public URLs from official page data after management status says published. |
50
- | `csdn` | CSDN | Supports cover/tags; account daily quota can block publish. |
51
- | `51cto` | 51CTO | Supports cover/tags/category mapping. |
52
- | `sohu` | Sohu | Publish/status works; platform policy can reject content. |
53
- | `netease` | Netease | Publish/status works; muted accounts cannot pass live publish. |
54
- | `toutiao` | Toutiao | Uses current creator management feed for status. |
55
- | `tencent` | Tencent Content Platform | Uses official frontend publish service and management list. |
56
-
57
- ## Install
48
+ Or with pnpm:
58
49
 
59
50
  ```bash
60
- npm install @skrillex1224/chrome-article-publish-extension
51
+ pnpm add @skrillex1224/chrome-article-publish-extension
61
52
  ```
62
53
 
63
- ## Chrome Extension Requirements
54
+ Chrome extension pages and service workers cannot use a bare npm import at runtime by themselves. Bundle this package into your extension with Vite, Rollup, tsup, webpack, or another build tool.
55
+
56
+ ## Manifest Requirements
64
57
 
65
- Your host extension needs the browser capabilities the adapters use:
58
+ A host extension needs enough Chrome permissions for cookies, cross-origin requests, platform page context, downloads, and managed temporary tabs:
66
59
 
67
60
  ```json
68
61
  {
62
+ "manifest_version": 3,
63
+ "background": {
64
+ "service_worker": "background.js",
65
+ "type": "module"
66
+ },
69
67
  "permissions": [
70
68
  "storage",
71
69
  "unlimitedStorage",
@@ -74,16 +72,19 @@ Your host extension needs the browser capabilities the adapters use:
74
72
  "declarativeNetRequestWithHostAccess",
75
73
  "scripting",
76
74
  "tabs",
75
+ "tabGroups",
77
76
  "downloads"
78
77
  ],
79
- "host_permissions": ["http://*/*", "https://*/*"],
80
- "background": { "type": "module" }
78
+ "host_permissions": [
79
+ "http://*/*",
80
+ "https://*/*"
81
+ ]
81
82
  }
82
83
  ```
83
84
 
84
- The user must log in to each target platform in the same Chrome profile that runs the extension.
85
+ If your extension uses a side panel UI, also add `sidePanel`. The npm package itself does not require a side panel.
85
86
 
86
- ## Usage
87
+ ## Quick Start
87
88
 
88
89
  ```ts
89
90
  import {
@@ -91,49 +92,86 @@ import {
91
92
  createExtensionRuntime,
92
93
  getStatus,
93
94
  publish,
95
+ type PublishEvent,
94
96
  } from '@skrillex1224/chrome-article-publish-extension'
95
97
 
96
- const runtime = createExtensionRuntime({ timeout: 120_000 })
98
+ const runtime = createExtensionRuntime({
99
+ timeout: 3_600_000,
100
+ })
101
+
102
+ const auth = await checkAuth({
103
+ platform: 'bilibili',
104
+ runtime,
105
+ })
97
106
 
98
- const auth = await checkAuth({ platform: 'bilibili', runtime })
99
107
  if (!auth.isAuthenticated) {
100
108
  throw new Error(auth.error || 'Not authenticated')
101
109
  }
102
110
 
103
- const result = await publish({
111
+ const publishResult = await publish({
104
112
  platform: 'bilibili',
105
113
  runtime,
106
114
  article: {
107
- title: 'My article',
108
- markdown: '# Heading\n\nArticle body.',
109
- summary: 'Short summary',
115
+ title: '人为什么会饿',
116
+ markdown: '# 人为什么会饿\n\n饥饿是一套身体提醒系统。',
117
+ description: '人会饿,不只是因为胃空了。',
110
118
  cover: 'https://example.com/cover.jpg',
111
- tags: ['tag-a', 'tag-b'],
119
+ tags: ['健康', '科普'],
112
120
  },
113
- async onEvent(event) {
121
+ async onEvent(event: PublishEvent) {
114
122
  if (event.type === 'security_check') {
115
- console.log(event.method, event.url, event.expiresAt)
123
+ console.log(event.method, event.message, event.url, event.expiresAt)
116
124
  }
117
125
  },
118
126
  })
119
127
 
120
- if (result.state === 'failed') {
121
- throw new Error(result.error.message)
128
+ if (publishResult.state === 'failed') {
129
+ throw new Error(publishResult.error.message)
122
130
  }
123
131
 
124
132
  const status = await getStatus({
125
133
  platform: 'bilibili',
126
134
  runtime,
127
- statusRef: result.statusRef,
135
+ statusRef: publishResult.statusRef,
128
136
  })
129
137
 
130
138
  console.log(status.state, status.postUrl, status.message)
131
139
  ```
132
140
 
133
- ## API
141
+ ## Public API
142
+
143
+ ### `createExtensionRuntime(config?)`
144
+
145
+ Creates the Chrome extension runtime used by all adapters.
146
+
147
+ ```ts
148
+ type RuntimeConfig = {
149
+ timeout?: number
150
+ userAgent?: string
151
+ }
152
+ ```
153
+
154
+ Recommended timeout:
155
+
156
+ - Article-only publishing: at least `120_000`.
157
+ - Image/video upload publishing: use a longer timeout, for example `3_600_000`.
158
+
159
+ The runtime handles:
160
+
161
+ - authenticated `fetch()` from the extension profile
162
+ - cookie read/write
163
+ - local/session storage helpers
164
+ - declarative header rules
165
+ - downloads
166
+ - tab creation/reuse/close
167
+ - `chrome.scripting.executeScript`
168
+
169
+ Temporary tabs created by the runtime are grouped under a Chrome tab group named `Article Publish Runtime`. Existing user-opened matching platform tabs are reused and not closed by the runtime.
134
170
 
135
171
  ### `checkAuth(request)`
136
172
 
173
+ Checks whether the current Chrome profile is logged in to the platform.
174
+
137
175
  ```ts
138
176
  type CheckAuthRequest = {
139
177
  platform: string
@@ -149,15 +187,17 @@ type AuthResult = {
149
187
  }
150
188
  ```
151
189
 
152
- Use this before showing a platform as ready to publish.
190
+ Use this before showing a platform as ready to publish. A failed auth check should be displayed as "not logged in" or as the returned `error`.
153
191
 
154
192
  ### `publish(request)`
155
193
 
194
+ Publishes or submits an article to one platform.
195
+
156
196
  ```ts
157
197
  type PublishRequest = {
158
198
  platform: string
159
199
  runtime: RuntimeInterface
160
- article: Article | PublishArticleInput
200
+ article: PublishArticleInput
161
201
  onEvent?: (event: PublishEvent) => void | Promise<void>
162
202
  options?: {
163
203
  onImageProgress?: (current: number, total: number) => void
@@ -179,15 +219,19 @@ type PublishArticleInput = {
179
219
  }
180
220
  ```
181
221
 
182
- `markdown` is the only public article body format. Do not pass HTML. Adapters convert Markdown to the platform-specific HTML, Markdown, media caption, or rich-text payload internally.
222
+ Field contract:
183
223
 
184
- For Douyin image-text, pass `platform: 'douyin'` and `article.images: [{ url }]`. The adapter uploads those image URLs through ImageX and publishes a note. Douyin image-text no longer converts Markdown into image cards.
224
+ - `title`: platform title.
225
+ - `markdown`: canonical article body. This is the only public body input.
226
+ - `description`: summary, digest, excerpt, caption, or platform description field depending on the target platform.
227
+ - `cover`: cover image URL. Only `http://` and `https://` URLs are supported.
228
+ - `tags`: tag names as `string[]`. The caller should pass an array, not a semicolon-joined string.
229
+ - `images`: image URL list for image-text media flows, currently mainly Douyin image-text.
230
+ - `video`: video URL for video media flows, currently Douyin video.
185
231
 
186
- For Douyin video, pass `platform: 'douyin'` and `article.video.url`. The adapter runs inside a logged-in Chrome extension context, uses the official Creator page uploader for the video asset, and then calls the Creator publish API. If `article.cover` is present, the adapter uploads it as the video cover; otherwise it uses the platform-generated poster from the uploaded video.
232
+ Do not pass `html`, `body`, `content`, `summary`, or `category`. The new contract intentionally does not support those aliases. Adapters convert `markdown` to HTML, rich text, or platform-specific payloads internally.
187
233
 
188
- Douyin media inputs are URL-only and mutually exclusive: pass either `article.images[].url` or `article.video.url`, not both. `article.images[].url`, `article.video.url`, and `article.cover` must be `http://` or `https://` URLs.
189
-
190
- `publish()` returns only:
234
+ Return type:
191
235
 
192
236
  ```ts
193
237
  type PublishResult =
@@ -207,10 +251,18 @@ type PublishResult =
207
251
  }
208
252
  ```
209
253
 
210
- If a platform needs QR, CAPTCHA, or a manual security check, `publish()` emits `onEvent({ type: 'security_check', ... })` and waits until the platform flow completes or times out.
254
+ `state: 'succeeded'` means:
255
+
256
+ - the platform accepted the publish/submit request
257
+ - the adapter returned enough data for `getStatus()`
258
+ - `statusRef` must be stored by the host extension
259
+
260
+ It does not guarantee that the article is already publicly visible. Use `getStatus()` for that.
211
261
 
212
262
  ### `getStatus(request)`
213
263
 
264
+ Queries the platform status using the `statusRef` returned by `publish()`.
265
+
214
266
  ```ts
215
267
  type GetStatusRequest = {
216
268
  platform: string
@@ -218,6 +270,14 @@ type GetStatusRequest = {
218
270
  statusRef: PublishStatusRef
219
271
  }
220
272
 
273
+ type PublishStatusRef = {
274
+ platform: string
275
+ kind: string
276
+ id?: string
277
+ url?: string
278
+ data?: Record<string, string | number | boolean | null>
279
+ }
280
+
221
281
  type PublishStatusResult = {
222
282
  platform: string
223
283
  state: 'published' | 'reviewing' | 'rejected' | 'not_found' | 'unknown'
@@ -227,35 +287,328 @@ type PublishStatusResult = {
227
287
  }
228
288
  ```
229
289
 
230
- Store `statusRef` exactly as returned by `publish()`. Do not parse it in the host UI.
290
+ Store `statusRef` exactly as returned. The host UI should not parse or rewrite it.
291
+
292
+ Status meanings:
293
+
294
+ - `published`: the platform reports a public or published state. `postUrl` should be used when present.
295
+ - `reviewing`: the platform accepted the content but it is still in review or pending.
296
+ - `rejected`: the platform rejected or blocked the content. Show `message` to the user.
297
+ - `not_found`: the platform could not find the content by the returned reference.
298
+ - `unknown`: the adapter could not determine the state. Show `message` and allow retry/refresh.
231
299
 
232
- ## Runtime
300
+ ## Security Check Events
301
+
302
+ Some platforms may require QR code confirmation, CAPTCHA, or a manual security step. In that case `publish()` emits an event and waits until the platform flow completes or times out.
233
303
 
234
304
  ```ts
235
- const runtime = createExtensionRuntime({
236
- timeout: 120_000,
305
+ type PublishEvent = {
306
+ type: 'progress' | 'security_check'
307
+ platform: string
308
+ message: string
309
+ expiresAt: number
310
+ url?: string
311
+ method?: 'qrcode' | 'captcha' | 'manual'
312
+ }
313
+ ```
314
+
315
+ Host UI guidance:
316
+
317
+ - Display `message` directly.
318
+ - If `url` exists, show it as a QR image or openable verification link.
319
+ - Respect `expiresAt` and show timeout state if the user does not complete the action.
320
+ - Keep the publish task as `publishing` while the package is waiting.
321
+ - If `publish()` returns `failed`, show `error.message` and allow retry.
322
+
323
+ ## Douyin Media Rules
324
+
325
+ The package uses `platform: 'douyin'` for both Douyin image-text and Douyin video. The media input decides the mode.
326
+
327
+ Image-text:
328
+
329
+ ```ts
330
+ await publish({
331
+ platform: 'douyin',
332
+ runtime,
333
+ article: {
334
+ title: '图文标题',
335
+ markdown: '',
336
+ description: '图文描述',
337
+ tags: ['旅行', '生活'],
338
+ images: [
339
+ { url: 'https://example.com/1.jpg' },
340
+ { url: 'https://example.com/2.jpg' },
341
+ ],
342
+ },
343
+ })
344
+ ```
345
+
346
+ Video:
347
+
348
+ ```ts
349
+ await publish({
350
+ platform: 'douyin',
351
+ runtime,
352
+ article: {
353
+ title: '视频标题',
354
+ markdown: '',
355
+ description: '视频描述',
356
+ cover: 'https://example.com/cover.jpg',
357
+ tags: ['科技', 'AI'],
358
+ video: {
359
+ url: 'https://example.com/video.mp4',
360
+ filename: 'video.mp4',
361
+ mimeType: 'video/mp4',
362
+ },
363
+ },
237
364
  })
238
365
  ```
239
366
 
240
- `timeout` controls extension `fetch()` timeout in milliseconds. Keep it higher for platforms that upload images or videos.
367
+ Rules:
368
+
369
+ - Pass either `images` or `video`, not both.
370
+ - `images[].url`, `video.url`, and `cover` must be public `http(s)` URLs.
371
+ - The package does not convert Markdown into Douyin image cards. The extension UI has an optional Markdown-to-image helper for that UI workflow.
372
+
373
+ ## Extension UI
374
+
375
+ The included extension app lives in `extension-app/`. It is useful as:
376
+
377
+ - a ready-to-install manual publishing tool
378
+ - a reference implementation for another extension
379
+ - an example of how to keep UI state separate from the npm kernel
380
+
381
+ ### Build And Package
382
+
383
+ From `article-publish-extension/`:
384
+
385
+ ```bash
386
+ pnpm install
387
+ pnpm build:extension-app
388
+ pnpm zip:extension-app
389
+ ```
390
+
391
+ The zip output is:
241
392
 
242
- ## Testing In A Host Extension
393
+ ```text
394
+ chrome-article-publish-extension-1.0.10.zip
395
+ ```
243
396
 
244
- After loading the built extension in Chrome with remote debugging enabled:
397
+ For local development:
245
398
 
246
399
  ```bash
247
- CDP_URL=http://127.0.0.1:9224 \
248
- EXTENSION_ID=<your-extension-id> \
249
- node scripts/e2e-check-auth.mjs
400
+ pnpm --filter chrome-article-publish-extension build
401
+ pnpm --filter chrome-article-publish-extension test
402
+ pnpm --filter chrome-article-publish-extension typecheck
250
403
  ```
251
404
 
252
- The script sends `GET_PLATFORMS` and `CHECK_AUTH` through `chrome.runtime.sendMessage` and prints one row per platform.
405
+ ### Install In Chrome
406
+
407
+ 1. Open `chrome://extensions`.
408
+ 2. Enable Developer Mode.
409
+ 3. Choose "Load unpacked" and select `article-publish-extension/extension-app/dist`.
410
+ 4. Or unzip `chrome-article-publish-extension-1.0.10.zip` and load the extracted directory.
411
+ 5. Log in to target platforms in the same Chrome profile.
412
+ 6. Click the extension icon to open the side panel.
413
+
414
+ ### UI Flow
415
+
416
+ 1. **Create article**
417
+ - Click `新建`.
418
+ - The editor opens as a full extension tab.
419
+ - Fill `title`, `description`, `cover`, `tags`, `images`, `video`, and `markdown`.
420
+ - Save the article into local Chrome storage.
421
+
422
+ 2. **Import articles**
423
+ - Click `导入`.
424
+ - Upload a `.csv` or `.xlsx` file.
425
+ - Select sheet, map fields, preview rows, and import.
426
+ - `.xls` is not supported.
427
+
428
+ 3. **Select articles**
429
+ - The side panel lists saved articles.
430
+ - Click article cards or checkboxes to select one or more articles.
431
+ - Click `发布所选`.
432
+
433
+ 4. **Select platforms**
434
+ - The side panel checks login state with `checkAuth()`.
435
+ - Logged-in platforms can be selected.
436
+ - Not-logged-in platforms show the auth failure reason and should be opened manually for login.
437
+
438
+ 5. **Publish**
439
+ - Click `发布所选`.
440
+ - The background service worker calls package `publish()` for each selected article/platform task.
441
+ - Already succeeded tasks are skipped and not published again.
442
+ - Failed tasks can be retried.
443
+
444
+ 6. **Check status**
445
+ - After successful submission, the UI stores `statusRef`.
446
+ - Status refresh calls package `getStatus()`.
447
+ - The platform detail page shows per-article publish result, current status, reason, and public link when available.
448
+
449
+ 7. **Export links**
450
+ - Click `导出链接`.
451
+ - The UI exports an `.xlsx` workbook with article/platform link and status data.
452
+
453
+ ### Extension Storage Model
454
+
455
+ The UI stores records in Chrome local storage:
456
+
457
+ - `articlePublishExtension:articles:v2`
458
+ - `articlePublishExtension:tasks:v2`
459
+ - `articlePublishExtension:statuses:v2`
460
+ - `articlePublishExtension:auth:v2`
461
+ - `articlePublishExtension:sidePanel:v2`
462
+
463
+ Article records use the same hard-cut input model as the npm package:
464
+
465
+ ```ts
466
+ type ArticleRecord = {
467
+ id: string
468
+ title: string
469
+ description?: string
470
+ markdown: string
471
+ cover?: string
472
+ tags: string[]
473
+ images?: Array<{ url: string }>
474
+ video?: {
475
+ url: string
476
+ filename?: string
477
+ mimeType?: string
478
+ }
479
+ createdAt: number
480
+ updatedAt: number
481
+ }
482
+ ```
483
+
484
+ Publish tasks are keyed by `articleId + platform`. A task with `state: 'succeeded'` is not republished by the UI. A failed task can be retried, reusing the same logical task slot.
485
+
486
+ ### CSV/XLSX Import Fields
487
+
488
+ The import page accepts these canonical fields:
489
+
490
+ | Field | Chinese Alias | Required | Meaning |
491
+ |---|---|---:|---|
492
+ | `title` | `标题` | No | Article title. Empty title becomes `未命名文章`. |
493
+ | `markdown` | `正文` | No | Canonical article body. Empty string is allowed for media-only tasks. |
494
+ | `description` | `摘要` | No | Summary/description/caption. |
495
+ | `cover` | `封面` | No | Cover image URL. |
496
+ | `tags` | `标签` | No | JSON string array or text split by `;`, `;`, `,`, `,`, or newline. |
497
+ | `images` | `图片` | No | JSON array, URL list, or `{ "url": "..." }` array. |
498
+ | `video` | `视频` | No | One video URL. |
499
+
500
+ The UI intentionally keeps one English field and one Chinese alias per concept.
501
+
502
+ ### OSS Upload In The UI
503
+
504
+ The extension UI can upload local images/videos or generated Markdown image pages to OSS, then fill URL fields back into the article.
505
+
506
+ Configuration is stored only in Chrome local storage, not in source code or the npm package:
507
+
508
+ ```ts
509
+ type OssConfig = {
510
+ endpoint: string
511
+ accessKeyId: string
512
+ accessKeySecret: string
513
+ bucketName: string
514
+ prefix: string
515
+ }
516
+ ```
517
+
518
+ Do not ship real access keys in a public extension build. If browser direct upload is used, the OSS bucket must allow the extension origin in CORS settings.
519
+
520
+ The Markdown-to-image feature is UI-only. It is mainly for generating Douyin image-text `images`. The npm package still receives only URL-based `images`.
521
+
522
+ ## Package And UI Boundary
523
+
524
+ The npm package owns:
525
+
526
+ - platform adapters
527
+ - `checkAuth`
528
+ - `publish`
529
+ - `getStatus`
530
+ - platform-specific Markdown conversion
531
+ - Chrome extension runtime helpers
532
+ - security check events
533
+ - managed runtime tabs
534
+
535
+ The extension UI owns:
536
+
537
+ - article creation and editing
538
+ - local article/task/status storage
539
+ - CSV/XLSX import
540
+ - OSS upload configuration
541
+ - Markdown preview and Markdown-to-image generation
542
+ - platform selection
543
+ - retry buttons
544
+ - status polling cadence
545
+ - export workbook
546
+
547
+ The extension UI should call the package. It should not import adapter internals.
548
+
549
+ ## Local Verification
550
+
551
+ Useful commands:
552
+
553
+ ```bash
554
+ pnpm typecheck
555
+ pnpm build:npm
556
+ pnpm build:extension-app
557
+ pnpm zip:extension-app
558
+ pnpm --filter chrome-article-publish-extension typecheck
559
+ pnpm --filter chrome-article-publish-extension test
560
+ pnpm --filter chrome-article-publish-extension build
561
+ ```
562
+
563
+ Adapter-focused checks live in `scripts/`, for example:
564
+
565
+ ```bash
566
+ pnpm check:public-api
567
+ pnpm check:publish-contract
568
+ pnpm check:douyin-images
569
+ pnpm check:douyin-video
570
+ pnpm check:tencent-auth
571
+ pnpm check:tab-group
572
+ ```
573
+
574
+ Live E2E scripts require a logged-in Chrome profile and the target platform account. They should be run one platform at a time and their result should be verified through `publish()` and `getStatus()`.
575
+
576
+ ## Troubleshooting
577
+
578
+ ### `Not authenticated`
579
+
580
+ The user is not logged in to the platform in the same Chrome profile as the extension. Open the platform homepage, log in, then run `checkAuth()` again.
581
+
582
+ ### `Platform not found`
583
+
584
+ The `platform` string is not one of the supported package platform IDs. For the extension UI, `douyin-video` is a UI-only ID and maps to package platform `douyin`.
585
+
586
+ ### Publish succeeded but no public URL
587
+
588
+ Call `getStatus()` with the returned `statusRef`. Some platforms only expose a public URL after review or after management status changes to published.
589
+
590
+ ### Security check appears
591
+
592
+ Listen to `onEvent`. Show `event.message`, `event.url`, and `event.expiresAt`. The package waits until the platform flow completes or times out.
593
+
594
+ ### Images or video fail to upload
595
+
596
+ Check that media URLs are public `http(s)` URLs and that the platform account is allowed to upload that media type. For UI OSS uploads, also check OSS CORS and credentials.
597
+
598
+ ### Extension opens temporary platform tabs
599
+
600
+ Some adapters need a platform page context for upload/status data. Runtime-created tabs are grouped under `Article Publish Runtime` and are closed after use when the adapter requested a temporary tab. Existing matching user tabs are reused and not closed.
601
+
602
+ ### Bare import fails in extension page
603
+
604
+ Bundle the package. Chrome extension pages cannot resolve a bare specifier like `@skrillex1224/chrome-article-publish-extension` unless your build tool has bundled it.
253
605
 
254
606
  ## Not Included
255
607
 
256
- - No UI framework.
257
- - No task database.
258
- - No retry scheduler.
608
+ - No server-side publishing.
259
609
  - No cross-profile cookie copying.
260
- - No platform button-click publishing.
261
- - No compatibility promises for legacy result shapes from earlier internal tools.
610
+ - No old Wechatsync import dependency.
611
+ - No public CDN runtime loader.
612
+ - No built-in task database in the npm package.
613
+ - No publish scheduler in the npm package.
614
+ - No compatibility with old `html`, `body`, `content`, `summary`, or `category` inputs.
@@ -1,6 +1,6 @@
1
- import { P as PlatformAdapter, a as PublishOptions, A as AdapterRegistryEntry } from '../types-CwD2heJM.js';
2
- export { C as Category, D as Draft, I as ImageProgressCallback } from '../types-CwD2heJM.js';
3
- import { P as PlatformMeta, R as RuntimeInterface, A as AuthResult, a as Article, S as SyncResult, c as PublishStatusRef, d as PublishStatusResult, H as HeaderRule } from '../interface-D94XUord.js';
1
+ import { P as PlatformAdapter, a as PublishOptions, A as AdapterRegistryEntry } from '../types-DnVXM8BH.js';
2
+ export { C as Category, D as Draft, I as ImageProgressCallback } from '../types-DnVXM8BH.js';
3
+ import { P as PlatformMeta, R as RuntimeInterface, A as AuthResult, a as Article, S as SyncResult, c as PublishStatusRef, d as PublishStatusResult, H as HeaderRule } from '../interface-DsLdKJ51.js';
4
4
 
5
5
  /**
6
6
  * 适配器基类
@@ -15,7 +15,7 @@ import {
15
15
  createLogger,
16
16
  markdownToHtml,
17
17
  parseMarkdownImages
18
- } from "../chunk-CWVD3IOG.js";
18
+ } from "../chunk-23PL36OY.js";
19
19
 
20
20
  // src/adapters/base.ts
21
21
  var BaseAdapter = class {