Compare commits

..

49 Commits

Author SHA1 Message Date
Jörg Thalheim
84dab9e329 waypipe: disable gpu for now 2025-07-17 12:12:41 +02:00
hsjobeki
ef02fca062 Merge pull request 'buildClan: Add deprecation warning' (#4384) from Qubasa/clan-core:migrate_away_buildClan into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4384
Reviewed-by: hsjobeki <hsjobeki@gmail.com>
2025-07-17 12:12:41 +02:00
Michael Hoang
5f90d60bd6 Merge pull request 'flake: remove unnecessary follows for data-mesher' (#4383) from push-yzqmtrtrkkzt into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4383
2025-07-17 12:12:41 +02:00
Qubasa
02ed516a15 buildClan: Add deprecation warning 2025-07-17 12:12:41 +02:00
Luis Hebendanz
14a6af1259 Merge pull request 'inventory: Add missing default value for exports.instances and exports.machines' (#4382) from Qubasa/clan-core:fix_inv_missing_default into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4382
2025-07-17 12:12:41 +02:00
Michael Hoang
fd26dcd0d0 flake: remove unnecessary follows for data-mesher 2025-07-17 12:12:41 +02:00
clan-bot
3b316cbf3e Merge pull request 'Update disko' (#4381) from update-disko into main 2025-07-17 12:12:41 +02:00
Qubasa
d9657d8617 inventory: Add missing default value for exports.instances and exports.machines 2025-07-17 12:12:41 +02:00
brianmcgee
74fb95653a Merge pull request 'chore: add a check for background.jpg' (#4380) from chore/stupid-jpg-check into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4380
2025-07-17 12:12:41 +02:00
gitea-actions[bot]
6073ab4a0b Update disko 2025-07-17 12:12:41 +02:00
hsjobeki
9e35d040da Merge pull request 'UI/cubes: extend cubes scene' (#4375) from scene-progress into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4375
2025-07-17 12:12:41 +02:00
Brian McGee
0e2904b34b chore: add a check for background.jpg 2025-07-17 12:12:41 +02:00
brianmcgee
71ba979120 Merge pull request 'feat: onboarding workflow' (#4379) from ui/onboarding-workflow into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4379
2025-07-17 12:12:41 +02:00
Johannes Kirschbauer
fb4b8f2745 ui/cubes: align with design 2025-07-17 12:12:41 +02:00
Brian McGee
0028311805 feat: onboarding workflow 2025-07-17 12:12:41 +02:00
Johannes Kirschbauer
abacd19c12 ui/cubes: init story 2025-07-17 12:12:41 +02:00
Johannes Kirschbauer
9446009738 ui/storybook: add all stories 2025-07-17 12:12:41 +02:00
Johannes Kirschbauer
dc7566951c UI/cubes: group logic to add more meshed 2025-07-17 12:12:41 +02:00
Johannes Kirschbauer
67d2e18fb8 cubes: scene extend 2025-07-17 12:12:41 +02:00
Mic92
350b55ea16 Merge pull request 'Update data-mesher' (#4370) from update-data-mesher into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4370
2025-07-17 12:12:40 +02:00
Mic92
ce50278621 Merge pull request 'clan-cli: Move flash.py to clan_lib/flash' (#4374) from Qubasa/clan-core:move_flash into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4374
2025-07-17 12:12:40 +02:00
gitea-actions[bot]
d9262e47cb Update data-mesher 2025-07-17 12:12:40 +02:00
DavHau
ce411b4784 Merge pull request 'cleanup_install' (#4373) from Qubasa/clan-core:cleanup_install into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4373
2025-07-17 12:12:40 +02:00
Qubasa
55f0da45a8 clan-cli: Move flash.py to clan_lib/flash 2025-07-17 12:12:40 +02:00
brianmcgee
dc7b10efe2 Merge pull request 'feat: ui/toolbar' (#4357) from ui/toolbar into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4357
2025-07-17 12:12:40 +02:00
Qubasa
3687fd48ce clan-cli: Reference HostKeyCheck literal instead of duplicating the list everywhere 2025-07-17 12:12:40 +02:00
hsjobeki
a4d26497f9 Merge pull request 'cli: fix dot files not copied to $out in buildPythonApplication' (#4371) from pkgs-for into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4371
2025-07-17 12:12:40 +02:00
brianmcgee
1c14033d48 Merge pull request 'onboarding workflow' (#4366) from ui/onboarding-workflow into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4366
Reviewed-by: Mic92 <joerg@thalheim.io>
2025-07-17 12:12:40 +02:00
Brian McGee
13ca0f5050 feat(ui): toolbar component 2025-07-17 12:12:40 +02:00
Qubasa
85f219ca1e clan-lib: Remove duplicate fields from installOptions and instead use them from Remote 2025-07-17 12:12:40 +02:00
hsjobeki
503bb62864 Merge pull request 'clanInternals: refactor configsPerSystem, minimize diff' (#4369) from pkgs-for into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4369
2025-07-17 12:12:40 +02:00
Johannes Kirschbauer
c0225ea757 cli: fix dot files not copied $out in buildPythonApplication
File such as .envrc, .gitignore where not copied into the package and thus missing in all templates
2025-07-17 12:12:40 +02:00
Brian McGee
d6b27d8740 wip: onboarding workflow 2025-07-17 12:12:40 +02:00
Qubasa
c09cb834fa clan-lib: Change BuildOn enum to Literal type. Literals can be translated better to typescript 2025-07-17 12:12:40 +02:00
Johannes Kirschbauer
91434044e0 clanInternals: refactor configsPerSystem, minimize diff 2025-07-17 12:12:40 +02:00
Qubasa
56a76242ca clan-cli: Fix incorrect ipv6 check in check_machine_ssh_reachable 2025-07-17 12:12:40 +02:00
Kenji Berthold
7255673440 Merge pull request 'pkgs/cli: Validate clan directory for update-hardware-config' (#4367) from kenji/ke-hardware-update-validation into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4367
2025-07-17 12:12:39 +02:00
Luis Hebendanz
5b651752ba Merge pull request 'pkgs/cli: Fix ssh logging' (#4362) from kenji/ke-ssh-remove-debug into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4362
2025-07-17 12:12:39 +02:00
hsjobeki
b1aa79b33f Merge pull request 'revert bd3861c58056a847556c459ce420968044ce1459' (#4368) from hsjobeki-patch-1 into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4368
2025-07-17 12:12:39 +02:00
a-kenji
26fdfeec00 pkgs/cli: Validate clan directory for update-hardware-config 2025-07-17 12:12:39 +02:00
kenji
a0c3b6d33e Merge pull request 'pkgs/clan(templates): Add shell completions' (#4327) from kenji/ke-disko-shell into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4327
2025-07-17 12:12:39 +02:00
Mic92
8be8af9117 Merge pull request 'gitignore-images' (#4364) from gitignore-images into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4364
2025-07-17 12:12:39 +02:00
hsjobeki
338a6ad340 revert bd3861c580
revert Merge pull request 'Remove clanModules/*' (#4202) from remove-modules into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4202

See: https://git.clan.lol/clan/clan-core/issues/4365

Not all modules are migrated.
If they are not migrated, we need to write migration docs and please display the link to the migration docs
2025-07-17 12:12:39 +02:00
kenji
38fafba6d8 Merge pull request 'pkgs/cli: Add facts deprecation warning to clan facts help output' (#4329) from kenji/ke-facts-cli-warning into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4329
2025-07-17 12:12:39 +02:00
Jörg Thalheim
2dcc4bba15 update-flake-inputs: email/user doesn't need to be configured 2025-07-17 12:12:39 +02:00
clan-bot
b57897c6c9 Merge pull request 'Update sops-nix' (#4361) from update-sops-nix into main 2025-07-17 12:12:39 +02:00
Jörg Thalheim
b640be3dd2 run flake updates every 5 hours 2025-07-17 12:12:39 +02:00
pinpox
7266e4b273 Merge pull request 'Remove clanModules/*' (#4202) from remove-modules into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4202
2025-07-17 12:12:39 +02:00
pinpox
546eeb22d0 Remove clanModules 2025-07-17 12:12:39 +02:00
27 changed files with 1356 additions and 598 deletions

View File

@@ -1,6 +1,14 @@
{% extends "base.html" %} {% block extrahead %}
<style>
.md-main__inner {
max-width: 100% !important;
}
.md-content {
max-width: 100% !important;
}
.md-main__inner {
margin-top: 0 !important;
}
</style>
{% endblock %} {% block site_nav %}{% endblock %} {% block content %} {{
page.content }} {% endblock %}

View File

@@ -2,86 +2,5 @@
template: options.html
---
<script>
const variables = [
'--md-default-bg-color',
'--md-default-fg-color',
'--md-default-fg-color--light',
'--md-default-fg-color--lightest'
];
let colorScheme = 'default';
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'data-md-color-scheme') {
colorScheme = mutation.target.getAttribute('data-md-color-scheme');
console.log('color scheme changed', colorScheme);
}
});
});
observer.observe(document.body, {
attributes: true,
attributeFilter: ['data-md-color-scheme']
});
function syncCSSVariables() {
const iframe = document.getElementById('options-frame');
console.log('syncing css variables', iframe);
const iframeDoc = iframe.contentWindow.document;
const iframeRoot = iframeDoc.documentElement;
const targetElement = document.querySelector(`[data-md-color-scheme="${colorScheme}"]`);
const parentStyles = getComputedStyle(targetElement);
console.log('parent styles', parentStyles);
variables.forEach(varName => {
const value = parentStyles.getPropertyValue(varName);
if (value.trim()) {
console.log('setting', varName, value);
iframeRoot.style.setProperty(varName, value.trim());
}
});
// add our custom styling
addCustomCSS(iframe);
}
function addCustomCSS(iframe) {
const iframeDoc = iframe.contentWindow.document;
const cssLink = iframeDoc.createElement('link');
cssLink.id = "clan-css";
cssLink.rel = "stylesheet";
cssLink.type = "text/css";
cssLink.href = "/static/options.css";
iframeDoc.head.appendChild(cssLink);
}
function onIFrameLoad() {
const iframe = document.getElementById('options-frame');
// initial sync of css variables
syncCSSVariables(iframe);
// listen for theme changes
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
const lightModeQuery = window.matchMedia('(prefers-color-scheme: light)');
darkModeQuery.addEventListener('change', syncCSSVariables);
lightModeQuery.addEventListener('change', syncCSSVariables);
}
</script>
<iframe id="options-frame" src="/options-page/" onload="onIFrameLoad()" height="1000" width="100%"></iframe>
[asciinema-player](static/asciinema-player)
<iframe src="/options-page/" height="1000" width="100%"></iframe>

View File

@@ -20,7 +20,3 @@
.md-nav__item.md-nav__item--section > label > span {
color: var(--md-typeset-a-color);
}
iframe {
border: none;
}

View File

@@ -1,42 +0,0 @@
@font-face {
font-family: "Roboto";
src: url(./Roboto-Regular.ttf) format("truetype");
}
@font-face {
font-family: "Fira Code";
src: url(./FiraCode-VF.ttf) format("truetype");
}
:root {
--f-family: "Roboto";
--f-family-mono: "Fira Code";
--c-page: var(--md-default-bg-color);
--c-card: transparent;
}
header h1 {
color: var(--md-default-fg-color--light);
}
div.card {
border: .05rem solid var(--md-default-fg-color--lightest) !important;
}
form {
label {
gap: 1rem;
input {
background-color: #00000042 !important;
&:hover {
background-color: #ffffff1f !important;
}
}
}
}

6
flake.lock generated
View File

@@ -31,11 +31,11 @@
]
},
"locked": {
"lastModified": 1752541678,
"narHash": "sha256-dyhGzkld6jPqnT/UfGV2oqe7tYn7hppAqFvF3GZTyXY=",
"lastModified": 1752718651,
"narHash": "sha256-PkaR0qmyP9q/MDN3uYa+RLeBA0PjvEQiM0rTDDBXkL8=",
"owner": "nix-community",
"repo": "disko",
"rev": "2bf3421f7fed5c84d9392b62dcb9d76ef09796a7",
"rev": "d5ad4485e6f2edcc06751df65c5e16572877db88",
"type": "github"
},
"original": {

View File

@@ -30,7 +30,6 @@
inputs = {
flake-parts.follows = "flake-parts";
nixpkgs.follows = "nixpkgs";
systems.follows = "systems";
treefmt-nix.follows = "treefmt-nix";
};
};

View File

@@ -7,8 +7,29 @@
}:
rec {
buildClan =
# TODO: Once all templates and docs are migrated add: lib.warn "'buildClan' is deprecated. Use 'clan-core.lib.clan' instead"
module: (clan module).config;
module:
lib.warn ''
==================== DEPRECATION NOTICE ====================
Please migrate
from: 'clan = inputs.<clan-core>.lib.buildClan'
to : 'clan = inputs.<clan-core>.lib.clan'
in your flake.nix.
Please also migrate
from: 'inherit (clan) nixosConfigurations clanInternals; '
to : "
inherit (clan.config) nixosConfigurations clanInternals;
clan = clan.config;
"
in your flake.nix.
Reason:
- Improves consistency between flake-parts and non-flake-parts users.
- It also allows us to use the top level attribute 'clan' to expose
attributes that can be used for cross-clan functionality.
============================================================
'' (clan module).config;
clan =
{

View File

@@ -48,6 +48,7 @@ in
{
options = {
instances = lib.mkOption {
default = { };
# instances.<instanceName>...
type = types.attrsOf (submoduleWith {
modules = [
@@ -57,6 +58,7 @@ in
};
# instances.<machineName>...
machines = lib.mkOption {
default = { };
type = types.attrsOf (submoduleWith {
modules = [
config.exportsModule

View File

@@ -21,6 +21,12 @@ buildNpmPackage (_finalAttrs: {
mkdir -p api
cp -r ${clan-ts-api}/* api
cp -r ${fonts} ".fonts"
# only needed for the next couple weeks to make sure this file doesn't make it back into the git history
if [[ -f "${./ui}/src/routes/Onboarding/background.jpg" ]]; then
echo "background.jpg found, exiting"
exit 1
fi
'';
# todo figure out why this fails only inside of Nix

View File

@@ -3,7 +3,7 @@ import type { StorybookConfig } from "@kachurun/storybook-solid-vite";
const config: StorybookConfig = {
framework: "@kachurun/storybook-solid-vite",
stories: ["../src/components/**/*.mdx", "../src/components/**/*.stories.tsx"],
stories: ["../src/**/*.mdx", "../src/**/*.stories.tsx"],
addons: [
"@storybook/addon-links",
"@storybook/addon-docs",

View File

@@ -138,6 +138,10 @@
transition: all 0.5s ease;
}
}
& > span.typography {
@apply max-w-full overflow-hidden whitespace-nowrap text-ellipsis;
}
}
/* button group */

View File

@@ -58,7 +58,7 @@ export type Story = StoryObj<typeof meta>;
export const Bare: Story = {
args: {
onSelectFile: async () => {
return "/home/bob/clans/my-clan";
return "/home/github/clans/my-clan/foo/bar/baz/fizz/buzz";
},
input: {
placeholder: "e.g. 11/06/89",

View File

@@ -12,6 +12,8 @@ import { PolymorphicProps } from "@kobalte/core/polymorphic";
import { FieldProps } from "./Field";
import { Orienter } from "./Orienter";
import { createSignal } from "solid-js";
import { Tooltip } from "@kobalte/core/tooltip";
import { Typography } from "@/src/components/Typography/Typography";
export type HostFileInputProps = FieldProps &
TextFieldRootProps & {
@@ -20,10 +22,21 @@ export type HostFileInputProps = FieldProps &
};
export const HostFileInput = (props: HostFileInputProps) => {
const [value, setValue] = createSignal<string | undefined>(undefined);
const [value, setValue] = createSignal<string>(props.value || "");
let actualInputElement: HTMLInputElement | undefined;
const selectFile = async () => {
setValue(await props.onSelectFile());
try {
console.log("selecting file", props.onSelectFile);
setValue(await props.onSelectFile());
actualInputElement?.dispatchEvent(
new Event("input", { bubbles: true, cancelable: true }),
);
} catch (error) {
console.log("Error selecting file", error);
// todo work out how to display the error
}
};
return (
@@ -33,8 +46,6 @@ export const HostFileInput = (props: HostFileInputProps) => {
ghost: props.ghost,
})}
{...props}
value={value()}
onChange={setValue}
>
<Orienter orientation={props.orientation} align={"start"}>
<Label
@@ -43,16 +54,54 @@ export const HostFileInput = (props: HostFileInputProps) => {
{...props}
/>
<TextField.Input {...props.input} hidden={true} />
<TextField.Input
{...props.input}
hidden={true}
value={value()}
ref={(el: HTMLInputElement) => {
actualInputElement = el; // Capture for local use
}}
/>
<Button
hierarchy="secondary"
size={props.size}
startIcon="Folder"
onClick={selectFile}
>
{value() ? value() : "No Selection"}
</Button>
{!value() && (
<Button
hierarchy="secondary"
size={props.size}
startIcon="Folder"
onClick={selectFile}
disabled={props.disabled || props.readOnly}
>
No Selection
</Button>
)}
{value() && (
<Tooltip placement="top">
<Tooltip.Portal>
<Tooltip.Content class="tooltip-content">
<Typography
hierarchy="body"
size="xs"
weight="medium"
inverted={!props.inverted}
>
{value()}
</Typography>
<Tooltip.Arrow />
</Tooltip.Content>
</Tooltip.Portal>
<Tooltip.Trigger
as={Button}
hierarchy="secondary"
size={props.size}
startIcon="Folder"
onClick={selectFile}
disabled={props.disabled || props.readOnly}
>
{value()}
</Tooltip.Trigger>
</Tooltip>
)}
</Orienter>
</TextField>
);

View File

@@ -22,40 +22,3 @@ div.form-label {
}
}
}
div.tooltip-content {
@apply z-50 px-2 py-0.5 bg-inv-4 rounded-[0.125rem] leading-none;
max-width: min(calc(100vw - 16px), 380px);
transform-origin: var(--kb-tooltip-content-transform-origin);
animation: tooltipHide 250ms ease-in forwards;
&[data-expanded] {
animation: tooltipShow 250ms ease-out;
}
&.inverted {
@apply bg-def-2;
}
}
@keyframes tooltipShow {
from {
opacity: 0;
transform: scale(0.96);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes tooltipHide {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.96);
}
}

View File

@@ -1,12 +1,11 @@
import { Show } from "solid-js";
import { Typography } from "@/src/components/Typography/Typography";
import { Tooltip as KTooltip } from "@kobalte/core/tooltip";
import { Tooltip } from "@/src/components/Tooltip/Tooltip";
import Icon from "@/src/components/Icon/Icon";
import { TextField } from "@kobalte/core/text-field";
import { Checkbox } from "@kobalte/core/checkbox";
import { Combobox } from "@kobalte/core/combobox";
import "./Label.css";
import cx from "classnames";
export type Size = "default" | "s";
@@ -49,31 +48,27 @@ export const Label = (props: LabelProps) => {
{props.label}
</Typography>
{props.tooltip && (
<KTooltip placement="top">
<KTooltip.Trigger>
<Tooltip
placement="top"
inverted={props.inverted}
trigger={
<Icon
icon="Info"
color="tertiary"
inverted={props.inverted}
size={props.size == "default" ? "0.85em" : "0.75rem"}
/>
<KTooltip.Portal>
<KTooltip.Content
class={cx("tooltip-content", { inverted: props.inverted })}
>
<Typography
hierarchy="body"
size="xs"
weight="medium"
inverted={!props.inverted}
>
{props.tooltip}
</Typography>
<KTooltip.Arrow />
</KTooltip.Content>
</KTooltip.Portal>
</KTooltip.Trigger>
</KTooltip>
}
>
<Typography
hierarchy="body"
size="xs"
weight="medium"
inverted={!props.inverted}
>
{props.tooltip}
</Typography>
</Tooltip>
)}
</props.labelComponent>
{props.description && (

View File

@@ -0,0 +1,9 @@
div.tooltip-content {
@apply z-50 px-2 py-0.5 bg-inv-4 rounded-[0.125rem] leading-none;
max-width: min(calc(100vw - 16px), 380px);
&.inverted {
@apply bg-def-2;
}
}

View File

@@ -0,0 +1,40 @@
import { Meta, StoryObj } from "@kachurun/storybook-solid";
import { Tooltip, TooltipProps } from "@/src/components/Tooltip/Tooltip";
import { Typography } from "@/src/components/Typography/Typography";
import { Button } from "@/src/components/Button/Button";
const meta: Meta<TooltipProps> = {
title: "Components/Tooltip",
component: Tooltip,
decorators: [
(Story: StoryObj<TooltipProps>) => (
<div class="p-16">
<Story />
</div>
),
],
};
export default meta;
type Story = StoryObj<TooltipProps>;
export const Default: Story = {
args: {
placement: "top",
inverted: false,
trigger: <Button hierarchy="primary">Trigger</Button>,
children: (
<Typography hierarchy="body" size="xs" inverted={true} weight="medium">
Your Clan is being created
</Typography>
),
},
};
export const AnimateBounce: Story = {
args: {
...Default.args,
animation: "bounce",
},
};

View File

@@ -0,0 +1,34 @@
import "./Tooltip.css";
import {
Tooltip as KTooltip,
TooltipRootProps as KTooltipRootProps,
} from "@kobalte/core/tooltip";
import cx from "classnames";
import { JSX } from "solid-js";
export interface TooltipProps extends KTooltipRootProps {
inverted?: boolean;
trigger: JSX.Element;
children: JSX.Element;
animation?: "bounce";
}
export const Tooltip = (props: TooltipProps) => {
return (
<KTooltip {...props}>
<KTooltip.Trigger>{props.trigger}</KTooltip.Trigger>
<KTooltip.Portal>
<KTooltip.Content
class={cx("tooltip-content", {
inverted: props.inverted,
"animate-bounce": props.animation == "bounce",
})}
>
{props.placement == "bottom" && <KTooltip.Arrow />}
{props.children}
{props.placement == "top" && <KTooltip.Arrow />}
</KTooltip.Content>
</KTooltip.Portal>
</KTooltip>
);
};

View File

@@ -42,7 +42,7 @@ interface BackendReturnType<K extends OperationNames> {
* @property {Promise<BackendReturnType<K>>} result - A promise that resolves to the return type of the backend operation.
* @property {() => Promise<void>} cancel - A function to cancel the API call, returning a promise that resolves when cancellation is completed.
*/
interface ApiCall<K extends OperationNames> {
export interface ApiCall<K extends OperationNames> {
uuid: string;
result: Promise<OperationResponse<K>>;
cancel: () => Promise<void>;

View File

@@ -1,6 +1,18 @@
import { Component } from "solid-js";
import { RouteSectionProps } from "@solidjs/router";
import { RouteSectionProps, useNavigate } from "@solidjs/router";
import { activeClanURI } from "@/src/stores/clan";
import { navigateToClan } from "@/src/hooks/clan";
export const Layout: Component<RouteSectionProps> = (props) => (
<div class="size-full h-screen">{props.children}</div>
);
export const Layout: Component<RouteSectionProps> = (props) => {
const navigate = useNavigate();
// check for an active clan uri and redirect to it on first load
const activeURI = activeClanURI();
if (!props.location.pathname.startsWith("/clan/") && activeURI) {
navigateToClan(navigate, activeURI);
} else {
navigate("/");
}
return <div class="size-full h-screen">{props.children}</div>;
};

View File

@@ -81,5 +81,10 @@ main#welcome {
}
}
}
div.creating {
@apply w-[17.0625rem] h-[20.4375rem];
background: url(./cube.svg) center / cover no-repeat;
}
}
}

View File

@@ -1,18 +1,30 @@
import { Component, createSignal, Match, Setter, Show, Switch } from "solid-js";
import {
Accessor,
Component,
createSignal,
Match,
Setter,
Show,
Switch,
} from "solid-js";
import { RouteSectionProps, useNavigate } from "@solidjs/router";
import "./Onboarding.css";
import { Typography } from "@/src/components/Typography/Typography";
import { Button } from "@/src/components/Button/Button";
import { Tooltip } from "@/src/components/Tooltip/Tooltip";
import { Alert } from "@/src/components/Alert/Alert";
import { Divider } from "@/src/components/Divider/Divider";
import { Logo } from "@/src/components/Logo/Logo";
import { navigateToClan, selectClanFolder } from "@/src/hooks/clan";
import { activeClanURI } from "@/src/stores/clan";
import { activeClanURI, addClanURI, setActiveClanURI } from "@/src/stores/clan";
import {
createForm,
FormStore,
getError,
getErrors,
getValue,
SubmitHandler,
valiForm,
} from "@modular-forms/solid";
import { TextInput } from "@/src/components/Form/TextInput";
@@ -20,23 +32,31 @@ import { TextArea } from "@/src/components/Form/TextArea";
import { Fieldset } from "@/src/components/Form/Fieldset";
import * as v from "valibot";
import { HostFileInput } from "@/src/components/Form/HostFileInput";
import { callApi } from "@/src/hooks/api";
type State = "welcome" | "setup";
type State = "welcome" | "setup" | "creating";
const SetupSchema = v.object({
name: v.pipe(v.string(), v.nonEmpty("Please enter a name.")),
name: v.pipe(
v.string(),
v.nonEmpty("Please enter a name."),
v.regex(
new RegExp("^[a-zA-Z0-9_\\-]+$"),
"Name must be alphanumeric and can contain underscores and dashes, without spaces.",
),
),
description: v.pipe(v.string(), v.nonEmpty("Please describe your clan.")),
directory: v.pipe(v.string(), v.nonEmpty("Please select a directory.")),
directory: v.pipe(
// initial value is undefined, and I can't see how to handle this better in valibot, so for now when the type
// is incorrect we treat it as empty
v.string("Please select a directory."),
v.nonEmpty("Please select a directory."),
),
});
type SetupForm = v.InferInput<typeof SetupSchema>;
interface backgroundProps {
state: State;
form: FormStore<SetupForm>;
}
const background = (props: backgroundProps) => (
const background = (props: { state: State; form: FormStore<SetupForm> }) => (
<div class="background">
<div class="layer-1" />
<div class="layer-2" />
@@ -71,7 +91,11 @@ const background = (props: backgroundProps) => (
</div>
);
const welcome = (setState: Setter<State>) => {
const welcome = (props: {
setState: Setter<State>;
welcomeError: Accessor<string | undefined>;
setWelcomeError: Setter<string | undefined>;
}) => {
const navigate = useNavigate();
const selectFolder = async () => {
@@ -91,7 +115,23 @@ const welcome = (setState: Setter<State>) => {
Build your <br />
own darknet
</Typography>
<Button hierarchy="secondary" onClick={() => setState("setup")}>
{props.welcomeError() && (
<Alert
type="error"
icon="Info"
title="Your Clan creation failed"
description={props.welcomeError() || ""}
/>
)}
<Button
hierarchy="secondary"
onClick={() => {
// reset welcome error
props.setWelcomeError(undefined);
// move to next step
props.setState("setup");
}}
>
Start building
</Button>
<div class="separator">
@@ -114,6 +154,21 @@ const welcome = (setState: Setter<State>) => {
);
};
const creating = () => (
<div class="animate-pulse">
<Tooltip
open={true}
placement="top"
animation="bounce"
trigger={<div class="creating" />}
>
<Typography hierarchy="body" size="xs" weight="medium" inverted={true}>
Your Clan is being created
</Typography>
</Tooltip>
</div>
);
export const Onboarding: Component<RouteSectionProps> = (props) => {
const navigate = useNavigate();
@@ -126,13 +181,89 @@ export const Onboarding: Component<RouteSectionProps> = (props) => {
const [state, setState] = createSignal<State>("welcome");
// used to display an error in the welcome screen in the event of a failed
// clan creation
const [welcomeError, setWelcomeError] = createSignal<string | undefined>();
//
const [setupForm, { Form, Field }] = createForm<SetupForm>({
validate: valiForm(SetupSchema),
});
const metaError = () => {
const errors = getErrors(setupForm, ["name", "description"]);
return errors ? errors.name || errors.description : undefined;
const formError = () => {
const formErrors = getErrors(setupForm);
return (
formErrors.name ||
formErrors.description ||
formErrors.directory ||
undefined
);
};
const onSelectFile = async () => {
const req = callApi("get_system_file", {
file_request: {
mode: "select_folder",
title: "Select a folder for you new Clan",
},
});
const resp = await req.result;
if (resp.status === "error") {
// just throw the first error, I can't imagine why there would be multiple
// errors for this call
throw new Error(resp.errors[0].message);
}
if (resp.status === "success" && resp.data) {
return resp.data[0];
}
throw new Error("No data returned from api call");
};
const onSubmit: SubmitHandler<SetupForm> = async (
{ name, description, directory },
event,
) => {
const path = `${directory}/${name}`;
const req = callApi("create_clan", {
opts: {
dest: path,
// todo allow users to select a template
template: "minimal",
initial: {
meta: {
name: name,
description: description,
// todo it tries to 'delete' icon if it's not provided
// this logic is unexpected, and needs reviewed.
icon: null,
},
machines: {},
instances: {},
services: {},
},
},
});
setState("creating");
const resp = await req.result;
if (resp.status === "error") {
setWelcomeError(resp.errors[0].message);
setState("welcome");
return;
}
if (resp.status === "success") {
addClanURI(path);
setActiveClanURI(path);
navigateToClan(navigate, path);
}
};
return (
@@ -140,7 +271,13 @@ export const Onboarding: Component<RouteSectionProps> = (props) => {
{background({ form: setupForm, state: state() })}
<div class="container">
<Switch>
<Match when={state() === "welcome"}>{welcome(setState)}</Match>
<Match when={state() === "welcome"}>
{welcome({
setState,
welcomeError,
setWelcomeError,
})}
</Match>
<Match when={state() === "setup"}>
<div class="setup">
@@ -155,8 +292,16 @@ export const Onboarding: Component<RouteSectionProps> = (props) => {
Setup
</Typography>
</div>
<Form>
<Fieldset name="meta" error={metaError()}>
<Form onSubmit={onSubmit}>
{formError() && (
<Alert
type="error"
icon="Info"
title="Form error"
description={formError() || ""}
/>
)}
<Fieldset name="meta">
<Field name="name">
{(field, input) => (
<TextInput
@@ -195,15 +340,13 @@ export const Onboarding: Component<RouteSectionProps> = (props) => {
</Field>
</Fieldset>
<Fieldset
name="location"
error={getError(setupForm, "directory")}
>
<Fieldset name="location">
<Field name="directory">
{(field, input) => (
<HostFileInput
onSelectFile={async () => "test"}
onSelectFile={onSelectFile}
{...field}
value={field.value}
label="Select directory"
orientation="horizontal"
required={true}
@@ -228,6 +371,8 @@ export const Onboarding: Component<RouteSectionProps> = (props) => {
</Form>
</div>
</Match>
<Match when={state() === "creating"}>{creating()}</Match>
</Switch>
</div>
</main>

View File

@@ -0,0 +1,316 @@
<svg width="273" height="327" viewBox="0 0 273 327" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="70.9982" height="70.9982" rx="1" transform="matrix(0.86619 0.499716 -0.86619 0.499716 137.399 89.7148)" fill="url(#paint0_linear_4542_12187)" stroke="url(#paint1_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 75.8281 125.194)" fill="url(#paint2_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 75.8281 125.194)" fill="url(#paint3_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 75.8281 125.194)" stroke="url(#paint4_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 137.326 160.673)" fill="url(#paint5_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 137.326 160.673)" fill="url(#paint6_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 137.326 160.673)" stroke="url(#paint7_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9982" rx="1" transform="matrix(0.86619 0.499716 -0.86619 0.499716 62.5708 142.85)" fill="url(#paint8_linear_4542_12187)" stroke="url(#paint9_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 1 178.329)" fill="url(#paint10_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 1 178.329)" fill="url(#paint11_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 1 178.329)" stroke="url(#paint12_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 62.4976 213.808)" fill="url(#paint13_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 62.4976 213.808)" fill="url(#paint14_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 62.4976 213.808)" stroke="url(#paint15_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9982" rx="1" transform="matrix(0.86619 0.499715 -0.86619 0.499715 210.502 137.882)" fill="url(#paint16_linear_4542_12187)" stroke="url(#paint17_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 148.931 173.361)" fill="url(#paint18_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 148.931 173.361)" fill="url(#paint19_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 148.931 173.361)" stroke="url(#paint20_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499715 0 1 210.429 208.84)" fill="url(#paint21_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499715 0 1 210.429 208.84)" fill="url(#paint22_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499715 0 1 210.429 208.84)" stroke="url(#paint23_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9982" rx="1" transform="matrix(0.86619 0.499716 -0.86619 0.499716 137.158 185.084)" fill="url(#paint24_linear_4542_12187)" stroke="url(#paint25_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 75.5869 220.563)" fill="url(#paint26_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 75.5869 220.563)" fill="url(#paint27_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 75.5869 220.563)" stroke="url(#paint28_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 137.084 256.042)" fill="url(#paint29_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 137.084 256.042)" fill="url(#paint30_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 137.084 256.042)" stroke="url(#paint31_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9982" rx="1" transform="matrix(0.86619 0.499716 -0.86619 0.499716 135.915 0)" fill="url(#paint32_linear_4542_12187)" stroke="url(#paint33_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 74.3442 35.479)" fill="url(#paint34_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 74.3442 35.479)" fill="url(#paint35_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 74.3442 35.479)" stroke="url(#paint36_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 135.842 70.9579)" fill="url(#paint37_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 135.842 70.9579)" fill="url(#paint38_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 135.842 70.9579)" stroke="url(#paint39_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9982" rx="1" transform="matrix(0.86619 0.499716 -0.86619 0.499716 62.5708 47.2027)" fill="url(#paint40_linear_4542_12187)" stroke="url(#paint41_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 1 82.6817)" fill="url(#paint42_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 1 82.6817)" fill="url(#paint43_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 1 82.6817)" stroke="url(#paint44_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 62.4976 118.161)" fill="url(#paint45_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 62.4976 118.161)" fill="url(#paint46_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 62.4976 118.161)" stroke="url(#paint47_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9982" rx="1" transform="matrix(0.86619 0.499715 -0.86619 0.499715 210.502 42.234)" fill="url(#paint48_linear_4542_12187)" stroke="url(#paint49_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 148.931 77.713)" fill="url(#paint50_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 148.931 77.713)" fill="url(#paint51_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 148.931 77.713)" stroke="url(#paint52_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499715 0 1 210.429 113.192)" fill="url(#paint53_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499715 0 1 210.429 113.192)" fill="url(#paint54_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499715 0 1 210.429 113.192)" stroke="url(#paint55_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9982" rx="1" transform="matrix(0.86619 0.499716 -0.86619 0.499716 137.158 89.4367)" fill="url(#paint56_linear_4542_12187)" stroke="url(#paint57_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 75.5869 124.916)" fill="url(#paint58_linear_4542_12187)" fill-opacity="0.1"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 75.5869 124.916)" fill="url(#paint59_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 0.499716 0 1 75.5869 124.916)" stroke="url(#paint60_linear_4542_12187)" stroke-width="0.64"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 137.084 160.395)" fill="url(#paint61_linear_4542_12187)"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 137.084 160.395)" fill="url(#paint62_linear_4542_12187)" fill-opacity="0.24"/>
<rect width="70.9982" height="70.9578" rx="1" transform="matrix(0.86619 -0.499716 0 1 137.084 160.395)" stroke="url(#paint63_linear_4542_12187)" stroke-width="0.64"/>
<defs>
<linearGradient id="paint0_linear_4542_12187" x1="3.21525" y1="1.26987" x2="26.2123" y2="73.9372" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.94"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint1_linear_4542_12187" x1="73.1289" y1="21.7256" x2="19.1537" y2="-1.08702" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint2_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint3_linear_4542_12187" x1="71.0824" y1="68.7651" x2="43.5492" y2="9.42738" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint4_linear_4542_12187" x1="73.1289" y1="21.7132" x2="19.163" y2="-1.10842" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint5_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.81"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint6_linear_4542_12187" x1="22.5463" y1="67.3059" x2="51.7875" y2="28.1744" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint7_linear_4542_12187" x1="73.1289" y1="21.7132" x2="19.163" y2="-1.10842" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint8_linear_4542_12187" x1="3.21525" y1="1.26987" x2="26.2123" y2="73.9372" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.94"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint9_linear_4542_12187" x1="73.1289" y1="21.7256" x2="19.1537" y2="-1.08702" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint10_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint11_linear_4542_12187" x1="71.0824" y1="68.7651" x2="43.5492" y2="9.42738" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint12_linear_4542_12187" x1="73.1289" y1="21.7132" x2="19.163" y2="-1.10842" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint13_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.81"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint14_linear_4542_12187" x1="22.5463" y1="67.3059" x2="51.7875" y2="28.1744" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint15_linear_4542_12187" x1="73.129" y1="21.7132" x2="19.163" y2="-1.10843" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint16_linear_4542_12187" x1="3.21525" y1="1.26987" x2="26.2123" y2="73.9372" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.94"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint17_linear_4542_12187" x1="73.129" y1="21.7256" x2="19.1537" y2="-1.08702" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint18_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint19_linear_4542_12187" x1="71.0824" y1="68.7651" x2="43.5492" y2="9.42737" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint20_linear_4542_12187" x1="73.1289" y1="21.7132" x2="19.163" y2="-1.10843" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint21_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.81"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint22_linear_4542_12187" x1="22.5463" y1="67.3059" x2="51.7875" y2="28.1744" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint23_linear_4542_12187" x1="73.129" y1="21.7132" x2="19.163" y2="-1.10843" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint24_linear_4542_12187" x1="3.21525" y1="1.26987" x2="26.2123" y2="73.9372" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.94"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint25_linear_4542_12187" x1="73.1289" y1="21.7256" x2="19.1537" y2="-1.08702" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint26_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint27_linear_4542_12187" x1="71.0824" y1="68.7651" x2="43.5492" y2="9.42738" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint28_linear_4542_12187" x1="73.129" y1="21.7132" x2="19.163" y2="-1.10843" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint29_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.81"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint30_linear_4542_12187" x1="22.5463" y1="67.3059" x2="51.7875" y2="28.1743" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint31_linear_4542_12187" x1="73.1289" y1="21.7132" x2="19.163" y2="-1.10843" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint32_linear_4542_12187" x1="3.21525" y1="1.26987" x2="26.2123" y2="73.9372" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.94"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint33_linear_4542_12187" x1="73.129" y1="21.7256" x2="19.1537" y2="-1.08702" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint34_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint35_linear_4542_12187" x1="71.0824" y1="68.7651" x2="43.5492" y2="9.42738" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint36_linear_4542_12187" x1="73.1289" y1="21.7132" x2="19.163" y2="-1.10843" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint37_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.81"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint38_linear_4542_12187" x1="22.5463" y1="67.3059" x2="51.7875" y2="28.1744" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint39_linear_4542_12187" x1="73.1289" y1="21.7132" x2="19.163" y2="-1.10842" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint40_linear_4542_12187" x1="3.21525" y1="1.26987" x2="26.2123" y2="73.9372" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.94"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint41_linear_4542_12187" x1="73.1289" y1="21.7256" x2="19.1537" y2="-1.08702" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint42_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint43_linear_4542_12187" x1="71.0824" y1="68.7651" x2="43.5492" y2="9.42738" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint44_linear_4542_12187" x1="73.1289" y1="21.7132" x2="19.163" y2="-1.10842" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint45_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.81"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint46_linear_4542_12187" x1="22.5463" y1="67.3059" x2="51.7875" y2="28.1744" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint47_linear_4542_12187" x1="73.129" y1="21.7132" x2="19.163" y2="-1.10843" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint48_linear_4542_12187" x1="3.21525" y1="1.26987" x2="26.2123" y2="73.9372" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.94"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint49_linear_4542_12187" x1="73.129" y1="21.7256" x2="19.1537" y2="-1.08702" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint50_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint51_linear_4542_12187" x1="71.0824" y1="68.7651" x2="43.5492" y2="9.42738" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint52_linear_4542_12187" x1="73.1289" y1="21.7132" x2="19.163" y2="-1.10842" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint53_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.81"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint54_linear_4542_12187" x1="22.5463" y1="67.3059" x2="51.7875" y2="28.1744" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint55_linear_4542_12187" x1="73.129" y1="21.7132" x2="19.163" y2="-1.10843" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint56_linear_4542_12187" x1="3.21525" y1="1.26987" x2="26.2123" y2="73.9372" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.94"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint57_linear_4542_12187" x1="73.1289" y1="21.7256" x2="19.1537" y2="-1.08702" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint58_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint59_linear_4542_12187" x1="71.0824" y1="68.7651" x2="43.5492" y2="9.42738" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint60_linear_4542_12187" x1="73.129" y1="21.7132" x2="19.163" y2="-1.10843" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
<linearGradient id="paint61_linear_4542_12187" x1="3.21525" y1="1.26914" x2="26.1885" y2="73.9026" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.81"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint62_linear_4542_12187" x1="22.5463" y1="67.3059" x2="51.7875" y2="28.1744" gradientUnits="userSpaceOnUse">
<stop stop-color="#40A2A6"/>
<stop offset="1" stop-color="#91ACAF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint63_linear_4542_12187" x1="73.1289" y1="21.7132" x2="19.163" y2="-1.10842" gradientUnits="userSpaceOnUse">
<stop stop-color="#10191A"/>
<stop offset="1" stop-color="#2C4547"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,15 @@
.cubes-scene-container {
width: 100%;
height: 100vh;
cursor: pointer;
}
.toolbar-container {
position: absolute;
bottom: 10%;
width: 100%;
z-index: 10;
display: flex;
justify-content: center;
align-items: center;
}

View File

@@ -0,0 +1,15 @@
import { Meta, StoryObj } from "@kachurun/storybook-solid";
import { CubeScene } from "./cubes";
const meta: Meta = {
title: "scene/cubes",
component: CubeScene,
};
export default meta;
type Story = StoryObj;
export const Default: Story = {
args: {},
};

File diff suppressed because it is too large Load Diff