Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ yarn dev # watch モード

1. **型システム共有**: SharedパッケージでBaseCommandなどの基本型を定義し、ExtensionとHubで拡張
2. **AIサービス定義**: `packages/hub/public/data/ai-services.json` を Extension がビルド時/実行時の両方で参照
3. **e2eテスト**: `packages/hub` がデプロイする `/en/test` ページを Extension の Playwright テストが利用
3. **ABテスト配分設定**: `packages/hub/public/data/experiments.json` を Extension が実行時に参照し、オンボーディング等のABテストの配分比率を制御
4. **e2eテスト**: `packages/hub` がデプロイする `/en/test` ページを Extension の Playwright テストが利用

### Chrome拡張機能の構造 (packages/extension)

Expand Down Expand Up @@ -188,7 +189,7 @@ interface PageActionOption {
**Hub開発:**

- Hub は縮小版のため新機能は追加しない。コマンド共有プラットフォームとしての機能拡張は新リポジトリ(selection-command-hub)側で行う
- `ai-services.json` とテストページの変更時は Extension 側への影響を確認すること
- `ai-services.json` / `experiments.json` とテストページの変更時は Extension 側への影響を確認すること

**テスト:**

Expand Down
12 changes: 10 additions & 2 deletions packages/extension/e2e/onboarding-shots.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,20 @@ const pathToExtension = path.join(__dirname, "../dist")

type Shot = {
name: string
step: keyof typeof OnboardingStep
// Omitted for shots that should open on whichever screen the variant
// starts at (e.g. variant B's welcome overlay).
step?: keyof typeof OnboardingStep
phase?: StepPhase
variant?: "A" | "B"
}

// One shot per visually distinct screen - not one per StepPhase transition;
// e.g. Step2's WAIT_EXECUTE looks like Step1's, so only Step1 gets it.
const SHOTS: Shot[] = [
{ name: "00-intro", step: "INTRO" },
// Variant B of the onboarding A/B test (see services/experiments): no
// INTRO step, a brief welcome overlay in front of the first step instead.
{ name: "00b-welcome-variant-b", variant: "B" },
{ name: "01-search-explain", step: "SEARCH", phase: StepPhase.EXPLAIN },
{
name: "02-search-wait-execute",
Expand Down Expand Up @@ -90,8 +96,10 @@ test.describe("onboarding screenshots (design review)", () => {
fs.mkdirSync(outDir, { recursive: true })

for (const shot of SHOTS) {
const params = new URLSearchParams({ step: shot.step })
const params = new URLSearchParams()
if (shot.step) params.set("step", shot.step)
if (shot.phase) params.set("phase", shot.phase)
if (shot.variant) params.set("variant", shot.variant)
await page.goto(
`chrome-extension://${extensionId}/src/onboarding_page.html?${params}`,
)
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/de/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,12 @@
"onboarding_skipButton": {
"message": "Überspringen"
},
"onboarding_welcomeMessage": {
"message": "Willkommen!"
},
"onboarding_welcomeContinue": {
"message": "Zum Fortfahren klicken"
},
"onboarding_step1Explain": {
"message": "Markiere den folgenden Text, um sofort eine Suche auszuprobieren."
},
Expand Down
8 changes: 8 additions & 0 deletions packages/extension/public/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,14 @@
"message": "Skip",
"description": "Button shown on every onboarding step to end the onboarding early."
},
"onboarding_welcomeMessage": {
"message": "Welcome!",
"description": "Onboarding variant B welcome overlay, shown for a moment before the first step."
},
"onboarding_welcomeContinue": {
"message": "Click to continue",
"description": "Accessible label for the onboarding variant B welcome overlay, which advances on click."
},
"onboarding_step1Explain": {
"message": "Select the text below to try a search right away.",
"description": "Onboarding Step1 (Search command) instruction shown before the user selects the sample text."
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/es/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,12 @@
"onboarding_skipButton": {
"message": "Omitir"
},
"onboarding_welcomeMessage": {
"message": "¡Bienvenido!"
},
"onboarding_welcomeContinue": {
"message": "Haz clic para continuar"
},
"onboarding_step1Explain": {
"message": "Selecciona el siguiente texto para probar una búsqueda al instante."
},
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/fr/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,12 @@
"onboarding_skipButton": {
"message": "Passer"
},
"onboarding_welcomeMessage": {
"message": "Bienvenue !"
},
"onboarding_welcomeContinue": {
"message": "Cliquez pour continuer"
},
"onboarding_step1Explain": {
"message": "Sélectionnez le texte ci-dessous pour essayer une recherche immédiatement."
},
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/hi/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,12 @@
"onboarding_skipButton": {
"message": "छोड़ें"
},
"onboarding_welcomeMessage": {
"message": "स्वागत है!"
},
"onboarding_welcomeContinue": {
"message": "जारी रखने के लिए क्लिक करें"
},
"onboarding_step1Explain": {
"message": "तुरंत सर्च आज़माने के लिए नीचे दिए गए टेक्स्ट को चुनें।"
},
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/id/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,12 @@
"onboarding_skipButton": {
"message": "Lewati"
},
"onboarding_welcomeMessage": {
"message": "Selamat datang!"
},
"onboarding_welcomeContinue": {
"message": "Klik untuk melanjutkan"
},
"onboarding_step1Explain": {
"message": "Pilih teks di bawah ini untuk langsung mencoba pencarian."
},
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/it/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,12 @@
"onboarding_skipButton": {
"message": "Salta"
},
"onboarding_welcomeMessage": {
"message": "Benvenuto!"
},
"onboarding_welcomeContinue": {
"message": "Clicca per continuare"
},
"onboarding_step1Explain": {
"message": "Seleziona il testo qui sotto per provare subito una ricerca."
},
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/ja/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1311,6 +1311,12 @@
"onboarding_skipButton": {
"message": "スキップ"
},
"onboarding_welcomeMessage": {
"message": "ようこそ!"
},
"onboarding_welcomeContinue": {
"message": "クリックして続ける"
},
"onboarding_step1Explain": {
"message": "気になるテキストを選択して、すぐに検索してみましょう。"
},
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/ko/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,12 @@
"onboarding_skipButton": {
"message": "건너뛰기"
},
"onboarding_welcomeMessage": {
"message": "환영합니다!"
},
"onboarding_welcomeContinue": {
"message": "클릭하여 계속하기"
},
"onboarding_step1Explain": {
"message": "아래 텍스트를 선택해서 바로 검색해 보세요."
},
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/ms/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,12 @@
"onboarding_skipButton": {
"message": "Langkau"
},
"onboarding_welcomeMessage": {
"message": "Selamat datang!"
},
"onboarding_welcomeContinue": {
"message": "Klik untuk teruskan"
},
"onboarding_step1Explain": {
"message": "Pilih teks di bawah untuk cuba carian dengan segera."
},
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/pt_BR/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,12 @@
"onboarding_skipButton": {
"message": "Pular"
},
"onboarding_welcomeMessage": {
"message": "Bem-vindo!"
},
"onboarding_welcomeContinue": {
"message": "Clique para continuar"
},
"onboarding_step1Explain": {
"message": "Selecione o texto abaixo para testar uma pesquisa agora mesmo."
},
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/pt_PT/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,12 @@
"onboarding_skipButton": {
"message": "Ignorar"
},
"onboarding_welcomeMessage": {
"message": "Bem-vindo!"
},
"onboarding_welcomeContinue": {
"message": "Clique para continuar"
},
"onboarding_step1Explain": {
"message": "Selecione o texto abaixo para experimentar uma pesquisa de imediato."
},
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/ru/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,12 @@
"onboarding_skipButton": {
"message": "Пропустить"
},
"onboarding_welcomeMessage": {
"message": "Добро пожаловать!"
},
"onboarding_welcomeContinue": {
"message": "Нажмите, чтобы продолжить"
},
"onboarding_step1Explain": {
"message": "Выделите текст ниже, чтобы сразу попробовать поиск."
},
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/public/_locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,12 @@
"onboarding_skipButton": {
"message": "跳过"
},
"onboarding_welcomeMessage": {
"message": "欢迎!"
},
"onboarding_welcomeContinue": {
"message": "点击继续"
},
"onboarding_step1Explain": {
"message": "选择下面的文本,立即试试搜索功能。"
},
Expand Down
10 changes: 10 additions & 0 deletions packages/extension/src/background_script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
getOrCreateClientId,
} from "@/services/analytics"
import * as HubBackground from "@/services/hub/background"
import { ensureOnboardingAssignment } from "@/services/experiments"

import { importIf } from "@import-if"
importIf("production", "./lib/sentry/initialize")
Expand Down Expand Up @@ -471,6 +472,15 @@ chrome.runtime.onInstalled.addListener(async (details) => {
if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
await Settings.reset()
sendEvent(ANALYTICS_EVENTS.INSTALLED, {}, SCREEN.SERVICE_WORKER)
// Assign the onboarding A/B variant before the tab is created, so the
// page can render its first frame from storage without fetching the
// remote config itself. A failure here must never block onboarding -
// the page assigns on its own if no assignment is stored yet.
try {
await ensureOnboardingAssignment()
} catch (error) {
console.error("Failed to assign onboarding variant:", error)
}
chrome.tabs.create({ url: ONBOARDING_PAGE_PATH })
}

Expand Down
21 changes: 19 additions & 2 deletions packages/extension/src/components/onboarding/OnboardingFadeIn.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
import { ReactNode } from "react"
import clsx from "clsx"

// `rise` is the default onboarding entrance (fade + slight upward slide).
// `blur` additionally resolves the content out of a blur, used by variant
// B's welcome overlay for a softer, Linear-style transition.
export type FadeInEffect = "rise" | "blur"

type Props = {
children: ReactNode
className?: string
delay?: number // in milliseconds; optional delay before the fade-in starts
effect?: FadeInEffect
}

const EFFECT_CLASS: Record<FadeInEffect, string> = {
rise: "animate-onboarding-rise",
blur: "animate-onboarding-blur-in",
}

// Wraps a block of onboarding explanation text so it fades in from slightly
// below, per the PRD's "説明テキストは少し下からフェードイン" rule. Uses a
// `key`-less CSS animation (re-triggered by React remounting the element,
// e.g. when the step/phase changes) rather than a JS animation library.
export function OnboardingFadeIn({ children, className, delay }: Props) {
export function OnboardingFadeIn({
children,
className,
delay,
effect = "rise",
}: Props) {
return (
<div
className={clsx(
"motion-reduce:animate-none animate-onboarding-rise",
"motion-reduce:animate-none",
EFFECT_CLASS[effect],
className,
)}
style={{ animationDelay: delay ? `${delay}ms` : undefined }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { ReactNode } from "react"
import { t } from "@/services/i18n"
import { getProgress, showsSkip } from "./onboardingProgress"
import type { OnboardingStep } from "@/types/onboarding"
import type { OnboardingStep, StepPhase } from "@/types/onboarding"

const ICON_URL = chrome.runtime.getURL("SelectionCommandLogo.png")

type Props = {
step: OnboardingStep
phase: StepPhase
onSkip: () => void
children: ReactNode
}
Expand All @@ -17,8 +18,8 @@ type Props = {
// a header with the brand mark and a 4-segment progress indicator, and a
// Skip button fixed to the bottom-right corner. Individual step components
// now render only their own content.
export function OnboardingLayout({ step, onSkip, children }: Props) {
const progress = getProgress(step)
export function OnboardingLayout({ step, phase, onSkip, children }: Props) {
const progress = getProgress(step, phase)

return (
<div className="relative flex min-h-screen flex-col overflow-hidden bg-white">
Expand Down Expand Up @@ -61,7 +62,7 @@ export function OnboardingLayout({ step, onSkip, children }: Props) {
{children}
</div>

{showsSkip(step) && (
{showsSkip(step, phase) && (
<button
type="button"
onClick={onSkip}
Expand Down
Loading
Loading