@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 +441 -88
- package/dist/adapters/index.d.ts +3 -3
- package/dist/adapters/index.js +1 -1
- package/dist/{chunk-CWVD3IOG.js → chunk-23PL36OY.js} +16 -16
- package/dist/chunk-23PL36OY.js.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/{interface-D94XUord.d.ts → interface-DsLdKJ51.d.ts} +1 -1
- package/dist/runtime/index.d.ts +2 -2
- package/dist/{types-CwD2heJM.d.ts → types-DnVXM8BH.d.ts} +1 -1
- package/package.json +21 -19
- package/dist/chunk-CWVD3IOG.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,71 +1,69 @@
|
|
|
1
1
|
# @skrillex1224/chrome-article-publish-extension
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Chrome extension runtime package for publishing articles through platform adapters.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This repository contains two separate parts:
|
|
6
6
|
|
|
7
|
-
- `
|
|
8
|
-
- `
|
|
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
|
|
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
|
-
##
|
|
12
|
+
## Current Scope
|
|
15
13
|
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
+
pnpm add @skrillex1224/chrome-article-publish-extension
|
|
61
52
|
```
|
|
62
53
|
|
|
63
|
-
|
|
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
|
-
|
|
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": [
|
|
80
|
-
|
|
78
|
+
"host_permissions": [
|
|
79
|
+
"http://*/*",
|
|
80
|
+
"https://*/*"
|
|
81
|
+
]
|
|
81
82
|
}
|
|
82
83
|
```
|
|
83
84
|
|
|
84
|
-
|
|
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
|
-
##
|
|
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({
|
|
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
|
|
111
|
+
const publishResult = await publish({
|
|
104
112
|
platform: 'bilibili',
|
|
105
113
|
runtime,
|
|
106
114
|
article: {
|
|
107
|
-
title: '
|
|
108
|
-
markdown: '#
|
|
109
|
-
|
|
115
|
+
title: '人为什么会饿',
|
|
116
|
+
markdown: '# 人为什么会饿\n\n饥饿是一套身体提醒系统。',
|
|
117
|
+
description: '人会饿,不只是因为胃空了。',
|
|
110
118
|
cover: 'https://example.com/cover.jpg',
|
|
111
|
-
tags: ['
|
|
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 (
|
|
121
|
-
throw new Error(
|
|
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:
|
|
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:
|
|
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
|
-
|
|
222
|
+
Field contract:
|
|
183
223
|
|
|
184
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
236
|
-
|
|
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
|
-
|
|
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
|
-
|
|
393
|
+
```text
|
|
394
|
+
chrome-article-publish-extension-1.0.10.zip
|
|
395
|
+
```
|
|
243
396
|
|
|
244
|
-
|
|
397
|
+
For local development:
|
|
245
398
|
|
|
246
399
|
```bash
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
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
|
|
257
|
-
- No task database.
|
|
258
|
-
- No retry scheduler.
|
|
608
|
+
- No server-side publishing.
|
|
259
609
|
- No cross-profile cookie copying.
|
|
260
|
-
- No
|
|
261
|
-
- No
|
|
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.
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { P as PlatformAdapter, a as PublishOptions, A as AdapterRegistryEntry } from '../types-
|
|
2
|
-
export { C as Category, D as Draft, I as ImageProgressCallback } from '../types-
|
|
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-
|
|
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
|
* 适配器基类
|