create-ampless 1.0.0-alpha.89 → 1.0.0-alpha.92
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/dist/templates/_shared/docs/plugin-author-guide.ja.md +74 -1
- package/dist/templates/_shared/docs/plugin-author-guide.md +103 -4
- package/dist/templates/_shared/package.json +13 -13
- package/dist/templates/blog/pages/post.tsx +5 -0
- package/dist/templates/corporate/pages/post.tsx +5 -0
- package/dist/templates/dads/pages/post.tsx +5 -0
- package/dist/templates/docs/pages/post.tsx +5 -0
- package/dist/templates/landing/pages/post.tsx +5 -0
- package/dist/templates/minimal/pages/post.tsx +5 -0
- package/package.json +1 -1
|
@@ -28,6 +28,7 @@ ampless はテーマとプラグインの両方を提供します。用途に合
|
|
|
28
28
|
| 信頼できる副作用(S3 書き込み・外部 API 送信) | | ✓ (`writePublicAsset` + `trusted`) |
|
|
29
29
|
| テーマに依存しない `<head>` / `<body>` 注入(アナリティクス・同意バナー) | | ✓ (`publicHead` / `publicBodyEnd`) |
|
|
30
30
|
| 投稿単位の機械可読メタデータ(JSON-LD 等) | | ✓ (`schema` via `publicBodyForPost`) |
|
|
31
|
+
| 投稿本文の周囲の可視 HTML(reading-time、breadcrumb、share など) | | ✓ (`publicHtmlForPost`) |
|
|
31
32
|
| 複数の ampless サイトで共有したいコード | | ✓ (npm パッケージとして公開) |
|
|
32
33
|
|
|
33
34
|
判断の目安:
|
|
@@ -67,6 +68,7 @@ ampless プラグインは `AmplessPlugin` オブジェクトを返す TypeScrip
|
|
|
67
68
|
| `publicHead(ctx)` | root layout の `<head>` | 同期 (async layout から呼ばれる) | 1 |
|
|
68
69
|
| `publicBodyEnd(ctx)` | root layout の `<body>` 末尾 | 同期 | 1 |
|
|
69
70
|
| `publicBodyForPost(post, ctx)` | テーマの post ページテンプレート(投稿単位) | 同期 | 4 |
|
|
71
|
+
| `publicHtmlForPost(post, ctx)` | テーマの post ページテンプレート(投稿単位、可視 HTML) | 同期 | 6d |
|
|
70
72
|
| `ogImage` | `/og/[slug]` ルート | リクエスト時、公開 Lambda 内 | 既存 |
|
|
71
73
|
| `hooks` | trust_level に応じた processor Lambda | 非同期、SQS イベントで起動 | 既存 |
|
|
72
74
|
| `settings.public` | `/admin/plugins` フォーム | 宣言的なマニフェスト | 2 |
|
|
@@ -181,6 +183,7 @@ interface AmplessPlugin {
|
|
|
181
183
|
publicHead?(ctx): readonly PublicHeadDescriptor[]
|
|
182
184
|
publicBodyEnd?(ctx): readonly PublicBodyDescriptor[]
|
|
183
185
|
publicBodyForPost?(post: Post, ctx): readonly PublicPostBodyDescriptor[]
|
|
186
|
+
publicHtmlForPost?(post: Post, ctx): readonly PublicPostHtmlDescriptor[]
|
|
184
187
|
ogImage?: OgImageConfig
|
|
185
188
|
settings?: { public?: readonly PluginSettingField[] }
|
|
186
189
|
}
|
|
@@ -309,6 +312,7 @@ SSR がデッドラインなしでブロックします。ネットワーク呼
|
|
|
309
312
|
| `publicHead(ctx)` | `PublicHeadDescriptor[]` | 解析ローダー、フォント、jsonld、hreflang |
|
|
310
313
|
| `publicBodyEnd(ctx)` | `PublicBodyDescriptor[]` | GTM no-script フレーム、チャットウィジェット、末尾スニペット |
|
|
311
314
|
| `publicBodyForPost(post, ctx)` | `PublicPostBodyDescriptor[]` | 投稿単位の body 注入 — JSON-LD 構造化データ。テーマの post ページテンプレートが render する |
|
|
315
|
+
| `publicHtmlForPost(post, ctx)` | `PublicPostHtmlDescriptor[]` | 投稿単位の可視 HTML を `beforeContent` / `afterContent` に注入 — reading-time バッジ、breadcrumb、share リンク等。body は runtime が `sanitize-html` の厳格 allowlist で sanitize |
|
|
312
316
|
|
|
313
317
|
`ctx` オブジェクトの中身:
|
|
314
318
|
|
|
@@ -402,6 +406,21 @@ TypeScript としてサイトと同一の Node プロセスで動きます。des
|
|
|
402
406
|
|
|
403
407
|
`meta` / `link` を除いている理由:投稿単位のメタデータは Next.js `generateMetadata()` 経由の `metadata()` サーフェスが担い、フレームワークの deduplication・streaming と統合されている。`publicBodyForPost` は `generateMetadata` が生成できない構造化データ(`<script type="application/ld+json">`)のためだけに存在する。
|
|
404
408
|
|
|
409
|
+
### `PublicPostHtmlDescriptor`(Phase 6d)
|
|
410
|
+
|
|
411
|
+
`publicHtmlForPost` は `PublicPostHtmlDescriptor[]` を返します:
|
|
412
|
+
|
|
413
|
+
```ts
|
|
414
|
+
{
|
|
415
|
+
type: 'html',
|
|
416
|
+
id: 'display', // plugin-local 短識別子(≤ 64 文字、制御文字不可)
|
|
417
|
+
position: 'beforeContent' | 'afterContent',
|
|
418
|
+
body: '<p class="reading-time">約 3 分で読めます</p>',
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
runtime は `body` を `sanitize-html` の厳格 allowlist で sanitize し(詳しい allowlist と drop 対象は上の `publicHtmlForPost` 例を参照)、結果を `<div data-ampless-plugin="${namespace}" data-ampless-position="${position}">` で wrap します。テーマは `pages/post.tsx` で `{html.beforeContent}` / `{html.afterContent}` を embed するだけで、plugin の出力に対して `dangerouslySetInnerHTML` を書きません。
|
|
423
|
+
|
|
405
424
|
### JSON-LD 自動 escape
|
|
406
425
|
|
|
407
426
|
`scriptType === 'application/ld+json'` のとき、runtime は描画前に **`body` 文字列を自動 escape** する — `<` → `<`、`>` → `>`、`&` → `&`、U+2028 → ` `、U+2029 → ` `。この処理は `inlineScript` を受け付ける 3 つのサーフェス(`publicHead` / `publicBodyEnd` / `publicBodyForPost`)すべてで行われる。プラグイン作者は生の JSON 文字列を返せばよく、自前で escape しなくてよい。
|
|
@@ -470,6 +489,60 @@ export default function schemaJsonldPlugin() {
|
|
|
470
489
|
|
|
471
490
|
テーマの `pages/post.tsx` が `ampless.publicBodyForPost(post)` を呼び、返された descriptor を描画します。runtime は自動 escape した body を持つ `<script type="application/ld+json">` 要素をページに挿入します。
|
|
472
491
|
|
|
492
|
+
### `publicHtmlForPost` 例(Phase 6d)
|
|
493
|
+
|
|
494
|
+
**可視 HTML** を post の周囲に出したいとき(reading-time バッジ、breadcrumb、share リンク、micro-format 注釈など)は `publicHtmlForPost` を使います。runtime が body を sanitize したうえで `beforeContent` / `afterContent` スロットに embed するので、テーマ側で `dangerouslySetInnerHTML` を書く必要はありません。
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
import { definePlugin } from 'ampless'
|
|
498
|
+
|
|
499
|
+
export default function readingTimePlugin() {
|
|
500
|
+
return definePlugin({
|
|
501
|
+
name: 'reading-time',
|
|
502
|
+
apiVersion: 1,
|
|
503
|
+
trust_level: 'untrusted',
|
|
504
|
+
capabilities: ['publicHtmlForPost'],
|
|
505
|
+
publicHtmlForPost(post, _ctx) {
|
|
506
|
+
const words = countWords(post)
|
|
507
|
+
const minutes = Math.max(1, Math.round(words / 200))
|
|
508
|
+
return [{
|
|
509
|
+
type: 'html',
|
|
510
|
+
id: 'display',
|
|
511
|
+
position: 'beforeContent',
|
|
512
|
+
body: `<p class="reading-time" data-words="${words}" data-minutes="${minutes}">約 ${minutes} 分で読めます</p>`,
|
|
513
|
+
}]
|
|
514
|
+
},
|
|
515
|
+
})
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
テーマの `pages/post.tsx` は `const html = await ampless.publicHtmlForPost(post)` を 1 回呼び、スロットを embed します:
|
|
520
|
+
|
|
521
|
+
```tsx
|
|
522
|
+
{postBody} {/* publicBodyForPost — JSON-LD */}
|
|
523
|
+
{html.beforeContent} {/* publicHtmlForPost — beforeContent スロット */}
|
|
524
|
+
<div className="prose" dangerouslySetInnerHTML={{ __html: renderBody(post) }} />
|
|
525
|
+
{html.afterContent} {/* publicHtmlForPost — afterContent スロット */}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
**スロット位置**(v1): `'beforeContent'` / `'afterContent'` の 2 つ。
|
|
529
|
+
|
|
530
|
+
**Sanitizer(厳格、trust level に関わらず同一):**
|
|
531
|
+
|
|
532
|
+
- 許可タグ: `p` · `span` · `strong` · `em` · `a` · `code` · `br` · `ul` · `ol` · `li`
|
|
533
|
+
- 許可グローバル属性: `class` · `data-words` · `data-minutes` · `data-ampless-*`
|
|
534
|
+
- 許可 `<a>` 属性: `href` · `rel` · `target`。`target="_blank"` のとき sanitizer が `rel="noopener noreferrer"` を自動付与
|
|
535
|
+
- `href` で許可するスキーム: `http` / `https`。相対 URL (`./path` / `../path` / `/path` / `#anchor`) は素通り。`javascript:` / `data:` / `mailto:` / `tel:` / `vbscript:` は drop
|
|
536
|
+
- drop されるタグ・属性: `<img>` · `<iframe>` · `<video>` · `<audio>` · `<object>` · `<embed>` · `<form>` · `<style>` · インライン `style` · 全 event handler (`on*`)
|
|
537
|
+
|
|
538
|
+
allowlist 外のタグが必要になった場合は issue を立ててください。allowlist は設計上拡張するものであって、escape hatch ではありません。
|
|
539
|
+
|
|
540
|
+
**`id` は plugin-local。** 短い識別子(例: `'display'`)を使います。runtime が React `key` および wrapper `<div>` の `data-ampless-plugin` / `data-ampless-position` 属性を組むときに `${instanceId ?? name}:${id}` で resolve するので、plugin 作者が自前で namespace を埋め込む必要はありません。validator は `id` が空、制御文字を含む、64 文字超のいずれかなら descriptor を drop します。
|
|
541
|
+
|
|
542
|
+
**dedupe は position ごと。** 1 つの plugin instance が `beforeContent` と `afterContent` の両方に同じ `id` を返すのは OK(dedupe スコープが独立)。同じ position に同じ `id` を 2 回返すと最初の 1 件を残して 2 件目を warn 付きで drop します。
|
|
543
|
+
|
|
544
|
+
**複数 instance。** distinct な `instanceId` を持つ 2 つの `reading-time` instance(例: `reading-time-en` / `reading-time-jp`)は、同じ position に `id: 'display'` を返しても両方残ります(namespace が違うため)。
|
|
545
|
+
|
|
473
546
|
### クライアントサイドの DOM 操作はしない
|
|
474
547
|
|
|
475
548
|
`publicHead` または `publicBodyEnd` から返したインラインスクリプトは、React がページを hydrate する前、HTML のパース中に実行されます。**React が管理するサブツリー内の見える DOM を操作してはいけません** — hydration が走ると React は仮想 DOM と合わないツリーを検出し、`Hydration failed because the server rendered HTML didn't match the client` エラーを投げてサブツリーをゼロから再生成します。挿入したノードは消えてしまいます。
|
|
@@ -488,7 +561,7 @@ React 19 はさらに、クライアントコンポーネントのレンダー
|
|
|
488
561
|
- テーマが描画した要素のクラス / 属性 / テキストコンテンツの変更
|
|
489
562
|
- クライアントサイドで `#post-body` のような要素を読み取って投稿単位の HTML を挿入する — 現在 `publicHead`-for-post に相当するサーフェスはなく、サーバーレンダリング済みのサブツリーをクライアントサイドで書き換えると hydration と競合します
|
|
490
563
|
|
|
491
|
-
|
|
564
|
+
投稿単位の見える出力には `publicHtmlForPost` を使ってください(Phase 6d — 上の例と §6 の `PublicPostHtmlDescriptor` 参照)。runtime が post 本文の周囲の固定スロットにサーバーサイド HTML を出すので、hydration と競合しません。
|
|
492
565
|
|
|
493
566
|
---
|
|
494
567
|
|
|
@@ -35,6 +35,7 @@ your code where future-you (and other site authors) will look for it.
|
|
|
35
35
|
| Trusted side effects (S3 writes, external API push) | | ✓ (`writePublicAsset` + `trusted`) |
|
|
36
36
|
| Theme-independent `<head>` / `<body>` injection (analytics, consent) | | ✓ (`publicHead` / `publicBodyEnd`) |
|
|
37
37
|
| Per-post machine-readable metadata (JSON-LD, etc.) | | ✓ (`schema` via `publicBodyForPost`) |
|
|
38
|
+
| Per-post visible HTML around the body (reading-time, breadcrumb, share) | | ✓ (`publicHtmlForPost`) |
|
|
38
39
|
| Code you want to share across multiple ampless sites | | ✓ (publish as npm package) |
|
|
39
40
|
|
|
40
41
|
Rule of thumb:
|
|
@@ -88,6 +89,7 @@ surfaces:
|
|
|
88
89
|
| `publicHead(ctx)` | root layout `<head>` | sync (called from async layout) | 1 |
|
|
89
90
|
| `publicBodyEnd(ctx)` | root layout end of `<body>` | sync | 1 |
|
|
90
91
|
| `publicBodyForPost(post, ctx)` | theme post page template (per-post) | sync | 4 |
|
|
92
|
+
| `publicHtmlForPost(post, ctx)` | theme post page template (per-post, visible HTML) | sync | 6d |
|
|
91
93
|
| `ogImage` | `/og/[slug]` route | request-time, in public Lambda | Existing |
|
|
92
94
|
| `hooks` | trust_level-matched processor Lambda | async, on SQS event | Existing |
|
|
93
95
|
| `settings.public` | `/admin/plugins` form | declarative manifest | 2 |
|
|
@@ -210,6 +212,7 @@ interface AmplessPlugin {
|
|
|
210
212
|
publicHead?(ctx): readonly PublicHeadDescriptor[]
|
|
211
213
|
publicBodyEnd?(ctx): readonly PublicBodyDescriptor[]
|
|
212
214
|
publicBodyForPost?(post: Post, ctx): readonly PublicPostBodyDescriptor[]
|
|
215
|
+
publicHtmlForPost?(post: Post, ctx): readonly PublicPostHtmlDescriptor[]
|
|
213
216
|
ogImage?: OgImageConfig
|
|
214
217
|
settings?: { public?: readonly PluginSettingField[] }
|
|
215
218
|
}
|
|
@@ -380,6 +383,7 @@ elevated AWS access.
|
|
|
380
383
|
| `publicHead(ctx)` | `PublicHeadDescriptor[]` | Analytics loader, fonts, jsonld, hreflang |
|
|
381
384
|
| `publicBodyEnd(ctx)` | `PublicBodyDescriptor[]` | GTM no-script frame, chat widgets, tail snippets |
|
|
382
385
|
| `publicBodyForPost(post, ctx)` | `PublicPostBodyDescriptor[]` | Per-post body injection — JSON-LD structured data; rendered by the theme's post page template |
|
|
386
|
+
| `publicHtmlForPost(post, ctx)` | `PublicPostHtmlDescriptor[]` | Per-post visible HTML at `beforeContent` / `afterContent` — reading-time badge, breadcrumb, share links. Bodies are sanitized by the runtime under a strict `sanitize-html` allowlist |
|
|
383
387
|
|
|
384
388
|
The `ctx` object carries:
|
|
385
389
|
|
|
@@ -483,6 +487,27 @@ behaviour. `publicBodyForPost` exists solely for the structured data
|
|
|
483
487
|
use-case (`<script type="application/ld+json">`) that `generateMetadata`
|
|
484
488
|
cannot produce.
|
|
485
489
|
|
|
490
|
+
### `PublicPostHtmlDescriptor` (Phase 6d)
|
|
491
|
+
|
|
492
|
+
`publicHtmlForPost` returns `PublicPostHtmlDescriptor[]`:
|
|
493
|
+
|
|
494
|
+
```ts
|
|
495
|
+
{
|
|
496
|
+
type: 'html',
|
|
497
|
+
id: 'display', // plugin-local short identifier (≤ 64 chars, no control chars)
|
|
498
|
+
position: 'beforeContent' | 'afterContent',
|
|
499
|
+
body: '<p class="reading-time">~3 min read</p>',
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
The runtime sanitizes `body` with `sanitize-html` under a strict
|
|
504
|
+
allowlist (see the `publicHtmlForPost` example above for the full
|
|
505
|
+
allowlist + dropped-tag list) and wraps the result in
|
|
506
|
+
`<div data-ampless-plugin="${namespace}" data-ampless-position="${position}">`.
|
|
507
|
+
Themes embed `{html.beforeContent}` / `{html.afterContent}` in
|
|
508
|
+
`pages/post.tsx`; they never call `dangerouslySetInnerHTML` on plugin
|
|
509
|
+
output themselves.
|
|
510
|
+
|
|
486
511
|
### JSON-LD auto-escape
|
|
487
512
|
|
|
488
513
|
When `scriptType === 'application/ld+json'`, the runtime **automatically
|
|
@@ -574,6 +599,80 @@ and renders the returned descriptors. The runtime inserts a
|
|
|
574
599
|
`<script type="application/ld+json">` element with the auto-escaped
|
|
575
600
|
body into the page.
|
|
576
601
|
|
|
602
|
+
### `publicHtmlForPost` example (Phase 6d)
|
|
603
|
+
|
|
604
|
+
Use `publicHtmlForPost` when you need to render **visible HTML**
|
|
605
|
+
around a post — reading-time badge, breadcrumb, share links,
|
|
606
|
+
micro-format annotations, etc. The runtime sanitizes the body before
|
|
607
|
+
rendering and embeds the result at the `beforeContent` or
|
|
608
|
+
`afterContent` slot, so themes never call `dangerouslySetInnerHTML`
|
|
609
|
+
on plugin output.
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
import { definePlugin } from 'ampless'
|
|
613
|
+
|
|
614
|
+
export default function readingTimePlugin() {
|
|
615
|
+
return definePlugin({
|
|
616
|
+
name: 'reading-time',
|
|
617
|
+
apiVersion: 1,
|
|
618
|
+
trust_level: 'untrusted',
|
|
619
|
+
capabilities: ['publicHtmlForPost'],
|
|
620
|
+
publicHtmlForPost(post, _ctx) {
|
|
621
|
+
const words = countWords(post)
|
|
622
|
+
const minutes = Math.max(1, Math.round(words / 200))
|
|
623
|
+
return [{
|
|
624
|
+
type: 'html',
|
|
625
|
+
id: 'display',
|
|
626
|
+
position: 'beforeContent',
|
|
627
|
+
body: `<p class="reading-time" data-words="${words}" data-minutes="${minutes}">~${minutes} min read</p>`,
|
|
628
|
+
}]
|
|
629
|
+
},
|
|
630
|
+
})
|
|
631
|
+
}
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
The theme's `pages/post.tsx` calls
|
|
635
|
+
`const html = await ampless.publicHtmlForPost(post)` once and embeds
|
|
636
|
+
the slots:
|
|
637
|
+
|
|
638
|
+
```tsx
|
|
639
|
+
{postBody} {/* publicBodyForPost — JSON-LD */}
|
|
640
|
+
{html.beforeContent} {/* publicHtmlForPost — beforeContent slot */}
|
|
641
|
+
<div className="prose" dangerouslySetInnerHTML={{ __html: renderBody(post) }} />
|
|
642
|
+
{html.afterContent} {/* publicHtmlForPost — afterContent slot */}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
**Slot positions** (v1): `'beforeContent'` and `'afterContent'`.
|
|
646
|
+
|
|
647
|
+
**Sanitizer (strict, identical across trust levels):**
|
|
648
|
+
|
|
649
|
+
- Allowed tags: `p` · `span` · `strong` · `em` · `a` · `code` · `br` · `ul` · `ol` · `li`
|
|
650
|
+
- Allowed global attributes: `class` · `data-words` · `data-minutes` · `data-ampless-*`
|
|
651
|
+
- Allowed `<a>` attributes: `href` · `rel` · `target`. `target="_blank"` triggers auto-injection of `rel="noopener noreferrer"`.
|
|
652
|
+
- Allowed URL schemes on `href`: `http` / `https`. Relative URLs (`./path`, `../path`, `/path`, `#anchor`) pass through. `javascript:` / `data:` / `mailto:` / `tel:` / `vbscript:` are dropped.
|
|
653
|
+
- Dropped tags / attributes: `<img>` · `<iframe>` · `<video>` · `<audio>` · `<object>` · `<embed>` · `<form>` · `<style>` · inline `style` · all event handlers (`on*`).
|
|
654
|
+
|
|
655
|
+
If you need a tag outside this list, open an issue — the allowlist
|
|
656
|
+
expands by design, not by escape hatch.
|
|
657
|
+
|
|
658
|
+
**`id` is plugin-local.** Use a short identifier (e.g. `'display'`).
|
|
659
|
+
The runtime resolves it to `${instanceId ?? name}:${id}` when building
|
|
660
|
+
the React `key` and the wrapper `<div>`'s `data-ampless-plugin` /
|
|
661
|
+
`data-ampless-position` attributes. Do not embed your own namespace
|
|
662
|
+
in `id`. The validator drops descriptors whose `id` is empty,
|
|
663
|
+
contains control characters, or exceeds 64 characters.
|
|
664
|
+
|
|
665
|
+
**Dedupe is per-position.** Returning the same `id` to both
|
|
666
|
+
`beforeContent` and `afterContent` from a single plugin instance is
|
|
667
|
+
fine — they live in independent dedupe scopes. Returning the same
|
|
668
|
+
`id` twice to the same position keeps only the first occurrence and
|
|
669
|
+
warns.
|
|
670
|
+
|
|
671
|
+
**Multiple instances.** Two `reading-time` plugin instances with
|
|
672
|
+
distinct `instanceId` (e.g. `reading-time-en` / `reading-time-jp`)
|
|
673
|
+
can both emit `id: 'display'` to the same position; the runtime
|
|
674
|
+
keeps both because the namespaces differ.
|
|
675
|
+
|
|
577
676
|
### Client-side DOM mutation: don't
|
|
578
677
|
|
|
579
678
|
Inline scripts you return from `publicHead` or `publicBodyEnd` execute
|
|
@@ -612,10 +711,10 @@ something like `document.body.append(myNewElement)` may not even fire.
|
|
|
612
711
|
any client-side rewrite of a server-rendered subtree races against
|
|
613
712
|
hydration
|
|
614
713
|
|
|
615
|
-
For visible per-post output,
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
714
|
+
For visible per-post output, use `publicHtmlForPost` (Phase 6d — see
|
|
715
|
+
the example above and §6's `PublicPostHtmlDescriptor`). The runtime
|
|
716
|
+
emits server-side HTML at fixed slots around the post body, so
|
|
717
|
+
nothing races against hydration.
|
|
619
718
|
|
|
620
719
|
---
|
|
621
720
|
|
|
@@ -25,20 +25,20 @@
|
|
|
25
25
|
"@tiptap/pm": "^3.23.6",
|
|
26
26
|
"@tiptap/react": "^3.23.6",
|
|
27
27
|
"@tiptap/starter-kit": "^3.23.6",
|
|
28
|
-
"@ampless/plugin-analytics-ga4": "^0.2.0-alpha.
|
|
29
|
-
"@ampless/plugin-cookie-consent": "^0.1.0-alpha.
|
|
30
|
-
"@ampless/plugin-gtm": "^0.2.0-alpha.
|
|
31
|
-
"@ampless/plugin-og-image": "^0.2.0-alpha.
|
|
32
|
-
"@ampless/plugin-plausible": "^0.2.0-alpha.
|
|
33
|
-
"@ampless/plugin-rss": "^0.2.0-alpha.
|
|
34
|
-
"@ampless/plugin-schema-jsonld": "^0.1.1-alpha.
|
|
35
|
-
"@ampless/plugin-seo": "^0.2.0-alpha.
|
|
36
|
-
"@ampless/plugin-webhook": "^0.2.0-alpha.
|
|
37
|
-
"@ampless/admin": "^1.0.0-alpha.
|
|
38
|
-
"@ampless/backend": "^1.0.0-alpha.
|
|
39
|
-
"@ampless/runtime": "^1.0.0-alpha.
|
|
28
|
+
"@ampless/plugin-analytics-ga4": "^0.2.0-alpha.12",
|
|
29
|
+
"@ampless/plugin-cookie-consent": "^0.1.0-alpha.3",
|
|
30
|
+
"@ampless/plugin-gtm": "^0.2.0-alpha.11",
|
|
31
|
+
"@ampless/plugin-og-image": "^0.2.0-alpha.28",
|
|
32
|
+
"@ampless/plugin-plausible": "^0.2.0-alpha.11",
|
|
33
|
+
"@ampless/plugin-rss": "^0.2.0-alpha.28",
|
|
34
|
+
"@ampless/plugin-schema-jsonld": "^0.1.1-alpha.7",
|
|
35
|
+
"@ampless/plugin-seo": "^0.2.0-alpha.28",
|
|
36
|
+
"@ampless/plugin-webhook": "^0.2.0-alpha.28",
|
|
37
|
+
"@ampless/admin": "^1.0.0-alpha.57",
|
|
38
|
+
"@ampless/backend": "^1.0.0-alpha.45",
|
|
39
|
+
"@ampless/runtime": "^1.0.0-alpha.36",
|
|
40
40
|
"@digital-go-jp/tailwind-theme-plugin": "^0.3.4",
|
|
41
|
-
"ampless": "^1.0.0-alpha.
|
|
41
|
+
"ampless": "^1.0.0-alpha.28",
|
|
42
42
|
"aws-amplify": "^6.17.0",
|
|
43
43
|
"class-variance-authority": "^0.7.1",
|
|
44
44
|
"clsx": "^2.1.1",
|
|
@@ -39,6 +39,7 @@ export default async function BlogPost({ params }: PostCtx) {
|
|
|
39
39
|
if (!post) notFound()
|
|
40
40
|
|
|
41
41
|
const postBody = await ampless.publicBodyForPost(post)
|
|
42
|
+
const html = await ampless.publicHtmlForPost(post)
|
|
42
43
|
|
|
43
44
|
const defaultLightbox = settings.media.imageDisplay === 'lightbox'
|
|
44
45
|
const maxWidth = settings.media.imageMaxWidth ?? '100%'
|
|
@@ -79,6 +80,8 @@ export default async function BlogPost({ params }: PostCtx) {
|
|
|
79
80
|
|
|
80
81
|
{postBody}
|
|
81
82
|
|
|
83
|
+
{html.beforeContent}
|
|
84
|
+
|
|
82
85
|
<div
|
|
83
86
|
id="post-body"
|
|
84
87
|
className="prose prose-neutral dark:prose-invert max-w-none [&_img]:max-w-[var(--ampless-img-max-width)] [&_img]:mx-auto"
|
|
@@ -86,6 +89,8 @@ export default async function BlogPost({ params }: PostCtx) {
|
|
|
86
89
|
dangerouslySetInnerHTML={{ __html: renderBody(post) }}
|
|
87
90
|
/>
|
|
88
91
|
|
|
92
|
+
{html.afterContent}
|
|
93
|
+
|
|
89
94
|
<TagList tags={post.tags} className="mt-8 border-t pt-6" />
|
|
90
95
|
|
|
91
96
|
<footer className="mt-6 flex flex-wrap items-center gap-x-6 gap-y-2 text-sm text-gray-500">
|
|
@@ -29,6 +29,7 @@ export default async function CorporatePost({ params }: PostCtx) {
|
|
|
29
29
|
if (!post) notFound()
|
|
30
30
|
|
|
31
31
|
const postBody = await ampless.publicBodyForPost(post)
|
|
32
|
+
const html = await ampless.publicHtmlForPost(post)
|
|
32
33
|
|
|
33
34
|
const defaultLightbox = settings.media.imageDisplay === 'lightbox'
|
|
34
35
|
const maxWidth = settings.media.imageMaxWidth ?? '100%'
|
|
@@ -68,6 +69,8 @@ export default async function CorporatePost({ params }: PostCtx) {
|
|
|
68
69
|
|
|
69
70
|
{postBody}
|
|
70
71
|
|
|
72
|
+
{html.beforeContent}
|
|
73
|
+
|
|
71
74
|
<div
|
|
72
75
|
id="post-body"
|
|
73
76
|
className="prose prose-neutral dark:prose-invert max-w-none [&_img]:max-w-[var(--ampless-img-max-width)] [&_img]:mx-auto"
|
|
@@ -75,6 +78,8 @@ export default async function CorporatePost({ params }: PostCtx) {
|
|
|
75
78
|
dangerouslySetInnerHTML={{ __html: renderBody(post) }}
|
|
76
79
|
/>
|
|
77
80
|
|
|
81
|
+
{html.afterContent}
|
|
82
|
+
|
|
78
83
|
<TagList tags={post.tags} className="mt-8 border-t pt-6" />
|
|
79
84
|
</article>
|
|
80
85
|
|
|
@@ -29,6 +29,7 @@ export default async function DadsPost({ params }: PostCtx) {
|
|
|
29
29
|
if (!post) notFound()
|
|
30
30
|
|
|
31
31
|
const postBody = await ampless.publicBodyForPost(post)
|
|
32
|
+
const html = await ampless.publicHtmlForPost(post)
|
|
32
33
|
|
|
33
34
|
const defaultLightbox = settings.media.imageDisplay === 'lightbox'
|
|
34
35
|
const maxWidth = settings.media.imageMaxWidth ?? '100%'
|
|
@@ -73,6 +74,8 @@ export default async function DadsPost({ params }: PostCtx) {
|
|
|
73
74
|
|
|
74
75
|
{postBody}
|
|
75
76
|
|
|
77
|
+
{html.beforeContent}
|
|
78
|
+
|
|
76
79
|
<div
|
|
77
80
|
id="post-body"
|
|
78
81
|
className="prose prose-neutral dark:prose-invert max-w-none [&_a]:text-[var(--primary)] [&_a]:underline-offset-4 [&_img]:max-w-[var(--ampless-img-max-width)] [&_img]:mx-auto"
|
|
@@ -80,6 +83,8 @@ export default async function DadsPost({ params }: PostCtx) {
|
|
|
80
83
|
dangerouslySetInnerHTML={{ __html: renderBody(post) }}
|
|
81
84
|
/>
|
|
82
85
|
|
|
86
|
+
{html.afterContent}
|
|
87
|
+
|
|
83
88
|
<TagList tags={post.tags} className="mt-10 border-t pt-6" />
|
|
84
89
|
</article>
|
|
85
90
|
|
|
@@ -33,6 +33,7 @@ export default async function DocsPost({ params }: PostCtx) {
|
|
|
33
33
|
if (!post) notFound()
|
|
34
34
|
|
|
35
35
|
const postBody = await ampless.publicBodyForPost(post)
|
|
36
|
+
const html = await ampless.publicHtmlForPost(post)
|
|
36
37
|
|
|
37
38
|
const defaultLightbox = settings.media.imageDisplay === 'lightbox'
|
|
38
39
|
const maxWidth = settings.media.imageMaxWidth ?? '100%'
|
|
@@ -70,6 +71,8 @@ export default async function DocsPost({ params }: PostCtx) {
|
|
|
70
71
|
|
|
71
72
|
{postBody}
|
|
72
73
|
|
|
74
|
+
{html.beforeContent}
|
|
75
|
+
|
|
73
76
|
<div
|
|
74
77
|
id="post-body"
|
|
75
78
|
className="prose prose-neutral dark:prose-invert max-w-none [&_img]:max-w-[var(--ampless-img-max-width)] [&_img]:mx-auto"
|
|
@@ -77,6 +80,8 @@ export default async function DocsPost({ params }: PostCtx) {
|
|
|
77
80
|
dangerouslySetInnerHTML={{ __html: renderBody(post) }}
|
|
78
81
|
/>
|
|
79
82
|
|
|
83
|
+
{html.afterContent}
|
|
84
|
+
|
|
80
85
|
<TagList tags={post.tags} className="mt-10 border-t pt-6" />
|
|
81
86
|
</article>
|
|
82
87
|
</main>
|
|
@@ -29,6 +29,7 @@ export default async function LandingPost({ params }: PostCtx) {
|
|
|
29
29
|
if (!post) notFound()
|
|
30
30
|
|
|
31
31
|
const postBody = await ampless.publicBodyForPost(post)
|
|
32
|
+
const html = await ampless.publicHtmlForPost(post)
|
|
32
33
|
|
|
33
34
|
const defaultLightbox = settings.media.imageDisplay === 'lightbox'
|
|
34
35
|
const maxWidth = settings.media.imageMaxWidth ?? '100%'
|
|
@@ -70,6 +71,8 @@ export default async function LandingPost({ params }: PostCtx) {
|
|
|
70
71
|
|
|
71
72
|
{postBody}
|
|
72
73
|
|
|
74
|
+
{html.beforeContent}
|
|
75
|
+
|
|
73
76
|
<div
|
|
74
77
|
id="post-body"
|
|
75
78
|
className="prose prose-neutral dark:prose-invert max-w-none [&_img]:max-w-[var(--ampless-img-max-width)] [&_img]:mx-auto"
|
|
@@ -77,6 +80,8 @@ export default async function LandingPost({ params }: PostCtx) {
|
|
|
77
80
|
dangerouslySetInnerHTML={{ __html: renderBody(post) }}
|
|
78
81
|
/>
|
|
79
82
|
|
|
83
|
+
{html.afterContent}
|
|
84
|
+
|
|
80
85
|
<TagList tags={post.tags} className="mt-10 border-t pt-6" />
|
|
81
86
|
</article>
|
|
82
87
|
|
|
@@ -26,6 +26,7 @@ export default async function MinimalPost({ params }: PostCtx) {
|
|
|
26
26
|
if (!post) notFound()
|
|
27
27
|
|
|
28
28
|
const postBody = await ampless.publicBodyForPost(post)
|
|
29
|
+
const html = await ampless.publicHtmlForPost(post)
|
|
29
30
|
|
|
30
31
|
const defaultLightbox = settings.media.imageDisplay === 'lightbox'
|
|
31
32
|
const maxWidth = settings.media.imageMaxWidth ?? '100%'
|
|
@@ -51,6 +52,8 @@ export default async function MinimalPost({ params }: PostCtx) {
|
|
|
51
52
|
|
|
52
53
|
{postBody}
|
|
53
54
|
|
|
55
|
+
{html.beforeContent}
|
|
56
|
+
|
|
54
57
|
<div
|
|
55
58
|
id="post-body"
|
|
56
59
|
className="prose prose-neutral dark:prose-invert max-w-none [&_img]:max-w-[var(--ampless-img-max-width)] [&_img]:mx-auto"
|
|
@@ -58,6 +61,8 @@ export default async function MinimalPost({ params }: PostCtx) {
|
|
|
58
61
|
dangerouslySetInnerHTML={{ __html: renderBody(post) }}
|
|
59
62
|
/>
|
|
60
63
|
|
|
64
|
+
{html.afterContent}
|
|
65
|
+
|
|
61
66
|
<TagList tags={post.tags} className="mt-8 border-t pt-6" />
|
|
62
67
|
</article>
|
|
63
68
|
|