@ampless/plugin-ai-actions 0.1.0-beta.0
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/LICENSE +21 -0
- package/README.ja.md +101 -0
- package/README.md +101 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +158 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ampless contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
> English: [README.md](./README.md)
|
|
2
|
+
>
|
|
3
|
+
|
|
4
|
+
# @ampless/plugin-ai-actions
|
|
5
|
+
|
|
6
|
+
[ampless](https://github.com/heavymoons/ampless) の投稿ページ向け、人間から AI への導線プラグイン。本文の前後に最大 3 つのリンクを持つ小さな `<p class="ampless-ai-actions">` 要素を挿入します。
|
|
7
|
+
|
|
8
|
+
> **プレリリース / ベータ版。** v1.0 まではマイナーバージョンでも破壊的変更が入る可能性があります。
|
|
9
|
+
|
|
10
|
+
リンクは `publicHtmlForPost` capability(Phase 6d)経由で出力されます。`ampless.publicHtmlForPost(post)` を呼ぶテーマなら自動的に描画されます。HTML はランタイムが `sanitize-html` で strict allowlist に沿ってサニタイズするため、テーマ側で `dangerouslySetInnerHTML` を使う必要はありません。
|
|
11
|
+
|
|
12
|
+
AWS のデータ権限は不要です。すべて公開 Next.js プロセスのリクエスト時に動作します。`trust_level` は `untrusted`。
|
|
13
|
+
|
|
14
|
+
## `ai.markdownRoutes` が前提
|
|
15
|
+
|
|
16
|
+
**このプラグインが出力する全アクションは投稿の `/<slug>.md` Markdown 投影に依存します** — 2 つの外部 AI リンクも、prompt の `?q=` に絶対 `.md` URL を渡します。`cms.config.ts` で `ai.markdownRoutes: false` を設定しているサイトでは、「View as Markdown」リンクは 404 になり、Claude/ChatGPT リンク自体は開くものの、AI に渡る `.md` URL が存在しないため機能しません。**`ai.markdownRoutes` を無効化したサイトではこのプラグインを登録しないでください** — `ai.markdownRoutes` はデフォルトで有効なのでほとんどのサイトでは意識不要ですが、インストール前に確認してください。
|
|
17
|
+
|
|
18
|
+
## インストール
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @ampless/plugin-ai-actions@beta
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 設定
|
|
25
|
+
|
|
26
|
+
`cms.config.ts` に記述します:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { defineConfig } from 'ampless'
|
|
30
|
+
import aiActionsPlugin from '@ampless/plugin-ai-actions'
|
|
31
|
+
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
// ...
|
|
34
|
+
plugins: [
|
|
35
|
+
aiActionsPlugin(),
|
|
36
|
+
],
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| オプション | デフォルト | 備考 |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `showMarkdownLink` | `true` | `/<slug>.md` への「Markdown で表示」リンク。 |
|
|
43
|
+
| `showClaude` | `false` | 「Claude で開く」リンク。**opt-in** — 下記[外部 AI リンクについて](#外部-ai-リンクopt-in)を参照。 |
|
|
44
|
+
| `showChatgpt` | `false` | 「ChatGPT で開く」リンク。**opt-in** — 同上の注意点。 |
|
|
45
|
+
| `promptTemplate` | `'Read {url}'` | Claude/ChatGPT の `?q=` prefill に使うプロンプト。`{url}` は絶対 `.md` URL に置換されます。 |
|
|
46
|
+
| `position` | `'afterContent'` | `'beforeContent'` または `'afterContent'`。`@ampless/plugin-reading-time` とは逆のデフォルト — 記事を読んでから AI アクションを提示する意図。 |
|
|
47
|
+
| `instanceId` | `'ai-actions'` | ランタイムのキー解決に使う namespace。同じサイトで 2 回登録する場合のみ変更する。 |
|
|
48
|
+
|
|
49
|
+
`instanceId` を除く表示設定はデプロイなしで `/admin/plugins → AI アクション` から編集できます。コンストラクタの値は初期デフォルトに過ぎません。`instanceId` は `settings.public` に含まれないため、`cms.config.ts` でのみ設定できます。
|
|
50
|
+
|
|
51
|
+
## 外部 AI リンク(opt-in)
|
|
52
|
+
|
|
53
|
+
`showClaude` と `showChatgpt` はデフォルト **オフ** です。`https://claude.ai/new?q=...` と `https://chatgpt.com/?q=...` の URL prefill パターンは**広く使われているコミュニティ慣習**であり、Anthropic / OpenAI が公式に文書化・バージョン管理している URL 契約ではありません。ログイン状態(ログイン済み / 未ログイン)やプラットフォーム(デスクトップ / モバイル)によって挙動が異なる可能性があり、どちらのベンダーも予告なくこのクエリパラメータを変更・廃止し得ます。
|
|
54
|
+
|
|
55
|
+
どちらかのリンクを有効化する前に、自サイトで以下を確認してください:
|
|
56
|
+
|
|
57
|
+
- デスクトップでログイン済みの状態でプロンプトが正しく prefill されるか
|
|
58
|
+
- モバイルで正しく prefill されるか(ネイティブアプリとモバイルブラウザで挙動が異なることがある)
|
|
59
|
+
- 未ログイン状態でどうなるか
|
|
60
|
+
|
|
61
|
+
挙動が不安定であれば、リンクをオフのままにしてください — 「Markdown で表示」リンク(デフォルトでオン)だけでも、読者や AI ツールに綺麗な Markdown の入口を提供できます。読者はそれを開いて任意の AI チャットに手動でコピー & ペーストできます。
|
|
62
|
+
|
|
63
|
+
## 「Copy Markdown」ボタンが無い理由
|
|
64
|
+
|
|
65
|
+
初期検討では「Copy Markdown」ボタン(`onclick` によるクリップボード書き込み)も候補にありましたが、現行のプラグイン surface では安全に実現できないため実装していません:
|
|
66
|
+
|
|
67
|
+
- `publicHtmlForPost` の sanitizer はすべてのインラインイベントハンドラ(`onclick` 等)と `<button>` 要素を drop します — サニタイズ仕様は `@ampless/runtime` を参照。
|
|
68
|
+
- `publicPostScript`(ページ JS を追加できるもう一つの plugin surface)は外部の絶対 `http(s)` script `src` のみを受け付け、投稿単位のロジックを書けるインラインスクリプトチャネルは現状ありません。
|
|
69
|
+
|
|
70
|
+
inline-script capability の新設か plugin asset 配信の仕組みが無いと、この descriptor ベースの surface にクリップボード動作を配線する方法がありません。将来 Copy を実現できる capability が入るまでは、「Markdown で表示」+ ブラウザの「全選択 → コピー」が実用上の代替です。
|
|
71
|
+
|
|
72
|
+
## 出力 HTML
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<p class="ampless-ai-actions">
|
|
76
|
+
<a class="ampless-ai-actions-md" href="/my-post.md">View as Markdown</a>
|
|
77
|
+
<span class="ampless-ai-actions-sep"> · </span>
|
|
78
|
+
<a class="ampless-ai-actions-claude" href="https://claude.ai/new?q=Read%20https%3A%2F%2Fexample.com%2Fmy-post.md" target="_blank" rel="noopener noreferrer">Open in Claude</a>
|
|
79
|
+
<span class="ampless-ai-actions-sep"> · </span>
|
|
80
|
+
<a class="ampless-ai-actions-chatgpt" href="https://chatgpt.com/?q=Read%20https%3A%2F%2Fexample.com%2Fmy-post.md" target="_blank" rel="noopener noreferrer">Open in ChatGPT</a>
|
|
81
|
+
</p>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
(読みやすさのために改行・インデントを加えています。実際の出力に要素間の空白はありません。)
|
|
85
|
+
|
|
86
|
+
- クラス名(`ampless-ai-actions` / `ampless-ai-actions-md` / `ampless-ai-actions-claude` / `ampless-ai-actions-chatgpt` / `ampless-ai-actions-sep`)はテーマ CSS のための安定したフックです。デフォルト CSS は注入しません。
|
|
87
|
+
- ラベル("View as Markdown" / "Open in Claude" / "Open in ChatGPT")は v1 では英語固定です。ロケール対応は要望が出てから検討します。
|
|
88
|
+
- 「Markdown で表示」リンクは `site.url` が設定されていても常に**相対パス**(`/<slug>.md`)です — ページがどのドメインで配信されていても機能します。
|
|
89
|
+
- Claude/ChatGPT リンクは外部サービスに渡すため**絶対 URL** が必要です。実効 `site.url` が空の場合、有効化していてもこの 2 リンクは省略され、「Markdown で表示」のみ描画されます。
|
|
90
|
+
- 外部リンク(`target="_blank"`)には常に `rel="noopener noreferrer"` が付きます。プラグイン側で生成時点で付与しています(ランタイムの sanitizer も同じ属性を注入しますが、生成元で先に付けておくことでプラグイン自身の sanitize round-trip テストが完全一致になります)。
|
|
91
|
+
|
|
92
|
+
## トラストレベル
|
|
93
|
+
|
|
94
|
+
`untrusted`。`@ampless/runtime` が検証・サニタイズする HTML descriptor を返すだけです。DynamoDB、S3、Lambda プロセッサーには一切触れません。
|
|
95
|
+
|
|
96
|
+
## v1 では対応しないこと
|
|
97
|
+
|
|
98
|
+
- **Copy Markdown(クリップボード)** — 上記[「Copy Markdown」ボタンが無い理由](#copy-markdown-ボタンが無い理由)を参照。
|
|
99
|
+
- **MCP 接続情報** — サイトの MCP endpoint を案内するリンク / QR コードは、将来の公開読み取り専用 MCP サーバーの提供後に検討予定です。
|
|
100
|
+
- **テーマ CSS** — 安定したクラス名は提供しますが、デフォルト CSS は注入しません。
|
|
101
|
+
- **ロケール別ラベル** — 未対応です。リンクテキストは英語固定で、プラグインを複数回登録しても解決になりません(全 instance が同じ position バケットに描画されるため、テーマがロケール別のスロットを選べません)。要望が出た時点で検討します。
|
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
> 日本語版: [README.ja.md](./README.ja.md)
|
|
2
|
+
>
|
|
3
|
+
|
|
4
|
+
# @ampless/plugin-ai-actions
|
|
5
|
+
|
|
6
|
+
Human-to-AI bridge links for [ampless](https://github.com/heavymoons/ampless) post pages. Injects a small `<p class="ampless-ai-actions">` element with up to three links, before or after the post content.
|
|
7
|
+
|
|
8
|
+
> **Pre-release / beta.** Breaking changes possible in any minor version until v1.0.
|
|
9
|
+
|
|
10
|
+
The links are emitted via the `publicHtmlForPost` capability (Phase 6d). Themes that call `ampless.publicHtmlForPost(post)` automatically render it. The runtime sanitizes the HTML with `sanitize-html` under a strict allowlist before it reaches the page — no `dangerouslySetInnerHTML` needed in the theme.
|
|
11
|
+
|
|
12
|
+
No AWS data permissions are required — everything runs at request time inside the public Next.js process. The plugin's `trust_level` is `untrusted`.
|
|
13
|
+
|
|
14
|
+
## Requires `ai.markdownRoutes`
|
|
15
|
+
|
|
16
|
+
**Every action this plugin renders depends on the post's `/<slug>.md` markdown projection** — including the two external AI links, which pass the absolute `.md` URL in the `?q=` prompt. If `ai.markdownRoutes: false` is set in `cms.config.ts`, the "View as Markdown" link 404s, and the Claude/ChatGPT links still open but hand the AI a `.md` URL that doesn't exist. **Do not register this plugin on a site with `ai.markdownRoutes` disabled** — `ai.markdownRoutes` defaults to enabled, so most sites don't need to think about this, but double-check before installing.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @ampless/plugin-ai-actions@beta
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Configure
|
|
25
|
+
|
|
26
|
+
In `cms.config.ts`:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { defineConfig } from 'ampless'
|
|
30
|
+
import aiActionsPlugin from '@ampless/plugin-ai-actions'
|
|
31
|
+
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
// ...
|
|
34
|
+
plugins: [
|
|
35
|
+
aiActionsPlugin(),
|
|
36
|
+
],
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Option | Default | Notes |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `showMarkdownLink` | `true` | "View as Markdown" link to `/<slug>.md`. |
|
|
43
|
+
| `showClaude` | `false` | "Open in Claude" link. **Opt-in** — see [External AI links](#external-ai-links-opt-in) below. |
|
|
44
|
+
| `showChatgpt` | `false` | "Open in ChatGPT" link. **Opt-in** — same caveat. |
|
|
45
|
+
| `promptTemplate` | `'Read {url}'` | Prompt used for the Claude/ChatGPT `?q=` prefill. `{url}` is replaced with the absolute `.md` URL. |
|
|
46
|
+
| `position` | `'afterContent'` | `'beforeContent'` or `'afterContent'`. Opposite default of `@ampless/plugin-reading-time` — read the article, *then* offer the AI actions. |
|
|
47
|
+
| `instanceId` | `'ai-actions'` | Namespace used for runtime key resolution. Change only if registering the plugin twice. |
|
|
48
|
+
|
|
49
|
+
All display options except `instanceId` are also editable from `/admin/plugins → AI actions` without a redeploy — the constructor values above are just the initial defaults. `instanceId` is not part of `settings.public`, so it can only be set in `cms.config.ts`.
|
|
50
|
+
|
|
51
|
+
## External AI links (opt-in)
|
|
52
|
+
|
|
53
|
+
`showClaude` and `showChatgpt` default to **off**. The `https://claude.ai/new?q=...` and `https://chatgpt.com/?q=...` URL prefill pattern is a **widely-used community convention**, not a documented, versioned URL contract published by Anthropic or OpenAI. Behavior may differ by login state (signed in vs. signed out) and by platform (desktop vs. mobile), and either vendor could change or remove the query parameter without notice.
|
|
54
|
+
|
|
55
|
+
Before enabling either link, verify on your own site:
|
|
56
|
+
|
|
57
|
+
- Does the prompt prefill correctly when the visitor is logged in on desktop?
|
|
58
|
+
- Does it prefill correctly on mobile (native app vs. mobile browser can differ)?
|
|
59
|
+
- What happens when the visitor is signed out?
|
|
60
|
+
|
|
61
|
+
If the behavior is unreliable for your audience, leave the link off — the "View as Markdown" link alone (default on) already gives readers and AI tools a clean markdown entry point; a reader can open it and copy/paste into any AI chat manually.
|
|
62
|
+
|
|
63
|
+
## Why no "Copy Markdown" button
|
|
64
|
+
|
|
65
|
+
An earlier design considered a "Copy Markdown" button (clipboard write via `onclick`). It isn't implemented because the current plugin surface can't support it safely:
|
|
66
|
+
|
|
67
|
+
- `publicHtmlForPost`'s sanitizer drops all inline event handlers (`onclick`, etc.) and `<button>` elements — see the sanitizer profile in `@ampless/runtime`.
|
|
68
|
+
- `publicPostScript` (the other plugin surface that can add page JS) only accepts an external absolute `http(s)` script `src`; there is no inline-script channel for per-post logic today.
|
|
69
|
+
|
|
70
|
+
Without an inline-script capability or a plugin asset delivery mechanism, there's no way to wire a clipboard action into this descriptor-based surface. "View as Markdown" + browser "select all → copy" is the pragmatic substitute until a future capability makes Copy possible.
|
|
71
|
+
|
|
72
|
+
## Output HTML
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<p class="ampless-ai-actions">
|
|
76
|
+
<a class="ampless-ai-actions-md" href="/my-post.md">View as Markdown</a>
|
|
77
|
+
<span class="ampless-ai-actions-sep"> · </span>
|
|
78
|
+
<a class="ampless-ai-actions-claude" href="https://claude.ai/new?q=Read%20https%3A%2F%2Fexample.com%2Fmy-post.md" target="_blank" rel="noopener noreferrer">Open in Claude</a>
|
|
79
|
+
<span class="ampless-ai-actions-sep"> · </span>
|
|
80
|
+
<a class="ampless-ai-actions-chatgpt" href="https://chatgpt.com/?q=Read%20https%3A%2F%2Fexample.com%2Fmy-post.md" target="_blank" rel="noopener noreferrer">Open in ChatGPT</a>
|
|
81
|
+
</p>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
(Whitespace added above for readability — the actual output has no whitespace between elements.)
|
|
85
|
+
|
|
86
|
+
- The class names (`ampless-ai-actions`, `ampless-ai-actions-md`, `ampless-ai-actions-claude`, `ampless-ai-actions-chatgpt`, `ampless-ai-actions-sep`) are stable hooks for theme CSS. No default styling is injected.
|
|
87
|
+
- Labels ("View as Markdown", "Open in Claude", "Open in ChatGPT") are fixed English strings in v1 — locale-aware labels are deferred until requested.
|
|
88
|
+
- The "View as Markdown" link is always **relative** (`/<slug>.md`), even when `site.url` is configured — it works regardless of the domain the page is served from.
|
|
89
|
+
- The Claude/ChatGPT links require an **absolute** `.md` URL (external services need a full URL). When the effective `site.url` is empty, those two links are omitted even if enabled — only "View as Markdown" renders.
|
|
90
|
+
- External links (`target="_blank"`) always carry `rel="noopener noreferrer"`, generated up front by the plugin (the runtime's sanitizer would also inject it, but generating it at the source keeps the plugin's own sanitize round-trip test exact-match).
|
|
91
|
+
|
|
92
|
+
## Trust level
|
|
93
|
+
|
|
94
|
+
`untrusted`. The plugin only emits an HTML descriptor validated and sanitized by `@ampless/runtime`. It does not access DynamoDB, S3, or any Lambda processor.
|
|
95
|
+
|
|
96
|
+
## What it does not do (v1)
|
|
97
|
+
|
|
98
|
+
- **Copy Markdown (clipboard)** — see [Why no "Copy Markdown" button](#why-no-copy-markdown-button) above.
|
|
99
|
+
- **MCP connection info** — a link/QR code pointing readers at the site's MCP endpoint is planned for a later phase, once the public read-only MCP server ships.
|
|
100
|
+
- **Theme CSS** — stable class names are provided; no default CSS is injected.
|
|
101
|
+
- **Locale-aware labels** — not supported. Link text is a fixed English string, and registering the plugin multiple times does not help: all instances render into the same position bucket, so themes cannot pick a per-locale slot. Deferred until requested.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { AmplessPlugin } from 'ampless';
|
|
2
|
+
|
|
3
|
+
type AiActionsPosition = 'beforeContent' | 'afterContent';
|
|
4
|
+
interface AiActionsOptions {
|
|
5
|
+
/** Show the "View as Markdown" link. Default `true`. */
|
|
6
|
+
showMarkdownLink?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Show the "Open in Claude" link. Default `false` — opt-in.
|
|
9
|
+
* `https://claude.ai/new?q=` prefill is a community convention, not a
|
|
10
|
+
* documented URL contract; verify it works for your site before
|
|
11
|
+
* enabling. See README.md.
|
|
12
|
+
*/
|
|
13
|
+
showClaude?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Show the "Open in ChatGPT" link. Default `false` — opt-in. Same
|
|
16
|
+
* caveat as `showClaude`: `https://chatgpt.com/?q=` prefill is not a
|
|
17
|
+
* documented, versioned contract.
|
|
18
|
+
*/
|
|
19
|
+
showChatgpt?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Prompt template used to build the `?q=` query param for the Claude /
|
|
22
|
+
* ChatGPT links. `{url}` is replaced with the absolute `.md` URL of the
|
|
23
|
+
* post, then the whole string is `encodeURIComponent`-escaped. Default
|
|
24
|
+
* `'Read {url}'`.
|
|
25
|
+
*/
|
|
26
|
+
promptTemplate?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Where to inject the actions relative to the post body. Default
|
|
29
|
+
* `'afterContent'` — read the article first, then offer the AI actions
|
|
30
|
+
* (intentionally the opposite of `@ampless/plugin-reading-time`'s
|
|
31
|
+
* `'beforeContent'` default).
|
|
32
|
+
*/
|
|
33
|
+
position?: AiActionsPosition;
|
|
34
|
+
/**
|
|
35
|
+
* Optional namespace for this instance. Defaults to `'ai-actions'`.
|
|
36
|
+
* Set a distinct value if registering the plugin more than once.
|
|
37
|
+
*/
|
|
38
|
+
instanceId?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Escape characters that have special meaning in HTML attribute values and
|
|
42
|
+
* text content. Same narrow five-character escape used by
|
|
43
|
+
* `@ampless/plugin-reading-time` — no external library needed for this
|
|
44
|
+
* use case (the runtime's `sanitize-html` pass is the actual safety
|
|
45
|
+
* boundary; this is defense in depth on the string we hand it).
|
|
46
|
+
*/
|
|
47
|
+
declare function escapeHtml(s: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Factory for the AI-actions plugin. Returns a plugin that injects a
|
|
50
|
+
* `<p class="ampless-ai-actions">` element containing up to three links
|
|
51
|
+
* (View as Markdown / Open in Claude / Open in ChatGPT) before or after
|
|
52
|
+
* the post body via the `publicHtmlForPost` capability. Emits no
|
|
53
|
+
* descriptor at all when every link resolves to hidden.
|
|
54
|
+
*
|
|
55
|
+
* Usage in `cms.config.ts` (only if `ai.markdownRoutes` is enabled —
|
|
56
|
+
* the default — since every link this plugin renders points at the
|
|
57
|
+
* post's `/<slug>.md` route):
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* import aiActionsPlugin from '@ampless/plugin-ai-actions'
|
|
61
|
+
*
|
|
62
|
+
* export default defineConfig({
|
|
63
|
+
* plugins: [aiActionsPlugin()],
|
|
64
|
+
* })
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* All options are also editable from `/admin/plugins → AI actions`
|
|
68
|
+
* without a redeploy — the constructor values below are the initial
|
|
69
|
+
* defaults.
|
|
70
|
+
*/
|
|
71
|
+
declare function aiActionsPlugin(options?: AiActionsOptions): AmplessPlugin;
|
|
72
|
+
|
|
73
|
+
export { type AiActionsOptions, type AiActionsPosition, aiActionsPlugin as default, escapeHtml };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { definePlugin } from "ampless";
|
|
3
|
+
function escapeHtml(s) {
|
|
4
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5
|
+
}
|
|
6
|
+
var escapeAttr = escapeHtml;
|
|
7
|
+
function fixedEncodeURIComponent(s) {
|
|
8
|
+
return encodeURIComponent(s).replace(
|
|
9
|
+
/[!'()*]/g,
|
|
10
|
+
(c) => "%" + c.charCodeAt(0).toString(16).toUpperCase()
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
function stripTrailingSlashes(s) {
|
|
14
|
+
return s.replace(/\/+$/, "");
|
|
15
|
+
}
|
|
16
|
+
function aiActionsPlugin(options = {}) {
|
|
17
|
+
const {
|
|
18
|
+
showMarkdownLink: defaultShowMarkdownLink = true,
|
|
19
|
+
showClaude: defaultShowClaude = false,
|
|
20
|
+
showChatgpt: defaultShowChatgpt = false,
|
|
21
|
+
promptTemplate: defaultPromptTemplate = "Read {url}",
|
|
22
|
+
position: defaultPosition = "afterContent",
|
|
23
|
+
instanceId
|
|
24
|
+
} = options;
|
|
25
|
+
return definePlugin({
|
|
26
|
+
name: "ai-actions",
|
|
27
|
+
packageName: "@ampless/plugin-ai-actions",
|
|
28
|
+
instanceId,
|
|
29
|
+
displayName: { en: "AI actions", ja: "AI \u30A2\u30AF\u30B7\u30E7\u30F3" },
|
|
30
|
+
apiVersion: 1,
|
|
31
|
+
trust_level: "untrusted",
|
|
32
|
+
capabilities: ["publicHtmlForPost", "adminSettings"],
|
|
33
|
+
settings: {
|
|
34
|
+
public: [
|
|
35
|
+
{
|
|
36
|
+
type: "boolean",
|
|
37
|
+
key: "showMarkdownLink",
|
|
38
|
+
label: {
|
|
39
|
+
en: 'Show "View as Markdown" link',
|
|
40
|
+
ja: "\u300CMarkdown \u3067\u8868\u793A\u300D\u30EA\u30F3\u30AF\u3092\u8868\u793A"
|
|
41
|
+
},
|
|
42
|
+
description: {
|
|
43
|
+
en: "Links to the post's /<slug>.md projection. Requires ai.markdownRoutes to be enabled in cms.config.ts.",
|
|
44
|
+
ja: "\u6295\u7A3F\u306E /<slug>.md \u3078\u306E\u76F8\u5BFE\u30EA\u30F3\u30AF\u3002cms.config.ts \u306E ai.markdownRoutes \u304C\u6709\u52B9\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002"
|
|
45
|
+
},
|
|
46
|
+
default: defaultShowMarkdownLink
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: "boolean",
|
|
50
|
+
key: "showClaude",
|
|
51
|
+
label: {
|
|
52
|
+
en: 'Show "Open in Claude" link',
|
|
53
|
+
ja: "\u300CClaude \u3067\u958B\u304F\u300D\u30EA\u30F3\u30AF\u3092\u8868\u793A"
|
|
54
|
+
},
|
|
55
|
+
description: {
|
|
56
|
+
en: "Opt-in: links to https://claude.ai/new?q=... prefilled with the prompt template below. This URL prefill is a community convention, not a documented contract \u2014 verify it works on your site (logged-in desktop + mobile) before enabling.",
|
|
57
|
+
ja: "opt-in: \u4E0B\u8A18\u306E\u30D7\u30ED\u30F3\u30D7\u30C8\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3067 prefill \u3057\u305F https://claude.ai/new?q=... \u3078\u306E\u30EA\u30F3\u30AF\u3002\u3053\u306E URL prefill \u306F\u516C\u5F0F\u306B\u6587\u66F8\u5316\u3055\u308C\u305F\u5951\u7D04\u3067\u306F\u306A\u304F\u30B3\u30DF\u30E5\u30CB\u30C6\u30A3\u6163\u7FD2\u3067\u3059\u3002\u6709\u52B9\u5316\u524D\u306B\u81EA\u30B5\u30A4\u30C8\u3067\u52D5\u4F5C\u78BA\u8A8D\uFF08\u30ED\u30B0\u30A4\u30F3\u6E08\u307F PC / \u30E2\u30D0\u30A4\u30EB\uFF09\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
58
|
+
},
|
|
59
|
+
default: defaultShowClaude
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
type: "boolean",
|
|
63
|
+
key: "showChatgpt",
|
|
64
|
+
label: {
|
|
65
|
+
en: 'Show "Open in ChatGPT" link',
|
|
66
|
+
ja: "\u300CChatGPT \u3067\u958B\u304F\u300D\u30EA\u30F3\u30AF\u3092\u8868\u793A"
|
|
67
|
+
},
|
|
68
|
+
description: {
|
|
69
|
+
en: 'Opt-in: links to https://chatgpt.com/?q=... prefilled with the prompt template below. Same caveat as "Open in Claude" \u2014 this is a community convention, not a documented contract.',
|
|
70
|
+
ja: "opt-in: \u4E0B\u8A18\u306E\u30D7\u30ED\u30F3\u30D7\u30C8\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3067 prefill \u3057\u305F https://chatgpt.com/?q=... \u3078\u306E\u30EA\u30F3\u30AF\u3002\u300CClaude \u3067\u958B\u304F\u300D\u3068\u540C\u69D8\u3001\u516C\u5F0F\u306B\u6587\u66F8\u5316\u3055\u308C\u305F\u5951\u7D04\u3067\u306F\u306A\u304F\u30B3\u30DF\u30E5\u30CB\u30C6\u30A3\u6163\u7FD2\u3067\u3059\u3002"
|
|
71
|
+
},
|
|
72
|
+
default: defaultShowChatgpt
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: "text",
|
|
76
|
+
key: "promptTemplate",
|
|
77
|
+
maxLength: 500,
|
|
78
|
+
label: {
|
|
79
|
+
en: "Prompt template",
|
|
80
|
+
ja: "\u30D7\u30ED\u30F3\u30D7\u30C8\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8"
|
|
81
|
+
},
|
|
82
|
+
description: {
|
|
83
|
+
en: "Template for the Claude / ChatGPT prefill prompt. Use {url} for the absolute .md URL of the post.",
|
|
84
|
+
ja: "Claude / ChatGPT \u306E prefill \u30D7\u30ED\u30F3\u30D7\u30C8\u306E\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3002{url} \u3067\u6295\u7A3F\u306E\u7D76\u5BFE .md URL \u3092\u57CB\u3081\u8FBC\u3081\u307E\u3059\u3002"
|
|
85
|
+
},
|
|
86
|
+
default: defaultPromptTemplate
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
type: "select",
|
|
90
|
+
key: "position",
|
|
91
|
+
label: {
|
|
92
|
+
en: "Position",
|
|
93
|
+
ja: "\u8868\u793A\u4F4D\u7F6E"
|
|
94
|
+
},
|
|
95
|
+
description: {
|
|
96
|
+
en: "Where to inject the AI actions relative to the post content.",
|
|
97
|
+
ja: "AI \u30A2\u30AF\u30B7\u30E7\u30F3\u3092\u672C\u6587\u306E\u524D\u5F8C\u3069\u3061\u3089\u306B\u633F\u5165\u3059\u308B\u304B\u3002"
|
|
98
|
+
},
|
|
99
|
+
options: [
|
|
100
|
+
{
|
|
101
|
+
value: "beforeContent",
|
|
102
|
+
label: { en: "Before content", ja: "\u672C\u6587\u306E\u524D" }
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
value: "afterContent",
|
|
106
|
+
label: { en: "After content", ja: "\u672C\u6587\u306E\u5F8C" }
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
default: defaultPosition
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
publicHtmlForPost(post, ctx) {
|
|
114
|
+
const showMarkdownLink = ctx.setting("showMarkdownLink") ?? defaultShowMarkdownLink;
|
|
115
|
+
const showClaude = ctx.setting("showClaude") ?? defaultShowClaude;
|
|
116
|
+
const showChatgpt = ctx.setting("showChatgpt") ?? defaultShowChatgpt;
|
|
117
|
+
const promptTemplate = (ctx.setting("promptTemplate") ?? "").trim() || defaultPromptTemplate;
|
|
118
|
+
const position = ctx.setting("position") ?? defaultPosition;
|
|
119
|
+
const relativeMdUrl = `/${fixedEncodeURIComponent(post.slug)}.md`;
|
|
120
|
+
const siteUrl = stripTrailingSlashes((ctx.site.url ?? "").trim());
|
|
121
|
+
const absoluteMdUrl = siteUrl ? `${siteUrl}${relativeMdUrl}` : null;
|
|
122
|
+
const buttons = [];
|
|
123
|
+
if (showMarkdownLink) {
|
|
124
|
+
buttons.push(
|
|
125
|
+
`<a class="ampless-ai-actions-md" href="${escapeAttr(relativeMdUrl)}">View as Markdown</a>`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
if (showClaude && absoluteMdUrl) {
|
|
129
|
+
const q = encodeURIComponent(promptTemplate.replace("{url}", absoluteMdUrl));
|
|
130
|
+
const href = `https://claude.ai/new?q=${q}`;
|
|
131
|
+
buttons.push(
|
|
132
|
+
`<a class="ampless-ai-actions-claude" href="${escapeAttr(href)}" target="_blank" rel="noopener noreferrer">Open in Claude</a>`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
if (showChatgpt && absoluteMdUrl) {
|
|
136
|
+
const q = encodeURIComponent(promptTemplate.replace("{url}", absoluteMdUrl));
|
|
137
|
+
const href = `https://chatgpt.com/?q=${q}`;
|
|
138
|
+
buttons.push(
|
|
139
|
+
`<a class="ampless-ai-actions-chatgpt" href="${escapeAttr(href)}" target="_blank" rel="noopener noreferrer">Open in ChatGPT</a>`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (buttons.length === 0) return [];
|
|
143
|
+
const body = `<p class="ampless-ai-actions">${buttons.join('<span class="ampless-ai-actions-sep"> \xB7 </span>')}</p>`;
|
|
144
|
+
return [
|
|
145
|
+
{
|
|
146
|
+
type: "html",
|
|
147
|
+
id: "actions",
|
|
148
|
+
body,
|
|
149
|
+
position
|
|
150
|
+
}
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
export {
|
|
156
|
+
aiActionsPlugin as default,
|
|
157
|
+
escapeHtml
|
|
158
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ampless/plugin-ai-actions",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"description": "AI actions plugin for ampless — injects a View as Markdown link (and optional opt-in Open in Claude / Open in ChatGPT links) after (or before) the post content",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/heavymoons/ampless.git",
|
|
24
|
+
"directory": "packages/plugin-ai-actions"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/heavymoons/ampless/tree/main/packages/plugin-ai-actions#readme",
|
|
27
|
+
"bugs": "https://github.com/heavymoons/ampless/issues",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"ampless": "1.0.0-beta.58"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/sanitize-html": "^2.16.1",
|
|
33
|
+
"sanitize-html": "^2.17.4"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"ampless",
|
|
37
|
+
"plugin",
|
|
38
|
+
"ampless-plugin",
|
|
39
|
+
"ai",
|
|
40
|
+
"markdown",
|
|
41
|
+
"llm",
|
|
42
|
+
"cms"
|
|
43
|
+
],
|
|
44
|
+
"amplessPlugin": {
|
|
45
|
+
"apiVersion": 1,
|
|
46
|
+
"name": "ai-actions",
|
|
47
|
+
"trustLevel": "untrusted",
|
|
48
|
+
"capabilities": [
|
|
49
|
+
"publicHtmlForPost",
|
|
50
|
+
"adminSettings"
|
|
51
|
+
],
|
|
52
|
+
"displayName": {
|
|
53
|
+
"en": "AI actions",
|
|
54
|
+
"ja": "AI アクション"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsup",
|
|
59
|
+
"dev": "tsup --watch",
|
|
60
|
+
"lint": "tsc --noEmit",
|
|
61
|
+
"test": "vitest run --passWithNoTests"
|
|
62
|
+
}
|
|
63
|
+
}
|