Messing around
This commit is contained in:
parent
0b4889763e
commit
a5a39844a3
36 changed files with 2001 additions and 1 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
dist
|
10
README.md
10
README.md
|
@ -1,3 +1,11 @@
|
|||
# frontend-web
|
||||
|
||||
todo
|
||||
work in progress, todo: write readme.md
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
$ pnpm install
|
||||
$ pnpm run dev # for development
|
||||
$ pnpm run build # for production
|
||||
```
|
||||
|
|
8
config.md
Normal file
8
config.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# config/interactions
|
||||
|
||||
detailing the degrees of freedom
|
||||
|
||||
## threads
|
||||
|
||||
- show/hide unread threads
|
||||
- show/hide ignored threads
|
19
design.md
Normal file
19
design.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# design notes
|
||||
|
||||
## colors
|
||||
|
||||
- text:
|
||||
- text dim:
|
||||
- text bright:
|
||||
- background:
|
||||
- background-dim:
|
||||
- background-dim-2:
|
||||
- background-dim-3:
|
||||
- accent:
|
||||
- link:
|
||||
|
||||
## fonts
|
||||
|
||||
- main: Atkinson Hyperlegible
|
||||
- display:
|
||||
- monospace:
|
13
index.html
Normal file
13
index.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>jackwagon</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
20
package.json
Normal file
20
package.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "frontend-sdk",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"sdk": "git+https://git.celery.eu.org/jackwagon/sdk-ts",
|
||||
"solid-js": "^1.8.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"sass": "^1.69.5",
|
||||
"typescript": "^5.3.2",
|
||||
"vite": "^5.0.5",
|
||||
"vite-plugin-solid": "^2.7.2"
|
||||
}
|
||||
}
|
1230
pnpm-lock.yaml
Normal file
1230
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load diff
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
222
src/App.scss
Normal file
222
src/App.scss
Normal file
|
@ -0,0 +1,222 @@
|
|||
#root {
|
||||
display: grid;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
grid-template-areas: "nav-spaces header header header"
|
||||
"nav-spaces nav-rooms main sidebar"
|
||||
"status status main sidebar";
|
||||
grid-template-columns: 64px 256px 1fr 256px;
|
||||
grid-template-rows: 64px 1fr 72px;
|
||||
}
|
||||
|
||||
#header {
|
||||
background: var(--background-3);
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
#main {
|
||||
background: var(--background-1);
|
||||
grid-area: main;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& > .timeline {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
& > .actions {
|
||||
height: 72px;
|
||||
background: var(--background-2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
gap: 8px;
|
||||
|
||||
& > .create-thread {
|
||||
display: inline-flex;
|
||||
gap: 2px;
|
||||
|
||||
& > div {
|
||||
padding: 4px;
|
||||
background: #46c;
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#nav-rooms {
|
||||
background: var(--background-2);
|
||||
grid-area: nav-rooms;
|
||||
|
||||
& > ul {
|
||||
list-style: none;
|
||||
|
||||
& > li {
|
||||
cursor: pointer;
|
||||
padding: 4px 8px;
|
||||
|
||||
&:hover {
|
||||
background: #ffffff33;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#nav-spaces {
|
||||
background: var(--background-4);
|
||||
grid-area: nav-spaces;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
background: var(--background-2);
|
||||
grid-area: sidebar;
|
||||
}
|
||||
|
||||
#status {
|
||||
background: var(--background-3);
|
||||
grid-area: status;
|
||||
}
|
||||
|
||||
.timeline-thread {
|
||||
--title-font-size: calc(1rem * 1.2);
|
||||
--info-font-size: calc(1rem * 1);
|
||||
display: grid;
|
||||
padding: 4px;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
grid-template-columns: var(--title-font-size) 1fr;
|
||||
grid-template-rows: var(--title-font-size) var(--info-font-size);
|
||||
line-height: 1;
|
||||
|
||||
&:hover {
|
||||
background: #00000022;
|
||||
}
|
||||
|
||||
& > .icon {
|
||||
background: #34363b;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
& > .title {
|
||||
font-size: var(--title-font-size);
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
& > .info {
|
||||
grid-column: 1 / 3;
|
||||
font-size: var(--info-font-size);
|
||||
color: var(--foreground-2);
|
||||
}
|
||||
}
|
||||
|
||||
.toggle {
|
||||
padding: 4px;
|
||||
background: #555;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&.disabled {
|
||||
background: #555;
|
||||
}
|
||||
}
|
||||
|
||||
.message {
|
||||
contain: content;
|
||||
// padding: 4px;
|
||||
|
||||
&.compact {
|
||||
--name-width: 144px;
|
||||
|
||||
&.title {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
& .name {
|
||||
position: absolute;
|
||||
font-weight: bold;
|
||||
margin-left: 8px;
|
||||
max-width: var(--name-width);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
& .content {
|
||||
margin-left: calc(var(--name-width) + 16px);
|
||||
}
|
||||
|
||||
& .avatar, & .space {
|
||||
margin-left: 8px;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
&.cozy {
|
||||
&.title {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
& .name {
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
& .content {
|
||||
margin-left: 54px;
|
||||
}
|
||||
|
||||
& .avatar, & .space {
|
||||
margin-left: 8px;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
& .avatar, & .space {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
|
||||
&.avatar {
|
||||
background: #822eba;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
& a {
|
||||
color: #822eba;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #00000022;
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 72px;
|
||||
background: var(--background-2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
gap: 8px;
|
||||
|
||||
& > textarea {
|
||||
background: var(--background-1);
|
||||
border: solid var(--background-4) 1px;
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
height: 3rem;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
147
src/App.tsx
Normal file
147
src/App.tsx
Normal file
|
@ -0,0 +1,147 @@
|
|||
import { VoidProps, createSignal, onCleanup } from "solid-js";
|
||||
import "./App.scss";
|
||||
import { Client, Room, Timeline } from "sdk";
|
||||
|
||||
function App() {
|
||||
const client = new Client({
|
||||
baseUrl: "http://localhost:6167",
|
||||
deviceId: "PFXhfDYmCc",
|
||||
token: "Ebh15YkyCBJSFjP11oimVEtAdqL9ZXcl",
|
||||
userId: "@user:localhost",
|
||||
});
|
||||
|
||||
const [rooms, setRooms] = createSignal({ count: 0, rooms: [] as Array<Room> });
|
||||
const [room, setRoom] = createSignal(null as null | Room);
|
||||
const [asdf, setAsdf] = createSignal(true);
|
||||
|
||||
Object.assign(globalThis, { client });
|
||||
|
||||
client.lists.subscribe("rooms", {
|
||||
ranges: [[0, 120]],
|
||||
required_state: [["m.room.name", ""],["m.room.topic", ""]],
|
||||
// timeline_limit: 3,
|
||||
} as any);
|
||||
|
||||
client.on("list", (name, list) => {
|
||||
if (name === "rooms") setRooms({ ...list });
|
||||
});
|
||||
|
||||
client.start();
|
||||
onCleanup(() => client.stop());
|
||||
|
||||
const [offset, setOffset] = createSignal(0);
|
||||
const interval = setInterval(() => setOffset(offset() + 1), 100);
|
||||
onCleanup(() => clearInterval(interval));
|
||||
|
||||
return (
|
||||
<>
|
||||
<header id="header" onClick={() => setAsdf(!asdf())}>
|
||||
{room()?.id} {room()?.getState("m.room.name")?.content.name}
|
||||
</header>
|
||||
<main id="main">{asdf() ? <>
|
||||
<Threads />
|
||||
<div class="actions">
|
||||
<TimelineActions />
|
||||
</div>
|
||||
</> : <>
|
||||
<EventTimeline timeline={room()?.timelines.live || undefined} />
|
||||
<div class="input">
|
||||
<textarea placeholder="input text here"></textarea>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</main>
|
||||
<nav id="nav-rooms">
|
||||
<ul>
|
||||
<li onClick={() => setRoom(null)}>home</li>
|
||||
{rooms().rooms.map(i => <li onClick={() => setRoom(i)}>{i.getState("m.room.name")?.content.name}</li>)}
|
||||
</ul>
|
||||
</nav>
|
||||
<nav id="nav-spaces"></nav>
|
||||
<div id="sidebar">
|
||||
<EventTimeline timeline={room()?.timelines.live} />
|
||||
<div class="input">
|
||||
<textarea placeholder="input text here"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<footer id="status"></footer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Threads() {
|
||||
return (
|
||||
<div class="timeline">
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
<ThreadsItem />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function EventTimeline(props: VoidProps<{ timeline: Timeline | undefined }>) {
|
||||
return (
|
||||
<div class="timeline">
|
||||
{props.timeline?.events.map(i => <Message event={i} />)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Message(props: VoidProps<{ event: any }>) {
|
||||
const title = Math.random() > .5;
|
||||
const compact = false;
|
||||
|
||||
return <div class="message" classList={{ title, [compact ? "compact" : "cozy"]: true }}>
|
||||
{title && !compact && <div class="avatar"></div>}
|
||||
{title && compact && <div class="name">{props.event.sender}</div>}
|
||||
<div class="content">
|
||||
{title && !compact && <div class="name">{props.event.sender}</div>}
|
||||
<div class="body">{props.event.content.body || `event: (${props.event.type})`}</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function ThreadsItem() {
|
||||
return <div class="timeline-thread">
|
||||
<div class="icon"></div>
|
||||
<div class="title">Thread title</div>
|
||||
<div class="info">Info here, date created, other options</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function TimelineActions() {
|
||||
return <>
|
||||
<Toggle enabled="[x] Include ignored" disabled="[ ] Include ignored" initial={false} />
|
||||
<Toggle enabled="[x] Unread" disabled="[ ] Unread" initial={false} />
|
||||
<Toggle enabled="[x] Watching" disabled="[ ] Watching" initial={false} />
|
||||
<div class="create-thread">
|
||||
<div>New thread</div>
|
||||
<div>+</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
function Toggle(props: { enabled: string, disabled: string, initial: boolean }) {
|
||||
const [disabled, setDisabled] = createSignal(props.initial);
|
||||
return (
|
||||
<div
|
||||
class="toggle"
|
||||
classList={{ disabled: disabled() }}
|
||||
onClick={() => setDisabled(!disabled())}
|
||||
>{disabled() ? props.enabled : props.disabled}</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
93
src/assets/fonts/atkinson-hyperlegible/OFL.txt
Normal file
93
src/assets/fonts/atkinson-hyperlegible/OFL.txt
Normal file
|
@ -0,0 +1,93 @@
|
|||
Copyright 2020 Braille Institute of America, Inc.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
BIN
src/assets/fonts/iosevka/iosevka-zesty-bold-extended-italic.ttf
Normal file
BIN
src/assets/fonts/iosevka/iosevka-zesty-bold-extended-italic.ttf
Normal file
Binary file not shown.
BIN
src/assets/fonts/iosevka/iosevka-zesty-bold-extended.ttf
Normal file
BIN
src/assets/fonts/iosevka/iosevka-zesty-bold-extended.ttf
Normal file
Binary file not shown.
BIN
src/assets/fonts/iosevka/iosevka-zesty-bold-italic.ttf
Normal file
BIN
src/assets/fonts/iosevka/iosevka-zesty-bold-italic.ttf
Normal file
Binary file not shown.
BIN
src/assets/fonts/iosevka/iosevka-zesty-bold.ttf
Normal file
BIN
src/assets/fonts/iosevka/iosevka-zesty-bold.ttf
Normal file
Binary file not shown.
BIN
src/assets/fonts/iosevka/iosevka-zesty-extended-italic.ttf
Normal file
BIN
src/assets/fonts/iosevka/iosevka-zesty-extended-italic.ttf
Normal file
Binary file not shown.
BIN
src/assets/fonts/iosevka/iosevka-zesty-extended.ttf
Normal file
BIN
src/assets/fonts/iosevka/iosevka-zesty-extended.ttf
Normal file
Binary file not shown.
BIN
src/assets/fonts/iosevka/iosevka-zesty-italic.ttf
Normal file
BIN
src/assets/fonts/iosevka/iosevka-zesty-italic.ttf
Normal file
Binary file not shown.
BIN
src/assets/fonts/iosevka/iosevka-zesty.ttf
Normal file
BIN
src/assets/fonts/iosevka/iosevka-zesty.ttf
Normal file
Binary file not shown.
1
src/assets/solid.svg
Normal file
1
src/assets/solid.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 166 155.3"><path d="M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z" fill="#76b3e1"/><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="27.5" y1="3" x2="152" y2="63.5"><stop offset=".1" stop-color="#76b3e1"/><stop offset=".3" stop-color="#dcf2fd"/><stop offset="1" stop-color="#76b3e1"/></linearGradient><path d="M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z" opacity=".3" fill="url(#a)"/><path d="M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z" fill="#518ac8"/><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="95.8" y1="32.6" x2="74" y2="105.2"><stop offset="0" stop-color="#76b3e1"/><stop offset=".5" stop-color="#4377bb"/><stop offset="1" stop-color="#1f3b77"/></linearGradient><path d="M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z" opacity=".3" fill="url(#b)"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="18.4" y1="64.2" x2="144.3" y2="149.8"><stop offset="0" stop-color="#315aa9"/><stop offset=".5" stop-color="#518ac8"/><stop offset="1" stop-color="#315aa9"/></linearGradient><path d="M134 80a45 45 0 00-48-15L24 85 4 120l112 19 20-36c4-7 3-15-2-23z" fill="url(#c)"/><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="75.2" y1="74.5" x2="24.4" y2="260.8"><stop offset="0" stop-color="#4377bb"/><stop offset=".5" stop-color="#1a336b"/><stop offset="1" stop-color="#1a336b"/></linearGradient><path d="M114 115a45 45 0 00-48-15L4 120s53 40 94 30l3-1c17-5 23-21 13-34z" fill="url(#d)"/></svg>
|
After Width: | Height: | Size: 1.6 KiB |
39
src/fonts.scss
Normal file
39
src/fonts.scss
Normal file
|
@ -0,0 +1,39 @@
|
|||
@font-face {
|
||||
font-family: "Atkinson Hyperlegible";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-display: swap;
|
||||
src: url(./assets/fonts/atkinson-hyperlegible/AtkinsonHyperlegible-Regular.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Atkinson Hyperlegible";
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
font-display: swap;
|
||||
src: url(./assets/fonts/atkinson-hyperlegible/AtkinsonHyperlegible-Bold.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Atkinson Hyperlegible";
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
font-display: swap;
|
||||
src: url(./assets/fonts/atkinson-hyperlegible/AtkinsonHyperlegible-Italic.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Atkinson Hyperlegible";
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
font-display: swap;
|
||||
src: url(./assets/fonts/atkinson-hyperlegible/AtkinsonHyperlegible-BoldItalic.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Iosevka Zesty";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-display: swap;
|
||||
src: url(./assets/fonts/iosevka/iosevka-zesty.ttf);
|
||||
}
|
39
src/index.scss
Normal file
39
src/index.scss
Normal file
|
@ -0,0 +1,39 @@
|
|||
@import "./fonts.scss";
|
||||
|
||||
:root {
|
||||
font: 16px/1.5 "Atkinson Hyperlegible", Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
--background-1: #24262b;
|
||||
--background-2: #1e2024;
|
||||
--background-3: #191b1d;
|
||||
--background-4: #17181a;
|
||||
--foreground-1: #eae8efdd;
|
||||
--foreground-2: #eae8efb4;
|
||||
|
||||
color: var(--foreground-1);
|
||||
|
||||
// --background-1: #1a1923;
|
||||
// --background-2: #14131d;
|
||||
// --background-3: #100e18;
|
||||
// --background-4: #0a0e11;
|
||||
|
||||
/*
|
||||
--fg-text: #c7c6ca;
|
||||
--fg-link: #b18cf3;
|
||||
--fg-dimmed: #7f879b;
|
||||
*/
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
7
src/index.tsx
Normal file
7
src/index.tsx
Normal file
|
@ -0,0 +1,7 @@
|
|||
/* @refresh reload */
|
||||
import { render } from "solid-js/web";
|
||||
import "./index.scss";
|
||||
import App from "./App";
|
||||
|
||||
const root = document.getElementById("root");
|
||||
render(() => <App />, root!);
|
20
src/interesting.tsx
Normal file
20
src/interesting.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
function Dots(props: any) {
|
||||
return (
|
||||
<svg width="100%">
|
||||
<line stroke-dasharray="
|
||||
10, 10, 30, 10, 30, 10, 30, 30,
|
||||
10, 10, 30, 30,
|
||||
30, 10, 10, 10, 30, 10, 10, 30,
|
||||
30, 10, 10, 10, 30, 30,
|
||||
10, 10, 30, 10, 30, 30,
|
||||
10, 10, 30, 30,
|
||||
30, 10, 30, 10, 10, 30,
|
||||
30, 10, 30, 10, 30, 30,
|
||||
30, 10, 10, 30"
|
||||
x1="0" y1="5px" x2="100%" y2="5px"
|
||||
style="stroke: var(--background-2)" stroke-width="10"
|
||||
stroke-dashoffset={props.offset()}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
46
src/service.ts
Normal file
46
src/service.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
// A *service worker*, to cache and provide data offline
|
||||
|
||||
// self.addEventListener("install", () => {
|
||||
|
||||
// });
|
||||
|
||||
// self.addEventListener("fetch", (event) => {
|
||||
// // const { request } = event;
|
||||
// // if (request.url === "/" && request.method === "GET") {
|
||||
|
||||
// // event.respondWith(
|
||||
// // fetch(request).catch(function(error) {
|
||||
// // // `fetch()` throws an exception when the server is unreachable but not
|
||||
// // // for valid HTTP responses, even `4xx` or `5xx` range.
|
||||
// // console.error(
|
||||
// // '[onfetch] Failed. Serving cached offline fallback ' +
|
||||
// // error
|
||||
// // );
|
||||
// // return caches.open('offline').then(function(cache) {
|
||||
// // return cache.match('offline.html');
|
||||
// // });
|
||||
// // })
|
||||
// // );
|
||||
// // }
|
||||
// });
|
||||
|
||||
// /*
|
||||
// self.addEventListener('install', function(event) {
|
||||
// // Put `offline.html` page into cache
|
||||
// var offlineRequest = new Request('offline.html');
|
||||
// event.waitUntil(
|
||||
// fetch(offlineRequest).then(function(response) {
|
||||
// return caches.open('offline').then(function(cache) {
|
||||
// console.log('[oninstall] Cached offline page', response.url);
|
||||
// return cache.put(offlineRequest, response);
|
||||
// });
|
||||
// })
|
||||
// );
|
||||
// });
|
||||
|
||||
// self.addEventListener('push', function(event) {
|
||||
// event.waitUntil(
|
||||
// self.registration.showNotification(title, { body })
|
||||
// );
|
||||
// });
|
||||
// */
|
21
src/state.ts
Normal file
21
src/state.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
// (possibly persisted) state in the app
|
||||
|
||||
interface GlobalState {
|
||||
// - popups
|
||||
// - emoji picker
|
||||
// - recent rooms
|
||||
// - main is threads, timeline
|
||||
// - sidebar is members, timeline
|
||||
}
|
||||
|
||||
interface RoomState {
|
||||
// - scroll position
|
||||
// - new thread config
|
||||
// - uploading files
|
||||
}
|
||||
|
||||
interface ThreadState {
|
||||
// - scroll position
|
||||
// - message config (input, reply, edit)
|
||||
// - uploading files
|
||||
}
|
19
src/util.ts
Normal file
19
src/util.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
class Lru {
|
||||
private items: Array<string> = [];
|
||||
|
||||
constructor(
|
||||
public readonly max: number,
|
||||
private dispose: (id: string) => void,
|
||||
) {
|
||||
if (max <= 0) throw new Error("max can't be zero");
|
||||
}
|
||||
|
||||
touch(id: string) {
|
||||
const idx = this.items.indexOf(id);
|
||||
if (idx !== -1) this.items.splice(idx, 1);
|
||||
this.items.push(id);
|
||||
if (this.items.length > this.max) {
|
||||
this.dispose(this.items.shift()!);
|
||||
}
|
||||
}
|
||||
}
|
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" />
|
1
src/worker.ts
Normal file
1
src/worker.ts
Normal file
|
@ -0,0 +1 @@
|
|||
// A *shared worker*, to efficiently sync between multiple sessions?
|
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "solid-js",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
10
tsconfig.node.json
Normal file
10
tsconfig.node.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
6
vite.config.ts
Normal file
6
vite.config.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import solid from 'vite-plugin-solid'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [solid()],
|
||||
})
|
Loading…
Reference in a new issue