Skip to content

feat: share assistant messages as branded social cards - #8040

Merged
bolichen97 merged 1 commit into
mainfrom
feat/share-message-card
Sep 4, 2026
Merged

feat: share assistant messages as branded social cards#8040
bolichen97 merged 1 commit into
mainfrom
feat/share-message-card

Conversation

@CrysisDeu

@CrysisDeu CrysisDeu commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

What

A Share action on assistant replies that turns a message into a branded social card — for posting "Kiro Crew did this amazing thing" moments to X / LinkedIn without screenshots or copy-paste gymnastics.

  • New hover-row button on assistant messages opens a share dialog.
  • The dialog renders the reply as a fixed-brand card (gradient + ghost mark + "Made with Kiro Crew" footer), optionally paired with the user question it answered (Q&A pairs travel best on feeds). The card deliberately ignores the dashboard theme so exported images are always on-brand.
  • Click-to-edit card text for redaction/polish before posting (edits never touch the original message).
  • Export paths: download 2x PNG, or copy image + caption in one clipboard item (paste straight into a post composer); clipboard-less browsers fall back to a download automatically.
  • X / LinkedIn intent buttons open the platform post box with the editable caption prefilled.
  • Pre-share sensitive-content scan (AWS access keys, GitHub/Slack/bearer tokens, private key blocks, local home paths, loopback/private URLs) raises a warning banner over the combined card + caption text. High-precision patterns only — a nudge, not a DLP gate; everything is client-side and nothing leaves the machine until the user posts.

Screenshots

Default view (Q&A pair, prefilled caption):

share dialog

The REAL exported PNG (captured from the download action), carrying the official Kiro ghost brand mark:

exported card

Sensitive-content warning after planting an AWS-key-shaped string in the caption:

sensitive warning

Implementation notes

  • website/src/pages/chat/share/shareSupport.ts (pure helpers: scan, intent URLs, clipboard/download fallbacks, prev-question lookup), ShareCard.tsx (the DOM node the PNG is rasterized from), ShareMessageModal.tsx (dialog).
  • New dependency html-to-image@1.11.13 (exact pin), imported on demand inside the export handler so no chat render path pays for it; the modal itself is lazy()-loaded behind the button.
  • Both message hosts pass prevUserText (ChatPage, app-sdk messageRenderers).
  • i18n: keys added to en.manual.json + all 12 locale catalogs, en-XA regenerated; product name interpolated via {{productName}} (no hardcoded brand in catalog values); zh-CN full-width punctuation and hi informal tone follow the style suites.
  • Buttons follow the two-actions-per-row dialog convention.

Tested

  • npx tsc -b clean; full vitest run: 1777 files / 27919 tests passed (1 expected fail, 2 skipped — same as base).
  • 20 new unit tests: scan positives/negatives, intent URL encoding, excerpt clamping (word-boundary + CJK), question pairing default/toggle, caption-typed findings, X intent open, 2x export, clipboard success and refusal→download fallback.
  • website/scripts/capture-share-message-card.mjs drives the REAL built dist (harness screenshots above) and asserts the card pairs the question, carries the excerpt, and the warning fires.
  • eslint: 0 errors, 0 new warnings.

Not in scope (follow-ups)

  • LLM-generated caption (background-role model) and Web Share API share({files}) for mobile.
  • Arbitrary multi-message selection beyond the Q&A pair.

@CrysisDeu
CrysisDeu requested a review from a team September 3, 2026 00:51
@CrysisDeu
CrysisDeu requested a review from a team as a code owner September 3, 2026 00:51
@CrysisDeu
CrysisDeu requested a review from dwu96 September 3, 2026 00:51
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Sep 3, 2026
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — 🟡 CONCERNS

Design-level review of 51fa475a3c8a5f30c37182511dcf18d4bc615e34 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All the evidence is in. The design is sound overall — client-side only, reversible, lazy-loaded dependency, proportionate scan — but the description claims app-sdk host wiring that doesn't exist in the diff.

Design-Verdict: CONCERNS

Sound, reversible client-side feature, but the description claims app-sdk host wiring the diff doesn't contain — Share never renders in app-embedded chats.

Watch

  • Phantom description / availability coupling. The description says "Both message hosts pass prevUserText (ChatPage, app-sdk messageRenderers)", but messageRenderers.tsx is untouched — and since it passes neither onFork nor onPlanFromHere, the gate (onFork || onPlanFromHere) && <div ...> means the overflow menu and Share never render there at all. Either wire that host or fix the description; longer-term, "menu exists" inferred from fork-handler presence is identity-by-absence — an explicit capability prop would stop the next host from silently inheriting the wrong answer.
  • LinkedIn prefill rides an undocumented endpoint (/feed/?shareActive=true&text=); when LinkedIn changes it the button opens a bare feed with the caption silently dropped. Worth a code comment naming the fragility so the eventual breakage is diagnosable.

Suggestions

  • The local_path pattern (/home/..., /Users/...) will fire on a large fraction of dev-agent replies — the most frequent finding is the least sensitive kind, which habituates users past the banner and defeats the stated "high-precision, worth interrupting for" principle; consider dropping or narrowing it.

[DESIGN-REVIEWED] 51fa475

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — 🟡 CONCERNS

UX-level review of 51fa475a3c8a5f30c37182511dcf18d4bc615e34 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

UX-Verdict: CONCERNS

The share flow's last step — pasting the card into the composer — happens in a tab where none of the product's guidance can follow.

Watch

  • "Share on X/LinkedIn" opens a text-only composer; the card must be hand-pasted. openIntent copies the PNG then navigates the new tab; the instruction ("Copied — paste the image into the post box") renders in the original tab the user just left. A first-timer who hits Post ships the boilerplate caption ("Kiro Crew just did this for me…") with no card — an outward, public miss. Medium frequency × public-post impact × until-learned. Smallest fix: label the buttons for what they do ("Open X post box"), keeping the promise honest at the point of click.
  • No pending feedback on the intent path. busy === 'intent' disables buttons but renders no spinner, and the pre-opened tab sits blank until the export settles — the user's attention locus (the clicked button, then the tab) shows nothing for the export duration. Every intent share × brief confusion. Fix: reuse the Loader2 swap the Download/Copy buttons already have.
  • Dismissal discards redaction work with no guard. Escape or click-outside closes the Radix dialog and drops card edits + a composed caption (shareOpen unmounts everything). The edit affordance exists for redacting secrets, so careful edit-then-accidental-dismiss is a plausible loop. Low frequency × minutes of lost work × every time. Fix: dirty-check before onClose.

Suggestions

  • EditableBlock's aria-label is the instruction "Click the card text to edit it"; a screen reader gets that as the field's name. Name it ("Card text"), demote the instruction to aria-description.
  • PR description claims a "gradient" card and a "hover-row button"; the pixels show flat purple and a ⋯-menu item — align the description before humans review against it.

[UX-REVIEWED] 51fa475

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — 🟡 CONCERNS

Premise-level review of 51fa475a3c8a5f30c37182511dcf18d4bc615e34 — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

First-Principles-Verdict: CONCERNS

The description claims two hosts ship prevUserText; the diff wires one — and the permanent overflow trigger every user now sees is undeclared.

What this change ships

Intent: let a user post an assistant reply to X/LinkedIn as a branded PNG card instead of a screenshot — an ADDITION.

  1. "Share as image" item on assistant replies — justified (the feature itself)
  2. "More actions" trigger now permanent on every loaded assistant message — undeclared
  3. Dialog renders a fixed-brand card, pairing the user question by default (toggleable) — justified
  4. Card text is click-to-edit for redaction before export — justified
  5. Download 2x PNG / copy image+caption, download fallback — justified (clipboard support is a platform rule)
  6. X/LinkedIn buttons pre-copy the card, then open the prefilled composer — justified
  7. Pre-share sensitive-content warning (5 kinds) — justified (external-publishing boundary)
  8. New lazy dependency html-to-image — justified; no existing rasterizer (grepped html2canvas|dom-to-image|modern-screenshot: 0)
  9. Screenshot harness + 3 committed PNGs — repo convention (40+ capture-*.mjs siblings, temp-screenshots/ committed by prior merges)
  10. Feature-map row + keys in 13 catalogs — mandated by docs/i18n gates

Watch

  • Description: "Both message hosts pass prevUserText (ChatPage, app-sdk messageRenderers)." The diff touches only ChatPage.tsx (grepped the patch for messageRenderers: 0 hunks), and website/src/app-sdk/messageRenderers.tsx:288 passes neither prevUserText nor the fork/plan handlers the menu is gated on — app-sdk-hosted replies get no Share at all. Ship what the description says, or correct it.
  • Item 2 is a visibility change every existing user notices (the trigger previously appeared only in fork's unavailable state — see the flipped assertions in AssistantMessage.test.tsx), and the description instead calls Share a "hover-row button". Derived from the row's two-action cap, but a human should see it was never declared.

[FIRST-PRINCIPLES-REVIEWED] 51fa475

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 51fa475a3c8a5f30c37182511dcf18d4bc615e34 and found no blocking issues.

This comment is updated in place on each push.

Review details

FINDING -- website/src/pages/chat/AssistantMessage.tsx:386 -- app-SDK assistant rows pass neither handler, so "onFork || onPlanFromHere" hides Share -> Fix: render Share independently of fork/plan handlers.
[GPT-REVIEWED] 51fa475

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 51fa475a3c8a5f30c37182511dcf18d4bc615e34: <one-sentence reason>

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 51fa475a3c8a5f30c37182511dcf18d4bc615e34 — this comment is updated in place on each push.

Review details

Based on my analysis of the diff and the ShareMessageModal.tsx / shareSupport.ts code:

Candidate 1 — In openIntent, the tab is navigated to the composer regardless of export result. exportBlob() returns null when toBlob fails to rasterize; the if (blob) guard skips the copy, then if (tab) tab.location.href = url still runs, opening a caption-only composer — contradicting the code's own stated guarantee. And if exportBlob throws (e.g. the dynamic import('html-to-image') chunk fails), the exception propagates past finally, the navigation line is skipped, and the pre-opened blank tab is left dangling. Both behaviors are certain from the code; the trigger (export failure) is a real, if uncommon, path. Consequence is a UX/promise-violation, not data loss/crash/security → FINDING.

Candidate 2 — Requires messageText to mutate while the modal is mounted. The dialog is a modal overlay over a single completed assistant message; I could not find a live path where that message's content changes while the dialog blocks the chat behind it. (a) does not hold "in practice" — dropped.

Candidate 3 — Missing feedback when export yields no blob (silent reset). Shares the root cause with Candidate 1 (unsurfaced exportBlob null) but the consequence is a weaker cosmetic dead-button; the more concrete surface is Candidate 1. Merged/dropped.

No new grounded findings at the required bar.

No BLOCKING findings — one advisory FINDING on the share export-failure path.

FINDING — website/src/pages/chat/share/ShareMessageModal.tsx:1492 — on export failure if (tab) tab.location.href = url still opens the composer with nothing copied (caption-only post, defeating the "never publishes a caption-only post" guarantee), and a thrown exportBlob() skips that line entirely, leaving a dangling blank window.open('', '_blank') tab → Fix: only navigate the tab once a blob was confirmed copied/downloaded; on null-or-throw, close the pre-opened tab and set an error feedback state instead of navigating.

[OPUS-REVIEWED] 51fa475

Verdict parsed from the review's SHA-scoped output markers for commit 51fa475a3c8a5f30c37182511dcf18d4bc615e34.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 51fa475a3c8a5f30c37182511dcf18d4bc615e34: <one-sentence reason>

@CrysisDeu
CrysisDeu force-pushed the feat/share-message-card branch from f77f757 to e9b3cd8 Compare September 3, 2026 01:09
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 3, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/share-message-card branch from e9b3cd8 to 71876b2 Compare September 3, 2026 01:26
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 3, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/share-message-card branch from 71876b2 to 14a7b3f Compare September 3, 2026 01:58
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 3, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/share-message-card branch from 14a7b3f to 08da5a2 Compare September 3, 2026 02:11
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Sep 3, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/share-message-card branch from 08da5a2 to 8ba51fd Compare September 3, 2026 02:34
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 3, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/share-message-card branch from 8ba51fd to 28469cb Compare September 3, 2026 03:21
@github-actions github-actions Bot added the readiness: action required A blocking check or review needs attention label Sep 3, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/share-message-card branch from f6ae7d3 to bfb7125 Compare September 3, 2026 08:35
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision merge conflict Branch has merge conflicts with its base — author must resolve before merge and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Sep 3, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/share-message-card branch from bfb7125 to d0cd58b Compare September 3, 2026 18:31
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed merge conflict Branch has merge conflicts with its base — author must resolve before merge readiness: passed Eligible automated validation passed for the current revision labels Sep 3, 2026
@CrysisDeu
CrysisDeu force-pushed the feat/share-message-card branch from d0cd58b to 9b9a6be Compare September 3, 2026 18:54
A Share action on assistant replies opens a dialog that renders the
message as a fixed-brand card (optionally paired with the question it
answered), exports it as a 2x PNG via download or clipboard, and opens
X / LinkedIn intent composers with an editable, prefilled caption.

The card text is click-to-edit for redaction before posting, and a
high-precision scan (AWS keys, API tokens, private keys, local paths,
loopback URLs) raises a pre-share warning banner. Everything runs
client-side; html-to-image (pinned) is loaded on demand so chat renders
never pay for it. Catalog strings ship in all 13 locales with
{{productName}} interpolation.
@CrysisDeu
CrysisDeu force-pushed the feat/share-message-card branch from 9b9a6be to 51fa475 Compare September 3, 2026 19:32
@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Sep 3, 2026
@bolichen97
bolichen97 enabled auto-merge (squash) September 4, 2026 00:21

@bolichen97 bolichen97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving on the strength of a full readiness audit of every open PR against main, not a
line-by-line reading of this diff — recording that plainly so the next reader knows what this
stamp does and does not cover.

Verified against this exact head SHA:

  • readiness: passed present, and PR Readiness — the one required status context on main
    (ruleset protected-branches) — is success on this head.
  • No check run on this head is failure, cancelled, timed_out or still in flight. Skipped
    jobs are path-filtered conditionals, none of them required.
  • mergeable: true, and the head is not far enough behind main for its green CI to describe a
    base that no longer exists.
  • No surviving reviewer CHANGES_REQUESTED: any such review is on an older commit and therefore
    already dismissed by dismiss_stale_reviews_on_push.
  • Every issue comment, inline review comment and review thread was read and classified. Nothing
    left is an unresolved human change request — the remainder is bot review-lane output, resolved
    or outdated threads, explicitly non-blocking suggestions, and author status notes.

Auto-merge (squash) is armed, so this lands once every other ruleset requirement is met.

@bolichen97
bolichen97 merged commit b206a9f into main Sep 4, 2026
66 checks passed
@bolichen97
bolichen97 deleted the feat/share-message-card branch September 4, 2026 00:21
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Sep 4, 2026
@bolichen97

Copy link
Copy Markdown
Collaborator

Open PR relationship audit

This is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion.

Relationship findings

  • PR #4085 is OVERLAPPING relative to this PR. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #4085: REBASE. Different goals in the same files with overlapping test hunks; the second to land needs a real merge and a glyph pass over the other's new spinners. Files: website/src/test/AssistantMessage.test.tsx.
  • PR #7255 is OVERLAPPING relative to this PR. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #7255: REBASE. Already-landed change inside a region the primary relocates; it is why the head currently conflicts. Files: website/src/pages/ChatPage.tsx.

No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants