26 Commits

Author SHA1 Message Date
dependabot[bot]
ebb68b9604 Bump @chakra-ui/react from 2.10.9 to 3.24.2
Bumps [@chakra-ui/react](https://github.com/chakra-ui/chakra-ui/tree/HEAD/packages/react) from 2.10.9 to 3.24.2.
- [Release notes](https://github.com/chakra-ui/chakra-ui/releases)
- [Changelog](https://github.com/chakra-ui/chakra-ui/blob/main/packages/react/CHANGELOG.md)
- [Commits](https://github.com/chakra-ui/chakra-ui/commits/@chakra-ui/react@3.24.2/packages/react)

---
updated-dependencies:
- dependency-name: "@chakra-ui/react"
  dependency-version: 3.24.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 08:52:16 +00:00
geoffsee
ae6a6e4064 Refactor model filtering logic into reusable basicFilters function. 2025-07-31 10:10:35 -04:00
geoffsee
67483d08db Update model path handling logic for FireworksAI and refine supported model filtering. 2025-07-27 12:30:47 -04:00
geoffsee
53268b528d Update hero label for home route in renderer routes. 2025-07-27 09:32:46 -04:00
geoffsee
f9d5fc8282 Remove unused submodules and related scripts 2025-07-27 09:00:25 -04:00
geoffsee
ce9bc4db07 "Swap default states for mapActive and aiActive in LandingComponent" 2025-07-17 14:11:15 -04:00
geoffsee
bd71bfcad3 - Remove unused BevyScene and related dependencies.
- Refactor `InstallButton` and relocate it to `install/`.
- Update `Toolbar` imports to reflect the new `InstallButton` structure.
- Introduce `handleInstall` functionality for PWA installation prompt handling.
2025-07-17 14:04:47 -04:00
Geoff Seemueller
4edee1e191 Potential fix for code scanning alert no. 5: Shell command built from environment values
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: Geoff Seemueller <28698553+geoffsee@users.noreply.github.com>
2025-07-17 13:47:50 -04:00
geoffsee
734f48d4a7 remove webhost in assistant prompt 2025-07-17 13:47:50 -04:00
geoffsee
66363cdf39 set ai as the default landing 2025-07-17 13:47:50 -04:00
geoffsee
36f8fcee87 Integrate PWA service worker registration using virtual:pwa-register. 2025-07-17 13:47:50 -04:00
geoffsee
f055cd39fe Update InputMenu to use clientChatStore.reset() instead of setActiveConversation when closing. 2025-07-17 13:47:50 -04:00
geoffsee
0183503425 Refactored layout components and styling: removed unused imports, adjusted positioning and padding for consistency. 2025-07-17 13:47:50 -04:00
geoffsee
a7ad06093a simplify landing page for my peace 2025-07-17 13:47:50 -04:00
geoffsee
c26d2467f4 sweet lander 2025-07-17 13:47:50 -04:00
geoffsee
818e0e672a chat + maps + ai + tools 2025-07-17 13:47:50 -04:00
geoffsee
48655474e3 mirror error handling behavior in cloudflare worker 2025-07-17 13:47:50 -04:00
geoffsee
ffabfd4ce5 add top level error handler to the router 2025-07-17 13:47:50 -04:00
geoffsee
fa5b7466bc Optimize WASM handling and integrate service worker caching.
Removed unused pointer events in BevyScene, updated Vite config with Workbox for service worker caching, and adjusted file paths in generate-bevy-bundle.js. Added WASM size optimization to ensure smaller and efficient builds, skipping optimization for files below 30MB.
2025-07-17 13:47:50 -04:00
geoffsee
6cc5e038a7 Add visible prop to toggle components and simplify conditional rendering 2025-07-17 13:47:50 -04:00
geoffsee
e72198628c Add "Install App" button to the toolbar using react-use-pwa-install library 2025-07-17 13:47:50 -04:00
geoffsee
c0428094c8 **Integrate PWA asset generator and update favicon and manifest configuration** 2025-07-17 13:47:50 -04:00
geoffsee
3901337163 - Refactor BevyScene to replace script injection with dynamic import.
- Update `NavItem` to provide fallback route for invalid `path`.
- Temporarily stub metric API endpoints with placeholders.
2025-07-17 13:47:50 -04:00
geoffsee
0ff8b5c03e * Introduced BevyScene React component in landing-component for rendering a 3D cockpit visualization.
* Included WebAssembly asset `yachtpit.js` for cockpit functionality.
* Added Bevy MIT license file.
* Implemented a service worker to cache assets locally instead of fetching them remotely.
* Added collapsible functionality to **Tweakbox** and included the `@chakra-ui/icons` dependency.
* Applied the `hidden` prop to the Tweakbox Heading for better accessibility.
* Refactored **Particles** component for improved performance, clarity, and maintainability.

  * Introduced helper functions for particle creation and count management.
  * Added responsive resizing with particle repositioning.
  * Optimized animation updates, including velocity adjustments for speed changes.
  * Ensured canvas size and particle state are cleanly managed on component unmount.
2025-07-17 13:47:50 -04:00
geoffsee
858282929c Refactor chat-stream-provider to simplify tool structure. Optimize WeatherTool implementation with enriched function schema. 2025-07-17 13:47:50 -04:00
geoffsee
06b6a68b9b Enable tool-based message generation in chat-stream-provider and add BasicValueTool and WeatherTool.
Updated dependencies to latest versions in `bun.lock`. Modified development script in `package.json` to include watch mode.
2025-07-17 13:47:50 -04:00
28 changed files with 148 additions and 378 deletions

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "crates/yachtpit"]
path = crates/yachtpit
url = https://github.com/seemueller-io/yachtpit.git

Submodule crates/yachtpit deleted from 348f20641c

View File

@@ -10,7 +10,6 @@
], ],
"scripts": { "scripts": {
"clean": "packages/scripts/cleanup.sh", "clean": "packages/scripts/cleanup.sh",
"restore:submodules": "rm -rf crates/yachtpit && (git rm --cached crates/yachtpit) && git submodule add --force https://github.com/seemueller-io/yachtpit.git crates/yachtpit ",
"test:all": "bun run --filter='*' tests", "test:all": "bun run --filter='*' tests",
"client:dev": "(cd packages/client && bun run dev)", "client:dev": "(cd packages/client && bun run dev)",
"server:dev": "bun build:client && (cd packages/server && bun run dev)", "server:dev": "bun build:client && (cd packages/server && bun run dev)",

View File

@@ -46,7 +46,6 @@ describe('AssistantSdk', () => {
expect(prompt).toContain('# Assistant Knowledge'); expect(prompt).toContain('# Assistant Knowledge');
expect(prompt).toContain('### Date: '); expect(prompt).toContain('### Date: ');
expect(prompt).toContain('### Web Host: ');
expect(prompt).toContain('### User Location: '); expect(prompt).toContain('### User Location: ');
expect(prompt).toContain('### Timezone: '); expect(prompt).toContain('### Timezone: ');
}); });

View File

@@ -23,7 +23,7 @@ export class AssistantSdk {
return `# Assistant Knowledge return `# Assistant Knowledge
## Assistant Name ## Assistant Name
### yachtpit-ai ### open-gsio
## Current Context ## Current Context
### Date: ${currentDate} ${currentTime} ### Date: ${currentDate} ${currentTime}
${maxTokens ? `### Max Response Length: ${maxTokens} tokens (maximum)` : ''} ${maxTokens ? `### Max Response Length: ${maxTokens} tokens (maximum)` : ''}

View File

@@ -15,10 +15,21 @@ export class FireworksAiChatProvider extends BaseChatProvider {
let modelPrefix = 'accounts/fireworks/models/'; let modelPrefix = 'accounts/fireworks/models/';
if (param.model.toLowerCase().includes('yi-')) { if (param.model.toLowerCase().includes('yi-')) {
modelPrefix = 'accounts/yi-01-ai/models/'; modelPrefix = 'accounts/yi-01-ai/models/';
} else if (param.model.toLowerCase().includes('/perplexity/')) {
modelPrefix = 'accounts/perplexity/models/';
} else if (param.model.toLowerCase().includes('/sentientfoundation/')) {
modelPrefix = 'accounts/sentientfoundation/models/';
} else if (param.model.toLowerCase().includes('/sentientfoundation-serverless/')) {
modelPrefix = 'accounts/sentientfoundation-serverless/models/';
} else if (param.model.toLowerCase().includes('/instacart/')) {
modelPrefix = 'accounts/instacart/models/';
} }
const finalModelIdentifier = param.model.includes(modelPrefix)
? param.model
: `${modelPrefix}${param.model}`;
console.log('using fireworks model', finalModelIdentifier);
return { return {
model: `${modelPrefix}${param.model}`, model: finalModelIdentifier,
messages: safeMessages, messages: safeMessages,
stream: true, stream: true,
}; };

View File

@@ -9,7 +9,6 @@
"generate:sitemap": "bun ./scripts/generate_sitemap.js open-gsio.seemueller.workers.dev", "generate:sitemap": "bun ./scripts/generate_sitemap.js open-gsio.seemueller.workers.dev",
"generate:robotstxt": "bun ./scripts/generate_robots_txt.js open-gsio.seemueller.workers.dev", "generate:robotstxt": "bun ./scripts/generate_robots_txt.js open-gsio.seemueller.workers.dev",
"generate:fonts": "cp -r ../../node_modules/katex/dist/fonts public/static", "generate:fonts": "cp -r ../../node_modules/katex/dist/fonts public/static",
"generate:bevy:bundle": "bun scripts/generate-bevy-bundle.js",
"generate:pwa:assets": "test ! -f public/pwa-64x64.png && pwa-assets-generator --preset minimal-2023 public/logo.png || echo 'PWA assets already exist'" "generate:pwa:assets": "test ! -f public/pwa-64x64.png && pwa-assets-generator --preset minimal-2023 public/logo.png || echo 'PWA assets already exist'"
}, },
"exports": { "exports": {
@@ -20,7 +19,7 @@
}, },
"devDependencies": { "devDependencies": {
"@chakra-ui/icons": "^2.2.4", "@chakra-ui/icons": "^2.2.4",
"@chakra-ui/react": "^2.10.6", "@chakra-ui/react": "^3.24.2",
"@cloudflare/workers-types": "^4.20241205.0", "@cloudflare/workers-types": "^4.20241205.0",
"@emotion/react": "^11.13.5", "@emotion/react": "^11.13.5",
"@emotion/styled": "^11.13.5", "@emotion/styled": "^11.13.5",

View File

@@ -1,196 +0,0 @@
import { execSync } from 'node:child_process';
import {
existsSync,
readdirSync,
readFileSync,
writeFileSync,
renameSync,
rmSync,
cpSync,
statSync,
} from 'node:fs';
import { resolve, dirname, join, basename } from 'node:path';
import { Logger } from 'tslog';
const logger = new Logger({
stdio: 'inherit',
prettyLogTimeZone: 'local',
type: 'pretty',
stylePrettyLogs: true,
prefix: ['\n'],
overwrite: true,
});
function main() {
bundleCrate();
cleanup();
logger.info('🎉 yachtpit built successfully');
}
const getRepoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf-8' }).trim();
const repoRoot = resolve(getRepoRoot);
const publicDir = resolve(repoRoot, 'packages/client/public');
const indexHtml = resolve(publicDir, 'index.html');
// Build the yachtpit project
const buildCwd = resolve(repoRoot, 'crates/yachtpit/crates/yachtpit');
logger.info(`🔨 Building in directory: ${buildCwd}`);
function needsRebuild() {
const optimizedWasm = join(buildCwd, 'dist', 'yachtpit_bg.wasm_optimized');
if (!existsSync(optimizedWasm)) return true;
}
const NEEDS_REBUILD = needsRebuild();
function bundleCrate() {
// ───────────── Build yachtpit project ───────────────────────────────────
logger.info('🔨 Building yachtpit...');
logger.info(`📁 Repository root: ${repoRoot}`);
// Check if submodules need to be initialized
const yachtpitPath = resolve(repoRoot, 'crates/yachtpit');
logger.info(`📁 Yachtpit path: ${yachtpitPath}`);
if (!existsSync(yachtpitPath)) {
logger.info('📦 Initializing submodules...');
execSync('git submodule update --init --remote', { stdio: 'inherit' });
} else {
logger.info(`✅ Submodules already initialized at: ${yachtpitPath}`);
}
try {
if (NEEDS_REBUILD) {
logger.info('🛠️ Changes detected — rebuilding yachtpit...');
execSync('trunk build --release', { cwd: buildCwd, stdio: 'inherit' });
logger.info('✅ Yachtpit built');
} else {
logger.info('⏩ No changes since last build — skipping yachtpit rebuild');
process.exit(0);
}
} catch (error) {
console.error('❌ Failed to build yachtpit:', error.message);
process.exit(1);
}
// ───────────── Copy assets to public directory ──────────────────────────
const yachtpitDistDir = join(buildCwd, 'dist');
logger.info(`📋 Copying assets to public directory...`);
// Remove existing yachtpit assets from public directory
const skipRemoveOldAssets = false;
if (!skipRemoveOldAssets) {
const existingAssets = readdirSync(publicDir).filter(
file => file.startsWith('yachtpit') && (file.endsWith('.js') || file.endsWith('.wasm')),
);
existingAssets.forEach(asset => {
const assetPath = join(publicDir, asset);
rmSync(assetPath, { force: true });
logger.info(`🗑️ Removed old asset: ${assetPath}`);
});
} else {
logger.warn('SKIPPING REMOVING OLD ASSETS');
}
// Copy new assets from yachtpit/dist to public directory
if (existsSync(yachtpitDistDir)) {
logger.info(`📍Located yachtpit build: ${yachtpitDistDir}`);
try {
cpSync(yachtpitDistDir, publicDir, {
recursive: true,
force: true,
});
logger.info(`✅ Assets copied from ${yachtpitDistDir} to ${publicDir}`);
} catch (error) {
console.error('❌ Failed to copy assets:', error.message);
process.exit(1);
}
} else {
console.error(`❌ Yachtpit dist directory not found at: ${yachtpitDistDir}`);
process.exit(1);
}
// ───────────── locate targets ───────────────────────────────────────────
const dstPath = join(publicDir, 'yachtpit.html');
// Regexes for the hashed filenames produced by most bundlers
const JS_RE = /^yachtpit-[\da-f]{16}\.js$/i;
const WASM_RE = /^yachtpit-[\da-f]{16}_bg\.wasm$/i;
// Always perform renaming of bundle files
const files = readdirSync(publicDir);
// helper that doesn't explode if the target file is already present
const safeRename = (from, to) => {
if (!existsSync(from)) return;
if (existsSync(to)) {
logger.info(` ${to} already exists removing and replacing.`);
rmSync(to, { force: true });
}
renameSync(from, to);
logger.info(`📝 Renamed: ${basename(from)}${basename(to)}`);
};
files.forEach(f => {
const fullPath = join(publicDir, f);
if (JS_RE.test(f)) safeRename(fullPath, join(publicDir, 'yachtpit.js'));
if (WASM_RE.test(f)) safeRename(fullPath, join(publicDir, 'yachtpit_bg.wasm'));
});
// ───────────── patch markup inside HTML ─────────────────────────────────
if (existsSync(indexHtml)) {
logger.info(`📝 Patching HTML file: ${indexHtml}`);
let html = readFileSync(indexHtml, 'utf8');
html = html
.replace(/yachtpit-[\da-f]{16}\.js/gi, 'yachtpit.js')
.replace(/yachtpit-[\da-f]{16}_bg\.wasm/gi, 'yachtpit_bg.wasm');
writeFileSync(indexHtml, html, 'utf8');
// ───────────── rename HTML entrypoint ─────────────────────────────────
if (basename(indexHtml) !== 'yachtpit.html') {
logger.info(`📝 Renaming HTML file: ${indexHtml}${dstPath}`);
// Remove existing yachtpit.html if it exists
if (existsSync(dstPath)) {
rmSync(dstPath, { force: true });
}
renameSync(indexHtml, dstPath);
}
} else {
logger.info(`⚠️ ${indexHtml} not found skipping HTML processing.`);
}
optimizeWasmSize();
}
function optimizeWasmSize() {
logger.info('🔨 Checking WASM size...');
const wasmPath = resolve(publicDir, 'yachtpit_bg.wasm');
const fileSize = statSync(wasmPath).size;
const sizeInMb = fileSize / (1024 * 1024);
if (sizeInMb > 30) {
logger.info(`WASM size is ${sizeInMb.toFixed(2)}MB, optimizing...`);
execSync(`wasm-opt -Oz -o ${wasmPath} ${wasmPath}`, {
encoding: 'utf-8',
});
logger.info(`✅ WASM size optimized`);
} else {
logger.info(
`⏩ Skipping WASM optimization, size (${sizeInMb.toFixed(2)}MB) is under 30MB threshold`,
);
}
}
function cleanup() {
logger.info('Running cleanup...');
rmSync(indexHtml, { force: true });
const creditsDir = resolve(`${repoRoot}/packages/client/public`, 'credits');
rmSync(creditsDir, { force: true, recursive: true });
}
main();

View File

@@ -1,35 +0,0 @@
import { IconButton } from '@chakra-ui/react';
import { HardDriveDownload } from 'lucide-react';
import React from 'react';
import { toolbarButtonZIndex } from './toolbar/Toolbar.tsx';
function InstallButton() {
// const install = usePWAInstall();
const install = () => {
console.warn('this does not work in all browsers');
};
return (
<IconButton
aria-label="Install App"
title="Install App"
icon={<HardDriveDownload />}
size="md"
bg="transparent"
stroke="text.accent"
color="text.accent"
onClick={() => install}
_hover={{
bg: 'transparent',
svg: {
stroke: 'accent.secondary',
transition: 'stroke 0.3s ease-in-out',
},
}}
zIndex={toolbarButtonZIndex}
/>
);
}
export default InstallButton;

View File

@@ -171,7 +171,7 @@ const InputMenu: React.FC<{ isDisabled?: boolean }> = observer(({ isDisabled })
bg="background.tertiary" bg="background.tertiary"
color="text.primary" color="text.primary"
onClick={() => { onClick={() => {
clientChatStore.setActiveConversation('conversation:new'); clientChatStore.reset();
onClose(); onClose();
}} }}
_hover={{ bg: 'rgba(0, 0, 0, 0.05)' }} _hover={{ bg: 'rgba(0, 0, 0, 0.05)' }}

View File

@@ -49,7 +49,7 @@ const InputTextArea: React.FC<InputTextAreaProps> = observer(
color="text.primary" color="text.primary"
borderRadius="20px" borderRadius="20px"
border="none" border="none"
placeholder="To Gilligan's island!" placeholder="Free my mind..."
_placeholder={{ _placeholder={{
color: 'gray.400', color: 'gray.400',
textWrap: 'nowrap', textWrap: 'nowrap',

View File

@@ -9,7 +9,7 @@ export function formatConversationMarkdown(messages: Instance<typeof IMessage>[]
if (message.role === 'user') { if (message.role === 'user') {
return `**You**: ${message.content}`; return `**You**: ${message.content}`;
} else if (message.role === 'assistant') { } else if (message.role === 'assistant') {
return `**yachtpit-ai**: ${message.content}`; return `**open-gsio**: ${message.content}`;
} }
return ''; return '';
}) })

View File

@@ -51,7 +51,7 @@ const MessageBubble = observer(({ msg, scrollRef }) => {
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
const isUser = msg.role === 'user'; const isUser = msg.role === 'user';
const senderName = isUser ? 'You' : 'yachtpit-ai'; const senderName = isUser ? 'You' : 'open-gsio';
const isLoading = !msg.content || !(msg.content.trim().length > 0); const isLoading = !msg.content || !(msg.content.trim().length > 0);
const messageRef = useRef(); const messageRef = useRef();

View File

@@ -104,7 +104,7 @@ describe('MessageBubble', () => {
it('should render assistant message correctly', () => { it('should render assistant message correctly', () => {
render(<MessageBubble msg={mockAssistantMessage} scrollRef={mockScrollRef} />); render(<MessageBubble msg={mockAssistantMessage} scrollRef={mockScrollRef} />);
expect(screen.getByText('yachtpit-ai')).toBeInTheDocument(); expect(screen.getByText('open-gsio')).toBeInTheDocument();
expect(screen.getByTestId('message-content')).toHaveTextContent('Assistant response'); expect(screen.getByTestId('message-content')).toHaveTextContent('Assistant response');
}); });

View File

@@ -0,0 +1,7 @@
import React, { useEffect, useState } from 'react';
function InstallButton() {
return <button onClick={handleInstall}>Install App</button>;
}
export default InstallButton;

View File

@@ -0,0 +1,61 @@
import { IconButton } from '@chakra-ui/react';
import { HardDriveDownload } from 'lucide-react';
import React, { useEffect, useState } from 'react';
import { toolbarButtonZIndex } from '../toolbar/Toolbar.tsx';
function InstallButton() {
const [deferredPrompt, setDeferredPrompt] = useState(null);
const [isInstalled, setIsInstalled] = useState(false);
useEffect(() => {
const handleBeforeInstallPrompt = e => {
// Prevent the default prompt
e.preventDefault();
setDeferredPrompt(e);
};
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
return () => {
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
};
}, []);
const handleInstall = () => {
if (deferredPrompt) {
deferredPrompt.prompt();
deferredPrompt.userChoice.then(choiceResult => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the installation prompt');
} else {
console.log('User dismissed the installation prompt');
}
});
setDeferredPrompt(null);
}
};
return (
<IconButton
aria-label="Install App"
title="Install App"
icon={<HardDriveDownload />}
size="md"
bg="transparent"
stroke="text.accent"
color="text.accent"
onClick={handleInstall}
_hover={{
bg: 'transparent',
svg: {
stroke: 'accent.secondary',
transition: 'stroke 0.3s ease-in-out',
},
}}
zIndex={toolbarButtonZIndex}
/>
);
}
export default InstallButton;

View File

@@ -1,58 +0,0 @@
import { Box, useBreakpointValue } from '@chakra-ui/react';
import React, { memo, useEffect, useMemo } from 'react';
export interface BevySceneProps {
speed?: number;
intensity?: number; // 0-1 when visible
glow?: boolean;
visible?: boolean; // NEW — defaults to true
}
const BevySceneInner: React.FC<BevySceneProps> = ({
speed = 1,
intensity = 1,
glow = false,
visible,
}) => {
const maxWidth = useBreakpointValue({ base: 640, md: 720 }, { ssr: true });
/* initialise once */
useEffect(() => {
let dispose: (() => void) | void;
(async () => {
const { default: init } = await import(/* webpackIgnore: true */ '/public/yachtpit.js');
dispose = await init(); // zero-arg, uses #yachtpit-canvas
})();
return () => {
if (typeof dispose === 'function') dispose();
};
}, []);
/* memoised styles */
const wrapperStyles = useMemo(
() => ({
position: 'absolute' as const,
inset: 0,
zIndex: 1,
maxWidth: maxWidth,
opacity: visible ? Math.min(Math.max(intensity, 0), 1) : 0,
filter: glow ? 'blur(1px)' : 'none',
transition: `opacity ${speed}s ease-in-out`,
display: visible ? 'block' : 'none', // optional: reclaim hit-testing entirely
}),
[visible, intensity, glow, speed],
);
return (
<Box as="div" sx={wrapperStyles}>
<canvas
id="yachtpit-canvas"
width={useBreakpointValue({ base: 640, md: 1280 }, { ssr: true })}
height={useBreakpointValue({ base: 360, md: 720 }, { ssr: true })}
aria-hidden
/>
</Box>
);
};
export const BevyScene = memo(BevySceneInner);

View File

@@ -3,14 +3,11 @@ import React, { useEffect, useState } from 'react';
import { useComponent } from '../contexts/ComponentContext.tsx'; import { useComponent } from '../contexts/ComponentContext.tsx';
import { BevyScene } from './BevyScene.tsx'; // import { BevyScene } from './BevyScene.tsx';
import Tweakbox from './Tweakbox.tsx'; import Tweakbox from './Tweakbox.tsx';
export const LandingComponent: React.FC = () => { export const LandingComponent: React.FC = () => {
const [speed, setSpeed] = useState(0.2);
const [intensity, setIntensity] = useState(0.99); const [intensity, setIntensity] = useState(0.99);
const [glow, setGlow] = useState(false);
const [bevyScene, setBevyScene] = useState(true);
const [mapActive, setMapActive] = useState(true); const [mapActive, setMapActive] = useState(true);
const [aiActive, setAiActive] = useState(false); const [aiActive, setAiActive] = useState(false);
@@ -27,22 +24,8 @@ export const LandingComponent: React.FC = () => {
}, []); }, []);
return ( return (
<Box <Box as="section" bg="background.primary" overflow="hidden">
as="section" <Box position="fixed" right={0} maxWidth="300px" minWidth="200px" zIndex={1000}>
bg="background.primary"
w="100%"
h="100vh"
overflow="hidden"
position="relative"
>
<Box
position="fixed"
bottom="100x"
right="12px"
maxWidth="300px"
minWidth="200px"
zIndex={1000}
>
<Tweakbox <Tweakbox
sliders={{ sliders={{
intensity: { intensity: {
@@ -56,13 +39,6 @@ export const LandingComponent: React.FC = () => {
}, },
}} }}
switches={{ switches={{
bevyScene: {
value: bevyScene,
onChange(enabled) {
setBevyScene(enabled);
},
label: 'Instruments',
},
GpsMap: { GpsMap: {
value: mapActive, value: mapActive,
onChange(enabled) { onChange(enabled) {
@@ -74,7 +50,7 @@ export const LandingComponent: React.FC = () => {
} }
setMapActive(enabled); setMapActive(enabled);
}, },
label: 'Map', label: 'GPS',
}, },
AI: { AI: {
value: aiActive, value: aiActive,
@@ -92,7 +68,7 @@ export const LandingComponent: React.FC = () => {
}} }}
/> />
</Box> </Box>
<BevyScene speed={speed} intensity={intensity} glow={glow} visible={bevyScene} /> {/*<BevyScene speed={speed} intensity={intensity} glow={glow} visible={bevyScene} />*/}
</Box> </Box>
); );
}; };

View File

@@ -37,7 +37,7 @@ const key =
function Map(props: { visible: boolean }) { function Map(props: { visible: boolean }) {
return ( return (
/* Full-screen wrapper — fills the viewport and becomes the positioning context */ /* Full-screen wrapper — fills the viewport and becomes the positioning context */
<Box w="100%" h="100vh" position="relative" overflow="hidden"> <Box position={'absolute'} top={0} w="100vw" h={'100vh'} overflow="hidden">
{/* Button bar — absolutely positioned inside the wrapper */} {/* Button bar — absolutely positioned inside the wrapper */}
<MapNext mapboxPublicKey={atob(key)} /> <MapNext mapboxPublicKey={atob(key)} />

View File

@@ -1,4 +1,4 @@
import { Box, Button, HStack, Input } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import Map, { import Map, {
FullscreenControl, FullscreenControl,
@@ -118,11 +118,10 @@ Type '{ city: string; population: string; image: string; state: string; latitude
right: 0, right: 0,
}} }}
> >
<GeolocateControl position="top-left" /> <GeolocateControl position="top-left" style={{ marginTop: '6rem' }} />
<FullscreenControl position="top-left" /> <FullscreenControl position="top-left" />
<NavigationControl position="top-left" /> <NavigationControl position="top-left" />
<ScaleControl position="top-left" /> <ScaleControl position="top-left" />
{pins} {pins}
{popupInfo && ( {popupInfo && (

View File

@@ -2,7 +2,7 @@ import { Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import BuiltWithButton from '../BuiltWithButton'; import BuiltWithButton from '../BuiltWithButton';
import InstallButton from '../InstallButton.tsx'; import InstallButton from '../install/InstallButton.tsx';
import GithubButton from './GithubButton'; import GithubButton from './GithubButton';
import SupportThisSiteButton from './SupportThisSiteButton'; import SupportThisSiteButton from './SupportThisSiteButton';

View File

@@ -6,7 +6,7 @@ import { useIsMobile } from '../components/contexts/MobileContext';
function Content({ children }) { function Content({ children }) {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
return ( return (
<Flex flexDirection="column" w="100%" h="100vh" p={!isMobile ? 4 : 1}> <Flex flexDirection="column" w="100%" h="100vh">
{children} {children}
</Flex> </Flex>
); );

View File

@@ -1,4 +1,6 @@
// runs before anything else // runs before anything else
import { registerSW } from 'virtual:pwa-register';
import UserOptionsStore from '../stores/UserOptionsStore'; import UserOptionsStore from '../stores/UserOptionsStore';
UserOptionsStore.initialize(); UserOptionsStore.initialize();
@@ -6,7 +8,11 @@ UserOptionsStore.initialize();
try { try {
const isLocal = window.location.hostname.includes('localhost'); const isLocal = window.location.hostname.includes('localhost');
if (!isLocal) { if (!isLocal) {
navigator.serviceWorker.register('/service-worker.js'); if ('serviceWorker' in navigator) {
// && !/localhost/.test(window.location)) {
registerSW();
}
// navigator.serviceWorker.register('/service-worker.js');
} else { } else {
(async () => { (async () => {
await navigator.serviceWorker.getRegistrations().then(registrations => { await navigator.serviceWorker.getRegistrations().then(registrations => {

View File

@@ -1,4 +1,4 @@
import { Box, Grid, GridItem, Stack } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import Chat from '../../components/chat/Chat.tsx'; import Chat from '../../components/chat/Chat.tsx';
@@ -22,16 +22,15 @@ export default function IndexPage() {
const component = useComponent(); const component = useComponent();
return ( return (
<Grid templateColumns="repeat(2, 1fr)" height="100%" width="100%" gap={0}> <Box height="100%" width="100%">
<GridItem>
<LandingComponent /> <LandingComponent />
</GridItem>
<GridItem p={2}>
<Box <Box
display={component.enabledComponent === 'ai' ? undefined : 'none'} display={component.enabledComponent === 'ai' ? undefined : 'none'}
width="100%" width="100%"
height="100%" height="100%"
overflowY="scroll" overflowY="scroll"
padding={'unset'}
> >
<Chat /> <Chat />
</Box> </Box>
@@ -39,10 +38,10 @@ export default function IndexPage() {
display={component.enabledComponent === 'gpsmap' ? undefined : 'none'} display={component.enabledComponent === 'gpsmap' ? undefined : 'none'}
width="100%" width="100%"
height="100%" height="100%"
padding={'unset'}
> >
<ReactMap visible={component.enabledComponent === 'gpsmap'} /> <ReactMap visible={component.enabledComponent === 'gpsmap'} />
</Box> </Box>
</GridItem> </Box>
</Grid>
); );
} }

View File

@@ -1,5 +1,5 @@
export default { export default {
'/': { sidebarLabel: 'Home', heroLabel: 'gsio' }, '/': { sidebarLabel: 'Home', heroLabel: 'o-gsio' },
'/connect': { sidebarLabel: 'Connect', heroLabel: 'connect' }, '/connect': { sidebarLabel: 'Connect', heroLabel: 'connect' },
'/privacy-policy': { '/privacy-policy': {
sidebarLabel: '', sidebarLabel: '',

View File

@@ -1,5 +1,5 @@
export const welcome_home_text = ` export const welcome_home_text = `
# yachtpit-ai # open-gsio
--- ---
<br/> <br/>

View File

@@ -22,10 +22,6 @@ const prebuildPlugin = () => ({
console.log('Generated robots.txt -> public/robots.txt'); console.log('Generated robots.txt -> public/robots.txt');
child_process.execSync('bun run generate:fonts'); child_process.execSync('bun run generate:fonts');
console.log('Copied fonts -> public/static/fonts'); console.log('Copied fonts -> public/static/fonts');
child_process.execSync('bun run generate:bevy:bundle', {
stdio: 'inherit',
});
console.log('Bundled bevy app -> public/yachtpit.html');
} }
}, },
}); });

View File

@@ -161,19 +161,29 @@ const ChatService = types
const openai = new OpenAI({ apiKey: provider.key, baseURL: provider.endpoint }); const openai = new OpenAI({ apiKey: provider.key, baseURL: provider.endpoint });
// 2a. List models const basicFilters = (model: any) => {
return (
!model.id.includes('whisper') &&
!model.id.includes('flux') &&
!model.id.includes('ocr') &&
!model.id.includes('tts') &&
!model.id.includes('guard')
);
}; // 2a. List models
try { try {
const listResp: any = yield openai.models.list(); // < async const listResp: any = yield openai.models.list(); // < async
const models = 'data' in listResp ? listResp.data : listResp; const models = 'data' in listResp ? listResp.data : listResp;
providerModels.set( providerModels.set(
provider.name, provider.name,
models.filter( models.filter((mdl: any) => {
(mdl: any) => if ('supports_chat' in mdl && mdl.supports_chat) {
!mdl.id.includes('whisper') && return basicFilters(mdl);
!mdl.id.includes('tts') && } else if ('supports_chat' in mdl && !mdl.supports_chat) {
!mdl.id.includes('guard'), return false;
), }
return basicFilters(mdl);
}),
); );
// 2b. Retrieve metadata // 2b. Retrieve metadata
@@ -318,7 +328,8 @@ const ChatService = types
); );
} }
if (message.includes('404')) { if (message.includes('404')) {
throw new ClientError(`Something went wrong, try again.`, 413, {}); console.log(message);
throw new ClientError(`Something went wrong, try again.`, 404, {});
} }
throw error; throw error;
} }