Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
2ba649e
fix: upstream sync regressions (wizard, i18n, profiles, routing)
leeroybrun Jan 12, 2026
dbd5c59
refactor: normalize settings navigation routes
leeroybrun Jan 12, 2026
90f8c21
fix: follow modal guidelines for profile deletion
leeroybrun Jan 12, 2026
2e58f8a
fix: update profile timestamps + memoize profiles page
leeroybrun Jan 12, 2026
2a0316d
fix(router): align typedRoutes navigation with expo-router
leeroybrun Jan 12, 2026
ec42982
fix: upstream sync regressions (wizard, i18n, profiles, routing)
leeroybrun Jan 12, 2026
2e65322
feat(settings): add profiles feature flag
leeroybrun Jan 13, 2026
ba866b8
fix(new-session): restore legacy session creation UI
leeroybrun Jan 12, 2026
8b37d10
refactor(pickers): unify selector layout and selection UI
leeroybrun Jan 13, 2026
306cd71
fix(sync): prevent settings version-mismatch retry loop
leeroybrun Jan 13, 2026
7b937d8
fix(security): avoid logging sensitive sync/message data
leeroybrun Jan 13, 2026
e25e11b
refactor(pickers): consolidate search toggle and remove default favor…
leeroybrun Jan 13, 2026
0cfafbc
fix(new-session): restore chip layout and mobile close
leeroybrun Jan 13, 2026
ec01fad
fix(profiles): improve tmux and env var spacing
leeroybrun Jan 13, 2026
80b1789
fix(ui): align SearchHeader with maxWidth layout
leeroybrun Jan 13, 2026
5d4f1f9
refactor(wizard): use machine and path picker modals
leeroybrun Jan 13, 2026
98db44f
refactor(wizard): restore inline machine/path selectors
leeroybrun Jan 14, 2026
33f305a
fix(wizard): always show working directory input
leeroybrun Jan 14, 2026
c276449
fix(wizard): embed profile list and stabilize picker row icons
leeroybrun Jan 14, 2026
4f49ef4
feat(wizard): embed profiles and use searchable directory picker
leeroybrun Jan 14, 2026
01281fb
refactor(wizard): reuse path picker UI and fix profile availability
leeroybrun Jan 14, 2026
c6f3441
refactor(wizard): unify bottom chips and profile icons
leeroybrun Jan 14, 2026
da8a1ef
fix(new-session): improve close and picker affordances
leeroybrun Jan 14, 2026
cf83504
refactor(pickers): split search toggles and unify wizard inputs
leeroybrun Jan 14, 2026
b4c2f26
fix(ui): align wizard search and env var cards
leeroybrun Jan 14, 2026
f0929d1
fix(ui): unify picker selection and profile icons
leeroybrun Jan 14, 2026
ecc6246
fix(ui): use profile compatibility glyph icons
leeroybrun Jan 14, 2026
3debc6c
fix(pickers): keep search visible with zero matches
leeroybrun Jan 14, 2026
3ca8e85
fix(ui): stabilize chip icons
leeroybrun Jan 14, 2026
f6ce61c
feat(profiles): add favorites and group lists
leeroybrun Jan 14, 2026
80c1abd
fix(paths): keep search visible in path picker
leeroybrun Jan 14, 2026
5c9702c
fix(ui): adjust icons
leeroybrun Jan 14, 2026
a9eb77c
fix(paths): preserve search focus when moving
leeroybrun Jan 14, 2026
7c5b888
fix(ui): tweak profile glyph sizing
leeroybrun Jan 14, 2026
406c96c
fix(ui): polish wizard labels and focus styles
leeroybrun Jan 14, 2026
d30d5db
fix(ui): simplify wizard headers and list icons
leeroybrun Jan 14, 2026
ba0c6c3
fix(ui): lock backend chip when profile selected
leeroybrun Jan 14, 2026
e8e6c4b
fix(profiles): gate backend changes by profile compatibility
leeroybrun Jan 14, 2026
f4b1ffe
feat(profiles): add backend compatibility and wizard AI step
leeroybrun Jan 14, 2026
71ffe40
fix(new-session): persist profile edits and preserve permissions
leeroybrun Jan 14, 2026
a57cbb5
fix(profiles): polish editor and picker UI
leeroybrun Jan 14, 2026
64e8422
fix(profiles): improve env var UX and editor safety
leeroybrun Jan 14, 2026
c197e9f
fix(web): alert modals not clickable when shown above expo-router modals
leeroybrun Jan 11, 2026
92e1f39
fix(ui): polish wizard profile actions and env var previews
leeroybrun Jan 14, 2026
45a5ac6
fix(i18n): restore dedicated English translations file
leeroybrun Jan 14, 2026
8336b40
fix(ui): stabilize profile selection and env vars modals
leeroybrun Jan 14, 2026
b79dfe5
fix(new-session): persist model mode in draft
leeroybrun Jan 14, 2026
78d696b
refactor(new-session): stop using lastUsedModelMode
leeroybrun Jan 14, 2026
8307d1d
fix(new-session): compact mobile profile actions
leeroybrun Jan 14, 2026
4692b3b
fix(ui): suppress selected background for single-item groups
leeroybrun Jan 14, 2026
f3815a1
fix(ui): collapse row actions on mobile
leeroybrun Jan 14, 2026
97d644e
refactor(new-session): reuse ItemRowActions for profile rows
leeroybrun Jan 14, 2026
17c531e
fix(ui): center modals and tighten chips
leeroybrun Jan 14, 2026
8ea98a2
fix(ui): tighten new session chip spacing
leeroybrun Jan 14, 2026
71a0edf
fix(ui): align new session input padding
leeroybrun Jan 14, 2026
3e40eb0
fix(ui): simplify chips and move session profile info
leeroybrun Jan 14, 2026
05233ff
fix(session-info): show profile under metadata
leeroybrun Jan 15, 2026
94f8b28
fix(profiles): align edit form buttons
leeroybrun Jan 15, 2026
7482c7c
fix(env-vars): improve preview scroll and machine resolution
leeroybrun Jan 15, 2026
b00bbc0
fix(new-session): refine wizard padding and bottom bar
leeroybrun Jan 15, 2026
3d1d6fb
fix(env-vars): clarify machine resolution and source var input
leeroybrun Jan 15, 2026
f0148f1
fix(new-session): improve wizard sizing and AI profile labels
leeroybrun Jan 15, 2026
6bb0209
fix(new-session): avoid nested scroll and align composer padding
leeroybrun Jan 15, 2026
a1bcc98
fix(env-vars): resolve via login shell and clarify machine status
leeroybrun Jan 15, 2026
2b8b6e7
fix(web): increase modal height and align env preview with daemon
leeroybrun Jan 15, 2026
4b70ee6
fix(env-vars): batch resolve in editor and support :=
leeroybrun Jan 15, 2026
21c7968
refactor(i18n): move translation types to _types
leeroybrun Jan 15, 2026
04628ed
refactor(profiles): remove provider config objects
leeroybrun Jan 15, 2026
5571035
fix(new-session): switch profile picker to id-based navigation
leeroybrun Jan 15, 2026
1a36c61
fix(env): align template semantics and preview safety
leeroybrun Jan 15, 2026
8adde3f
fix(sync): stop resetting model meta and gate logs
leeroybrun Jan 15, 2026
15a768d
Merge remote-tracking branch 'upstream/main' into slopus/pr/upstream-…
leeroybrun Jan 15, 2026
b870e1d
fix(new): align Gemini permission and model modes
leeroybrun Jan 15, 2026
47ae80a
fix(app): avoid bundling vitest test file
leeroybrun Jan 15, 2026
6673881
fix(agent-input): remove duplicate machine/path panel
leeroybrun Jan 15, 2026
7bb538f
fix(new): consume profileId param once
leeroybrun Jan 16, 2026
a4bf4eb
fix(new): polish standard modal controls
leeroybrun Jan 16, 2026
1af0fc0
fix(profiles): improve dark-mode action icon contrast
leeroybrun Jan 16, 2026
6b979e8
fix(new): restore standard modal input panel background
leeroybrun Jan 16, 2026
dfa6a22
chore(copy): update input placeholder
leeroybrun Jan 16, 2026
72055b6
feat(new): add Gemini model selection to wizard
leeroybrun Jan 16, 2026
40fdf02
feat(new): polish model selection step
leeroybrun Jan 16, 2026
cd790b2
fix(ui): improve selected icon contrast in dark mode
leeroybrun Jan 16, 2026
b0dd47e
fix(ui): align gemini permission mapping and cleanup profiles
leeroybrun Jan 16, 2026
bbd4ccc
fix(happy): address upstream-sync regressions
leeroybrun Jan 16, 2026
14e962c
fix(modal): improve web alert button layout
leeroybrun Jan 16, 2026
14c35f0
feat(path): handle Enter in wizard and picker
leeroybrun Jan 16, 2026
5439a83
feat(profile-edit): allow save from discard prompt
leeroybrun Jan 16, 2026
dba0855
fix(profiles): allow favoriting default environment
leeroybrun Jan 16, 2026
22f8574
merge: sync with origin/slopus/pr/upstream-sync-regressions-2026-01-12
leeroybrun Jan 16, 2026
919c46e
fix(sync): avoid settings downgrades and normalize favorites
leeroybrun Jan 16, 2026
12d4386
fix(sync): persist session model mode across restarts
leeroybrun Jan 16, 2026
8630a03
refactor(ui): localize selectors and move styles to unistyles
leeroybrun Jan 16, 2026
d41b07c
fix(ios): patch react-native-libsodium podspec
leeroybrun Jan 16, 2026
d740d89
fix(env-vars): avoid fetching secrets and sync card state
leeroybrun Jan 16, 2026
e955a0a
fix(ui): address reviewer feedback
leeroybrun Jan 16, 2026
c0e7162
fix(ui): address reviewer feedback
leeroybrun Jan 17, 2026
348b4bd
fix: address review feedback
leeroybrun Jan 17, 2026
fcefb25
feat(storage): add optional storage scoping
leeroybrun Jan 17, 2026
5a6c855
feat(env): add daemon env preview support
leeroybrun Jan 17, 2026
03df1f9
fix(env): make env previews policy-aware
leeroybrun Jan 17, 2026
66e8541
chore(expo): allow optional app config overrides
leeroybrun Jan 17, 2026
3e0e176
fix(auth): harden token storage on web
leeroybrun Jan 17, 2026
5061512
fix(persistence): validate draft modes from storage
leeroybrun Jan 17, 2026
b7e768d
fix(i18n): localize search accessibility labels
leeroybrun Jan 17, 2026
401e64b
chore(expo): default unknown APP_ENV to dev
leeroybrun Jan 17, 2026
f9676f7
fix(ui): address reviewer nits
leeroybrun Jan 17, 2026
c8ef4b5
fix(i18n): localize built-in profile names
leeroybrun Jan 17, 2026
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
70 changes: 35 additions & 35 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,42 +23,42 @@ This allows you to test production-like builds with real users before releasing

```bash
# Development variant (default)
npm run ios:dev
yarn ios:dev

# Preview variant
npm run ios:preview
yarn ios:preview

# Production variant
npm run ios:production
yarn ios:production
```

### Android Development

```bash
# Development variant
npm run android:dev
yarn android:dev

# Preview variant
npm run android:preview
yarn android:preview

# Production variant
npm run android:production
yarn android:production
```

### macOS Desktop (Tauri)

```bash
# Development variant - run with hot reload
npm run tauri:dev
yarn tauri:dev

# Build development variant
npm run tauri:build:dev
yarn tauri:build:dev

# Build preview variant
npm run tauri:build:preview
yarn tauri:build:preview

# Build production variant
npm run tauri:build:production
yarn tauri:build:production
```

**How Tauri Variants Work:**
Expand All @@ -71,13 +71,13 @@ npm run tauri:build:production

```bash
# Start dev server for development variant
npm run start:dev
yarn start:dev

# Start dev server for preview variant
npm run start:preview
yarn start:preview

# Start dev server for production variant
npm run start:production
yarn start:production
```

## Visual Differences
Expand All @@ -95,7 +95,7 @@ This makes it easy to distinguish which version you're testing!

1. **Build development variant:**
```bash
npm run ios:dev
yarn ios:dev
```

2. **Make your changes** to the code
Expand All @@ -104,19 +104,19 @@ This makes it easy to distinguish which version you're testing!

4. **Rebuild if needed** for native changes:
```bash
npm run ios:dev
yarn ios:dev
```

### Testing Preview (Pre-Release)

1. **Build preview variant:**
```bash
npm run ios:preview
yarn ios:preview
```

2. **Test OTA updates:**
```bash
npm run ota # Publishes to preview branch
yarn ota # Publishes to preview branch
```

3. **Verify** the preview build works as expected
Expand All @@ -125,17 +125,17 @@ This makes it easy to distinguish which version you're testing!

1. **Build production variant:**
```bash
npm run ios:production
yarn ios:production
```

2. **Submit to App Store:**
```bash
npm run submit
yarn submit
```

3. **Deploy OTA updates:**
```bash
npm run ota:production
yarn ota:production
```

## All Variants Simultaneously
Expand All @@ -144,9 +144,9 @@ You can install all three variants on the same device:

```bash
# Build all three variants
npm run ios:dev
npm run ios:preview
npm run ios:production
yarn ios:dev
yarn ios:preview
yarn ios:production
```

All three apps appear on your device with different icons and names!
Expand Down Expand Up @@ -195,12 +195,12 @@ You can connect different variants to different Happy CLI instances:

```bash
# Development app → Dev CLI daemon
npm run android:dev
# Connect to CLI running: npm run dev:daemon:start
yarn android:dev
# Connect to CLI running: yarn dev:daemon:start

# Production app → Stable CLI daemon
npm run android:production
# Connect to CLI running: npm run stable:daemon:start
yarn android:production
# Connect to CLI running: yarn stable:daemon:start
```

Each app maintains separate authentication and sessions!
Expand All @@ -210,7 +210,7 @@ Each app maintains separate authentication and sessions!
To test with a local Happy server:

```bash
npm run start:local-server
yarn start:local-server
```

This sets:
Expand All @@ -227,21 +227,21 @@ This shouldn't happen - each variant has a unique bundle ID. If it does:
1. Check `app.config.js` - verify `bundleId` is set correctly for the variant
2. Clean build:
```bash
npm run prebuild
npm run ios:dev # or whichever variant
yarn prebuild
yarn ios:dev # or whichever variant
```

### App not updating after changes

1. **For JS changes**: Hot reload should work automatically
2. **For native changes**: Rebuild the variant:
```bash
npm run ios:dev # Force rebuild
yarn ios:dev # Force rebuild
```
3. **For config changes**: Clean and prebuild:
```bash
npm run prebuild
npm run ios:dev
yarn prebuild
yarn ios:dev
```

### All three apps look the same
Expand All @@ -258,7 +258,7 @@ If they're all the same name, the variant might not be set correctly. Verify:
echo $APP_ENV

# Or look at the build output
npm run ios:dev # Should show "Happy (dev)" as the name
yarn ios:dev # Should show "Happy (dev)" as the name
```

### Connected device not found
Expand All @@ -270,7 +270,7 @@ For iOS connected device testing:
xcrun devicectl list devices

# Run on specific connected device
npm run ios:connected-device
yarn ios:connected-device
```

## Tips
Expand Down
27 changes: 21 additions & 6 deletions app.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
const variant = process.env.APP_ENV || 'development';
const name = {

// Allow opt-in overrides for local dev tooling without changing upstream defaults.
const nameOverride = (process.env.EXPO_APP_NAME || '').trim();
const bundleIdOverride = (process.env.EXPO_APP_BUNDLE_ID || '').trim();

const namesByVariant = {
development: "Happy (dev)",
preview: "Happy (preview)",
production: "Happy"
}[variant];
const bundleId = {
};
const bundleIdsByVariant = {
development: "com.slopus.happy.dev",
preview: "com.slopus.happy.preview",
production: "com.ex3ndr.happy"
}[variant];
};

// If APP_ENV is unknown, fall back to development-safe defaults to avoid generating
// an invalid Expo config with undefined name/bundle id.
const name = nameOverride || namesByVariant[variant] || namesByVariant.development;
const bundleId = bundleIdOverride || bundleIdsByVariant[variant] || bundleIdsByVariant.development;
// NOTE:
// The URL scheme is used for deep linking *and* by the Expo development client launcher flow.
// Keep the default stable for upstream users, but allow opt-in overrides for local dev variants
// (e.g. to avoid iOS scheme collisions between multiple installs).
const scheme = (process.env.EXPO_APP_SCHEME || '').trim() || "happy";

export default {
expo: {
Expand All @@ -18,7 +33,7 @@ export default {
runtimeVersion: "18",
orientation: "default",
icon: "./sources/assets/images/icon.png",
scheme: "happy",
scheme,
userInterfaceStyle: "automatic",
newArchEnabled: true,
notification: {
Expand Down Expand Up @@ -174,4 +189,4 @@ export default {
},
owner: "bulkacorp"
}
};
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
"@material/material-color-utilities": "^0.3.0",
"@stablelib/hex": "^2.0.1",
"@types/react": "~19.1.10",
"@types/react-test-renderer": "^19.1.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"cross-env": "^10.1.0",
"patch-package": "^8.0.0",
Expand Down
20 changes: 20 additions & 0 deletions patches/@more-tech+react-native-libsodium+1.5.5.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
diff --git a/node_modules/@more-tech/react-native-libsodium/react-native-libsodium.podspec b/node_modules/@more-tech/react-native-libsodium/react-native-libsodium.podspec
index 5dbd9f1..bc3da26 100644
--- a/node_modules/@more-tech/react-native-libsodium/react-native-libsodium.podspec
+++ b/node_modules/@more-tech/react-native-libsodium/react-native-libsodium.podspec
@@ -30,7 +30,14 @@ Pod::Spec.new do |s|
}
s.dependency "React-Codegen"
if ENV['RCT_USE_RN_DEP'] != '1'
- s.dependency 'RCT-Folly', folly_version
+ # `folly_version` is not always defined during podspec evaluation
+ # (e.g. Expo/RN >= 0.81), so fall back to an unpinned dependency.
+ folly_ver = defined?(folly_version) ? folly_version : nil
+ if folly_ver
+ s.dependency 'RCT-Folly', folly_ver
+ else
+ s.dependency 'RCT-Folly'
+ end
end
s.dependency "RCTRequired"
s.dependency "RCTTypeSafety"
25 changes: 20 additions & 5 deletions sources/-session/SessionView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { isRunningOnMac } from '@/utils/platform';
import { useDeviceType, useHeaderHeight, useIsLandscape, useIsTablet } from '@/utils/responsive';
import { formatPathRelativeToHome, getSessionAvatarId, getSessionName, useSessionStatus } from '@/utils/sessionUtils';
import { isVersionSupported, MINIMUM_CLI_VERSION } from '@/utils/versionUtils';
import type { ModelMode } from '@/sync/permissionTypes';
import { Ionicons } from '@expo/vector-icons';
import { useRouter } from 'expo-router';
import * as React from 'react';
Expand Down Expand Up @@ -196,10 +197,24 @@ function SessionViewLoaded({ sessionId, session }: { sessionId: string, session:
storage.getState().updateSessionPermissionMode(sessionId, mode);
}, [sessionId]);

const CONFIGURABLE_MODEL_MODES = [
'default',
'gemini-2.5-pro',
'gemini-2.5-flash',
'gemini-2.5-flash-lite',
] as const;
type ConfigurableModelMode = (typeof CONFIGURABLE_MODEL_MODES)[number];
const isConfigurableModelMode = React.useCallback((mode: ModelMode): mode is ConfigurableModelMode => {
return (CONFIGURABLE_MODEL_MODES as readonly string[]).includes(mode);
}, []);

// Function to update model mode (for Gemini sessions)
const updateModelMode = React.useCallback((mode: 'default' | 'gemini-2.5-pro' | 'gemini-2.5-flash' | 'gemini-2.5-flash-lite') => {
storage.getState().updateSessionModelMode(sessionId, mode);
}, [sessionId]);
const updateModelMode = React.useCallback((mode: ModelMode) => {
// Only Gemini model modes are configurable from the UI today.
if (isConfigurableModelMode(mode)) {
storage.getState().updateSessionModelMode(sessionId, mode);
}
}, [isConfigurableModelMode, sessionId]);

// Memoize header-dependent styles to prevent re-renders
const headerDependentStyles = React.useMemo(() => ({
Expand Down Expand Up @@ -280,8 +295,8 @@ function SessionViewLoaded({ sessionId, session }: { sessionId: string, session:
sessionId={sessionId}
permissionMode={permissionMode}
onPermissionModeChange={updatePermissionMode}
modelMode={modelMode as any}
onModelModeChange={updateModelMode as any}
modelMode={modelMode}
onModelModeChange={updateModelMode}
metadata={session.metadata}
connectionStatus={{
text: sessionStatus.statusText,
Expand Down
17 changes: 16 additions & 1 deletion sources/app/(app)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ export default function RootLayout() {
headerTitle: t('settings.features'),
}}
/>
<Stack.Screen
name="settings/profiles"
options={{
headerTitle: t('settingsFeatures.profiles'),
}}
/>
<Stack.Screen
name="terminal/connect"
options={{
Expand Down Expand Up @@ -311,6 +317,13 @@ export default function RootLayout() {
headerBackTitle: t('common.back'),
}}
/>
<Stack.Screen
name="new/pick/profile"
options={{
headerTitle: '',
headerBackTitle: t('common.back'),
}}
/>
<Stack.Screen
name="new/pick/profile-edit"
options={{
Expand All @@ -322,7 +335,9 @@ export default function RootLayout() {
name="new/index"
options={{
headerTitle: t('newSession.title'),
headerBackTitle: t('common.back'),
headerShown: true,
headerBackTitle: t('common.cancel'),
presentation: 'modal',
}}
/>
<Stack.Screen
Expand Down
1 change: 0 additions & 1 deletion sources/app/(app)/machine/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,6 @@ export default function MachineDetailScreen() {
disabled={!isMachineOnline(machine)}
selected={isSelected}
showChevron={false}
pressableStyle={isSelected ? { backgroundColor: theme.colors.surfaceSelected } : undefined}
showDivider={!hideDivider}
/>
);
Expand Down
Loading