mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-25 22:19:30 +01:00
Rewrites tgui kitchensink into something usable (#94400)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 0,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "tgui-workspace",
|
||||
@@ -49,6 +50,7 @@
|
||||
"marked-smartypants": "^1.1.9",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-json-tree": "^0.20.0",
|
||||
"tgui-core": "^5.3.1",
|
||||
"tgui-dev-server": "workspace:*",
|
||||
},
|
||||
@@ -221,6 +223,8 @@
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/lodash": ["@types/lodash@4.17.21", "", {}, "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ=="],
|
||||
|
||||
"@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="],
|
||||
|
||||
"@types/node": ["@types/node@24.0.12", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-LtOrbvDf5ndC9Xi+4QZjVL0woFymF/xSTKZKPgrrl7H7XoeDvnD+E2IclKVDyaK9UM756W/3BXqSU+JEHopA9g=="],
|
||||
@@ -353,10 +357,14 @@
|
||||
|
||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||
|
||||
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
|
||||
|
||||
"colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
|
||||
|
||||
"colorjs.io": ["colorjs.io@0.5.2", "", {}, "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw=="],
|
||||
@@ -605,6 +613,8 @@
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@2.2.0", "", {}, "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA=="],
|
||||
|
||||
"is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="],
|
||||
|
||||
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
|
||||
|
||||
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
|
||||
@@ -655,6 +665,8 @@
|
||||
|
||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
|
||||
"lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
|
||||
"lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="],
|
||||
@@ -821,8 +833,12 @@
|
||||
|
||||
"react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
|
||||
|
||||
"react-base16-styling": ["react-base16-styling@0.10.0", "", { "dependencies": { "@types/lodash": "^4.17.0", "color": "^4.2.3", "csstype": "^3.1.3", "lodash-es": "^4.17.21" } }, "sha512-H1k2eFB6M45OaiRru3PBXkuCcn2qNmx+gzLb4a9IPMR7tMH8oBRXU5jGbPDYG1Hz+82d88ED0vjR8BmqU3pQdg=="],
|
||||
|
||||
"react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
|
||||
|
||||
"react-json-tree": ["react-json-tree@0.20.0", "", { "dependencies": { "@types/lodash": "^4.17.15", "react-base16-styling": "^0.10.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-h+f9fUNAxzBx1rbrgUF7+zSWKGHDtt2VPYLErIuB0JyKGnWgFMM21ksqQyb3EXwXNnoMW2rdE5kuAaubgGOx2Q=="],
|
||||
|
||||
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
"readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||
@@ -919,6 +935,8 @@
|
||||
|
||||
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="],
|
||||
|
||||
"sirv": ["sirv@2.0.4", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ=="],
|
||||
|
||||
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
|
||||
|
||||
+13
-13
@@ -1,18 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "tgui-workspace",
|
||||
"version": "6.0.0",
|
||||
"packageManager": "bun@1.2.16",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"tgui:analyze": "rspack --analyze",
|
||||
"tgui:build": "rspack build",
|
||||
"tgui:dev": "bun --smol packages/tgui-dev-server/index.ts",
|
||||
"tgui:test": "bun test",
|
||||
"tgui:tsc": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@happy-dom/global-registrator": "^17.4.7",
|
||||
"@rspack/cli": "^1.4.5",
|
||||
@@ -26,5 +14,17 @@
|
||||
"sass-embedded": "^1.89.2",
|
||||
"sass-loader": "^16.0.5",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"packageManager": "bun@1.2.16",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"tgui:analyze": "rspack --analyze",
|
||||
"tgui:build": "rspack build",
|
||||
"tgui:dev": "bun --smol packages/tgui-dev-server/index.ts",
|
||||
"tgui:test": "bun test",
|
||||
"tgui:tsc": "tsc"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* TGUI Base16 Theme
|
||||
* Based on TGUI color scheme defined in css variables
|
||||
* @see https://github.com/chriskempson/base16
|
||||
* @see https://github.com/tgstation/tgui-core/blob/main/styles/vars-colors.scss
|
||||
*/
|
||||
export const tgui16 = {
|
||||
scheme: 'ntos16',
|
||||
author: 'Aleksej Komarov et al., (https://github.com/tgstation)',
|
||||
// Backgrounds / UI surfaces
|
||||
base00: '#1A1A1A',
|
||||
base01: '#313234',
|
||||
base02: '#3a3a36',
|
||||
base03: '#6b6b5b',
|
||||
|
||||
// Foreground / text
|
||||
base04: '#a59f85',
|
||||
base05: '#f8f8f2', // default text (near white)
|
||||
base06: '#f5f4f1',
|
||||
base07: '#f9f8f5',
|
||||
|
||||
// Accent colors
|
||||
base08: '#f92672',
|
||||
base09: '#fd971f',
|
||||
base0A: '#f4bf75',
|
||||
base0B: '#5BA626', // --color-good
|
||||
base0C: '#a1efe4',
|
||||
base0D: '#47739E', // blue primary
|
||||
base0E: '#ae81ff',
|
||||
base0F: '#cc6633',
|
||||
} as const;
|
||||
@@ -4,62 +4,107 @@
|
||||
* @license MIT
|
||||
*/
|
||||
import { useState } from 'react';
|
||||
import { Section, Stack, Tabs } from 'tgui-core/components';
|
||||
|
||||
import { JSONTree } from 'react-json-tree';
|
||||
import { Divider, NoticeBox, Section, Stack, Tabs } from 'tgui-core/components';
|
||||
import { useBackend } from '../backend';
|
||||
import { tgui16 } from '../constants/theme';
|
||||
import { Pane, Window } from '../layouts';
|
||||
|
||||
const r = import.meta.webpackContext('../', {
|
||||
recursive: false,
|
||||
include: /\.stories\.tsx$/,
|
||||
});
|
||||
type Props = {
|
||||
panel?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {{
|
||||
* meta: {
|
||||
* title: string,
|
||||
* render: () => any,
|
||||
* },
|
||||
* }[]}
|
||||
*/
|
||||
function getStories() {
|
||||
return r.keys().map((path) => r(path));
|
||||
enum Tab {
|
||||
Config = 'config',
|
||||
Data = 'data',
|
||||
Shared = 'shared',
|
||||
Chunks = 'outgoingPayloadQueues',
|
||||
Components = 'components',
|
||||
}
|
||||
|
||||
export function KitchenSink(props) {
|
||||
const tabs = [
|
||||
{ name: 'Config', value: Tab.Config },
|
||||
{ name: 'Data', value: Tab.Data },
|
||||
{ name: 'Shared', value: Tab.Shared },
|
||||
{ name: 'Chunks', value: Tab.Chunks },
|
||||
] as const;
|
||||
|
||||
export function KitchenSink(props: Props) {
|
||||
const { panel } = props;
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
|
||||
const stories = getStories();
|
||||
if (stories.length === 0) {
|
||||
return <div>Loading stories...</div>;
|
||||
}
|
||||
const [activeTab, setActiveTab] = useState(Tab.Config);
|
||||
|
||||
const story = stories[pageIndex];
|
||||
const Layout = panel ? Pane : Window;
|
||||
|
||||
return (
|
||||
<Layout title="Kitchen Sink" width={600} height={500}>
|
||||
<Layout.Content>
|
||||
<Stack fill>
|
||||
<Stack.Item>
|
||||
<Section fill fitted>
|
||||
<Tabs vertical>
|
||||
{stories.map((story, i) => (
|
||||
<Tabs.Tab
|
||||
key={i}
|
||||
color="transparent"
|
||||
selected={i === pageIndex}
|
||||
onClick={() => setPageIndex(i)}
|
||||
>
|
||||
{story.meta.title}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Section>
|
||||
<Stack.Item grow>
|
||||
<Tabs vertical>
|
||||
{tabs.map((tab) => (
|
||||
<Tabs.Tab
|
||||
key={tab.name}
|
||||
className="candystripe"
|
||||
selected={activeTab === tab.value}
|
||||
onClick={() => setActiveTab(tab.value)}
|
||||
>
|
||||
{tab.name}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
<Divider />
|
||||
<Tabs.Tab
|
||||
selected={activeTab === Tab.Components}
|
||||
onClick={() => setActiveTab(Tab.Components)}
|
||||
>
|
||||
Components
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow={4}>
|
||||
{activeTab === Tab.Components ? (
|
||||
<ComponentsPage />
|
||||
) : (
|
||||
<TreePage tab={activeTab} />
|
||||
)}
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>{story.meta.render()}</Stack.Item>
|
||||
</Stack>
|
||||
</Layout.Content>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
function ComponentsPage() {
|
||||
return (
|
||||
<Section fill>
|
||||
<NoticeBox info>All component stories have been moved.</NoticeBox>
|
||||
View them here{' '}
|
||||
<a href="https://tgstation.github.io/tgui-core">
|
||||
https://tgstation.github.io/tgui-core
|
||||
</a>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
type TreeProps = {
|
||||
tab: Tab;
|
||||
};
|
||||
|
||||
function TreePage(props: TreeProps) {
|
||||
const { tab } = props;
|
||||
|
||||
const backend = useBackend();
|
||||
const inView = backend[tab];
|
||||
|
||||
return (
|
||||
<Section
|
||||
fill
|
||||
scrollable
|
||||
title={`${backend.config.interface.name ?? 'TGUI'} data`}
|
||||
>
|
||||
<div style={{ border: 'thin solid var(--color-base)' }}>
|
||||
<JSONTree data={inView} theme={tgui16} />
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"marked-smartypants": "^1.1.9",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-json-tree": "^0.20.0",
|
||||
"tgui-core": "^5.3.1",
|
||||
"tgui-dev-server": "workspace:*"
|
||||
},
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2021 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Button, ByondUi, Section, TextArea } from 'tgui-core/components';
|
||||
|
||||
import { logger } from '../logging';
|
||||
|
||||
export const meta = {
|
||||
title: 'ByondUi',
|
||||
render: () => <Story />,
|
||||
};
|
||||
|
||||
function Story() {
|
||||
const [code, setCode] = useState(
|
||||
`Byond.winset('${Byond.windowId}', {\n 'is-visible': true,\n})`,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Section title="Button">
|
||||
<ByondUi
|
||||
params={{
|
||||
type: 'button',
|
||||
text: 'Button',
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
<Section
|
||||
title="Make BYOND calls"
|
||||
buttons={
|
||||
<Button
|
||||
icon="chevron-right"
|
||||
onClick={() =>
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const result = new Function(`return (${code})`)();
|
||||
if (result?.then) {
|
||||
logger.log('Promise');
|
||||
result.then(logger.log);
|
||||
} else {
|
||||
logger.log(result);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.log(err);
|
||||
}
|
||||
})
|
||||
}
|
||||
>
|
||||
Evaluate
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<TextArea fluid height="10em" onChange={setCode}>
|
||||
{code}
|
||||
</TextArea>
|
||||
</Section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2021 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { storage } from 'common/storage';
|
||||
import { Button, LabeledList, NoticeBox, Section } from 'tgui-core/components';
|
||||
|
||||
export const meta = {
|
||||
title: 'Storage',
|
||||
render: () => <Story />,
|
||||
};
|
||||
|
||||
const Story = (props) => {
|
||||
if (!window.localStorage) {
|
||||
return <NoticeBox>Local storage is not available.</NoticeBox>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Section
|
||||
title="Local Storage"
|
||||
buttons={
|
||||
<Button
|
||||
icon="recycle"
|
||||
onClick={() => {
|
||||
localStorage.clear();
|
||||
storage.clear();
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Keys in use">
|
||||
{localStorage.length}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2021 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Input, LabeledList, Section } from 'tgui-core/components';
|
||||
|
||||
import { useLocalState } from '../backend';
|
||||
|
||||
export const meta = {
|
||||
title: 'Themes',
|
||||
render: () => <Story />,
|
||||
};
|
||||
|
||||
function Story() {
|
||||
const [theme, setTheme] = useLocalState('kitchenSinkTheme', '');
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Use theme">
|
||||
<Input placeholder="theme_name" value={theme} onChange={setTheme} />
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2021 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Box } from 'tgui-core/components';
|
||||
|
||||
export function BoxWithSampleText(props) {
|
||||
return (
|
||||
<Box {...props}>
|
||||
<Box italic>Jackdaws love my big sphinx of quartz.</Box>
|
||||
<Box mt={1} bold>
|
||||
The wide electrification of the southern provinces will give a powerful
|
||||
impetus to the growth of agriculture.
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user