Skip to content

Commit 8d6d30e

Browse files
committed
feat(auth): implement isolated Paper design system
1 parent a0f47b1 commit 8d6d30e

116 files changed

Lines changed: 6647 additions & 1786 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎apps/desktop/package.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
"electron-updater": "6.8.9",
4444
"micromatch": "4.0.8",
4545
"safe-regex2": "5.1.2",
46-
"pdf-lib": "1.17.1"
46+
"pdf-lib": "1.17.1",
47+
"@sim/auth-ui": "workspace:*"
4748
},
4849
"devDependencies": {
4950
"@electron/fuses": "1.8.0",

‎apps/desktop/scripts/build.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,17 @@ const renderer: BuildOptions = {
108108
{
109109
name: 'desktop-tailwind',
110110
setup(builder) {
111-
builder.onLoad({ filter: /shell\.css$/ }, async ({ path }) => {
111+
builder.onLoad({ filter: /(?:shell|utilities|styles\.module)\.css$/ }, async ({ path }) => {
112112
const config = await loadPostcssConfig({}, resolve('../sim'))
113113
const result = await postcss(config.plugins).process(readFileSync(path, 'utf8'), {
114114
from: path,
115115
})
116116
return {
117-
contents: result.css,
118-
loader: 'css',
117+
contents: result.css.replace(
118+
/url\(["']?\.\/assets\/(geist[^)"']+)["']?\)/g,
119+
'url(./$1)'
120+
),
121+
loader: path.endsWith('.module.css') ? 'local-css' : 'css',
119122
resolveDir: dirname(path),
120123
watchFiles: result.messages.flatMap((message) =>
121124
message.type === 'dependency' ? [message.file as string] : []
@@ -130,6 +133,10 @@ const renderer: BuildOptions = {
130133
async function run(): Promise<void> {
131134
execFileSync(process.execPath, ['run', 'scripts/build-native.ts'], { stdio: 'inherit' })
132135
compileComputerUse()
136+
mkdirSync('dist/renderer', { recursive: true })
137+
for (const font of ['geist-variable.woff2', 'geist-mono-variable.woff2']) {
138+
cpSync(resolve('../../packages/auth-ui/src/assets', font), join('dist/renderer', font))
139+
}
133140
if (watch) {
134141
const { context } = await import('esbuild')
135142
const rendererCtx = await context(renderer)

‎apps/desktop/src/main/local-pages.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ const LOCAL_PAGES: ReadonlySet<string> = new Set<LocalPage>(['offline.html', 'se
3737
const SERVABLE_FILES: ReadonlySet<string> = new Set([
3838
...LOCAL_PAGES,
3939
'SeasonSansUprightsVF.woff2',
40+
'geist-variable.woff2',
41+
'geist-mono-variable.woff2',
4042
'server.js',
4143
'server.css',
4244
'offline.js',

‎apps/desktop/src/renderer/auth.css‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
html,
2+
body {
3+
margin: 0;
4+
padding: 0;
5+
background: white;
6+
color-scheme: light;
7+
}

‎apps/desktop/src/renderer/offline/index.tsx‎

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import { useState } from 'react'
2+
import {
3+
AuthRoot,
4+
AuthShellView,
5+
AuthTextLink,
6+
AuthWordmark,
7+
AuthButton as Chip,
8+
AuthStatusContent as StatusPageContent,
9+
} from '@sim/auth-ui'
210
import { observeDesktopTitleBar, type SimDesktopApi } from '@sim/desktop-bridge'
3-
import { Chip, LogoPage, SimWordmark, StatusPageContent } from '@sim/emcn'
411
import { createRoot } from 'react-dom/client'
5-
import { initializeShellPage } from '@/renderer/shell'
6-
import '@/renderer/shell.css'
12+
import '@/renderer/auth.css'
713

814
const ERROR_COPY = {
915
offline: {
@@ -57,51 +63,55 @@ function OfflinePage({ isSimCloud }: OfflinePageProps) {
5763
}
5864

5965
return (
60-
<LogoPage
61-
center
62-
className='desktop-title-bar-page'
63-
titleBar={
64-
<div aria-hidden className='desktop-login-window-drag-region desktop-window-drag-region' />
65-
}
66-
logo={
67-
<span role='img' aria-label='Sim' className='flex h-[30px] items-center'>
68-
<SimWordmark />
69-
</span>
70-
}
71-
>
72-
<StatusPageContent
73-
titleId='title'
74-
title={copy.title}
75-
description={copy.message}
76-
detail={
77-
<p
78-
id='detail'
79-
role='status'
80-
className='max-w-full break-words text-[var(--text-muted)] text-caption'
66+
<AuthRoot>
67+
<AuthShellView
68+
homeHref='https://sim.ai'
69+
logo={<AuthWordmark />}
70+
titlebar={<div data-auth-titlebar='' aria-hidden='true' />}
71+
footer={
72+
<AuthTextLink
73+
onClick={() => {
74+
void bridge?.openExternal('mailto:support@sim.ai')
75+
}}
8176
>
82-
{actionError || detail}
83-
</p>
77+
Contact support
78+
</AuthTextLink>
8479
}
8580
>
86-
<Chip id='retry' variant='primary' onClick={() => bridge?.offlineRetry()}>
87-
Retry
88-
</Chip>
89-
{isSimCloud ? (
90-
<Chip id='status' onClick={checkStatus}>
91-
Check status
81+
<StatusPageContent
82+
titleId='title'
83+
title={copy.title}
84+
description={copy.message}
85+
detail={
86+
<p
87+
id='detail'
88+
role='status'
89+
className='au:max-w-full au:break-words au:text-[var(--auth-secondary)] au:text-caption'
90+
>
91+
{actionError || detail}
92+
</p>
93+
}
94+
>
95+
<Chip fullWidth id='retry' variant='primary' onClick={() => bridge?.offlineRetry()}>
96+
Retry
9297
</Chip>
93-
) : null}
94-
<Chip id='server' onClick={() => bridge?.server?.open()}>
95-
Change server
96-
</Chip>
97-
</StatusPageContent>
98-
</LogoPage>
98+
{isSimCloud ? (
99+
<Chip fullWidth id='status' onClick={checkStatus}>
100+
Check status
101+
</Chip>
102+
) : null}
103+
<Chip fullWidth variant='ghost' id='server' onClick={() => bridge?.server?.open()}>
104+
Change server
105+
</Chip>
106+
</StatusPageContent>
107+
</AuthShellView>
108+
</AuthRoot>
99109
)
100110
}
101111

102112
const container = document.getElementById('root')
103113
if (!container) throw new Error('Offline page root is missing')
104-
void initializeShellPage().then(() => {
114+
{
105115
observeDesktopTitleBar(document.documentElement, navigator.userAgent, bridge)
106116
const root = createRoot(container)
107117
root.render(<OfflinePage isSimCloud={false} />)
@@ -111,4 +121,4 @@ void initializeShellPage().then(() => {
111121
root.render(<OfflinePage isSimCloud={isSimCloud} />)
112122
})
113123
.catch(() => {})
114-
})
124+
}

‎apps/desktop/src/renderer/server/index.tsx‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { createRoot } from 'react-dom/client'
22
import { ServerModal } from '@/renderer/server/server-modal'
3-
import { initializeShellPage, shellWindow } from '@/renderer/shell'
4-
import '@/renderer/shell.css'
3+
import type { ShellWindowApi } from '@/shared/shell'
4+
5+
const shellWindow = (window as Window & { simShell?: ShellWindowApi }).simShell
6+
import '@/renderer/auth.css'
57

68
const container = document.getElementById('root')
79
if (!container || !shellWindow) throw new Error('Server modal host is unavailable')
@@ -17,4 +19,4 @@ async function renderServerModal() {
1719
}
1820
}
1921

20-
void initializeShellPage().then(renderServerModal)
22+
void renderServerModal()
Lines changed: 86 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
import { useRef, useState } from 'react'
2+
import { AuthButton, AuthField, AuthHeader, AuthInput, AuthRoot, AuthStack } from '@sim/auth-ui'
23
import type { DesktopServerConfiguration } from '@sim/desktop-bridge'
3-
import {
4-
ChipModalBody,
5-
ChipModalDescription,
6-
ChipModalField,
7-
ChipModalFooter,
8-
ChipModalHeader,
9-
ChipModalSurface,
10-
} from '@sim/emcn'
11-
import { mountShellModal } from '@/renderer/shell'
124
import type { ShellWindowApi } from '@/shared/shell'
135

146
interface ServerModalProps {
@@ -22,7 +14,24 @@ function closeWindow() {
2214
}
2315

2416
function mountServerModal(element: HTMLDivElement | null) {
25-
return mountShellModal(element, closeWindow)
17+
if (!element) return
18+
element.querySelector<HTMLInputElement>('input')?.focus()
19+
const host = (window as Window & { simShell?: ShellWindowApi }).simShell
20+
const resize = () => host?.resizeContent(Math.ceil(element.scrollHeight))
21+
const observer = new ResizeObserver(resize)
22+
observer.observe(element)
23+
const onKey = (event: KeyboardEvent) => {
24+
if (event.key === 'Escape') {
25+
event.preventDefault()
26+
closeWindow()
27+
}
28+
}
29+
document.addEventListener('keydown', onKey)
30+
resize()
31+
return () => {
32+
observer.disconnect()
33+
document.removeEventListener('keydown', onKey)
34+
}
2635
}
2736

2837
export function ServerModal({ server, configuration, initialError }: ServerModalProps) {
@@ -58,50 +67,73 @@ export function ServerModal({ server, configuration, initialError }: ServerModal
5867
}
5968

6069
return (
61-
<ChipModalSurface
62-
ref={mountServerModal}
63-
role='dialog'
64-
aria-modal='true'
65-
aria-labelledby='server-title'
66-
aria-describedby='server-description'
67-
className='max-h-screen'
68-
>
69-
<ChipModalHeader
70-
onClose={closeWindow}
71-
className='[-webkit-app-region:drag] [&_button]:[-webkit-app-region:no-drag]'
70+
<AuthRoot>
71+
<div
72+
ref={mountServerModal}
73+
role='dialog'
74+
aria-modal='true'
75+
aria-label='Sim server'
76+
className='au:p-6'
7277
>
73-
<span id='server-title'>Sim server</span>
74-
</ChipModalHeader>
75-
<ChipModalBody>
76-
<ChipModalDescription id='server-description'>
77-
Point this app at your own Sim deployment. Self-hosted servers must use HTTPS; localhost
78-
may use HTTP.
79-
</ChipModalDescription>
80-
<ChipModalField
81-
type='input'
82-
inputType='url'
83-
title='Server URL'
84-
value={origin}
85-
onChange={(value) => {
86-
setOrigin(value)
87-
setError(undefined)
88-
setMessage('')
89-
}}
90-
autoComplete='off'
91-
placeholder='https://sim.example.com'
92-
disabled={pending}
93-
error={error}
94-
hint={<span role='status'>{pending ? 'Connecting…' : message}</span>}
95-
/>
96-
</ChipModalBody>
97-
<ChipModalFooter
98-
onCancel={closeWindow}
99-
primaryAction={{
100-
label: pending ? 'Connecting…' : 'Connect',
101-
onClick: connect,
102-
disabled: pending || !origin.trim(),
103-
}}
104-
/>
105-
</ChipModalSurface>
78+
<AuthStack>
79+
<div className='au:[-webkit-app-region:drag]'>
80+
<AuthHeader
81+
title='Sim server'
82+
description='Connect to your organization’s Sim deployment.'
83+
/>
84+
</div>
85+
<form
86+
onSubmit={(event) => {
87+
event.preventDefault()
88+
void connect()
89+
}}
90+
className='au:space-y-5'
91+
>
92+
<AuthField
93+
htmlFor='server-origin'
94+
label='Server URL'
95+
errors={error ? [error] : undefined}
96+
hint={
97+
<>
98+
<span>Use HTTPS for self-hosted servers. Localhost may use HTTP.</span>
99+
<span role='status'>{message && ` ${message}`}</span>
100+
</>
101+
}
102+
>
103+
<AuthInput
104+
id='server-origin'
105+
type='url'
106+
value={origin}
107+
onChange={(event) => {
108+
setOrigin(event.target.value)
109+
setError(undefined)
110+
setMessage('')
111+
}}
112+
autoComplete='off'
113+
placeholder='https://sim.example.com'
114+
disabled={pending}
115+
error={Boolean(error)}
116+
aria-describedby='server-origin-hint'
117+
/>
118+
</AuthField>
119+
<div className='au:grid au:grid-cols-2 au:gap-2'>
120+
<AuthButton onClick={closeWindow} fullWidth>
121+
Cancel
122+
</AuthButton>
123+
<AuthButton
124+
type='submit'
125+
variant='primary'
126+
disabled={!origin.trim()}
127+
loading={pending}
128+
loadingLabel='Connecting…'
129+
fullWidth
130+
>
131+
Connect
132+
</AuthButton>
133+
</div>
134+
</form>
135+
</AuthStack>
136+
</div>
137+
</AuthRoot>
106138
)
107139
}

‎apps/sim/app/(auth)/components/auth-divider.tsx‎

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)