Merge branch 'main' into achalmers/kw-fn-sketches

This commit is contained in:
Adam Chalmers
2025-02-03 11:01:55 -06:00
28 changed files with 194 additions and 97 deletions

View File

@ -2,8 +2,8 @@ NODE_ENV=development
DEV=true
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
BASE_URL=https://api.dev.zoo.dev
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
VITE_KC_SITE_APP_URL=https://app.dev.zoo.dev
VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=5000
# ONLY add your token in .env.development.local if you want to skip auth, otherwise this token takes precedence!

View File

@ -1,5 +1,8 @@
NODE_ENV=production
DEV=false
VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
VITE_KC_API_BASE_URL=https://api.zoo.dev
VITE_KC_SITE_BASE_URL=https://zoo.dev
VITE_KC_SITE_APP_URL=https://app.zoo.dev
VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=15000

View File

@ -134,8 +134,6 @@ jobs:
max_attempts: 3
command: yarn install
- run: yarn tronb:vite
- name: Prepare certificate and variables (Windows only)
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
run: |
@ -165,8 +163,8 @@ jobs:
- name: Build the app (debug)
if: ${{ env.IS_RELEASE == 'false' && env.IS_NIGHTLY == 'false' }}
# electron-builder doesn't have a concept of release vs debug,
# this is just not doing any codesign or release yml generation
run: yarn electron-builder --config
# this is just not doing any codesign or release yml generation, and points to dev infra
run: yarn tronb:package:dev
- name: Build the app (release)
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
@ -185,7 +183,7 @@ jobs:
with:
timeout_minutes: 10
max_attempts: 3
command: yarn electron-builder --config --publish always
command: yarn tronb:package:prod
- name: List artifacts in out/
run: ls -R out
@ -246,7 +244,7 @@ jobs:
with:
timeout_minutes: 10
max_attempts: 3
command: yarn electron-builder --config --publish always
command: yarn tronb:package:prod
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
@ -390,19 +388,19 @@ jobs:
- name: Authenticate to Google Cloud
if: ${{ env.IS_NIGHTLY == 'true' }}
uses: 'google-github-actions/auth@v2.1.7'
uses: 'google-github-actions/auth@v2.1.8'
with:
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
- name: Set up Google Cloud SDK
if: ${{ env.IS_NIGHTLY == 'true' }}
uses: google-github-actions/setup-gcloud@v2.1.2
uses: google-github-actions/setup-gcloud@v2.1.4
with:
project_id: ${{ env.GOOGLE_CLOUD_PROJECT_ID }}
- name: Upload nightly files to public bucket
if: ${{ env.IS_NIGHTLY == 'true' }}
uses: google-github-actions/upload-cloud-storage@v2.2.1
uses: google-github-actions/upload-cloud-storage@v2.2.2
with:
path: out
glob: '*'

View File

@ -123,9 +123,9 @@ jobs:
if: steps.download-wasm.outcome == 'failure'
shell: bash
run: yarn build:wasm
- name: build electron
- name: build web
shell: bash
run: yarn tron:package
run: yarn tronb:vite:dev
# - name: Run ubuntu/chrome snapshots
# if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
# shell: bash

View File

@ -108,17 +108,17 @@ jobs:
run: yarn files:set-notes
- name: Authenticate to Google Cloud
uses: 'google-github-actions/auth@v2.1.7'
uses: 'google-github-actions/auth@v2.1.8'
with:
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
- name: Set up Google Cloud SDK
uses: google-github-actions/setup-gcloud@v2.1.2
uses: google-github-actions/setup-gcloud@v2.1.4
with:
project_id: ${{ env.GOOGLE_CLOUD_PROJECT_ID }}
- name: Upload release files to public bucket
uses: google-github-actions/upload-cloud-storage@v2.2.1
uses: google-github-actions/upload-cloud-storage@v2.2.2
with:
path: out
glob: '*'

View File

@ -101,7 +101,7 @@ This will start the application and hot-reload on changes.
Devtools can be opened with the usual Cmd-Opt-I (Mac) or Ctrl-Shift-I (Linux and Windows).
To build, run `yarn tron:package`.
To build with electron-builder, run `yarn tronb:package:dev` (or `yarn tronb:package:prod` to point to the .env.production variables)
## Checking out commits / Bisecting

View File

@ -75,3 +75,6 @@ publish:
channel: latest
releaseInfo:
releaseNotesFile: release-notes.md
protocols:
- name: Zoo Studio
schemes: ['zoo-studio']

View File

@ -9,23 +9,8 @@ const rootDir = process.cwd()
const config: ForgeConfig = {
packagerConfig: {
asar: true,
osxSign: (process.env.BUILD_RELEASE === 'true' && {}) || undefined,
osxNotarize:
(process.env.BUILD_RELEASE === 'true' && {
appleId: process.env.APPLE_ID || '',
appleIdPassword: process.env.APPLE_PASSWORD || '',
teamId: process.env.APPLE_TEAM_ID || '',
}) ||
undefined,
executableName: 'zoo-modeling-app',
icon: path.resolve(rootDir, 'assets', 'icon'),
protocols: [
{
name: 'Zoo Studio',
schemes: ['zoo-studio'],
},
],
extendInfo: 'Info.plist', // Information for file associations.
},
rebuildConfig: {},
makers: [],

1
interface.d.ts vendored
View File

@ -65,6 +65,7 @@ export interface IElectronAPI {
VITE_KC_API_WS_MODELING_URL: string
VITE_KC_API_BASE_URL: string
VITE_KC_SITE_BASE_URL: string
VITE_KC_SITE_APP_URL: string
VITE_KC_SKIP_AUTH: string
VITE_KC_CONNECTION_TIMEOUT_MS: string
VITE_KC_DEV_TOKEN: string

View File

@ -103,11 +103,11 @@
"make:dev": "make dev",
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
"tron:start": "electron-forge start",
"tron:package": "electron-forge package",
"chrome:test": "PLATFORM=web NODE_ENV=development yarn playwright test --config=playwright.config.ts --project='Google Chrome' --grep-invert='@snapshot'",
"tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
"tronb:vite": "vite build -c vite.main.config.ts && vite build -c vite.preload.config.ts && vite build -c vite.renderer.config.ts",
"tronb:package": "electron-builder --config electron-builder.yml",
"tronb:vite:dev": "vite build -c vite.main.config.ts -m development && vite build -c vite.preload.config.ts -m development && vite build -c vite.renderer.config.ts -m development",
"tronb:vite:prod": "vite build -c vite.main.config.ts && vite build -c vite.preload.config.ts && vite build -c vite.renderer.config.ts",
"tronb:package:dev": "yarn tronb:vite:dev && electron-builder --config electron-builder.yml",
"tronb:package:prod": "yarn tronb:vite:prod && electron-builder --config electron-builder.yml --publish always",
"test-setup": "yarn install && yarn build:wasm",
"test": "vitest --mode development",
"test:unit": "vitest run --mode development --exclude **/kclSamples.test.ts",
@ -116,10 +116,10 @@
"test:playwright:electron:windows": "playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\" --quiet",
"test:playwright:electron:macos": "playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot' --quiet",
"test:playwright:electron:ubuntu": "playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot' --quiet",
"test:playwright:electron:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
"test:playwright:electron:windows:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\"",
"test:playwright:electron:macos:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot'",
"test:playwright:electron:ubuntu:local": "yarn tron:package && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot'",
"test:playwright:electron:local": "yarn tronb:package:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot'",
"test:playwright:electron:windows:local": "yarn tronb:package:dev && set NODE_ENV='development' && playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\"",
"test:playwright:electron:macos:local": "yarn tronb:package:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot'",
"test:playwright:electron:ubuntu:local": "yarn tronb:package:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot'",
"test:unit:local": "yarn simpleserver:bg && yarn test:unit; kill-port 3000",
"test:unit:kcl-samples:local": "yarn simpleserver:bg && yarn test:unit:kcl-samples; kill-port 3000"
},

View File

@ -29,6 +29,7 @@ import * as TWEEN from '@tweenjs/tween.js'
import { isQuaternionVertical } from './helpers'
import { reportRejection } from 'lib/trap'
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models'
const ORTHOGRAPHIC_CAMERA_SIZE = 20
const FRAMES_TO_ANIMATE_IN = 30
@ -406,7 +407,7 @@ export class CameraControls {
.sub(this.mouseDownPosition)
this.mouseDownPosition.copy(this.mouseNewPosition)
const interaction = this.getInteractionType(event)
let interaction = this.getInteractionType(event)
if (interaction === 'none') return
// If there's a valid interaction and the mouse is moving,
@ -753,8 +754,6 @@ export class CameraControls {
didChange = true
}
this.safeLookAtTarget(this.camera.up)
// Update the camera's matrices
this.camera.updateMatrixWorld()
if (didChange || forceUpdate) {
@ -1189,14 +1188,24 @@ export class CameraControls {
this.deferReactUpdate(this.reactCameraProperties)
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
}
getInteractionType = (event: MouseEvent) =>
_getInteractionType(
getInteractionType = (
event: MouseEvent
): CameraDragInteractionType_type | 'none' => {
const initialInteractionType = _getInteractionType(
this.interactionGuards,
event,
this.enablePan,
this.enableRotate,
this.enableZoom
)
if (
initialInteractionType === 'rotate' &&
this.engineCommandManager.settings.cameraOrbit === 'trackball'
) {
return 'rotatetrackball'
}
return initialInteractionType
}
}
// Pure function helpers

View File

@ -119,6 +119,7 @@ export const ModelingMachineProvider = ({
cameraProjection,
highlightEdges,
showScaleGrid,
cameraOrbit,
},
},
},
@ -1154,6 +1155,7 @@ export const ModelingMachineProvider = ({
enableSSAO: enableSSAO.current,
showScaleGrid: showScaleGrid.current,
cameraProjection: cameraProjection.current,
cameraOrbit: cameraOrbit.current,
},
token
)
@ -1183,6 +1185,13 @@ export const ModelingMachineProvider = ({
editorManager.selectionRanges = modelingState.context.selectionRanges
}, [modelingState.context.selectionRanges])
// When changing camera modes reset the camera to the default orientation to correct
// the up vector otherwise the conconical orientation for the camera modes will be
// wrong
useEffect(() => {
sceneInfra.camControls.resetCameraPosition().catch(reportRejection)
}, [cameraOrbit.current])
useEffect(() => {
const onConnectionStateChanged = ({ detail }: CustomEvent) => {
// If we are in sketch mode we need to exit it.

View File

@ -33,7 +33,7 @@ export const OpenInDesktopAppHandler = (props: React.PropsWithChildren) => {
function onOpenInDesktopApp() {
const newSearchParams = new URLSearchParams(globalThis.location.search)
newSearchParams.delete(ASK_TO_OPEN_QUERY_PARAM)
const newURL = `${ZOO_STUDIO_PROTOCOL}${globalThis.location.pathname.replace(
const newURL = `${ZOO_STUDIO_PROTOCOL}://${globalThis.location.pathname.replace(
'/',
''
)}${searchParams.size > 0 ? `?${newSearchParams.toString()}` : ''}`

View File

@ -19,7 +19,7 @@ import { commandBarActor } from 'machines/commandBarMachine'
import { useSelector } from '@xstate/react'
import { copyFileShareLink } from 'lib/links'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { DEV } from 'env'
import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings'
import { useToken } from 'machines/appMachine'
const ProjectSidebarMenu = ({
@ -112,6 +112,7 @@ function ProjectMenuPopover({
const { onProjectClose } = useLspContext()
const exportCommandInfo = { name: 'Export', groupId: 'modeling' }
const makeCommandInfo = { name: 'Make', groupId: 'modeling' }
const shareCommandInfo = { name: 'share-file-link', groupId: 'code' }
const findCommand = (obj: { name: string; groupId: string }) =>
Boolean(
commands.find((c) => c.name === obj.name && c.groupId === obj.groupId)
@ -193,7 +194,7 @@ function ProjectMenuPopover({
id: 'share-link',
Element: 'button',
children: 'Share link to file',
disabled: !DEV,
disabled: IS_NIGHTLY_OR_DEBUG || !findCommand(shareCommandInfo),
onClick: async () => {
await copyFileShareLink({
token: token ?? '',

View File

@ -10,6 +10,7 @@ export const VITE_KC_API_WS_MODELING_URL = env.VITE_KC_API_WS_MODELING_URL as
| undefined
export const VITE_KC_API_BASE_URL = env.VITE_KC_API_BASE_URL as string
export const VITE_KC_SITE_BASE_URL = env.VITE_KC_SITE_BASE_URL as string
export const VITE_KC_SITE_APP_URL = env.VITE_KC_SITE_APP_URL as string
export const VITE_KC_SKIP_AUTH = env.VITE_KC_SKIP_AUTH as string | undefined
export const VITE_KC_CONNECTION_TIMEOUT_MS =
env.VITE_KC_CONNECTION_TIMEOUT_MS as string | undefined

View File

@ -16,14 +16,15 @@ export function useSetupEngineManager(
streamRef: React.RefObject<HTMLDivElement>,
modelingSend: ReturnType<typeof useModelingContext>['send'],
modelingContext: ReturnType<typeof useModelingContext>['context'],
settings = {
settings: SettingsViaQueryString = {
pool: null,
theme: Themes.System,
highlightEdges: true,
enableSSAO: true,
showScaleGrid: false,
cameraProjection: 'perspective',
} as SettingsViaQueryString,
cameraOrbit: 'spherical',
},
token?: string
) {
const networkContext = useNetworkContext()

View File

@ -1389,6 +1389,7 @@ export class EngineCommandManager extends EventTarget {
enableSSAO: true,
showScaleGrid: false,
cameraProjection: 'perspective',
cameraOrbit: 'spherical',
}
}
@ -1437,6 +1438,7 @@ export class EngineCommandManager extends EventTarget {
enableSSAO: true,
showScaleGrid: false,
cameraProjection: 'orthographic',
cameraOrbit: 'spherical',
},
// When passed, use a completely separate connecting code path that simply
// opens a websocket and this is a function that is called when connected.

View File

@ -68,8 +68,6 @@ export const KCL_DEFAULT_DEGREE = `360`
/** localStorage key for the playwright test-specific app settings file */
export const TEST_SETTINGS_FILE_KEY = 'playwright-test-settings'
export const DEFAULT_HOST = 'https://api.zoo.dev'
export const PROD_APP_URL = 'https://app.zoo.dev'
export const SETTINGS_FILE_NAME = 'settings.toml'
export const TOKEN_FILE_NAME = 'token.txt'
export const PROJECT_SETTINGS_FILE_NAME = 'project.toml'
@ -145,7 +143,7 @@ export const VIEW_NAMES_SEMANTIC = {
export const SIDEBAR_BUTTON_SUFFIX = '-pane-button'
/** Custom URL protocol our desktop registers */
export const ZOO_STUDIO_PROTOCOL = 'zoo-studio:'
export const ZOO_STUDIO_PROTOCOL = 'zoo-studio'
/**
* A query parameter that triggers a modal

View File

@ -1,11 +1,13 @@
import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning'
import { Command, CommandArgumentOption } from './commandTypes'
import { kclManager } from './singletons'
import { codeManager, kclManager } from './singletons'
import { isDesktop } from './isDesktop'
import { FILE_EXT } from './constants'
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
import { reportRejection } from './trap'
import { IndexLoaderData } from './types'
import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings'
import { copyFileShareLink } from './links'
interface OnSubmitProps {
sampleName: string
@ -132,21 +134,22 @@ export function kclCommands(commandProps: KclCommandConfig): Command[] {
},
},
},
// {
// name: 'share-file-link',
// displayName: 'Share file',
// description: 'Create a link that contains a copy of the current file.',
// groupId: 'code',
// needsReview: false,
// icon: 'link',
// onSubmit: () => {
// copyFileShareLink({
// token: commandProps.authToken,
// code: codeManager.code,
// name: commandProps.projectData.project?.name || '',
// units: commandProps.settings.defaultUnit,
// }).catch(reportRejection)
// },
// },
{
name: 'share-file-link',
displayName: 'Share file',
hide: IS_NIGHTLY_OR_DEBUG ? undefined : 'desktop',
description: 'Create a link that contains a copy of the current file.',
groupId: 'code',
needsReview: false,
icon: 'link',
onSubmit: () => {
copyFileShareLink({
token: commandProps.authToken,
code: codeManager.code,
name: commandProps.projectData.project?.name || '',
units: commandProps.settings.defaultUnit,
}).catch(reportRejection)
},
},
]
}

View File

@ -1,3 +1,4 @@
import { VITE_KC_SITE_APP_URL } from 'env'
import { createCreateFileUrl } from './links'
describe(`link creation tests`, () => {
@ -8,7 +9,7 @@ describe(`link creation tests`, () => {
// Converted with external online tools
const expectedEncodedCode = `ZXh0cnVzaW9uRGlzdGFuY2UgPSAxMg%3D%3D`
const expectedLink = `http://localhost:3000/?create-file=true&name=test&units=mm&code=${expectedEncodedCode}&ask-open-desktop=true`
const expectedLink = `${VITE_KC_SITE_APP_URL}/?create-file=true&name=test&units=mm&code=${expectedEncodedCode}&ask-open-desktop=true`
const result = createCreateFileUrl({ code, name, units })
expect(result.toString()).toBe(expectedLink)

View File

@ -1,11 +1,7 @@
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
import {
ASK_TO_OPEN_QUERY_PARAM,
CREATE_FILE_URL_PARAM,
PROD_APP_URL,
} from './constants'
import { ASK_TO_OPEN_QUERY_PARAM, CREATE_FILE_URL_PARAM } from './constants'
import { stringToBase64 } from './base64'
import { DEV, VITE_KC_API_BASE_URL } from 'env'
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_APP_URL } from 'env'
import toast from 'react-hot-toast'
import { err } from './trap'
export interface FileLinkParams {
@ -51,8 +47,7 @@ export async function copyFileShareLink(
* open the URL in the desktop app.
*/
export function createCreateFileUrl({ code, name, units }: FileLinkParams) {
// Use the dev server if we are in development mode
let origin = DEV ? 'http://localhost:3000' : PROD_APP_URL
let origin = VITE_KC_SITE_APP_URL
const searchParams = new URLSearchParams({
[CREATE_FILE_URL_PARAM]: String(true),
name,

View File

@ -20,6 +20,7 @@ import { toSync } from 'lib/utils'
import { reportRejection } from 'lib/trap'
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
import { OnboardingStatus } from 'wasm-lib/kcl/bindings/OnboardingStatus'
import { CameraOrbitType } from 'wasm-lib/kcl/bindings/CameraOrbitType'
/**
* A setting that can be set at the user or project level
@ -380,6 +381,30 @@ export function createSettings() {
})),
},
}),
/**
* What methodology to use for orbiting the camera
*/
cameraOrbit: new Setting<CameraOrbitType>({
defaultValue: 'spherical',
hideOnLevel: 'project',
description: 'What methodology to use for orbiting the camera',
validate: (v) => ['spherical', 'trackball'].includes(v),
commandConfig: {
inputType: 'options',
defaultValueFromContext: (context) =>
context.modeling.cameraOrbit.current,
options: (cmdContext, settingsContext) =>
(['spherical', 'trackball'] as const).map((v) => ({
name: v.charAt(0).toUpperCase() + v.slice(1),
value: v,
isCurrent:
settingsContext.modeling.cameraOrbit.shouldShowCurrentLabel(
cmdContext.argumentsToSubmit.level as SettingsLevel,
v
),
})),
},
}),
/**
* Whether to highlight edges of 3D objects
*/

View File

@ -4,6 +4,7 @@ import { AtLeast, PathValue, Paths } from 'lib/types'
import { CommandArgumentConfig } from 'lib/commandTypes'
import { Themes } from 'lib/theme'
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
import { CameraOrbitType } from 'wasm-lib/kcl/bindings/CameraOrbitType'
export interface SettingsViaQueryString {
pool: string | null
@ -12,6 +13,7 @@ export interface SettingsViaQueryString {
enableSSAO: boolean
showScaleGrid: boolean
cameraProjection: CameraProjectionType
cameraOrbit: CameraOrbitType
}
export enum UnitSystem {

View File

@ -49,6 +49,7 @@ export function configurationToSettingsPayload(
modeling: {
defaultUnit: configuration?.settings?.modeling?.base_unit,
cameraProjection: configuration?.settings?.modeling?.camera_projection,
cameraOrbit: configuration?.settings?.modeling?.camera_orbit,
mouseControls: mouseControlsToCameraSystem(
configuration?.settings?.modeling?.mouse_controls
),

View File

@ -31,23 +31,27 @@ let mainWindow: BrowserWindow | null = null
// Check the command line arguments for a project path
const args = parseCLIArgs()
// If it's not set, scream.
const NODE_ENV = process.env.NODE_ENV || 'production'
if (!process.env.NODE_ENV)
console.warn(
'*FOX SCREAM* process.env.NODE_ENV is not explicitly set!, defaulting to production'
)
// Default prod values
// @ts-ignore: TS1343
const viteEnv = import.meta.env
const NODE_ENV = process.env.NODE_ENV || viteEnv.MODE
// dotenv override when present
dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
process.env.VITE_KC_API_WS_MODELING_URL ??=
'wss://api.zoo.dev/ws/modeling/commands'
process.env.VITE_KC_API_BASE_URL ??= 'https://api.zoo.dev'
process.env.VITE_KC_SITE_BASE_URL ??= 'https://zoo.dev'
process.env.VITE_KC_SKIP_AUTH ??= 'false'
process.env.VITE_KC_CONNECTION_TIMEOUT_MS ??= '15000'
// default vite values based on mode
process.env.NODE_ENV ??= viteEnv.MODE
process.env.DEV ??= viteEnv.DEV + ''
process.env.BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
process.env.VITE_KC_API_WS_MODELING_URL ??= viteEnv.VITE_KC_API_WS_MODELING_URL
process.env.VITE_KC_API_BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
process.env.VITE_KC_SITE_BASE_URL ??= viteEnv.VITE_KC_SITE_BASE_URL
process.env.VITE_KC_SITE_APP_URL ??= viteEnv.VITE_KC_SITE_APP_URL
process.env.VITE_KC_SKIP_AUTH ??= viteEnv.VITE_KC_SKIP_AUTH
process.env.VITE_KC_CONNECTION_TIMEOUT_MS ??=
viteEnv.VITE_KC_CONNECTION_TIMEOUT_MS
// Likely convenient to keep for debugging
console.log('process.env', process.env)
/// Register our application to handle all "zoo-studio:" protocols.
if (process.defaultApp) {
@ -89,22 +93,43 @@ const createWindow = (pathToOpen?: string, reuse?: boolean): BrowserWindow => {
})
}
// Deep Link: Case of a cold start from Windows or Linux
if (
!pathToOpen &&
process.argv.length > 1 &&
process.argv[1].indexOf(ZOO_STUDIO_PROTOCOL + '://') > -1
) {
pathToOpen = process.argv[1]
console.log('Retrieved deep link from argv', pathToOpen)
}
// Deep Link: Case of a second window opened for macOS
// @ts-ignore
if (!pathToOpen && global['openUrls'] && global['openUrls'][0]) {
// @ts-ignore
pathToOpen = global['openUrls'][0]
console.log('Retrieved deep link from open-url', pathToOpen)
}
const pathIsCustomProtocolLink =
pathToOpen?.startsWith(ZOO_STUDIO_PROTOCOL) ?? false
// and load the index.html of the app.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
const filteredPath = pathToOpen
? decodeURI(pathToOpen.replace(ZOO_STUDIO_PROTOCOL, ''))
? decodeURI(pathToOpen.replace(ZOO_STUDIO_PROTOCOL + '://', ''))
: ''
const fullHashBasedUrl = `${MAIN_WINDOW_VITE_DEV_SERVER_URL}/#/${filteredPath}`
newWindow.loadURL(fullHashBasedUrl).catch(reportRejection)
} else {
if (pathIsCustomProtocolLink && pathToOpen) {
// We're trying to open a custom protocol link
const filteredPath = pathToOpen
? decodeURI(pathToOpen.replace(ZOO_STUDIO_PROTOCOL, ''))
: ''
// TODO: fix the replace %3 thing
const urlNoProtocol = pathToOpen
.replace(ZOO_STUDIO_PROTOCOL + '://', '')
.replaceAll('%3D', '')
.replaceAll('%3', '')
const filteredPath = decodeURI(urlNoProtocol)
const startIndex = path.join(
__dirname,
`../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`
@ -342,7 +367,7 @@ export function getAutoUpdater(): AppUpdater {
app.on('ready', () => {
// Disable auto updater on non-versioned builds
if (packageJSON.version === '0.0.0') {
if (packageJSON.version === '0.0.0' && viteEnv.MODE !== 'production') {
return
}
@ -459,6 +484,14 @@ function parseCLIArgs(): minimist.ParsedArgs {
}
function registerStartupListeners() {
// Linux and Windows from https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Deep Link: second instance for Windows and Linux
const url = commandLine.pop()?.slice(0, -1)
console.log('Retrieved deep link from commandLine', url)
createWindow(url)
})
/**
* macOS: when someone drops a file to the not-yet running VSCode, the open-file event fires even before
* the app-ready event. We listen very early for open-file and remember this upon startup as path to open.
@ -478,7 +511,7 @@ function registerStartupListeners() {
})
/**
* macOS: react to open-url requests.
* macOS: react to open-url requests (including Deep Link on second instances)
*/
const openUrls: string[] = []
// @ts-ignore

View File

@ -185,6 +185,7 @@ contextBridge.exposeInMainWorld('electron', {
'VITE_KC_API_WS_MODELING_URL',
'VITE_KC_API_BASE_URL',
'VITE_KC_SITE_BASE_URL',
'VITE_KC_SITE_APP_URL',
'VITE_KC_SKIP_AUTH',
'VITE_KC_CONNECTION_TIMEOUT_MS',
'VITE_KC_DEV_TOKEN',

View File

@ -259,6 +259,9 @@ pub struct ModelingSettings {
/// The projection mode the camera should use while modeling.
#[serde(default, alias = "cameraProjection", skip_serializing_if = "is_default")]
pub camera_projection: CameraProjectionType,
/// The methodology the camera should use to orbit around the model.
#[serde(default, alias = "cameraOrbit", skip_serializing_if = "is_default")]
pub camera_orbit: CameraOrbitType,
/// The controls for how to navigate the 3D view.
#[serde(default, alias = "mouseControls", skip_serializing_if = "is_default")]
pub mouse_controls: MouseControlType,
@ -415,6 +418,21 @@ pub enum CameraProjectionType {
Orthographic,
}
/// The types of camera orbit methods.
#[derive(Debug, Default, Eq, PartialEq, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, Display, FromStr)]
#[ts(export)]
#[serde(rename_all = "snake_case")]
#[display(style = "snake_case")]
pub enum CameraOrbitType {
/// Orbit using a spherical camera movement.
#[default]
#[display("spherical")]
Spherical,
/// Orbit using a trackball camera movement.
#[display("trackball")]
Trackball,
}
/// Settings that affect the behavior of the KCL text editor.
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq, Eq, Validate)]
#[serde(rename_all = "snake_case")]
@ -543,6 +561,8 @@ mod tests {
use pretty_assertions::assert_eq;
use validator::Validate;
use crate::settings::types::CameraOrbitType;
use super::{
AppColor, AppSettings, AppTheme, AppearanceSettings, CameraProjectionType, CommandBarSettings, Configuration,
ModelingSettings, OnboardingStatus, ProjectSettings, Settings, TextEditorSettings, UnitLength,
@ -594,6 +614,7 @@ textWrapping = true
modeling: ModelingSettings {
base_unit: UnitLength::In,
camera_projection: CameraProjectionType::Orthographic,
camera_orbit: Default::default(),
mouse_controls: Default::default(),
highlight_edges: Default::default(),
show_debug_panel: true,
@ -656,6 +677,7 @@ includeSettings = false
modeling: ModelingSettings {
base_unit: UnitLength::Yd,
camera_projection: Default::default(),
camera_orbit: Default::default(),
mouse_controls: Default::default(),
highlight_edges: Default::default(),
show_debug_panel: true,
@ -723,6 +745,7 @@ defaultProjectName = "projects-$nnn"
modeling: ModelingSettings {
base_unit: UnitLength::Yd,
camera_projection: Default::default(),
camera_orbit: CameraOrbitType::Spherical,
mouse_controls: Default::default(),
highlight_edges: Default::default(),
show_debug_panel: true,
@ -802,6 +825,7 @@ projectDirectory = "/Users/macinatormax/Documents/kittycad-modeling-projects""#;
modeling: ModelingSettings {
base_unit: UnitLength::Mm,
camera_projection: Default::default(),
camera_orbit: Default::default(),
mouse_controls: Default::default(),
highlight_edges: true.into(),
show_debug_panel: false,

View File

@ -129,6 +129,7 @@ includeSettings = false
modeling: ModelingSettings {
base_unit: UnitLength::Yd,
camera_projection: Default::default(),
camera_orbit: Default::default(),
mouse_controls: Default::default(),
highlight_edges: Default::default(),
show_debug_panel: true,