Skip to content
Merged
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
2 changes: 1 addition & 1 deletion studio-app/src/CloudStudio.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ export function CloudProjects({ onClose, quick = false, initialGrant = null }) {
</>
) : (
<form onSubmit={login}>
<HostedSignIn auth={c.auth} />
<label>
{t('account.copy76')}
<input
Expand Down Expand Up @@ -107,6 +106,7 @@ export function CloudProjects({ onClose, quick = false, initialGrant = null }) {
{t('account.copy79')}
</Button>
{c.auth.auth_ready === false && <p role="alert">{t('account.copy80')}</p>}
<HostedSignIn auth={c.auth} />
</form>
)}
<FloatingMessage tone="error" text={c.error ? c.error : null} />
Expand Down
21 changes: 5 additions & 16 deletions studio-app/src/HostedSignIn.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
// WorkOS AuthKit sign-in (h3-studio#305): Google, GitHub or email on a hosted page.
// Shown only when /api/auth/config reports it. It sits first inside the existing username
// form; the username fields after it stay folded away until "existing account" is opened.
import React, { useState } from 'react';
// WorkOS AuthKit sign-in (h3-studio#305): Google or email on a hosted page. Shown only when
// /api/auth/config reports it, below the username and password form (owner, 2026-11-02).
import React from 'react';
import { Button } from './ui/Button.jsx';
import { LogIn } from 'lucide-react';
import { useI18n } from './i18n/I18nProvider.jsx';
import { hostedSignInEnabled, hostedSignInUrl } from './account-access.js';

// `passwordFirst` opens with the username form unfolded (an existing password account, #305).
export default function HostedSignIn({ auth, passwordFirst = false }) {
export default function HostedSignIn({ auth }) {
const { t } = useI18n();
const [legacy, setLegacy] = useState(passwordFirst);
if (!hostedSignInEnabled(auth)) return null;
const here = () => window.location.pathname + window.location.search;
return (
<div className={'hosted-sign-in' + (legacy ? ' legacy-open' : '')}>
<div className="hosted-sign-in">
<Button
variant="primary"
className="keep-label hosted-sign-in-main"
Expand All @@ -32,14 +29,6 @@ export default function HostedSignIn({ auth, passwordFirst = false }) {
<a href="/content">{t('signIn.contentPolicy')}</a>
{t('signIn.consentAfter')}
</small>
<Button
variant="link"
className="hosted-sign-in-legacy"
aria-expanded={legacy}
onClick={() => setLegacy(!legacy)}
>
{t(legacy ? 'signIn.hideUsername' : 'signIn.orUsername')}
</Button>
</div>
);
}
4 changes: 2 additions & 2 deletions studio-app/src/LoginDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useI18n } from './i18n/I18nProvider.jsx';
const quietly = (promise) => promise.catch(() => {});

// Password / hosted sign-in dialog shared by the landing page and the Telegram connect page.
export function Login({ onClose, onDone, title, lead, passwordFirst = false }) {
export function Login({ onClose, onDone, title, lead }) {
const { t } = useI18n();
const c = useCloud(),
who = useAccountLabel(c.account),
Expand Down Expand Up @@ -49,7 +49,6 @@ export function Login({ onClose, onDone, title, lead, passwordFirst = false }) {
}}
>
<p>{lead ?? t('workspace.copy119')}</p>
<HostedSignIn auth={c.auth} passwordFirst={passwordFirst} />
<label className="qc-field">
{t('workspace.copy3')}
<input
Expand Down Expand Up @@ -91,6 +90,7 @@ export function Login({ onClose, onDone, title, lead, passwordFirst = false }) {
{!c.auth && (
<Button onClick={() => quietly(cloud.initialize())}>{t('workspace.copy6')}</Button>
)}
<HostedSignIn auth={c.auth} />
</form>
)}
</Dialog>
Expand Down
5 changes: 3 additions & 2 deletions studio-app/src/StoryboardConnected.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,10 @@ function receiptCost(receipt) {
}
// Name the shot an estimate refused and say why, instead of a generic "fix something".
function shotRefusal(problem, live, t, locale) {
if (problem?.message !== 'workbench.inputsInvalid' || !problem.shotId) return problem;
if (problem?.message !== 'workbench.inputsInvalid') return problem;
const index = live.shots.findIndex((shot) => shot.id === problem.shotId);
if (index < 0) return problem;
// Never show the raw key: without the shot, say the general thing.
if (index < 0) return Error(t('workbench.inputsInvalid'));
const shot = live.shots[index],
why = storyboardPresentation(expandStoryboard(shot, live, { locale }), locale).warnings[0];
return Error(
Expand Down
21 changes: 2 additions & 19 deletions studio-app/src/credits.css
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,13 @@
}
}

/* Hosted sign-in (#305): one clear button; the username form for existing accounts folds away. */
/* Hosted sign-in (#305): one clear button below the username and password form. */
.hosted-sign-in {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 10px;
margin: 4px 0 2px;
margin: 14px 0 2px;
}
.hosted-sign-in > .hosted-sign-in-main {
justify-content: center;
Expand All @@ -248,23 +248,6 @@
.hosted-sign-in a {
color: var(--accent);
}
.hosted-sign-in > .hosted-sign-in-legacy {
align-self: center;
height: auto;
min-height: 0;
padding: 4px 6px;
background: none;
border: 0;
color: var(--muted);
font-size: 12px;
text-decoration: underline;
}
/* Everything after the hosted block (username, password, submit) stays hidden until opened;
alerts stay visible. */
.hosted-sign-in:not(.legacy-open) ~ :not([role='alert']) {
display: none;
}

.receipt-rows {
display: grid;
grid-template-columns: auto 1fr;
Expand Down
8 changes: 2 additions & 6 deletions studio-app/src/i18n/creditsCatalogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,14 @@ export const creditsCatalogs = {
'legal.footer': 'Site links',
'legal.draft': 'Draft for review. This page is not yet in effect.',
'legal.version': 'Version {version}',
'signIn.hosted': 'Continue with Google, GitHub or email',
'signIn.hosted': 'Continue with Google or email',
'signIn.consentBefore': 'By continuing you agree to the ',
'signIn.terms': 'Terms',
'signIn.comma': ', ',
'signIn.privacy': 'Privacy Policy',
'signIn.and': ' and ',
'signIn.contentPolicy': 'Content Policy',
'signIn.consentAfter': '.',
'signIn.orUsername': 'Existing account? Use your username',
'signIn.hideUsername': 'Hide username sign-in',
'credits.word': 'credits',
'credits.chipTitle': 'Credits · top up',
'credits.chipLabel': '{count} credits available. Open credits.',
Expand Down Expand Up @@ -125,16 +123,14 @@ export const creditsCatalogs = {
'legal.footer': '网站链接',
'legal.draft': '草稿,待审阅。本页内容尚未生效。',
'legal.version': '版本 {version}',
'signIn.hosted': '用 Google、GitHub 或邮箱继续',
'signIn.hosted': '用 Google 或邮箱继续',
'signIn.consentBefore': '继续即表示你同意',
'signIn.terms': '服务条款',
'signIn.comma': '、',
'signIn.privacy': '隐私政策',
'signIn.and': '和',
'signIn.contentPolicy': '内容规范',
'signIn.consentAfter': '。',
'signIn.orUsername': '老账号?用用户名登录',
'signIn.hideUsername': '收起用户名登录',
'credits.word': '积分',
'credits.chipTitle': '积分余额 · 点击充值',
'credits.chipLabel': '可用 {count} 积分,打开积分页',
Expand Down
Loading