{projectState.projects.value
- .filter((project) => project.name !== 'default-project' && project.repositoryPath)
+ .filter((project) => project.name !== 'etherealengine/default-project' && project.repositoryPath)
.map((project) => (
{
const userAvatar = useFind(userAvatarPath, {
diff --git a/packages/common/src/regex/index.ts b/packages/common/src/regex/index.ts
index 2b89670699..2b7b6d835a 100644
--- a/packages/common/src/regex/index.ts
+++ b/packages/common/src/regex/index.ts
@@ -45,12 +45,13 @@ export const CSS_URL_REGEX = /(@import\s+["']([^"']+)["']|url\((?!['"]?(?:data):
export const ABSOLUTE_URL_PROTOCOL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/
/**
- * Captures project name and asset path from a URL.
+ * Captures org name, project name and asset path from a URL.
* For eg: `/path/to/projects/project123/assets/images/logo.png` will capture following groups
- * - `project123` => Group 1
- * - `assets/images/logo.png` => Group 2
+ * - `@org123` => Group 1
+ * - `project123` => Group 2
+ * - `assets/images/logo.png` => Group 3
*/
-export const STATIC_ASSET_REGEX = /^(?:.*\/(?:projects|static-resources)\/([^\/]*)\/((?:assets\/|).*)$)/
+export const STATIC_ASSET_REGEX = /^(?:.*\/(?:projects|static-resources)\/([^\/]*)\/([^\/]*)\/((?:assets\/|).*)$)/
// =====================================================================
// ========================= ID Regex Patterns =========================
@@ -111,22 +112,22 @@ export const INSTALLATION_SIGNED_REGEX = /https:\/\/oauth2:[\w\d\s\-_]+@github.c
/**
* This regex is used to match specific file paths or directory structures that start with `projects/`, followed by one or more valid characters (letters, digits, hyphens, underscores, or forward slashes), and end with `/assets/`
*/
-export const ASSETS_REGEX = /projects\/[a-zA-Z0-9-_\/]+\/assets\//
+export const ASSETS_REGEX = /projects\/+[a-zA-Z0-9-_@]+\/[a-zA-Z0-9-_]+\/assets\//
/**
* This regex matches strings that start with `projects/`, followed by one or more characters that can be letters, digits, hyphens, underscores, or forward slashes.
*/
-export const PROJECT_REGEX = /projects\/[a-zA-Z0-9-_\/]+/
+export const PROJECT_REGEX = /projects\/+[a-zA-Z0-9-_@]+\/[a-zA-Z0-9-_]/
/**
* This regex matches strings that start with `projects/`, followed by one or more characters that can be letters, digits, hyphens, underscores, or forward slashes, and then `/public/`.
*/
-export const PROJECT_PUBLIC_REGEX = /projects\/[a-zA-Z0-9-_\/]+\/public\//
+export const PROJECT_PUBLIC_REGEX = /projects\/+[a-zA-Z0-9-_@]+\/[a-zA-Z0-9-_]+\/public\//
/**
* This regex matches strings that start with `projects/`, followed by one or more characters that can be letters, digits, hyphens, underscores, or forward slashes, and then `/thumbnails/`.
*/
-export const PROJECT_THUMBNAIL_REGEX = /projects\/[a-zA-Z0-9-_\/]+\/thumbnails\//
+export const PROJECT_THUMBNAIL_REGEX = /projects\/+[a-zA-Z0-9-_@]+\/[a-zA-Z0-9-_]+\/thumbnails\//
export const VALID_PROJECT_NAME = /^(?!\s)[\w\-\s]+$/
diff --git a/packages/common/tests/regex.test.ts b/packages/common/tests/regex.test.ts
index 6f3c7b4a4b..e2cc0c8617 100644
--- a/packages/common/tests/regex.test.ts
+++ b/packages/common/tests/regex.test.ts
@@ -252,31 +252,36 @@ describe('regex.test', () => {
it('should match static asset URLs', () => {
const positiveCases = [
{
- url: 'https://example.com/projects/default-project/assets/images/logo.png',
+ url: 'https://example.com/projects/etherealengine/default-project/assets/images/logo.png',
+ orgName: 'etherealengine',
projectName: 'default-project',
assetPath: 'assets/images/logo.png'
},
{
- url: 'https://example.com/static-resources/default-project/assets/images/logo.png',
+ url: 'https://example.com/static-resources/etherealengine/default-project/assets/images/logo.png',
+ orgName: 'etherealengine',
projectName: 'default-project',
assetPath: 'assets/images/logo.png'
},
{
- url: 'https://example.com/projects/default-project/assets/animations/emotes.glb',
+ url: 'https://example.com/projects/etherealengine/default-project/assets/animations/emotes.glb',
+ orgName: 'etherealengine',
projectName: 'default-project',
assetPath: 'assets/animations/emotes.glb'
},
{
- url: 'https://example.com/projects/default-project/assets/animations/locomotion.glb',
+ url: 'https://example.com/projects/etherealengine/default-project/assets/animations/locomotion.glb',
+ orgName: 'etherealengine',
projectName: 'default-project',
assetPath: 'assets/animations/locomotion.glb'
}
]
- positiveCases.forEach(({ url, projectName, assetPath }) => {
+ positiveCases.forEach(({ url, orgName, projectName, assetPath }) => {
const match = STATIC_ASSET_REGEX.exec(url)
assert.ok(match, `Expected '${url}' to match STATIC_ASSET_REGEX`)
- assert.equal(match?.[1], projectName, `Expected project name '${projectName}' in '${url}'. Found ${match?.[1]}`)
- assert.equal(match?.[2], assetPath, `Expected asset path '${assetPath}' in '${url}'. Found ${match?.[2]}`)
+ assert.equal(match?.[1], orgName, `Expected org name name '${orgName}' in '${url}'. Found ${match?.[1]}`)
+ assert.equal(match?.[2], projectName, `Expected project name '${projectName}' in '${url}'. Found ${match?.[2]}`)
+ assert.equal(match?.[3], assetPath, `Expected asset path '${assetPath}' in '${url}'. Found ${match?.[3]}`)
})
})
@@ -284,7 +289,7 @@ describe('regex.test', () => {
const negativeCases = [
'https://example.com/static-resources/',
'https://example.com/project/subdir/assets',
- 'https://example.com/default-project/assets/animations/emotes.glb'
+ 'https://example.com/etherealengine/default-project/assets/animations/emotes.glb'
]
negativeCases.forEach((url) => {
assert.doesNotMatch(url, STATIC_ASSET_REGEX, `Expected '${url}' to not match STATIC_ASSET_REGEX`)
@@ -581,9 +586,9 @@ describe('regex.test', () => {
describe('ASSETS_REGEX', () => {
it('should match assets URLs', () => {
const positiveCases = [
- 'https://example.com/projects/default-project/assets/images/logo.png',
- 'https://example.com/projects/default-project/assets/animations/emotes.glb',
- 'https://example.com/projects/default-project/assets/animations/locomotion.glb'
+ 'https://example.com/projects/etherealengine/default-project/assets/images/logo.png',
+ 'https://example.com/projects/etherealengine/default-project/assets/animations/emotes.glb',
+ 'https://example.com/projects/etherealengine/default-project/assets/animations/locomotion.glb'
]
positiveCases.forEach((url) => {
assert.match(url, ASSETS_REGEX, `Expected '${url}' to match ASSETS_REGEX`)
@@ -592,9 +597,9 @@ describe('regex.test', () => {
it('should not match non-assets URLs', () => {
const negativeCases = [
- 'https://example.com/projects/default-project/scene.json',
- 'https://example.com/projects/default-project/assets',
- 'https://example.com/default-project/assets/animations/emotes.glb'
+ 'https://example.com/projects/etherealengine/default-project/scene.json',
+ 'https://example.com/projects/etherealengine/default-project/assets',
+ 'https://example.com/etherealengine/default-project/assets/animations/emotes.glb'
]
negativeCases.forEach((url) => {
assert.doesNotMatch(url, ASSETS_REGEX, `Expected '${url}' to not match ASSETS_REGEX`)
@@ -605,9 +610,9 @@ describe('regex.test', () => {
describe('PROJECT_REGEX', () => {
it('should match valid project paths', () => {
const positiveCases = [
- 'projects/project123',
- 'projects/project-name',
- 'projects/project_name',
+ 'projects/etherealengine/project123',
+ 'projects/etherealengine/project-name',
+ 'projects/etherealengine/project_name',
'projects/project/123',
'projects/project/abc_def'
]
@@ -630,9 +635,9 @@ describe('regex.test', () => {
describe('PROJECT_PUBLIC_REGEX', () => {
it('should match valid project paths', () => {
const positiveCases = [
- 'projects/project123/public/',
- 'projects/project-name/public/',
- 'projects/project_name/public/',
+ 'projects/etherealengine/project123/public/',
+ 'projects/etherealengine/project-name/public/',
+ 'projects/etherealengine/project_name/public/',
'projects/project/123/public/',
'projects/project/abc_def/public/'
]
@@ -643,10 +648,10 @@ describe('regex.test', () => {
it('should not match invalid project paths', () => {
const negativeCases = [
- 'projects/project123/public', // (missing trailing slash)
- 'projects/project-name/private/', // (incorrect folder private instead of public)
- 'projects/project$name/public/', // (contains invalid character $)
- 'projects/project-@name/public/', // (contains invalid character @)
+ 'projects/etherealengine/project123/public', // (missing trailing slash)
+ 'projects/etherealengine/project-name/private/', // (incorrect folder private instead of public)
+ 'projects/etherealengine/project$name/public/', // (contains invalid character $)
+ 'projects/etherealengine/project-@name/public/', // (contains invalid character @)
'projects/' // (missing project name and /public/)
]
negativeCases.forEach((value) => {
@@ -658,9 +663,9 @@ describe('regex.test', () => {
describe('PROJECT_THUMBNAIL_REGEX', () => {
it('should match valid project thumbnail paths', () => {
const positiveCases = [
- 'projects/project123/thumbnails/',
- 'projects/project-name/thumbnails/',
- 'projects/project_name/thumbnails/',
+ 'projects/etherealengine/project123/thumbnails/',
+ 'projects/etherealengine/project-name/thumbnails/',
+ 'projects/etherealengine/project_name/thumbnails/',
'projects/project/123/thumbnails/',
'projects/project/abc_def/thumbnails/'
]
@@ -671,10 +676,10 @@ describe('regex.test', () => {
it('should not match invalid project thumbnail paths', () => {
const negativeCases = [
- 'projects/project123/thumbnails', // (missing trailing slash)
- 'projects/project-name/private/', // (incorrect folder private instead of public)
- 'projects/project$name/thumbnails/', // (contains invalid character $)
- 'projects/project-@name/thumbnails/', // (contains invalid character @)
+ 'projects/etherealengine/project123/thumbnails', // (missing trailing slash)
+ 'projects/etherealengine/project-name/private/', // (incorrect folder private instead of public)
+ 'projects/etherealengine/project$name/thumbnails/', // (contains invalid character $)
+ 'projects/etherealengine/project-@name/thumbnails/', // (contains invalid character @)
'projects/' // (missing project name and /thumbnail/)
]
negativeCases.forEach((value) => {
diff --git a/packages/editor/src/components/prefabs/PrefabEditors.tsx b/packages/editor/src/components/prefabs/PrefabEditors.tsx
index 3d56b4ef8d..21c319662c 100644
--- a/packages/editor/src/components/prefabs/PrefabEditors.tsx
+++ b/packages/editor/src/components/prefabs/PrefabEditors.tsx
@@ -41,106 +41,106 @@ export const PrefabShelfState = defineState({
[
{
name: '3D Model',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/3d-model.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/3d-model.prefab.gltf`,
category: 'Geo',
detail: 'Blank 3D model ready for your own assets'
},
{
name: 'Primitive Geometry',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/geo.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/geo.prefab.gltf`,
category: 'Geo'
},
{
name: 'Ground Plane',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/ground-plane.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/ground-plane.prefab.gltf`,
category: 'Geo'
},
{
name: 'Point Light',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/point-light.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/point-light.prefab.gltf`,
category: 'Lighting'
},
{
name: 'Spot Light',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/spot-light.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/spot-light.prefab.gltf`,
category: 'Lighting'
},
{
name: 'Directional Light',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/directional-light.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/directional-light.prefab.gltf`,
category: 'Lighting'
},
{
name: 'Ambient Light',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/ambient-light.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/ambient-light.prefab.gltf`,
category: 'Lighting'
},
{
name: 'Hemisphere Light',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/hemisphere-light.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/hemisphere-light.prefab.gltf`,
category: 'Lighting'
},
{
name: 'Box Collider',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/box-collider.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/box-collider.prefab.gltf`,
category: 'Collider',
detail: 'Simple box collider'
},
{
name: 'Sphere Collider',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/sphere-collider.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/sphere-collider.prefab.gltf`,
category: 'Collider',
detail: 'Simple sphere collider'
},
{
name: 'Cylinder Collider',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/cylinder-collider.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/cylinder-collider.prefab.gltf`,
category: 'Collider',
detail: 'Simple cylinder collider'
},
{
name: 'Text',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/text.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/text.prefab.gltf`,
category: 'Text'
},
{
name: 'Title',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/title.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/title.prefab.gltf`,
category: 'Text'
},
{
name: 'Body',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/body.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/body.prefab.gltf`,
category: 'Text'
},
{
name: 'Image',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/image.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/image.prefab.gltf`,
category: 'Image'
},
{
name: 'Video',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/video.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/video.prefab.gltf`,
category: 'Video'
},
{
name: 'Skybox',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/skybox.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/skybox.prefab.gltf`,
category: 'Lookdev'
},
{
name: 'Postprocessing',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/postprocessing.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/postprocessing.prefab.gltf`,
category: 'Lookdev'
},
{
name: 'Fog',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/fog.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/fog.prefab.gltf`,
category: 'Lookdev'
},
{
name: 'Camera',
- url: `${config.client.fileServer}/projects/default-project/assets/prefabs/camera.prefab.gltf`,
+ url: `${config.client.fileServer}/projects/etherealengine/default-project/assets/prefabs/camera.prefab.gltf`,
category: 'Camera'
}
] as PrefabShelfItem[],
diff --git a/packages/editor/src/components/projects/CreateProjectDialog.tsx b/packages/editor/src/components/projects/CreateProjectDialog.tsx
index d42b148dec..302a4d6027 100644
--- a/packages/editor/src/components/projects/CreateProjectDialog.tsx
+++ b/packages/editor/src/components/projects/CreateProjectDialog.tsx
@@ -32,8 +32,6 @@ import { useTranslation } from 'react-i18next'
import ProjectFields from '@etherealengine/client-core/src/admin/common/Project/ProjectFields'
import { ProjectUpdateState } from '@etherealengine/client-core/src/admin/services/ProjectUpdateService'
-import { DefaultUpdateSchedule } from '@etherealengine/common/src/interfaces/ProjectPackageJsonType'
-import { ProjectType } from '@etherealengine/common/src/schemas/projects/project.schema'
import { getMutableState, useHookstate } from '@etherealengine/hyperflux'
import { Button } from '../inputs/Button'
@@ -41,7 +39,7 @@ import styles from './styles.module.scss'
interface Props {
open: boolean
- onSuccess: (name: string, repositoryPath?: string) => Promise
+ onSuccess: (orgname: string, reponame: string, repositoryPath?: string) => Promise
onClose: () => void
}
@@ -50,28 +48,19 @@ export const CreateProjectDialog = ({ open, onSuccess, onClose }: Props): any =>
const [processing, setProcessing] = useState(false)
const [error, setError] = useState('')
- const [projectName, setProjectName] = useState('')
-
- const project = {
- id: '',
- name: 'tempProject',
- thumbnail: '',
- repositoryPath: '',
- needsRebuild: false,
- updateType: 'none' as ProjectType['updateType'],
- updateSchedule: DefaultUpdateSchedule,
- commitSHA: '',
- commitDate: new Date()
- }
+ const [orgName, setOrgName] = useState('')
+ const [repoName, setRepoName] = useState('')
+
+ const name = `${orgName}/${repoName}`
- const projectUpdateStatus = useHookstate(getMutableState(ProjectUpdateState)[project.name]).value
+ const projectUpdateStatus = useHookstate(getMutableState(ProjectUpdateState)[name]).value
const handleCreateProject = async () => {
- if (!projectName) return
+ if (!repoName || !orgName) return
setProcessing(true)
try {
- await onSuccess(projectName, projectUpdateStatus?.destinationURL)
+ await onSuccess(orgName, repoName, projectUpdateStatus?.destinationURL)
closeDialog()
} catch (err) {
setError(err.message)
@@ -87,7 +76,7 @@ export const CreateProjectDialog = ({ open, onSuccess, onClose }: Props): any =>
}
const closeDialog = () => {
- setProjectName('')
+ setRepoName('')
onClose()
}
@@ -109,6 +98,23 @@ export const CreateProjectDialog = ({ open, onSuccess, onClose }: Props): any =>
) : (
+ setOrgName(e.target.value.toLowerCase())}
+ onKeyDown={handleSubmitOnEnter}
+ />
+
input: styles.input
}
}}
- value={projectName}
- onChange={(e) => setProjectName(e.target.value.toLowerCase())}
+ value={repoName}
+ onChange={(e) => setRepoName(e.target.value.toLowerCase())}
onKeyDown={handleSubmitOnEnter}
/>
{error && error.length > 0 && {error}
}
@@ -130,7 +136,7 @@ export const CreateProjectDialog = ({ open, onSuccess, onClose }: Props): any =>
diff --git a/packages/editor/src/components/projects/ProjectsPage.tsx b/packages/editor/src/components/projects/ProjectsPage.tsx
index aa37488b6e..9318401f56 100644
--- a/packages/editor/src/components/projects/ProjectsPage.tsx
+++ b/packages/editor/src/components/projects/ProjectsPage.tsx
@@ -266,8 +266,8 @@ const ProjectsPage = ({ studioPath }: { studioPath: string }) => {
}
}
- const onCreateProject = async (name: string, repositoryPath?: string) => {
- projectCreateQuery({ name, repositoryPath }, { query: { action: 'studio' } })
+ const onCreateProject = async (orgname: string, projectName: string, repositoryPath?: string) => {
+ projectCreateQuery({ name: `${orgname}/${projectName}`, repositoryPath }, { query: { action: 'studio' } })
}
const onCreatePermission = async (userInviteCode: InviteCode, projectId: string) => {
@@ -367,7 +367,7 @@ const ProjectsPage = ({ studioPath }: { studioPath: string }) => {
@@ -534,7 +534,7 @@ const ProjectsPage = ({ studioPath }: { studioPath: string }) => {
) : null} */}
- {activeProjectValue?.name !== 'default-project' && (
+ {activeProjectValue?.name !== 'etherealengine/default-project' && (