From 9011ff212652b99e424f267718cedf40a494a01a Mon Sep 17 00:00:00 2001 From: Ruk Doe Date: Wed, 16 Sep 2026 17:29:42 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E8=AA=8D=E8=A8=BC=E5=89=8D?= =?UTF-8?q?=E3=81=AE=E7=94=BB=E9=9D=A2=E3=81=A7=E4=BD=95=E3=81=8C=E6=B5=81?= =?UTF-8?q?=E3=82=8C=E3=82=8B=E3=81=8B=E8=A6=8B=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 𝕏 が 2026 年 9 月から、従量課金のアプリで書き込み権限を求めると同意画面に 「Sensitive permissions requested」の枠を出すようになった。開発者側で消す 手段が無く、チェックを入れないと認可ボタンを押せないので、押す前に知らせる。 あわせて認証前の画面を作り直した。以前は署名ボタンと注意書きが縦に並ぶだけで、 何が起きるものなのか分からなかった。 - 実際に流れるポストの見本を並べて置く(行の並びは summary.ts と同じ) - 通信簿に出る指標を色付きの札で並べる(反応=桃・伸び=青系・行動=緑系) - 認証済みの画面は自動ポストの入切を主色の枠の箱に入れ、アカウントは 表ではなく丸+名前で出す - 上に 3px の帯(𝕏 の黒 → 主色) app.scss に .panel / .cluster と --ui-ok / --ui-shadow / --ui-tint を戻した。 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01JgGYf5qbjDXYhtzcpLsGvv --- src/app.scss | 27 ++ src/lib/components/SummaryScreen.svelte | 418 ++++++++++++++++++------ src/routes/+layout.svelte | 6 + 3 files changed, 356 insertions(+), 95 deletions(-) diff --git a/src/app.scss b/src/app.scss index 3b2455b..c8aede1 100644 --- a/src/app.scss +++ b/src/app.scss @@ -69,6 +69,10 @@ */ --ui-warn: #a45a06; --ui-info: var(--pico-primary); + --ui-ok: var(--pico-ins-color); + --ui-shadow: var(--pico-card-box-shadow); + /* 主色を薄く敷くとき(見出しの上の小さな札)に使う */ + --ui-tint: color-mix(in srgb, var(--pico-primary) 12%, var(--pico-background-color)); /* 注意書きと札の地。地の色に混ぜるので、明暗どちらでも浮かない */ --ui-err-bg: color-mix(in srgb, var(--ui-err) 12%, var(--pico-background-color)); --ui-warn-bg: color-mix(in srgb, var(--ui-warn) 12%, var(--pico-background-color)); @@ -256,6 +260,29 @@ textarea { margin: 0; } +/* ---- 箱 ---- */ +.panel { + border: 1px solid var(--ui-base-300); + border-radius: var(--pico-border-radius); + background: var(--ui-surface); + box-shadow: var(--ui-shadow); +} +/* 中身を縦に積む箱。**同じ要素に付ける**ので子孫ではなく複合で書く */ +.panel.body { + display: flex; + flex-direction: column; + gap: 0.7rem; + padding: 1rem; +} + +/* ---- 並べ方 ---- */ +.cluster { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; +} + /* ---- 注意書き ---- */ .note { display: flex; diff --git a/src/lib/components/SummaryScreen.svelte b/src/lib/components/SummaryScreen.svelte index 330acac..765abe5 100644 --- a/src/lib/components/SummaryScreen.svelte +++ b/src/lib/components/SummaryScreen.svelte @@ -24,6 +24,9 @@ let { message, wkey, summary, keyInfo }: Props = $props(); let saving = $state(false); let postingNow = $state(false); +// 認証済みかどうか。認証前は何をするものかを見せる側に倒す +const signedIn = $derived(wkey !== undefined && keyInfo !== undefined); + // Pinned rather than left to the runtime: the server renders this in the // container's UTC and the browser would re-render it nine hours later on // hydration, so the time visibly jumps the moment the page opens. JST is also @@ -34,6 +37,31 @@ const dateTime = new Intl.DateTimeFormat("ja-JP", { timeZone: "Asia/Tokyo", }); +/** + * 認証前に見せる見本。**架空の数字**で、行の並びは実際に投稿するものと同じ + * (summary.ts の組み立てに合わせてある)。繋ぐ前に何が流れるか分かるようにするため。 + */ +const SAMPLE = [ + "9月15日のポスト: 4件 (前日比 +1)", + "うちリプライ 1件", + "いいね 128・リポスト 12・返信 8・ブックマーク 3", + "インプレッション 12,400 (平均 3,100・最高 6,800)", + "プロフィールクリック 24・リンククリック 9", + "5日連続でポスト中", + "", + "#ポスト通信簿", +].join("\n"); + +/** 通信簿に出る指標。色は意味で決める(反応=桃、伸び=青、行動=緑) */ +const METRICS = [ + { label: "いいね", tone: "pink" }, + { label: "リポスト", tone: "green" }, + { label: "返信", tone: "blue" }, + { label: "ブックマーク", tone: "amber" }, + { label: "インプレッション", tone: "violet" }, + { label: "プロフィール・リンククリック", tone: "teal" }, +] as const; + async function postNow() { try { postingNow = true; @@ -82,52 +110,66 @@ async function toggleSummary(event: Event) { } -
+
{#if message !== undefined} {message} - {:else if wkey !== undefined && keyInfo} - -

- 初めてONにしたときは、その時点までの当日分をすぐ投稿します。 -
- 自動ポストはポストが0件の日をスキップします (今すぐ投稿は0件でも投稿します)。リポストは数に含めません。 - {#if summary?.lastPostedAt} + + {:else if signedIn} + +

+
+

自動ポスト

+ + {summary?.enabled ? "ON" : "OFF"} + +
+ +

+ 初めてONにしたときは、その時点までの当日分をすぐ投稿します。
- 最終投稿: {dateTime.format(summary.lastPostedAt)} - {/if} -

- {#if summary?.enabled} - - - {/if} - {#if summary?.lastError} - 前回の自動ポスト: {summary.lastError} - {/if} +

+ {#if summary?.enabled} + +
+ +
+ {/if} + {#if summary?.lastError} + 前回の自動ポスト: {summary.lastError} + {/if} +
+ {@const ret = keyInfo} +
{#if ret?.error} {ret.error} {:else} -

現在のアカウント

+

現在のアカウント

{#if ret?.status === 429} {:else} -
- - - - - - - - - - - - - - - -
IDNameUsername
{ret?.data?.id}{ret?.data?.name}{ret?.data?.username}
+
+ +
+
{ret?.data?.name}
+
@{ret?.data?.username} ・ ID {ret?.data?.id}
+
{/if} -

+

別のアカウントを使用する場合は下記で認証画面内でアカウント選択できます

{/if} +
+
+ {:else} + +
+
+ 毎日 0:00 (JST) に自動ポスト +

ポスト通信簿

+

+ 前日のポストの反応と伸びをまとめて、あなたのアカウントから1件だけポストします。 + 自分の積み上げが毎朝1行で残ります。 +

+ + +
+
+ 𝕏 の認証画面に「Sensitive permissions requested」と出ます。 +

+ 自動ポストのために書き込み権限を求めるアプリすべてに 𝕏 が出す確認です。 + 「I trust this app」にチェックを入れると認可ボタンが押せるようになります。 +

+
+
+

+ 保存するのは User ID と 𝕏 のトークンだけです。ポストの中身も、あなたのほかの情報も保存しません。 +

+
+ + +
+
+ + こんなポストが流れます + サンプル +
+
{SAMPLE}
+
+
+
+

通信簿に出るもの

+
    + {#each METRICS as m (m.label)} +
  • {m.label}
  • + {/each} +
+

+ 前日比と連続ポスト日数も付きます。数字が0の行と、ポストが無かった日は出しません(1件 $0.015 かかるため)。 +

+
{/if} - -

運営費について

-

- 𝕏のAPIが従量課金のため、自動ポスト1件につき $0.015、集計の読み取りに1ポストあたり - $0.005 かかり、運営者が負担しています。支えていただける方は - Ko-fi - へ。 -

-

プライバシー

-

- サーバに保存するのはUser ID・Access Token・Refresh - Tokenのみで、そのほかのユーザー情報は一切取得していません。 -

+ +
+

運営費について

+

+ 𝕏のAPIが従量課金のため、自動ポスト1件につき $0.015、集計の読み取りに1ポストあたり + $0.005 かかり、運営者が負担しています。支えていただける方は + Ko-fi + へ。 +

+
+ +
+

プライバシー

+

+ サーバに保存するのはUser ID・Access Token・Refresh + Tokenのみで、そのほかのユーザー情報は一切取得していません。 +

+
+

不具合の報告は @5yuim @@ -191,34 +283,175 @@ async function toggleSummary(event: Event) {

diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 28b3206..c4b1e32 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -48,6 +48,8 @@ if (browser) { /> + +