Initial commit
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
npm-debug.log*
|
||||||
80
README.md
Normal file
80
README.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Unreal Outliner Control
|
||||||
|
|
||||||
|
A Vite + React + TypeScript web app that mirrors the Unreal Editor Outliner for the currently open world.
|
||||||
|
|
||||||
|
The app prefers Unreal Remote Control's WebSocket server, then falls back to HTTP when the socket is unavailable.
|
||||||
|
|
||||||
|
By default it connects to:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ws://127.0.0.1:30020
|
||||||
|
```
|
||||||
|
|
||||||
|
When WebSocket is available, the app sends Unreal's Remote Control `http` WebSocket message type and tunnels the same HTTP route:
|
||||||
|
|
||||||
|
```http
|
||||||
|
PUT /remote/object/call
|
||||||
|
```
|
||||||
|
|
||||||
|
The HTTP fallback request goes through Vite's dev proxy and targets `http://127.0.0.1:30010/remote/object/call` by default. Both transports call:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"objectPath": "/Script/UnrealEd.Default__EditorActorSubsystem",
|
||||||
|
"functionName": "GetAllLevelActors",
|
||||||
|
"parameters": {},
|
||||||
|
"generateTransaction": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Selecting an actor also loads a Details pane using:
|
||||||
|
|
||||||
|
```http
|
||||||
|
PUT /remote/object/describe
|
||||||
|
PUT /remote/object/property
|
||||||
|
```
|
||||||
|
|
||||||
|
The property request uses `READ_ACCESS` and omits `propertyName`, which asks Unreal for all readable properties exposed on that UObject.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
Start Unreal Editor, enable the Remote Control API, and make sure it is listening on `127.0.0.1:30010`.
|
||||||
|
|
||||||
|
Then run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://127.0.0.1:5173/
|
||||||
|
```
|
||||||
|
|
||||||
|
If your Unreal Remote Control HTTP server is on another URL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
set UNREAL_REMOTE_URL=http://127.0.0.1:30010
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
If your Unreal Remote Control WebSocket server is on another URL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
set VITE_UNREAL_WS_URL=ws://127.0.0.1:30020
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Useful UI Libraries
|
||||||
|
|
||||||
|
For an Unreal-like outliner, a custom tree/table gives the closest visual match because Unreal's Slate UI is not available as a web component.
|
||||||
|
|
||||||
|
Good libraries if the app grows:
|
||||||
|
|
||||||
|
- `react-arborist` for a performant tree with drag/drop and renaming.
|
||||||
|
- `@tanstack/react-table` plus `@tanstack/react-virtual` for a large outliner with resizable columns and virtualization.
|
||||||
|
- `ag-grid-community` if you want a full data-grid with tree data, sorting, filtering, and column tooling.
|
||||||
|
|
||||||
|
This first version keeps the UI custom so it can look closer to the Unreal Outliner screenshot.
|
||||||
12
index.html
Normal file
12
index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Unreal Outliner Control</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1869
package-lock.json
generated
Normal file
1869
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
package.json
Normal file
23
package.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "unreal-outliner-control",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --host 127.0.0.1 --port 5173",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview --host 127.0.0.1 --port 4173"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lucide-react": "^0.544.0",
|
||||||
|
"react": "^19.1.1",
|
||||||
|
"react-dom": "^19.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-react": "^5.0.4",
|
||||||
|
"@types/react": "^19.2.14",
|
||||||
|
"@types/react-dom": "^19.2.3",
|
||||||
|
"typescript": "^5.9.2",
|
||||||
|
"vite": "^7.1.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
public/favicon.svg
Normal file
4
public/favicon.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<rect width="32" height="32" rx="4" fill="#171717"/>
|
||||||
|
<path d="M8 7h16v4H8zM8 14h11v4H8zM8 21h16v4H8z" fill="#d7d7d7"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 191 B |
1135
src/main.tsx
Normal file
1135
src/main.tsx
Normal file
File diff suppressed because it is too large
Load Diff
521
src/styles.css
Normal file
521
src/styles.css
Normal file
@@ -0,0 +1,521 @@
|
|||||||
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
font-family: "Segoe UI", "Inter", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
|
background: #101010;
|
||||||
|
color: #c7c7c7;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
min-width: 320px;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #101010;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell {
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 10px;
|
||||||
|
background: #101010;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
width: min(1220px, 100%);
|
||||||
|
min-height: calc(100vh - 20px);
|
||||||
|
height: calc(100vh - 20px);
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 48px 56px 36px 1fr 50px;
|
||||||
|
border: 1px solid #0a0a0a;
|
||||||
|
background: #171717;
|
||||||
|
box-shadow: inset 0 0 0 1px #232323;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
height: 48px;
|
||||||
|
background: #1f1f1f;
|
||||||
|
border-bottom: 1px solid #0c0c0c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
min-width: 240px;
|
||||||
|
padding: 0 14px;
|
||||||
|
color: #bdbdbd;
|
||||||
|
font-size: 20px;
|
||||||
|
border-right: 1px solid #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.active {
|
||||||
|
color: #f0f0f0;
|
||||||
|
background: #242424;
|
||||||
|
box-shadow: inset 0 3px 0 #2d6eaf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab svg:last-child {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 36px 30px minmax(160px, 1fr) 36px 36px 36px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 7px 12px;
|
||||||
|
background: #262626;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: inline-grid;
|
||||||
|
place-items: center;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: #cfcfcf;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button:hover:not(:disabled) {
|
||||||
|
background: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button:disabled {
|
||||||
|
color: #777;
|
||||||
|
cursor: wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
height: 36px;
|
||||||
|
min-width: 0;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 30px 1fr 24px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 8px;
|
||||||
|
color: #cfcfcf;
|
||||||
|
background: #0d0d0d;
|
||||||
|
border: 1px solid #343434;
|
||||||
|
border-radius: 18px;
|
||||||
|
box-shadow: inset 0 0 0 1px #050505;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search::after {
|
||||||
|
content: "";
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-right: 3px solid #cfcfcf;
|
||||||
|
border-bottom: 3px solid #cfcfcf;
|
||||||
|
transform: rotate(45deg) translateY(-3px);
|
||||||
|
justify-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search input {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
color: #dadada;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search input::placeholder {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.columns {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 38px minmax(260px, 1.3fr) minmax(140px, 0.55fr) minmax(130px, 0.52fr) minmax(150px, 0.62fr);
|
||||||
|
align-items: center;
|
||||||
|
background: #303030;
|
||||||
|
color: #c7c7c7;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.columns span {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 8px;
|
||||||
|
border-right: 2px solid #1b1b1b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.columns .visibility {
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: minmax(180px, 1fr) minmax(250px, 0.9fr);
|
||||||
|
background: #171717;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rows {
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
background: repeating-linear-gradient(#181818 0 30px, #141414 30px 60px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.outliner-row {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 760px;
|
||||||
|
height: 30px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(298px, 1.3fr) minmax(140px, 0.55fr) minmax(130px, 0.52fr) minmax(150px, 0.62fr);
|
||||||
|
align-items: center;
|
||||||
|
color: #bebebe;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-align: left;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outliner-row.selected {
|
||||||
|
background: #2b4056;
|
||||||
|
color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outliner-row:hover:not(.selected) {
|
||||||
|
background: rgba(92, 105, 116, 0.24);
|
||||||
|
color: #d9d9d9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 20px;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding-left: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indent {
|
||||||
|
width: calc(var(--depth) * 18px);
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expander {
|
||||||
|
width: 18px;
|
||||||
|
height: 22px;
|
||||||
|
display: inline-grid;
|
||||||
|
place-items: center;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
color: #adadad;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-cell,
|
||||||
|
.level-cell,
|
||||||
|
.id-cell {
|
||||||
|
color: #8f8f8f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.folder-icon {
|
||||||
|
color: #d0a64f;
|
||||||
|
fill: #d0a64f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.world-icon,
|
||||||
|
.actor-icon {
|
||||||
|
color: #c2c2c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 0 20px;
|
||||||
|
color: #cfcfcf;
|
||||||
|
background: #303030;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
min-height: 220px;
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
|
justify-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 24px;
|
||||||
|
color: #bdbdbd;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty strong {
|
||||||
|
color: #f0f0f0;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-panel {
|
||||||
|
min-height: 0;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 38px 58px 44px 1fr;
|
||||||
|
border-top: 2px solid #0d0d0d;
|
||||||
|
background: #202020;
|
||||||
|
color: #cfcfcf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-tabs {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
background: #151515;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-tab {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 9px;
|
||||||
|
min-width: 210px;
|
||||||
|
padding: 0 12px;
|
||||||
|
color: #c7c7c7;
|
||||||
|
font-size: 20px;
|
||||||
|
background: #232323;
|
||||||
|
border-right: 1px solid #101010;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-tab.muted {
|
||||||
|
color: #a8a8a8;
|
||||||
|
background: #181818;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-tab.active {
|
||||||
|
color: #dedede;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-tab svg:last-child {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-object {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 28px 1fr;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 9px 14px;
|
||||||
|
border-bottom: 1px solid #171717;
|
||||||
|
background: #242424;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-object strong,
|
||||||
|
.details-object span {
|
||||||
|
display: block;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-object strong {
|
||||||
|
color: #f1f1f1;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-object span {
|
||||||
|
margin-top: 2px;
|
||||||
|
color: #a8a8a8;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-search-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(140px, 1fr) 34px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-bottom: 1px solid #141414;
|
||||||
|
background: #262626;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-search {
|
||||||
|
height: 31px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 26px 1fr;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 8px;
|
||||||
|
color: #cfcfcf;
|
||||||
|
background: #0d0d0d;
|
||||||
|
border: 1px solid #343434;
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-search input {
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
color: #d8d8d8;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-tool {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #c7c7c7;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-tool:hover {
|
||||||
|
background: #363636;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-categories {
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
background: #1f1f1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-category h3 {
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 10px;
|
||||||
|
color: #d0d0d0;
|
||||||
|
background: #303030;
|
||||||
|
border-top: 1px solid #393939;
|
||||||
|
border-bottom: 1px solid #171717;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row {
|
||||||
|
min-height: 40px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(180px, 0.45fr) minmax(220px, 0.55fr);
|
||||||
|
border-bottom: 1px solid #171717;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-name,
|
||||||
|
.detail-value {
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 0 14px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-name {
|
||||||
|
color: #c8c8c8;
|
||||||
|
border-right: 1px solid #151515;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-value {
|
||||||
|
color: #e0e0e0;
|
||||||
|
background: #242424;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-empty {
|
||||||
|
min-height: 130px;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
color: #a8a8a8;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spin {
|
||||||
|
animation: spin 0.85s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.shell {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
min-height: 100vh;
|
||||||
|
height: 100vh;
|
||||||
|
border: 0;
|
||||||
|
grid-template-rows: 44px 52px 34px 1fr 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
grid-template-rows: minmax(170px, 1fr) minmax(260px, 0.95fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
min-width: auto;
|
||||||
|
flex: 1;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab svg:last-child,
|
||||||
|
.tab:not(.active) span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
grid-template-columns: 32px 28px minmax(120px, 1fr) 32px 32px 32px;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.columns,
|
||||||
|
.outliner-row {
|
||||||
|
min-width: 720px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-tab {
|
||||||
|
min-width: 160px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row {
|
||||||
|
grid-template-columns: minmax(130px, 0.42fr) minmax(170px, 0.58fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
21
tsconfig.json
Normal file
21
tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||||
|
"allowJs": false,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": []
|
||||||
|
}
|
||||||
16
vite.config.ts
Normal file
16
vite.config.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
|
||||||
|
const unrealRemoteUrl = process.env.UNREAL_REMOTE_URL ?? "http://127.0.0.1:30010";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
"/remote": {
|
||||||
|
target: unrealRemoteUrl,
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user