)
}
\ No newline at end of file
diff --git a/website/src/editor/themes/dark.ts b/website/src/editor/theme.ts
similarity index 97%
rename from website/src/editor/themes/dark.ts
rename to website/src/editor/theme.ts
index b2f6c85..ce59ba1 100644
--- a/website/src/editor/themes/dark.ts
+++ b/website/src/editor/theme.ts
@@ -1,6 +1,6 @@
import {HighlightStyle, tags as t} from '@codemirror/highlight'
-import {customTags} from '../java/language'
import {EditorView} from '@codemirror/view'
+import {customTags} from './java/language'
export const theme = EditorView.theme({
".cm-scroller": {
diff --git a/website/src/editor/themes/light.ts b/website/src/editor/themes/light.ts
deleted file mode 100644
index c7027db..0000000
--- a/website/src/editor/themes/light.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import {EditorView} from '@codemirror/view'
-import {HighlightStyle, tags as t} from '@codemirror/highlight'
-import {customTags} from '../java/language'
-
-export const theme = EditorView.theme({
- ".cm-scroller": {
- fontFamily: `'JetBrains Mono', 'Roboto Mono', Menlo, Monaco, source-code-pro, Consolas, monospace`
- },
- "&": {
- color: "#24292e",
- backgroundColor: "#f6f8fa",
- padding: "10px 0"
- },
- ".cm-matchingBracket": {
- color: "#2b2b2b",
- },
- ".cm-activeLineGutter, .cm-activeLine": {
- backgroundColor: "#f8f8f8"
- },
- ".cm-content": {
- caretColor: "#424242"
- },
- "&.cm-focused .cm-cursor": {
- borderLeftColor: "#b6b6b6"
- },
- "&.cm-focused .cm-selectionBackground, ::selection": {
- backgroundColor: "#a2a2a2"
- },
- ".cm-gutters": {
- backgroundColor: "#f6f8fa",
- color: "#ddd",
- border: "none"
- },
-}, {dark: false})
-
-export const highlighting = HighlightStyle.define([
- {tag: t.function(t.name), color: '#00627A'},
- {tag: t.standard(t.typeName), fontStyle: 'bold', color: '#E75A7C'},
- {tag: customTags.annotationAttribute, color: '#8250df'},
- {tag: t.typeName, fontStyle: 'bold', color: '#89D2DC'},
- {tag: t.function(customTags.call), color: '#502cc5'},
- {tag: t.constant(t.variableName), color: '#0550ae'},
- {tag: t.className, color: '#6564DB'},
- {tag: t.keyword, color: '#D73A49'},
- {tag: t.definitionKeyword, color: '#D73A49'},
- {tag: t.controlKeyword, color: '#D73A49'},
- {tag: t.operatorKeyword, color: '#D73A49'},
- {tag: t.annotation, color: "#9E880D"},
- {tag: t.function(t.definition(t.variableName)), color: '#871094'},
- {tag: t.definition(t.variableName), color: "#6f42c1"},
- {tag: t.constant, color: '#005cc5'},
- {tag: t.operator, color: '#D73A49'},
- {tag: t.number, color: '#1750EB'},
- {tag: t.string, color: '#067D17'},
- {tag: t.comment, color: '#6a737d'}
-])
diff --git a/website/src/editor/themes/themes.ts b/website/src/editor/themes/themes.ts
deleted file mode 100644
index 283a913..0000000
--- a/website/src/editor/themes/themes.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import * as darkTheme from './dark'
-import * as lightTheme from './light'
-import {ThemeTable} from '../../theme/ThemeContext'
-import {Extension} from '@codemirror/state'
-
-export const editorThemes: ThemeTable
= {
- light: lightTheme.theme,
- dark: darkTheme.theme
-}
-
-export const highlightingThemes: ThemeTable = {
- light: lightTheme.highlighting,
- dark: darkTheme.highlighting
-}
diff --git a/website/src/header/Header.style.ts b/website/src/header/Header.style.ts
deleted file mode 100644
index 4a76e51..0000000
--- a/website/src/header/Header.style.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import styled from "styled-components";
-import {Header as AntdHeader} from "antd/lib/layout/layout";
-import {Menu as AntdMenu} from "antd";
-import themed from '../theme/themed'
-
-export const Header = styled(AntdHeader)`
- box-shadow: ${themed('header.shadow')};
- background: ${themed('header.background')} !important;
- ul {
- background: none;
- }
- .ant-menu-horizontal {
- border-bottom: none;
- }
-`
-
-export const Menu = styled(AntdMenu)`
- margin-right: auto;
-`
\ No newline at end of file
diff --git a/website/src/header/Header.tsx b/website/src/header/Header.tsx
deleted file mode 100644
index 2e41dfb..0000000
--- a/website/src/header/Header.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import {Button, Space} from "antd";
-import {Logo} from "./Logo";
-import * as icons from "@ant-design/icons";
-import React from "react";
-import * as Styled from './Header.style'
-import {useTranslation} from "react-i18next";
-import ToggleButton from '../theme/ToggleButton'
-import {GithubOutlined} from '@ant-design/icons'
-
-export interface HeaderProperties {
- onShare?: () => void
-}
-
-export default function Header(properties: HeaderProperties) {
- const {t} = useTranslation()
- return (
-
-
-
-
-
-
-
-
-
- )
-}
\ No newline at end of file
diff --git a/website/src/header/Logo.style.ts b/website/src/header/Logo.style.ts
deleted file mode 100644
index 4828d36..0000000
--- a/website/src/header/Logo.style.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import styled from "styled-components";
-
-export const Logo = styled.span`
- span:first-child {
- color: #00758f;
- }
-
- span:last-child {
- color: #f29110;
- }
-
- cursor: pointer;
- font-size: 30px;
- font-weight: bolder;
- font-family: 'Titillium Web', Roboto, 'Helvetica Neue', 'Noto Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol' !important;
-`
diff --git a/website/src/header/Logo.tsx b/website/src/header/Logo.tsx
deleted file mode 100644
index bd098d3..0000000
--- a/website/src/header/Logo.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from "react";
-
-import * as Styled from './Logo.style'
-import {useHistory} from 'react-router-dom'
-
-export function Logo() {
- const history = useHistory()
- return (
- history.push('/')}>
- Java
- Sheets
-
- )
-}
diff --git a/website/src/index.css b/website/src/index.css
index bf43771..cfc2412 100644
--- a/website/src/index.css
+++ b/website/src/index.css
@@ -1,6 +1,27 @@
@import url('https://fonts.googleapis.com/css2?family=Titillium+Web:ital,wght@0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300&display=swap');
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
+:root {
+ --header-bg: #0B0B0B;
+ --card-border-color: #323232;
+ --text-color: #fff;
+ --primary-fg: #fff;
+ --primary-bg: #2051FF;
+ --primary-hover-bg: #2f5cfd;
+
+ --secondary-fg: #fff;
+ --secondary-bg: #212121;
+ --secondary-hover-bg: #313131;
+
+ --danger-fg: #FF2D52;
+ --danger-bg: #FF2D5219;
+ --danger-hover-bg: #FF4E6D19;
+
+ --snippet-header-bg: #161616;
+ --snippet-doc-bg: #1E1E1E;
+ --snippet-code-bg: #111111;
+}
+
html {
height: 100% !important;
}
@@ -8,8 +29,8 @@ html {
body {
margin: 0;
font-family: 'Titillium Web', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
- sans-serif;
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100% !important;
diff --git a/website/src/index.tsx b/website/src/index.tsx
index 8b87f76..50aa370 100644
--- a/website/src/index.tsx
+++ b/website/src/index.tsx
@@ -1,33 +1,28 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import './index.css';
-import App from './App';
-import reportWebVitals from './reportWebVitals';
+import React from 'react'
+import ReactDOM from 'react-dom'
+import './index.css'
+import App from './App'
+import reportWebVitals from './reportWebVitals'
import {Provider} from 'react-redux'
import store from './store'
import {I18nextProvider} from 'react-i18next'
import i18n from './i18n'
-import ThemeProvider, {detectTheme, installThemes} from './theme/ThemeContext'
import {BrowserRouter} from 'react-router-dom'
-installThemes().then(() => {
- ReactDOM.render(
-
-
- >}>
-
-
-
-
-
-
-
-
-
- ,
- document.getElementById('root')
- );
-})
+ReactDOM.render(
+
+
+ >}>
+
+
+
+
+
+
+
+ ,
+ document.getElementById('root')
+)
reportWebVitals().catch(details => {
console.log({error: 'failed to report web vitals', details})
diff --git a/website/src/layout/Footer.module.css b/website/src/layout/Footer.module.css
new file mode 100644
index 0000000..e69de29
diff --git a/website/src/layout/Footer.tsx b/website/src/layout/Footer.tsx
new file mode 100644
index 0000000..54662d2
--- /dev/null
+++ b/website/src/layout/Footer.tsx
@@ -0,0 +1,40 @@
+import version from '../version'
+
+import styles from './Footer.module.css'
+import GithubFillIcon from 'remixicon-react/GithubFillIcon'
+import GitBranchFillIcon from 'remixicon-react/GitBranchFillIcon'
+
+export default function Footer() {
+ return (
+
+ )
+}
+
+const DiscordIcon = () => (
+
+)
\ No newline at end of file
diff --git a/website/src/layout/Header.module.css b/website/src/layout/Header.module.css
new file mode 100644
index 0000000..fbf2284
--- /dev/null
+++ b/website/src/layout/Header.module.css
@@ -0,0 +1,50 @@
+.header {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 2em;
+ background: var(--header-bg);
+ border-bottom: 0.2em solid var(--card-border-color);
+}
+
+.branding {
+ display: flex;
+ flex-direction: column;
+}
+
+.logo {
+ color: var(--text-color);
+ text-transform: uppercase;
+ display: flex;
+ align-items: center;
+ font-size: 2em;
+}
+
+.slogan {
+ font-size: 0.8em;
+ font-weight: 500;
+}
+
+.logo strong {
+ font-weight: 700;
+}
+
+.logo span {
+ font-weight: 300;
+}
+
+.share-button {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 0.5em;
+ padding: 0.5em 1em;
+ border-radius: 50em;
+ color: var(--primary-fg);
+ background: var(--primary-bg);
+ transition: .2s ease-in background-color;
+}
+
+.share-button:hover {
+ background: var(--primary-hover-bg);
+}
\ No newline at end of file
diff --git a/website/src/layout/Header.tsx b/website/src/layout/Header.tsx
new file mode 100644
index 0000000..bd1233d
--- /dev/null
+++ b/website/src/layout/Header.tsx
@@ -0,0 +1,30 @@
+import React from 'react'
+import {useTranslation} from 'react-i18next'
+import styles from './Header.module.css'
+
+export interface HeaderProps {
+ onShare?: () => void
+}
+
+export default function Header(props: HeaderProps) {
+ const {t} = useTranslation()
+ return (
+
+
+
+ Java
+ Sheets
+
+
+ The online JShell editor
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/website/src/layout/Page.module.css b/website/src/layout/Page.module.css
new file mode 100644
index 0000000..e69de29
diff --git a/website/src/layout/Page.tsx b/website/src/layout/Page.tsx
new file mode 100644
index 0000000..434622f
--- /dev/null
+++ b/website/src/layout/Page.tsx
@@ -0,0 +1,20 @@
+import React from 'react'
+import Header from './Header'
+import Footer from './Footer'
+
+import styles from './Page.module.css'
+
+export interface PageProps {
+ children?: React.ReactNode
+ onShare?: () => void
+}
+
+export default function Page(props: PageProps) {
+ return (
+
+
+ )
+}
\ No newline at end of file
diff --git a/website/src/sheet/ImportedSheet.tsx b/website/src/sheet/ImportedSheet.tsx
index 5812151..0216d2f 100644
--- a/website/src/sheet/ImportedSheet.tsx
+++ b/website/src/sheet/ImportedSheet.tsx
@@ -7,7 +7,6 @@ import Client from '../client'
import styled from 'styled-components'
import {LoadingOutlined} from '@ant-design/icons'
import {Empty} from 'antd'
-import themed from '../theme/themed'
interface ImportedSheetProperties {
evaluating?: boolean
@@ -52,7 +51,6 @@ const Centered = styled.div`
const ErrorBox = styled.div`
margin: 50px auto auto auto;
- background: ${themed('snippet.card.background')};
border-radius: 5px;
padding: 20px;
max-width: 300px;
@@ -61,7 +59,6 @@ const ErrorBox = styled.div`
width: 100%;
}
code {
- background: ${themed('snippet.card.icon.background')};
width: 100%;
padding: 10px;
}
diff --git a/website/src/sheet/ShareModal.tsx b/website/src/sheet/ShareModal.tsx
index f8a7747..8de8cb0 100644
--- a/website/src/sheet/ShareModal.tsx
+++ b/website/src/sheet/ShareModal.tsx
@@ -1,7 +1,6 @@
import {Button} from 'antd'
import {CheckOutlined, CopyOutlined, ShareAltOutlined} from '@ant-design/icons'
import * as Styled from './ShareModal.style'
-import * as StyledTitle from './snippet/Title.style'
import useTimedFlag from '../util/useTimedFlag'
import copy from 'copy-to-clipboard'
@@ -18,7 +17,7 @@ function createLink(sheetId: string) {
export default function ShareModal(properties: ShareModalProperties) {
const [clicked, setClicked] = useTimedFlag(false, 1000)
- const link =createLink(properties.sheetId || 'none')
+ const link = createLink(properties.sheetId || 'none')
const onClick = () => {
setClicked(true)
@@ -27,12 +26,7 @@ export default function ShareModal(properties: ShareModalProperties) {
return (
-
- Share
-
- }
+ title="Share"
visible={properties.visible}
onCancel={() => properties.onVisibilityChange?.(false)}
onOk={() => properties.onVisibilityChange?.(false)}
diff --git a/website/src/sheet/Sheet.tsx b/website/src/sheet/Sheet.tsx
index a45ae33..5dede96 100644
--- a/website/src/sheet/Sheet.tsx
+++ b/website/src/sheet/Sheet.tsx
@@ -13,10 +13,11 @@ import {useDispatch} from 'react-redux'
import {StartEvaluationRequest} from "@jsheets/protocol/src/jsheets/api/snippet_runtime_pb";
import {reorderSnippet} from './state'
import {listSnippetsInState, SheetState, SnippetState} from './index'
-import Snippet, {SnippetReference} from './snippet/Snippet'
+import SnippetContext from './snippet/Snippet'
import {useDraggableIds} from './snippet/draggableId'
+import {SnippetReference} from './snippet/SnippetX'
-const MemoizedSnippet = React.memo(Snippet)
+const MemoizedSnippet = React.memo(SnippetContext)
function useReorder(): (result: DropResult) => void {
const dispatch = useDispatch()
diff --git a/website/src/sheet/evaluation.ts b/website/src/sheet/evaluation.ts
new file mode 100644
index 0000000..523d6ea
--- /dev/null
+++ b/website/src/sheet/evaluation.ts
@@ -0,0 +1,8 @@
+import {atom, useAtom} from 'jotai'
+
+const isEvaluatingAtom = atom(false)
+
+export function useIsEvaluating(): boolean {
+ const [state] = useAtom(isEvaluatingAtom)
+ return state
+}
\ No newline at end of file
diff --git a/website/src/sheet/snippet/Snippet.module.css b/website/src/sheet/snippet/Snippet.module.css
new file mode 100644
index 0000000..e94cd58
--- /dev/null
+++ b/website/src/sheet/snippet/Snippet.module.css
@@ -0,0 +1,56 @@
+.snippet {
+ overflow: hidden;
+ border-radius: 0.5em;
+}
+
+.snippet-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background-color: var(--snippet-header-bg);
+ border-bottom: 0.2em solid var(--card-border-color);
+}
+
+.snippet-header-title {
+ font-size: 1.2em;
+ font-weight: 500;
+ user-select: inherit;
+}
+
+.snippet-header-actions {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ gap: 0.5em;
+}
+
+.edit-button, .run-button {
+ font-size: 0.9em;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 0.5em;
+ padding: 0.5em 1em;
+ border-radius: 0.2em;
+ transition: .2s ease-in background-color;
+}
+
+.run-button {
+ color: var(--primary-fg);
+ background: var(--primary-bg);
+}
+
+.run-button:hover {
+ background: var(--primary-hover-bg);
+}
+
+.edit-button {
+ color: var(--secondary-fg);
+ background: var(--secondary-bg);
+}
+
+.edit-button:hover {
+ background: var(--secondary-hover-bg);
+}
+
+.snippet-body { }
\ No newline at end of file
diff --git a/website/src/sheet/snippet/Snippet.style.ts b/website/src/sheet/snippet/Snippet.style.ts
deleted file mode 100644
index 9b72f53..0000000
--- a/website/src/sheet/snippet/Snippet.style.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import styled from 'styled-components'
-import themed from '../../theme/themed'
-
-export const Card = styled.div`
- max-width: 1000px;
- margin: 50px auto 0 auto;
- overflow: hidden;
- box-shadow: ${themed('snippet.card.shadow')};
- border: none;
- border-radius: 3px;
- background: ${themed('background')};
- &:first-child {
- margin-top: 0;
- }
-`
-
-export const CardHead = styled.div`
- padding: 0 15px;
- height: 70px;
- box-shadow: ${themed('snippet.card.head.shadow')} !important;
- display: flex;
-`
-
-export const CardBody = styled.div`
- padding: 0;
- overflow: hidden;
-`
\ No newline at end of file
diff --git a/website/src/sheet/snippet/Snippet.tsx b/website/src/sheet/snippet/Snippet.tsx
index 0608a5b..49788f9 100644
--- a/website/src/sheet/snippet/Snippet.tsx
+++ b/website/src/sheet/snippet/Snippet.tsx
@@ -1,192 +1,40 @@
-import * as Styled from './Snippet.style'
-import {ExperimentOutlined} from '@ant-design/icons'
import React from 'react'
-import Title from './Title'
-import {UseSnippet, useSnippet} from '../useSheet'
-import {SnippetState} from '../index'
-import ComponentList from "./component/ComponentList";
-import SnippetExtras from "./SnippetExtras";
-import {
- SnippetComponentListRef,
- SnippetComponentReference
-} from './component/reference'
-import * as SnippetProtocol from "@jsheets/protocol/src/jsheets/api/snippet_pb";
-import {StartEvaluationRequest} from "@jsheets/protocol/src/jsheets/api/snippet_runtime_pb";
-import createEvaluateRequest from './createEvaluateREquest'
-
-export interface SnippetPosition {
- highestOrder: number
- lowestOrder: number
- moveUp: () => void
- moveDown: () => void
-}
-
-interface ExistingSnippetProperties extends UseSnippet {
- sheetId: string
- snippet: SnippetState
- position: SnippetPosition
- headProperties?: any
- isCooldown?: boolean
- running?: boolean
- evaluate: (request: StartEvaluationRequest) => void
- capture?: (reference: SnippetReference) => void
-}
-
-const MemoizedTitle = React.memo(Title)
-const MemoizedExtras = React.memo(SnippetExtras)
-
-interface ExistingSnippetState {
- editingTitle: boolean
-}
-
-export interface SnippetReference {
- listSources(): Map
- serialize(): SnippetProtocol.Snippet
-}
-
-class ExistingSnippet
- extends React.Component
- implements SnippetReference {
-
- private readonly componentsReferences: React.MutableRefObject
- = React.createRef()
-
- constructor(props: ExistingSnippetProperties) {
- super(props)
- this.componentsReferences.current = {components: new Map()}
- this.state = {editingTitle: props.snippet.title === ''}
- }
-
- changeEditingTitle = (target: boolean) => {
- if (!target && this.props.snippet.title === '') {
- this.props.changeDetails({title: 'None'})
- }
- this.setState({editingTitle: target})
- }
-
- componentDidMount() {
- this.props.capture?.(this)
- }
-
- render() {
- return (
-
-
- }
- onChange={title => this.props.changeDetails({title})}
- onEditingChange={this.changeEditingTitle}
- text={this.props.snippet.title}
- />
-
-
-
-
-
-
- )
- }
-
- private registerComponent = (id: string, reference: SnippetComponentReference) => {
- this.componentsReferences.current?.components.set(id, reference)
- }
-
- run = () => {
- const start = createEvaluateRequest(
- this.props.sheetId,
- this.props.snippet,
- this.listSources('code')
- )
- this.props.evaluate(start)
- }
-
- serialize = () => {
- const message = new SnippetProtocol.Snippet()
- message.setId(this.props.snippet.id)
- message.setName(this.props.snippet.title)
- message.setOrder(this.props.snippet.order)
- message.setComponentsList(this.serializeComponents())
- return message
- }
-
- serializeComponents = () => {
- const references = this.componentsReferences.current?.components
- const output: SnippetProtocol.Snippet.Component[] = []
- if (!references) {
- return output
- }
- for (const component of Object.values(this.props.snippet.components)) {
- const reference = references.get(component.id)
- const serialized = reference?.serialize()
- if (serialized) {
- serialized.setOrder(component.order)
- output.push(serialized)
- }
- }
- return output
- }
-
- listSources = (type?: 'text' | 'code'): Map => {
- const componentRefTable = this.componentsReferences.current?.components
- if (!componentRefTable) {
- return new Map()
- }
- const sources = new Map()
- for (const component of Object.values(this.props.snippet.components)) {
- if (component.type !== type) {
- continue
- }
- const ref = componentRefTable.get(component.id)
- const source = ref?.content()
- if (source) {
- sources.set(component.id, source)
- }
- }
- return sources
- }
-}
-
-export interface SnippetProperties {
- id: string
- sheetId: string
- position: SnippetPosition
- dragHandleProps?: any
- running?: boolean
- isCooldown?: boolean
- evaluate: (request: StartEvaluationRequest) => void
- capture?: (reference: SnippetReference) => void
-}
-
-export default function Snippet(properties: SnippetProperties) {
- const snippetContext = useSnippet(properties.id)
- if (!snippetContext.snippet) {
- return <>>
- }
+import SnippetContext from './SnippetContext'
+import styles from './Snippet.module.css'
+import ComponentList from './component/ComponentList'
+import TerminalLineIcon from 'remixicon-react/TerminalLineIcon'
+import AddLineIcon from 'remixicon-react/AddLineIcon'
+import CloseLineIcon from 'remixicon-react/CloseLineIcon'
+
+export default function Snippet() {
+ const {snippet} = React.useContext(SnippetContext)
return (
-
+
+
+
+ {snippet.title}
+
+
+
+
+
+
+
+
+
+
+
+
)
}
\ No newline at end of file
diff --git a/website/src/sheet/snippet/SnippetContext.tsx b/website/src/sheet/snippet/SnippetContext.tsx
new file mode 100644
index 0000000..d1d2e79
--- /dev/null
+++ b/website/src/sheet/snippet/SnippetContext.tsx
@@ -0,0 +1,13 @@
+import {ComponentState, SnippetState} from '../index'
+import React from 'react'
+
+export interface SnippetContextProps {
+ snippet: SnippetState
+ addComponent?: (component: Partial) => void
+ removeComponent?: (id: string) => void
+ setEditingTitle?: (state: boolean) => void
+ changeTitle?: (target: string) => void
+}
+
+export default React.createContext(
+ {snippet: {id: '', title: '', components: {}, order: 0}})
\ No newline at end of file
diff --git a/website/src/sheet/snippet/SnippetExtras.style.ts b/website/src/sheet/snippet/SnippetExtras.style.ts
deleted file mode 100644
index aafb98e..0000000
--- a/website/src/sheet/snippet/SnippetExtras.style.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import styled from "styled-components";
-import {Space} from "antd";
-
-export const SnippetExtras = styled(Space)`
- margin: auto 0 auto auto;
- & > .ant-button {
- cursor: pointer !important;
- }
-`
diff --git a/website/src/sheet/snippet/SnippetExtras.tsx b/website/src/sheet/snippet/SnippetExtras.tsx
deleted file mode 100644
index 217e2c2..0000000
--- a/website/src/sheet/snippet/SnippetExtras.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-import {Button, Dropdown, Menu} from "antd";
-import {
- ClockCircleOutlined,
- CodeOutlined,
- CommentOutlined, DeleteOutlined, EditOutlined,
- FireOutlined, MoreOutlined,
- PlusOutlined
-} from "@ant-design/icons";
-import * as Styled from "./SnippetExtras.style";
-import React from "react";
-import {ComponentState} from "../index";
-import {TFunction} from 'i18next'
-import {useTranslation} from 'react-i18next'
-
-type AddComponent = (component: Partial) => void
-
-export interface SnippetExtrasProperties {
- editingTitle: boolean
- setEditingTitle: (state: boolean) => void
- delete: () => void
- addComponent: AddComponent
- onRun: () => void
- running?: boolean
- isCooldown?: boolean
-}
-
-const AddMenu: React.FC<{addComponent: AddComponent, t: TFunction}> = ({addComponent, t}) => (
-
- }
- onClick={() => addComponent({type: 'code'})}
- >{t('snippet.menu.add.code')}
- }
- onClick={() => addComponent({type: 'text'})}
- >{t('snippet.menu.add.comment')}
-
- }>
-
-
-)
-
-const EditMenu: React.FC = properties => (
-
- }
- onClick={() => properties.setEditingTitle(!properties.editingTitle)}
- >{properties.t('snippet.menu.edit.rename')}
- }
- onClick={properties.delete}
- >{properties.t('snippet.menu.edit.delete')}
-
- }>
-
-)
-
-export default function SnippetExtras(properties: SnippetExtrasProperties) {
- const {t} = useTranslation()
- return (
-
-
- : }
- disabled={properties.isCooldown}
- loading={properties.running}
- onClick={properties.onRun}
- >{t('snippet.menu.run.button')}
-
-
- )
-}
\ No newline at end of file
diff --git a/website/src/sheet/snippet/SnippetX.tsx b/website/src/sheet/snippet/SnippetX.tsx
new file mode 100644
index 0000000..e6275c1
--- /dev/null
+++ b/website/src/sheet/snippet/SnippetX.tsx
@@ -0,0 +1,188 @@
+import {ExperimentOutlined} from '@ant-design/icons'
+import React from 'react'
+import {UseSnippet, useSnippet} from '../useSheet'
+import {SnippetState} from '../index'
+import ComponentList from './component/ComponentList'
+import {
+ SnippetComponentListRef,
+ SnippetComponentReference
+} from './component/reference'
+import * as SnippetProtocol from '@jsheets/protocol/src/jsheets/api/snippet_pb'
+import {
+ StartEvaluationRequest
+} from '@jsheets/protocol/src/jsheets/api/snippet_runtime_pb'
+import createEvaluationRequest from './createEvaluationRequest'
+import styles from './Snippet.module.css'
+import SnippetContext from './SnippetContext'
+
+export interface SnippetPosition {
+ highestOrder: number
+ lowestOrder: number
+ moveUp: () => void
+ moveDown: () => void
+}
+
+interface ExistingSnippetProperties extends UseSnippet {
+ sheetId: string
+ snippet: SnippetState
+ position: SnippetPosition
+ headProperties?: any
+ isCooldown?: boolean
+ running?: boolean
+ evaluate: (request: StartEvaluationRequest) => void
+ capture?: (reference: SnippetReference) => void
+}
+
+interface ExistingSnippetState {
+ editingTitle: boolean
+}
+
+export interface SnippetReference {
+ listSources(): Map
+
+ serialize(): SnippetProtocol.Snippet
+}
+
+class ExistingSnippet
+ extends React.Component
+ implements SnippetReference {
+
+ private readonly componentsReferences: React.MutableRefObject
+ = React.createRef()
+
+ constructor(props: ExistingSnippetProperties) {
+ super(props)
+ this.componentsReferences.current = {components: new Map()}
+ this.state = {editingTitle: props.snippet.title === ''}
+ }
+
+ changeEditingTitle = (target: boolean) => {
+ if (!target && this.props.snippet.title === '') {
+ this.props.changeDetails({title: 'None'})
+ }
+ this.setState({editingTitle: target})
+ }
+
+ componentDidMount() {
+ this.props.capture?.(this)
+ }
+
+ render() {
+ const {snippet} = React.useContext(SnippetContext)
+ return (
+
+
+
{snippet.title}
+
+
+
+
+
+
+
+
+ )
+ }
+
+ private registerComponent = (id: string, reference: SnippetComponentReference) => {
+ this.componentsReferences.current?.components.set(id, reference)
+ }
+
+ run = () => {
+ const start = createEvaluationRequest(
+ this.props.sheetId,
+ this.props.snippet,
+ this.listSources('code')
+ )
+ this.props.evaluate(start)
+ }
+
+ serialize = () => {
+ const message = new SnippetProtocol.Snippet()
+ message.setId(this.props.snippet.id)
+ message.setName(this.props.snippet.title)
+ message.setOrder(this.props.snippet.order)
+ message.setComponentsList(this.serializeComponents())
+ return message
+ }
+
+ serializeComponents = () => {
+ const references = this.componentsReferences.current?.components
+ const output: SnippetProtocol.Snippet.Component[] = []
+ if (!references) {
+ return output
+ }
+ for (const component of Object.values(this.props.snippet.components)) {
+ const reference = references.get(component.id)
+ const serialized = reference?.serialize()
+ if (serialized) {
+ serialized.setOrder(component.order)
+ output.push(serialized)
+ }
+ }
+ return output
+ }
+
+ listSources = (type?: 'text' | 'code'): Map => {
+ const componentRefTable = this.componentsReferences.current?.components
+ if (!componentRefTable) {
+ return new Map()
+ }
+ const sources = new Map()
+ for (const component of Object.values(this.props.snippet.components)) {
+ if (component.type !== type) {
+ continue
+ }
+ const ref = componentRefTable.get(component.id)
+ const source = ref?.content()
+ if (source) {
+ sources.set(component.id, source)
+ }
+ }
+ return sources
+ }
+}
+
+export interface SnippetProperties {
+ id: string
+ sheetId: string
+ position: SnippetPosition
+ dragHandleProps?: any
+ running?: boolean
+ isCooldown?: boolean
+ evaluate: (request: StartEvaluationRequest) => void
+ capture?: (reference: SnippetReference) => void
+}
+
+export default function SnippetX(properties: SnippetProperties) {
+ const snippetContext = useSnippet(properties.id)
+ if (!snippetContext.snippet) {
+ return <>>
+ }
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/website/src/sheet/snippet/Title.style.tsx b/website/src/sheet/snippet/Title.style.tsx
deleted file mode 100644
index 4b8c891..0000000
--- a/website/src/sheet/snippet/Title.style.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import styled from 'styled-components'
-import {Input as AntdInput} from 'antd'
-import themed from '../../theme/themed'
-
-export const Title = styled.span`
- display: flex;
- font-size: 18px;
- cursor: default !important;
- & > * {
- margin: auto 0 auto 0;
- }
-`
-
-export const IconBox = styled.span`
- padding: 8px;
- display: flex;
- border-radius: 5px;
- margin: auto 5px auto 0;
- background: ${themed('snippet.card.icon.background')};
- .anticon {
- color: ${themed('base.primary')};
- margin: auto 0;
- }
-`
-
-export const Input = styled(AntdInput)`
- margin: auto 10px auto 5px !important;
- max-width: 300px;
- height: 34px !important;
- padding: 2px !important;
- font-size: 18px !important;
- border: none !important;
- outline: none !important;
- &:focus {
- outline: none !important;
- }
-`
-
-export const Text = styled.span`
- margin: auto 0 auto 7px;
- cursor: text;
- user-select: text !important;
-`
\ No newline at end of file
diff --git a/website/src/sheet/snippet/Title.tsx b/website/src/sheet/snippet/Title.tsx
deleted file mode 100644
index 5171be1..0000000
--- a/website/src/sheet/snippet/Title.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import React, {useEffect} from 'react'
-import * as Styled from './Title.style'
-import {Input} from 'antd'
-
-interface TitleProperties {
- icon: React.ReactNode
- text: string
- editing: boolean
- onEditingChange?: (target: boolean) => void
- onChange?: (text: string) => void
-}
-
-export default function Title(properties: TitleProperties) {
- const ref = React.createRef()
- useEffect(() => {
- if (properties.editing) {
- ref.current?.focus()
- }
- }, [ref, properties.editing])
- return (
-
- {properties.icon}
- {properties.editing
- ? properties.onEditingChange?.(false)}
- onChange={event => properties.onChange?.(event.target.value)}
- />
- : properties.onEditingChange?.(true)}>{properties.text}
- }
-
-
- )
-}
diff --git a/website/src/sheet/snippet/component/ComponentContainer.style.ts b/website/src/sheet/snippet/component/ComponentContainer.style.ts
index ef5587a..5b8f96e 100644
--- a/website/src/sheet/snippet/component/ComponentContainer.style.ts
+++ b/website/src/sheet/snippet/component/ComponentContainer.style.ts
@@ -1,6 +1,5 @@
-import styled from "styled-components";
-import {Button} from "antd";
-import themed from '../../../theme/themed'
+import styled from 'styled-components'
+import {Button} from 'antd'
export const ComponentOptions = styled.div`
z-index: 20;
@@ -11,6 +10,7 @@ export const ComponentOptions = styled.div`
transition: all .1s ease-in-out;
right: -10px;
opacity: 0;
+
& > :not(:first-child) {
margin-left: 5px;
}
@@ -23,10 +23,10 @@ export const ComponentInputArea = styled.div`
export const Component = styled.div`
min-height: 45px; // To include the options
transition: opacity .1s ease-in, box-shadow .1s ease-in;
- background: ${themed('snippet.component.background')};
+ background: var(--snippet-code-bg);
&:not(:first-child) {
- border-top: ${themed('snippet.component.borderTop')};
+ border-top: var(--card-border-color);
}
&.dragging-component {
@@ -48,6 +48,7 @@ export const DragHandle = styled.div`
border-radius: 2px;
font-size: 15px;
transition: background-color .2s ease-in;
+
&:hover {
background: rgba(250, 250, 250, 0.5);
}
diff --git a/website/src/sheet/snippet/component/ComponentList.tsx b/website/src/sheet/snippet/component/ComponentList.tsx
index f42fb09..77b78f3 100644
--- a/website/src/sheet/snippet/component/ComponentList.tsx
+++ b/website/src/sheet/snippet/component/ComponentList.tsx
@@ -47,7 +47,7 @@ function useComponents(
)
}
-export default function ComponentList(properties: ComponentListProperties) {
+export default function ComponentList() {
const reorder = useReorder(properties.snippetId)
const components = useComponents(properties.components, properties.capture)
const draggableId = useDraggableId(properties.snippetId)
diff --git a/website/src/sheet/snippet/component/TextComponent.style.ts b/website/src/sheet/snippet/component/TextComponent.style.ts
index 0eb7c67..82fa5b0 100644
--- a/website/src/sheet/snippet/component/TextComponent.style.ts
+++ b/website/src/sheet/snippet/component/TextComponent.style.ts
@@ -1,6 +1,5 @@
import styled, {createGlobalStyle} from 'styled-components'
import RichMarkdownEditor from 'rich-markdown-editor'
-import themed from '../../../theme/themed'
export const TextComponent = styled.div`
padding: 0;
@@ -19,9 +18,9 @@ export const Editor = styled(RichMarkdownEditor)`
:first-child {
padding: 10px !important;
}
- background: ${themed('snippet.textComponent.background')} !important;
+ background: var(--snippet-doc-bg) !important;
* > div {
- background: ${themed('snippet.textComponent.background')} !important;
+ background: var(--snippet-doc-bg) !important;
}
& > :first-child h1 {
margin: 0 0 0.25em;
diff --git a/website/src/sheet/snippet/component/TextComponent.tsx b/website/src/sheet/snippet/component/TextComponent.tsx
index 4141369..acd284a 100644
--- a/website/src/sheet/snippet/component/TextComponent.tsx
+++ b/website/src/sheet/snippet/component/TextComponent.tsx
@@ -3,9 +3,7 @@ import React from 'react'
import {EditorComponentProperties} from './EditorComponent'
import {SnippetComponentReference} from './reference'
import RichMarkdownEditor from 'rich-markdown-editor'
-import {ThemeContext} from '../../../theme/ThemeContext'
import * as SnippetProtocol from '@jsheets/protocol/src/jsheets/api/snippet_pb'
-import * as jspb from "google-protobuf";
export interface TextComponentProperties {
value: string
@@ -21,8 +19,6 @@ export default class TextComponent
extends React.Component
implements SnippetComponentReference {
- static contextType = ThemeContext
-
private readonly editorRef = React.createRef()
render() {
diff --git a/website/src/sheet/snippet/createEvaluateREquest.ts b/website/src/sheet/snippet/createEvaluationRequest.ts
similarity index 96%
rename from website/src/sheet/snippet/createEvaluateREquest.ts
rename to website/src/sheet/snippet/createEvaluationRequest.ts
index ce95371..bc37c72 100644
--- a/website/src/sheet/snippet/createEvaluateREquest.ts
+++ b/website/src/sheet/snippet/createEvaluationRequest.ts
@@ -2,7 +2,7 @@ import {findComponentByIdInState, SnippetState} from '../index'
import * as EvaluationProtocol from '@jsheets/protocol/src/jsheets/api/snippet_runtime_pb'
import * as SnippetProtocol from '@jsheets/protocol/src/jsheets/api/snippet_pb'
-export default function createEvaluateRequest(
+export default function createEvaluationRequest(
sheetId: string,
snippet: SnippetState,
componentSources: Map
diff --git a/website/src/theme/ThemeContext.tsx b/website/src/theme/ThemeContext.tsx
deleted file mode 100644
index 2562487..0000000
--- a/website/src/theme/ThemeContext.tsx
+++ /dev/null
@@ -1,122 +0,0 @@
-import React, {useEffect, useState} from 'react'
-import {insertPrefetches, updateTheme} from './updateTheme'
-import {light as lightTheme, dark as darkTheme} from './theme'
-import {
- DefaultTheme,
- ThemeProvider as StyledThemeProvider
-} from 'styled-components'
-
-export type ThemeKey = 'light' | 'dark'
-
-type UpdateTheme = React.Dispatch>
-
-export interface ThemeContextProperties {
- theme: ThemeKey
- update: UpdateTheme
-}
-
-export const ThemeContext = React.createContext(
- {
- theme: 'light',
- update: () => 'light'
- }
-)
-
-export function useTheme(): [ThemeKey, UpdateTheme] {
- const {theme, update} = React.useContext(ThemeContext)
- return [theme, update]
-}
-
-type ToggleTheme = () => void
-
-export function useThemeToggle(): ToggleTheme {
- const [, update] = useTheme()
- return () => update((current: ThemeKey) => toggle(current))
-}
-
-function toggle(current: ThemeKey): ThemeKey {
- return current === 'light' ? 'dark' : 'light'
-}
-
-export interface ThemeProviderProperties {
- initialTheme?: ThemeKey
- children: React.ReactNode
-}
-
-export type ThemeTable = Record
-
-const themeSources: ThemeTable = {
- light: '/theme/light.css',
- dark: '/theme/dark.css'
-}
-
-const themeVariables: ThemeTable = {
- light: lightTheme,
- dark: darkTheme
-}
-
-const themeStorageKey = 'jsheets.theme'
-
-export function detectTheme(): ThemeKey {
- try {
- const stored = localStorage.getItem(themeStorageKey)
- if (stored) {
- return stored === 'dark' ? 'dark' : 'light'
- }
- } catch (error) {
- console.error({message: 'failed to detect theme', error})
- }
- return selectUserPreference()
-}
-
-function storeTheme(theme: ThemeKey) {
- try {
- localStorage.setItem(themeStorageKey, theme)
- } catch (error) {
- console.error({message: 'failed to store theme', error})
- }
-}
-
-function selectUserPreference(): ThemeKey {
- const prefersDark = window.matchMedia &&
- window.matchMedia('(prefers-color-scheme: dark)').matches
- return prefersDark ? 'dark' : 'light'
-}
-
-export async function installThemes(): Promise {
- return new Promise(resolve => {
- const initial = detectTheme()
- insertPrefetches(themeSources)
- updateTheme(initial, themeSources[initial], () => resolve(initial))
- })
-}
-
-function useUpdate(call: () => void, dependencies: any[]) {
- const firstRender = React.useRef(true)
- useEffect(() => {
- if (firstRender.current) {
- firstRender.current = false
- } else {
- call()
- }
- }, [...dependencies, firstRender])
-}
-
-export default function ThemeProvider(properties: ThemeProviderProperties) {
- const [theme, update] = useState(properties.initialTheme || 'light')
- const variables = themeVariables[theme]
-
- useUpdate(() => {
- const link = themeSources[theme]
- updateTheme(theme, link)
- storeTheme(theme)
- }, [theme])
-
- return (
-
-
- {properties.children}
-
-
- )
-}
\ No newline at end of file
diff --git a/website/src/theme/ToggleButton.tsx b/website/src/theme/ToggleButton.tsx
deleted file mode 100644
index 30c65dc..0000000
--- a/website/src/theme/ToggleButton.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import {Button} from 'antd'
-import {BulbFilled, BulbOutlined} from '@ant-design/icons'
-import {useTheme, useThemeToggle} from './ThemeContext'
-
-export default function ToggleButton() {
- const [theme] = useTheme()
- const toggle = useThemeToggle()
- return (
- : }
- onClick={toggle}
- />
- )
-}
\ No newline at end of file
diff --git a/website/src/theme/dark.less b/website/src/theme/dark.less
deleted file mode 100644
index a8d007a..0000000
--- a/website/src/theme/dark.less
+++ /dev/null
@@ -1,17 +0,0 @@
-@import "../../node_modules/antd/dist/antd.dark.less";
-
-@primary-color: #00758f; // #FFA518;
-@border-radius-base: 3px;
-@background-color-base: #1f1f1f;
-@font-family: 'Titillium Web', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
-'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
-sans-serif;
-@card-shadow: 1px 0 1px 2px rgba(50%, 100, 100, 100);
-
-body {
- background: @background-color-base;
-}
-
-.ant-layout {
- background: none;
-}
\ No newline at end of file
diff --git a/website/src/theme/light.less b/website/src/theme/light.less
deleted file mode 100644
index 02e722d..0000000
--- a/website/src/theme/light.less
+++ /dev/null
@@ -1,17 +0,0 @@
-@import "../../node_modules/antd/dist/antd.less";
-
-@primary-color: #00758f; // #FFA518;
-@border-radius-base: 3px;
-@background-color-base: #f0f2f5;
-@font-family: 'Titillium Web', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
-'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
-sans-serif;
-@card-shadow: 1px 0 1px 2px rgba(50%, 100, 100, 100);
-
-body {
- background: #f0f2f5;
-}
-
-.ant-layout {
- background: none;
-}
\ No newline at end of file
diff --git a/website/src/theme/theme.ts b/website/src/theme/theme.ts
deleted file mode 100644
index e634fe8..0000000
--- a/website/src/theme/theme.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-export const light = {
- background: '#fff',
- base: {
- primary: '#00758f'
- },
- header: {
- background: '#fff',
- shadow: '0 5px 5px 0 rgb(230 230 230 / 90%)',
- },
- snippet: {
- card: {
- background: '#ffffff',
- shadow: '0 3px 5px 0 rgb(200 200 200 / 30%)',
- head: {
- shadow: 'inset 0 -1px 0 0 #eeeeee'
- },
- icon: {
- background: '#f0f2f5'
- }
- },
- component: {
- background: 'rgb(253, 253, 253)',
- borderTop: '1px solid #ededed'
- },
- textComponent: {
- background: 'rgb(253, 253, 253)'
- }
- }
-}
-
-export const dark: typeof light = {
- background: '#252525',
- base: {
- primary: '#00758f'
- },
- header: {
- background: '#252525',
- shadow: '0 5px 5px 0 rgb(23 23 23 / 90%)'
- },
- snippet: {
- card: {
- background: '#313131',
- shadow: '0 3px 5px 0 rgb(35 35 35 / 30%)',
- head: {
- shadow: 'inset 0 -1px 0 0 #171717'
- },
- icon: {
- background: '#313131'
- }
- },
- component: {
- background: '#3c3f40',
- borderTop: '1px solid #3c3f40',
- },
- textComponent: {
- background: '#3c3f40'
- }
- }
-}
-
-type ThemeType = typeof light
-
-declare module 'styled-components' {
- export interface DefaultTheme extends ThemeType {}
-}
\ No newline at end of file
diff --git a/website/src/theme/themed.ts b/website/src/theme/themed.ts
deleted file mode 100644
index 14a2725..0000000
--- a/website/src/theme/themed.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import {DefaultTheme} from 'styled-components'
-
-export default function themed(name: string) {
- return ({theme}: {theme: DefaultTheme}) => {
- return accessPath(undefined, theme , name.split('.'))
- }
-}
-
-function accessPath(
- parent: any,
- object: any,
- path: string[],
- index: number = 0): any {
- if (object === undefined) {
- if (parent !== undefined) {
- const possibleFields = Object.keys(parent)
- throw new Error(
- `could not find ${path[index]} in ${path[index - 1]} (only has: ${possibleFields})`
- )
- }
- throw new Error(`could not find ${path[index]}`)
- }
- if (path.length === index) {
- return object
- }
- const child = object[path[index]]
- return accessPath(object, child, path, index + 1)
-}
\ No newline at end of file
diff --git a/website/src/theme/updateTheme.ts b/website/src/theme/updateTheme.ts
deleted file mode 100644
index 4357913..0000000
--- a/website/src/theme/updateTheme.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-const commentNodeType = 8
-
-export function findCommentNode(comment: string) {
- for (const node of document.head.childNodes) {
- if (node.nodeType === commentNodeType && node?.nodeValue?.trim() === comment) {
- return node;
- }
- }
- return null;
-}
-
-export function createLinkElement(attributes: Partial) {
- const elements = document.createElement('link')
- for (const [attribute, value] of Object.entries(attributes)) {
- // @ts-ignore
- elements[attribute] = value;
- }
- return elements
-}
-
-function insertStyle(linkElement: HTMLElement, insertionPoint?: string) {
- const insert = insertionPoint
- ? selectInsertionMethod(insertionPoint)
- : insertToHead
- insert(linkElement)
-}
-
-function insertToHead(element: Node) {
- return document.head.appendChild(element)
-}
-
-function insertBefore(parent: Node, point: Node) {
- return (element: Node) => parent.insertBefore(element, point?.nextSibling)
-}
-
-function selectInsertionMethod(name: string): (element: Node) => void {
- const point = findCommentNode(name)
- const parent = point?.parentNode
- return parent && point
- ? insertBefore(parent, point)
- : insertToHead
-}
-
-function createStyleLink(id: string, link: string, callback?: Callback) {
- const temporaryId = id + '_temp'
- return createLinkElement({
- type: 'text/css',
- rel: 'stylesheet',
- id: temporaryId,
- href: link,
- onload: () => {
- document.getElementById(id)?.remove()
- const nextStyle = document.getElementById(temporaryId)
- nextStyle?.setAttribute('id', id)
- callback?.()
- }
- })
-}
-
-type ThemeSources = Record
-
-function *listPrefetches(themeSources: ThemeSources) {
- for (const [theme, link] of Object.entries(themeSources)) {
- const id = `theme-prefetch-${theme}`;
- if (document.getElementById(id)) {
- continue
- }
- yield createPrefetch(theme, id, link)
- }
-}
-
-function createPrefetch(theme: string, id: string, link: string) {
- const prefetch = document.createElement('link');
- prefetch.rel = 'prefetch'
- prefetch.type = 'text/css'
- prefetch.id = id;
- prefetch.href = link
- return prefetch
-}
-
-const activeThemeId = 'active-theme'
-const insertionPoint = 'active-theme'
-
-type Callback = () => void
-
-export function updateTheme(theme: string, link: string, callback?: Callback) {
- const element = createStyleLink(activeThemeId, link, callback)
- insertStyle(element, insertionPoint)
- document.body.setAttribute('data-theme', theme)
-}
-
-export function insertPrefetches(themes: ThemeSources) {
- const insert = selectInsertionMethod(insertionPoint)
- for (const prefetch of listPrefetches(themes)) {
- insert(prefetch)
- }
-}
\ No newline at end of file
diff --git a/website/src/version.ts b/website/src/version.ts
new file mode 100644
index 0000000..1bd6b7e
--- /dev/null
+++ b/website/src/version.ts
@@ -0,0 +1 @@
+export default '2.0.1'
\ No newline at end of file