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
5 changes: 5 additions & 0 deletions .changeset/install-clear-screen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@pythoughts/pythinker-code': patch
---

Clear the terminal before the install script's animated intro so earlier shell output no longer interleaves with the logo animation.
5 changes: 5 additions & 0 deletions .changeset/oauth-pages-light-redesign.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@pythoughts/pythinker-code": patch
---

Restyle the browser OAuth sign-in confirmation pages for all providers to match the website's light design.
4 changes: 2 additions & 2 deletions apps/pythinker-code/src/constant/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ export const ANTHROPIC_PLUGIN_MARKETPLACE_REPOSITORY = 'anthropics/claude-plugin
export const CLAUDE_PLUGIN_MARKETPLACE_PATH = '.claude-plugin/marketplace.json';
export const ANTHROPIC_PLUGIN_MARKETPLACE_URL =
`https://raw.githubusercontent.com/${ANTHROPIC_PLUGIN_MARKETPLACE_REPOSITORY}/HEAD/${CLAUDE_PLUGIN_MARKETPLACE_PATH}`;
export const PYTHINKER_CODE_INSTALL_SH_URL = 'https://pythinker.com/install.sh';
export const PYTHINKER_CODE_INSTALL_PS1_URL = 'https://pythinker.com/install.ps1';
export const PYTHINKER_CODE_INSTALL_SH_URL = 'https://code.pythinker.com/pythinker-code/install.sh';
export const PYTHINKER_CODE_INSTALL_PS1_URL = 'https://code.pythinker.com/pythinker-code/install.ps1';

// Native install commands, split by platform. Use these for prompt copy and spawn calls only; do not assemble the strings elsewhere.
export const NATIVE_INSTALL_COMMAND_UNIX = `curl -fsSL ${PYTHINKER_CODE_INSTALL_SH_URL} | bash`;
Expand Down
4 changes: 2 additions & 2 deletions apps/pythinker-code/test/cli/update/preflight.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ describe('runUpdatePreflight', () => {
// pipefail must come before the pipeline so a failed `curl` is not masked
// by the trailing `bash` exiting 0 (see "surfaces a failed curl" below).
expect(script).toContain('set -o pipefail');
expect(script).toContain('curl -fsSL https://pythinker.com/install.sh');
expect(script).toContain('curl -fsSL https://code.pythinker.com/pythinker-code/install.sh');
expect(script).toContain('| bash');
} finally {
Object.defineProperty(process, 'platform', { value: originalPlatform });
Expand All @@ -510,7 +510,7 @@ describe('runUpdatePreflight', () => {
try {
const { stdout, options } = captureOutput();
await expect(runUpdatePreflight('0.4.0', options)).resolves.toBe('continue');
expect(stdout.join('')).toContain('irm https://pythinker.com/install.ps1 | iex');
expect(stdout.join('')).toContain('irm https://code.pythinker.com/pythinker-code/install.ps1 | iex');
expect(promptForInstallChoice).not.toHaveBeenCalled();
expect(mocks.spawn).not.toHaveBeenCalled();
} finally {
Expand Down
12 changes: 8 additions & 4 deletions apps/pythinker-web/public/install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
# (added to the user PATH).
#
# Usage:
# irm https://pythinker.com/install.ps1 | iex
# irm https://code.pythinker.com/pythinker-code/install.ps1 | iex
#
# To pin a version when running the hosted script, set:
# $env:PYTHINKER_VERSION = "0.6.0"; irm https://pythinker.com/install.ps1 | iex
# $env:PYTHINKER_VERSION = "0.6.0"; irm https://code.pythinker.com/pythinker-code/install.ps1 | iex
#
# Or run the script directly:
# .\install.ps1 -Version 0.6.0
Expand All @@ -26,8 +26,8 @@ $Repo = "Pythoughts-labs/pythinker-code"
# CDN source of truth for the latest published version — same endpoint the
# in-app updater reads, so a fresh install and an auto-update always agree.
$CdnLatestUrl = "https://code.pythinker.com/pythinker-code/latest"
$InstallShUrl = "https://pythinker.com/install.sh"
$InstallPs1Url = "https://pythinker.com/install.ps1"
$InstallShUrl = "https://code.pythinker.com/pythinker-code/install.sh"
$InstallPs1Url = "https://code.pythinker.com/pythinker-code/install.ps1"
$NoColor = $env:NO_COLOR

try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
Expand Down Expand Up @@ -148,6 +148,10 @@ function Stop-AntennaSpin {
}

function Write-LogoAnimated {
# The intro absolute-positions to screen rows (grid at 5-9, progress at
# 17), which assumes a clean screen. Clear it first — otherwise earlier
# shell output interleaves with the animation.
Write-Host -NoNewline "${ESC}[2J${ESC}[H"
$rows = 5; $cols = 13
$frameMs = 60; $staggerMs = 40
if ($env:PYTHINKER_LOGO_FRAME_DELAY) { try { $frameMs = [int]([double]$env:PYTHINKER_LOGO_FRAME_DELAY * 1000) } catch {} }
Expand Down
23 changes: 14 additions & 9 deletions apps/pythinker-web/public/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@
# ~/.local/bin/pythinker
#
# Usage:
# curl -fsSL https://pythinker.com/install.sh | bash
# curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash
#
# # Pin a specific version:
# curl -fsSL https://pythinker.com/install.sh | bash -s -- --version 0.6.0
# curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash -s -- --version 0.6.0
#
# # Custom install prefix (default $HOME/.local):
# curl -fsSL https://pythinker.com/install.sh | bash -s -- --prefix /opt/pythinker
# curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash -s -- --prefix /opt/pythinker
#
# Supported targets (matches release artifact names pythinker-code-<target>.zip):
# linux-x64 (Linux x86_64)
# linux-arm64 (Linux ARM64)
# darwin-arm64 (macOS Apple Silicon)
# darwin-x64 (macOS Intel)
#
# Windows users: irm https://pythinker.com/install.ps1 | iex
# Windows users: irm https://code.pythinker.com/pythinker-code/install.ps1 | iex
set -euo pipefail

VERSION=""
Expand All @@ -38,21 +38,21 @@ installs it at
~/.local/bin/pythinker

Usage:
curl -fsSL https://pythinker.com/install.sh | bash
curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash

# Pin a specific version:
curl -fsSL https://pythinker.com/install.sh | bash -s -- --version 0.6.0
curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash -s -- --version 0.6.0

# Custom install prefix (default $HOME/.local):
curl -fsSL https://pythinker.com/install.sh | bash -s -- --prefix /opt/pythinker
curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash -s -- --prefix /opt/pythinker

Supported targets (matches release artifact names pythinker-code-<target>.zip):
linux-x64 (Linux x86_64)
linux-arm64 (Linux ARM64)
darwin-arm64 (macOS Apple Silicon)
darwin-x64 (macOS Intel)

Windows users: irm https://pythinker.com/install.ps1 | iex
Windows users: irm https://code.pythinker.com/pythinker-code/install.ps1 | iex
EOF
}

Expand Down Expand Up @@ -327,6 +327,11 @@ print_logo_animated() {
local FRAME_DELAY="${PYTHINKER_LOGO_FRAME_DELAY:-0.06}"
local STAGGER_DELAY="${PYTHINKER_LOGO_STAGGER_DELAY:-0.04}"

# The whole intro absolute-positions to screen rows (grid at 5-9,
# progress at 17), which assumes a clean screen. Clear it first —
# otherwise earlier shell output interleaves with the animation.
printf '\033[2J\033[H'

# Hide the text cursor while we redraw in place — otherwise the block
# cursor on terminals like Terminal.app renders as a stray white block
# at the cursor's current cell. Show it again before returning so the
Expand Down Expand Up @@ -589,7 +594,7 @@ case "$os/$arch" in
platform_display="macOS x64" ;;
MINGW*/*|MSYS*/*|CYGWIN*/*)
fail "On Windows, use the PowerShell installer:
powershell -c \"irm https://pythinker.com/install.ps1 | iex\"" ;;
powershell -c \"irm https://code.pythinker.com/pythinker-code/install.ps1 | iex\"" ;;
*)
fail "unsupported target: $os/$arch" ;;
esac
Expand Down
72 changes: 50 additions & 22 deletions packages/oauth/src/oauth-pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,53 +39,77 @@ const PYTHINKER_LOGO_SVG =
'</svg>';

const SUCCESS_BADGE_SVG =
'<svg class="badge-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="28" height="28" aria-hidden="true" focusable="false">' +
'<circle cx="12" cy="12" r="11" fill="#1a3d2f" stroke="#3ecf8e" stroke-width="1.5"/>' +
'<path d="M7.5 12.2l3 3 6-6.5" fill="none" stroke="#3ecf8e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>' +
'<svg class="badge-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="44" height="44" aria-hidden="true" focusable="false">' +
'<circle cx="12" cy="12" r="11" fill="#f0fdf4" stroke="#15803d" stroke-width="1.5"/>' +
'<path class="badge-draw" d="M7.5 12.2l3 3 6-6.5" fill="none" stroke="#15803d" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>' +
'</svg>';

const ERROR_BADGE_SVG =
'<svg class="badge-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="28" height="28" aria-hidden="true" focusable="false">' +
'<circle cx="12" cy="12" r="11" fill="#3d1a1a" stroke="#f87171" stroke-width="1.5"/>' +
'<path d="M8.5 8.5l7 7M15.5 8.5l-7 7" fill="none" stroke="#f87171" stroke-width="2" stroke-linecap="round"/>' +
'<svg class="badge-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="44" height="44" aria-hidden="true" focusable="false">' +
'<circle cx="12" cy="12" r="11" fill="#fef2f2" stroke="#dc2626" stroke-width="1.5"/>' +
'<path class="badge-draw" d="M8.5 8.5l7 7M15.5 8.5l-7 7" fill="none" stroke="#dc2626" stroke-width="2" stroke-linecap="round"/>' +
'</svg>';

// Palette and card treatment mirror apps/site/src/style.css (light canvas,
// grid background, hairline card, blue accent). Inline-only: the Inter/mono
// families fall back to system fonts because this page must work offline.
const GRID_BG =
'url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'48\' height=\'48\'%3E%3Cpath d=\'M48 0H0v48\' fill=\'none\' stroke=\'%23111113\' stroke-opacity=\'0.045\' stroke-width=\'1\'/%3E%3C/svg%3E")';

const PAGE_STYLES =
':root{color-scheme:light}' +
'*,*::before,*::after{box-sizing:border-box}' +
'html,body{height:100%;margin:0}' +
'body{' +
'font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;' +
'background:#0b0f19;' +
'background-image:radial-gradient(ellipse 80% 60% at 50% -10%,rgba(62,207,142,.12),transparent),' +
'radial-gradient(ellipse 60% 50% at 100% 100%,rgba(238,154,135,.08),transparent);' +
'color:#e8eaed;' +
"font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',helvetica,arial,sans-serif;" +
`background-color:#ffffff;background-image:${GRID_BG};color:#111113;` +
'display:flex;align-items:center;justify-content:center;padding:24px;' +
'}' +
'.card{' +
'width:min(100%,480px);padding:48px 40px 44px;border-radius:20px;' +
'background:linear-gradient(180deg,#1a2030 0%,#161b22 100%);' +
'border:1px solid #2d333b;' +
'box-shadow:0 24px 48px rgba(0,0,0,.35),0 0 0 1px rgba(255,255,255,.04) inset;' +
'width:min(100%,440px);padding:40px 36px 32px;border-radius:18px;' +
'background:#ffffff;' +
'border:1px solid #e5e7eb;' +
'box-shadow:0 24px 70px -42px rgba(17,17,19,.35);' +
'display:flex;flex-direction:column;align-items:center;text-align:center;' +
'animation:card-in .35s ease-out both;' +
'}' +
'.logo{margin-bottom:8px}' +
'.logo{margin-bottom:10px}' +
'.logo svg{width:64px;height:80px}' +
'.brand{' +
'margin:0 0 28px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:#6b7280;' +
"margin:0 0 24px;font-family:'JetBrains Mono',ui-monospace,'SF Mono',monospace;" +
'font-size:12px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;color:#8b8d94;' +
'}' +
'.badge{margin-bottom:16px}' +
'.badge{margin-bottom:14px}' +
'.badge-icon{display:block}' +
'.badge-draw{stroke-dasharray:24;stroke-dashoffset:24;animation:badge-draw .4s ease-out .25s forwards}' +
'h1{' +
'margin:0 0 12px;font-size:26px;line-height:1.25;font-weight:650;letter-spacing:-.02em;color:#f5f7fa;' +
'margin:0 0 10px;font-size:24px;line-height:1.19;font-weight:600;letter-spacing:-.025em;color:#111113;' +
'}' +
'p.message{' +
'margin:0 0 24px;font-size:16px;line-height:1.55;color:#55565c;max-width:34ch;' +
'}' +
'.status{' +
'width:100%;display:flex;align-items:center;justify-content:center;gap:8px;' +
'padding:10px 14px;border-radius:12px;' +
'background:#f7f7f8;border:1px solid #e5e7eb;' +
"font-family:'JetBrains Mono',ui-monospace,'SF Mono',monospace;" +
'font-size:13px;color:#55565c;' +
'}' +
Comment thread
coderabbitai[bot] marked this conversation as resolved.
'p{' +
'margin:0;font-size:15px;line-height:1.6;color:#9aa0a8;max-width:34ch;' +
'.status .ok{color:#15803d}' +
'.status .err{color:#dc2626}' +
'@keyframes card-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}' +
'@keyframes badge-draw{to{stroke-dashoffset:0}}' +
'@media (prefers-reduced-motion:reduce){' +
'.card{animation:none}' +
'.badge-draw{animation:none;stroke-dashoffset:0}' +
'}';

function renderOAuthPage(options: {
readonly title: string;
readonly heading: string;
readonly message: string;
readonly badge: string;
readonly statusLine: string;
}): string {
return (
'<!doctype html><html lang="en"><head>' +
Expand All @@ -99,7 +123,8 @@ function renderOAuthPage(options: {
'<p class="brand">Pythinker Code</p>' +
`<div class="badge">${options.badge}</div>` +
`<h1>${options.heading}</h1>` +
`<p>${options.message}</p>` +
`<p class="message">${options.message}</p>` +
`<div class="status" role="status">${options.statusLine}</div>` +
'</main></body></html>'
);
}
Expand All @@ -110,6 +135,7 @@ export function renderOAuthSuccessPage(): string {
heading: "You're logged in to Pythinker",
message: 'You can close this tab and return to Pythinker.',
badge: SUCCESS_BADGE_SVG,
statusLine: 'status: <span class="ok">authenticated</span>',
});
}

Expand All @@ -119,6 +145,7 @@ export function renderOpenAICodexOAuthSuccessPage(): string {
heading: 'OpenAI Codex sign-in complete',
message: 'You can close this tab and return to Pythinker Code.',
badge: SUCCESS_BADGE_SVG,
statusLine: 'provider: openai-codex &middot; status: <span class="ok">authenticated</span>',
});
}

Expand All @@ -128,5 +155,6 @@ export function renderOAuthErrorPage(): string {
heading: 'Sign-in failed',
message: 'The authorization server reported an error. Return to Pythinker for details.',
badge: ERROR_BADGE_SVG,
statusLine: 'status: <span class="err">failed</span>',
});
}
Loading