@acms-subir/core 0.3.1 → 0.3.3-next.20260929113209
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/CHANGELOG.md +13 -0
- package/integration.mjs +37 -0
- package/package.json +2 -1
- package/src/components/editor/extensions/cards.tsx +3 -38
- package/src/components/editor/extensions/mediaCards.tsx +4 -0
- package/src/components/import/ImportExport.tsx +519 -0
- package/src/components/import/runner.ts +284 -0
- package/src/lib/embeds.ts +50 -0
- package/src/lib/import/apply.ts +430 -0
- package/src/lib/import/bundle.ts +167 -0
- package/src/lib/import/export.ts +54 -0
- package/src/lib/import/ghost.ts +247 -0
- package/src/lib/import/lexical.ts +290 -0
- package/src/lib/import/migration.ts +27 -0
- package/src/lib/import/zipread.ts +72 -0
- package/src/lib/repo/index.ts +12 -8
- package/src/lib/repo/migrations.ts +2 -0
- package/src/lib/repo/sql.ts +116 -2
- package/src/lib/repo/types.ts +17 -0
- package/src/pages/admin/import.astro +16 -0
- package/src/pages/admin/settings.astro +11 -0
- package/src/pages/api/export.ts +41 -0
- package/src/pages/api/import/asset.ts +32 -0
- package/src/pages/api/import/index.ts +47 -0
- package/src/pages/api/import/media.ts +31 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @acms-subir/core
|
|
2
2
|
|
|
3
|
+
## 0.3.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 2c349c0: Fix blank admin editors in `astro dev` on sites that install core from npm. Browser-side dependencies (TipTap, prismjs, mammoth and others) are now pre-bundled by Vite, so islands no longer fail with "doesn't provide an export named …".
|
|
8
|
+
|
|
9
|
+
Also fixed:
|
|
10
|
+
|
|
11
|
+
- A first request storm after an upgrade could run a new migration twice and fail with `UNIQUE constraint failed: migrations.version`.
|
|
12
|
+
- File and bookmark cards reloaded as plain links when a post was reopened in the editor.
|
|
13
|
+
- YouTube embeds now keep their start time (`?t=` / `start=`) and accept `youtube.com/live/…` links.
|
|
14
|
+
- `@tiptap/core` is now a declared dependency instead of relying on hoisting.
|
|
15
|
+
|
|
3
16
|
## 0.3.1
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/integration.mjs
CHANGED
|
@@ -68,6 +68,7 @@ const ROUTES = [
|
|
|
68
68
|
['/api/email/run', 'api/email/run.ts'],
|
|
69
69
|
['/api/ops/status', 'api/ops/status.ts'],
|
|
70
70
|
['/admin/ai', 'admin/ai.astro'],
|
|
71
|
+
['/admin/import', 'admin/import.astro'],
|
|
71
72
|
['/admin/tags', 'admin/tags.astro'],
|
|
72
73
|
['/admin/users', 'admin/users.astro'],
|
|
73
74
|
// API
|
|
@@ -86,6 +87,23 @@ const ROUTES = [
|
|
|
86
87
|
['/api/mcp', 'api/mcp.ts'],
|
|
87
88
|
['/api/media/upload', 'api/media/upload.ts'],
|
|
88
89
|
['/api/restore', 'api/restore.ts'],
|
|
90
|
+
['/api/export', 'api/export.ts'],
|
|
91
|
+
['/api/import', 'api/import/index.ts'],
|
|
92
|
+
['/api/import/asset', 'api/import/asset.ts'],
|
|
93
|
+
['/api/import/media', 'api/import/media.ts'],
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
// Packages imported by browser-side code (admin islands and the libs they
|
|
97
|
+
// pull in). Keep in sync when a component gains a new dependency; the
|
|
98
|
+
// integration test checks every entry is a declared dependency.
|
|
99
|
+
export const CLIENT_DEPS = [
|
|
100
|
+
'react', 'react-dom', 'react-dom/client',
|
|
101
|
+
'@tiptap/core', '@tiptap/react', '@tiptap/react/menus', '@tiptap/pm/state', '@tiptap/pm/view', '@tiptap/pm/model',
|
|
102
|
+
'@tiptap/starter-kit', '@tiptap/extensions', '@tiptap/extension-table', '@tiptap/extension-highlight',
|
|
103
|
+
'@tiptap/extension-code-block-lowlight', '@tiptap/suggestion',
|
|
104
|
+
'@dnd-kit/core', '@dnd-kit/sortable', '@dnd-kit/utilities',
|
|
105
|
+
'prismjs', 'simple-icons', 'lowlight', 'hast-util-to-html', 'marked', 'turndown', 'zod',
|
|
106
|
+
'mammoth/mammoth.browser.js',
|
|
89
107
|
];
|
|
90
108
|
|
|
91
109
|
// First segments of the fixed routes above — a configured base landing on one
|
|
@@ -515,6 +533,25 @@ export default function acms(options = {}) {
|
|
|
515
533
|
}
|
|
516
534
|
updateConfig({ vite: { plugins: [acmsVitePlugin(userConfig)] } });
|
|
517
535
|
|
|
536
|
+
// Installed from npm, core's admin islands are source files inside
|
|
537
|
+
// node_modules, so Vite's dev scanner never crawls them: their
|
|
538
|
+
// dependencies are found only at runtime and CommonJS ones
|
|
539
|
+
// (use-sync-external-store via @tiptap/react, prismjs, mammoth) are
|
|
540
|
+
// served raw, which breaks hydration ("doesn't provide an export
|
|
541
|
+
// named …"), and late discoveries force a mid-load reload. Pre-bundle
|
|
542
|
+
// the whole client graph up front. The `core > dep` form resolves
|
|
543
|
+
// each package from core's own location, so it also works when the
|
|
544
|
+
// package manager doesn't hoist them. Build output is unaffected.
|
|
545
|
+
if (command === 'dev') {
|
|
546
|
+
updateConfig({
|
|
547
|
+
vite: {
|
|
548
|
+
optimizeDeps: {
|
|
549
|
+
include: CLIENT_DEPS.map((dep) => `@acms-subir/core > ${dep}`),
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
|
|
518
555
|
// "Project-local routes win" is enforced here: Astro registers
|
|
519
556
|
// injected and file-based routes at equal priority (and the injected
|
|
520
557
|
// one is served on a collision), so a route is only injected when the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acms-subir/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3-next.20260929113209",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"@dnd-kit/sortable": "^10.0.0",
|
|
60
60
|
"@dnd-kit/utilities": "^3.2.2",
|
|
61
61
|
"@libsql/client": "^0.15.0",
|
|
62
|
+
"@tiptap/core": "^3.30.1",
|
|
62
63
|
"@tiptap/extension-code-block-lowlight": "^3.30.1",
|
|
63
64
|
"@tiptap/extension-highlight": "3.30.1",
|
|
64
65
|
"@tiptap/extension-table": "^3.30.1",
|
|
@@ -3,6 +3,9 @@ import { NodeViewWrapper, NodeViewContent, ReactNodeViewRenderer } from '@tiptap
|
|
|
3
3
|
import type { NodeViewProps } from '@tiptap/react';
|
|
4
4
|
import { useState } from 'react';
|
|
5
5
|
import { cleanEventName } from '../../../lib/analytics';
|
|
6
|
+
import { detectEmbed } from '../../../lib/embeds';
|
|
7
|
+
|
|
8
|
+
export { detectEmbed, type EmbedInfo } from '../../../lib/embeds';
|
|
6
9
|
|
|
7
10
|
// Card nodes shared by the post editor. renderHTML emits the public markup
|
|
8
11
|
// (`.pc-*` classes, styled in src/styles/cards.css); parseHTML round-trips it
|
|
@@ -129,44 +132,6 @@ function ImageCardView({ node, updateAttributes, deleteNode, selected }: NodeVie
|
|
|
129
132
|
|
|
130
133
|
// ---- embed ----------------------------------------------------------------
|
|
131
134
|
|
|
132
|
-
export interface EmbedInfo {
|
|
133
|
-
provider: string;
|
|
134
|
-
embedSrc: string;
|
|
135
|
-
aspect: boolean; // true → 16:9, false → fixed height
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function detectEmbed(raw: string): EmbedInfo | null {
|
|
139
|
-
let url: URL;
|
|
140
|
-
try {
|
|
141
|
-
url = new URL(raw.trim());
|
|
142
|
-
} catch {
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
const host = url.hostname.replace(/^www\./, '');
|
|
146
|
-
if (host === 'youtube.com' || host === 'm.youtube.com' || host === 'youtu.be' || host === 'youtube-nocookie.com') {
|
|
147
|
-
const id =
|
|
148
|
-
host === 'youtu.be'
|
|
149
|
-
? url.pathname.slice(1).split('/')[0]
|
|
150
|
-
: url.pathname.startsWith('/shorts/') || url.pathname.startsWith('/embed/')
|
|
151
|
-
? url.pathname.split('/')[2]
|
|
152
|
-
: url.searchParams.get('v');
|
|
153
|
-
if (id) return { provider: 'youtube', embedSrc: `https://www.youtube-nocookie.com/embed/${id}`, aspect: true };
|
|
154
|
-
}
|
|
155
|
-
if (host === 'vimeo.com' || host === 'player.vimeo.com') {
|
|
156
|
-
const id = url.pathname.split('/').filter(Boolean).pop();
|
|
157
|
-
if (id && /^\d+$/.test(id)) return { provider: 'vimeo', embedSrc: `https://player.vimeo.com/video/${id}`, aspect: true };
|
|
158
|
-
}
|
|
159
|
-
if (host === 'loom.com' && url.pathname.startsWith('/share/')) {
|
|
160
|
-
return { provider: 'loom', embedSrc: `https://www.loom.com/embed/${url.pathname.split('/')[2]}`, aspect: true };
|
|
161
|
-
}
|
|
162
|
-
if (host === 'codepen.io' && url.pathname.includes('/pen/')) {
|
|
163
|
-
return { provider: 'codepen', embedSrc: raw.replace('/pen/', '/embed/'), aspect: false };
|
|
164
|
-
}
|
|
165
|
-
if (url.protocol === 'https:') {
|
|
166
|
-
return { provider: 'iframe', embedSrc: raw, aspect: false };
|
|
167
|
-
}
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
135
|
|
|
171
136
|
export const EmbedCard = Node.create({
|
|
172
137
|
name: 'embedCard',
|
|
@@ -40,6 +40,8 @@ export const FileCard = Node.create({
|
|
|
40
40
|
return [
|
|
41
41
|
{
|
|
42
42
|
tag: 'a.pc-filecard',
|
|
43
|
+
// outrank the Link mark's a[href] rule, or the card reloads as a plain link
|
|
44
|
+
priority: 60,
|
|
43
45
|
getAttrs: (el) => {
|
|
44
46
|
const e = el as HTMLElement;
|
|
45
47
|
return {
|
|
@@ -288,6 +290,8 @@ export const BookmarkCard = Node.create({
|
|
|
288
290
|
return [
|
|
289
291
|
{
|
|
290
292
|
tag: 'a.pc-bookmark',
|
|
293
|
+
// outrank the Link mark's a[href] rule, or the card reloads as a plain link
|
|
294
|
+
priority: 60,
|
|
291
295
|
getAttrs: (el) => {
|
|
292
296
|
const e = el as HTMLElement;
|
|
293
297
|
return {
|