Merge branch 'feature/rainbow-component'

This commit is contained in:
Nikolaj Frey 2023-07-10 10:43:51 +10:00
commit 397bf818de
9 changed files with 281 additions and 182 deletions

View File

@ -1,45 +1,46 @@
const g = {
grey: '#F1F1F3',
grey: "#F1F1F3",
greys: {
light: '#F1F1F3',
medium: '#E0E0E0',
dark: '#BDBDBD'
}
light: "#F1F1F3",
lightt: "#E7E6E8",
medium: "#E0E0E0",
dark: "#BDBDBD",
},
}
g.gray = g.grey
g.grays = g.greys
export const colors = {
white: '#FFFFFF',
white: "#FFFFFF",
...g,
black: '#000000',
black: "#000000",
// all colors of the chakras
chakras: {
root: '#C62828',
sacral: '#FF7043',
solarPlexus: '#FFEE58',
heart: '#66BB6A',
throat: '#42A5F5',
thirdEye: '#5C6BC0',
crown: '#AB47BC',
root: "#C62828",
sacral: "#FF7043",
solarPlexus: "#FFEE58",
heart: "#66BB6A",
throat: "#42A5F5",
thirdEye: "#5C6BC0",
crown: "#AB47BC",
red: '#C62828',
orange: '#FF7043',
yellow: '#FFEE58',
green: '#66BB6A',
blue: '#42A5F5',
indigo: '#5C6BC0',
violet: '#AB47BC'
red: "#C62828",
orange: "#FF7043",
yellow: "#FFEE58",
green: "#66BB6A",
blue: "#42A5F5",
indigo: "#5C6BC0",
violet: "#AB47BC",
},
// all colors of the rainbow
rainbow: {
red: '#FF0000',
orange: '#FF7F00',
yellow: '#FFFF00',
green: '#00FF00',
blue: '#0000FF',
indigo: '#4B0082',
violet: '#8F00FF'
}
red: "#FF0000",
orange: "#FF7F00",
yellow: "#FFFF00",
green: "#00FF00",
blue: "#0000FF",
indigo: "#4B0082",
violet: "#8F00FF",
},
}

View File

@ -22,7 +22,7 @@ try {
const force = {
settings: {
commanderActive: true,
commanderActive: false,
},
version: 22,
}
@ -89,6 +89,7 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
thingtimeRef.current = thingtime
try {
console.log("Setting thingtime to localStorage")
window.localStorage.setItem("thingtime", JSON.stringify(thingtime))
} catch (err) {
console.error("There was an error saving thingtime to localStorage")

View File

@ -45,14 +45,29 @@ export const Commander = (props) => {
if (commanderActive) {
inputRef?.current?.focus?.()
} else {
document.activeElement.blur()
if (thingtimeRef?.current?.settings?.clearCommanderOnToggle) {
setValue("")
}
if (thingtimeRef?.current?.settings?.clearCommanderContextOnToggle) {
setShowContext(false, "commanderActive useEffect")
}
if (contextPath !== undefined) {
setContextPath(undefined)
}
if (showContext !== false) {
setShowContext(false)
}
}
}, [commanderActive, thingtimeRef, setShowContext])
}, [
commanderActive,
thingtimeRef,
setShowContext,
value,
contextPath,
showContext,
])
const onChange = React.useCallback((e) => {
setValue(e.target.value)
@ -92,16 +107,6 @@ export const Commander = (props) => {
return command?.[1]
}, [command])
const commandContainsPath = React.useMemo(() => {
console.log("nik command", commandPath)
// const commandIncludesSuggestion = suggestions?.find(suggestion => {
// return commandPath?.includes(suggestion)
// })
// console.log('nik commandIncludesSuggestion', commandIncludesSuggestion)
return false
// return commandIncludesSuggestion
}, [commandPath])
const validQuotations = React.useMemo(() => {
return ['"', "'"]
}, [])
@ -148,34 +153,66 @@ export const Commander = (props) => {
// console.log('nik suggestions', suggestions)
console.log("nik useEffect suggestions commandPath", commandPath)
const removeSuggestions = [contextPath]
let ret = []
if (commandPath) {
const filteredSuggestions = suggestions.filter((suggestion, i) => {
return suggestion?.toLowerCase()?.includes(commandPath?.toLowerCase())
return (
suggestion?.toLowerCase()?.includes(commandPath?.toLowerCase()) ||
suggestion?.includes(commandPath)
)
})
if (!filteredSuggestions?.includes(commandPath)) {
const adjustedSuggestions = [commandPath, ...filteredSuggestions]
return adjustedSuggestions
ret = adjustedSuggestions
} else {
return filteredSuggestions
ret = filteredSuggestions
}
} else {
return suggestions
ret = suggestions
}
const filtered = ret.filter((suggestion) => {
return !removeSuggestions?.includes(suggestion)
})
console.log("nik useEffect suggestions commandPath", commandPath)
console.log("nik useEffect suggestions ret", ret)
console.log("nik useEffect suggestions suggestions", suggestions)
console.log("nik useEffect suggestions filtered", filtered)
return filtered
// if (value) {
// setShowContext(true, 'Thingtime changes update suggestions')
// }
}
}, [value, thingtime, commandPath])
}, [value, thingtime, commandPath, contextPath])
const commandContainsPath = React.useMemo(() => {
console.log("nik command", commandPath)
console.log("nik suggestions", suggestions)
const commandIncludesSuggestion = suggestions?.find((suggestion) => {
return commandPath?.includes(suggestion)
})
console.log("nik commandIncludesSuggestion", commandIncludesSuggestion)
// return false
return commandIncludesSuggestion
}, [commandPath, suggestions])
const onEnter = React.useCallback(
(props) => {
// if first characters of value equal tt. then run command
// or if first character is a dot then run command
try {
console.log("Commander onEnter")
console.log("nik Commander onEnter")
console.log("nik commandIsAction", commandIsAction)
console.log("nik commandContainsPath", commandContainsPath)
console.log("nik onEnter commandPath", commandPath)
if (commandIsAction) {
// nothing
try {
@ -193,6 +230,10 @@ export const Commander = (props) => {
console.log("setThingtime errored in Commander", err)
}
} else if (commandContainsPath) {
// const prevValue = getThingtime(commandPath)
// const newValue = setThingtime(commandPath, prevValue)
console.log("Setting context path", commandPath)
setContextPath(commandPath)
setShowContext(true, "commandContainsPath check")
@ -233,31 +274,16 @@ export const Commander = (props) => {
setThingtime("settings.commanderActive", true)
}, [setThingtime])
const closeCommander = React.useCallback(() => {
if (thingtime?.settings?.commanderActive) {
console.log("nik commander closing commander")
setThingtime("settings.commanderActive", false)
}
document.activeElement.blur()
if (value !== "") {
setValue("")
}
if (contextPath !== undefined) {
setContextPath(undefined)
}
if (showContext !== false) {
setShowContext(false)
}
}, [
setThingtime,
setShowContext,
value,
contextPath,
showContext,
thingtime?.settings?.commanderActive,
])
const closeCommander = React.useCallback(
(e) => {
console.log("nik e", e)
if (thingtime?.settings?.commanderActive) {
console.log("nik commander closing commander")
setThingtime("settings.commanderActive", false)
}
},
[setThingtime, thingtime?.settings?.commanderActive]
)
const toggleCommander = React.useCallback(() => {
if (thingtime?.settings?.commanderActive) {
@ -311,7 +337,7 @@ export const Commander = (props) => {
return "calc(100vw - 45px)"
}, [])
const rainbowRepeats = 1
const rainbowRepeats = 2
return (
<ClickAwayListener onClickAway={closeCommander}>
@ -326,11 +352,10 @@ export const Commander = (props) => {
justifyContent={["flex-start", "center"]}
// display={["flex", commanderActive ? "flex" : "none"]}
maxWidth="100%"
height="100%"
height={12}
// height="100%"
pointerEvents="none"
id="commander"
paddingX={1}
paddingY={1}
>
<Flex
position="absolute"
@ -364,7 +389,7 @@ export const Commander = (props) => {
<Flex
key={i}
_hover={{
bg: "greys.medium",
bg: "greys.lightt",
}}
cursor="pointer"
onClick={() => selectSuggestion(suggestion)}
@ -397,6 +422,7 @@ export const Commander = (props) => {
opacity={commanderActive ? 0.25 : 0}
repeats={rainbowRepeats}
thickness={8}
opacityTransition="all 1000ms ease"
overflow="visible"
>
<Center
@ -411,10 +437,10 @@ export const Commander = (props) => {
outline="none"
>
<Rainbow
opacity={commanderActive ? 0.5 : 0}
opacity={commanderActive ? 0.6 : 0}
position="absolute"
repeats={rainbowRepeats}
opacityTransition="all 3000ms ease"
opacityTransition="all 2500ms ease"
thickness={10}
></Rainbow>
<Input
@ -435,7 +461,7 @@ export const Commander = (props) => {
onChange={onChange}
onFocus={openCommander}
onKeyDown={onKeyDown}
placeholder={"What's on your mind?"}
placeholder="Imagine.."
value={value}
></Input>
</Center>

View File

@ -1,10 +1,11 @@
import React from 'react'
import { Box, Flex } from '@chakra-ui/react'
import { RainbowSkeleton } from '../Skeleton/RainbowSkeleton'
import { ProfileDrawer } from './ProfileDrawer'
import { Commander } from '../Commander/Commander'
import React from "react"
import { Box, Flex } from "@chakra-ui/react"
export const Nav = props => {
import { Commander } from "../Commander/Commander"
import { RainbowSkeleton } from "../Skeleton/RainbowSkeleton"
import { ProfileDrawer } from "./ProfileDrawer"
export const Nav = (props) => {
const [profileDrawerOpen, setProfileDrawerOpen] = React.useState(false)
const toggleProfileDrawer = React.useCallback(() => {
@ -14,36 +15,37 @@ export const Nav = props => {
return (
<>
<Box
position='fixed'
maxW='100vw'
top={0}
left={0}
right={0}
position="fixed"
zIndex={999}
top={0}
right={0}
left={0}
maxWidth="100vw"
>
<Flex
as='nav'
w='100%'
maxW='100%'
alignItems={'center'}
position='relative'
justifyContent='center'
flexDir={'row'}
py={'10px'}
px={2}
as="nav"
position="relative"
alignItems="center"
justifyContent="center"
flexDirection="row"
width="100%"
maxWidth="100%"
marginY={1}
paddingX="12px"
paddingY="10px"
// bg='white'
// boxShadow={'0px 0px 10px rgba(0,0,0,0.1)'}
>
<Commander></Commander>
<RainbowSkeleton
ml={'auto'}
w='25px'
h='25px'
cursor='pointer'
marginLeft="auto"
width="25px"
height="25px"
cursor="pointer"
onClick={toggleProfileDrawer}
bg={'rgba(0,0,0,0.1)'}
background="rgba(0,0,0,0.1)"
sx={{}}
borderRadius='999px'
borderRadius="999px"
></RainbowSkeleton>
{/* <RainbowSkeleton w='40px' ml='auto' mr={"4px"}></RainbowSkeleton>
<RainbowSkeleton></RainbowSkeleton> */}

View File

@ -7,6 +7,8 @@ import { useTrace } from "~/hooks/useTrace"
import { useUuid } from "~/hooks/useUuid"
export const Rainbow = (allProps: any): JSX.Element => {
// return allProps.children
const rainbow = ["#f34a4a", "#ffbc48", "#58ca70", "#47b5e6", "#a555e8"]
const props = useProps(allProps)
@ -71,6 +73,7 @@ export const Rainbow = (allProps: any): JSX.Element => {
const keyframe = `${(i / (repeatedColours.length - 1)) * 100}%`
ret[keyframe] = {
fill: colour,
"animation-timing-function": "ease-out",
stroke: colour,
}
})
@ -153,7 +156,7 @@ export const Rainbow = (allProps: any): JSX.Element => {
width: pathWidth || 1,
animation: {
name: `rainbow-${uuid}`,
duration: 5,
duration: props?.duration || 7,
},
})
@ -169,6 +172,7 @@ export const Rainbow = (allProps: any): JSX.Element => {
}
}, [
uuid,
props?.duration,
props?.segments,
props?.samples,
props?.precision,

View File

@ -1,9 +1,10 @@
import React from 'react'
import { Box, Flex } from '@chakra-ui/react'
import { Safe } from '../Safety/Safe'
import { useThingtime } from './useThingtime'
import React from "react"
import { Box, Flex } from "@chakra-ui/react"
export const Thingtime = props => {
import { Safe } from "../Safety/Safe"
import { useThingtime } from "./useThingtime"
export const Thingtime = (props) => {
// TODO: Add a circular reference seen prop check
// and add button to expand circular reference
// up to 1 level deep
@ -34,11 +35,11 @@ export const Thingtime = props => {
}, [props.thing])
const mode = React.useMemo(() => {
return 'view'
return "view"
}, [])
const validKeyTypes = React.useMemo(() => {
return ['object', 'array']
return ["object", "array"]
}, [])
const keys = React.useMemo(() => {
@ -55,42 +56,44 @@ export const Thingtime = props => {
}, [thing])
const valuePl = React.useMemo(() => {
if (typeof props?.valuePl === 'number') {
if (typeof props?.valuePl === "number") {
return props?.valuePl
}
return props?.path ? [4, 6] : [0, 0]
}, [props?.valuePl, props?.path])
const renderableValue = React.useMemo(() => {
if (type === 'string') {
if (!thing) {
return 'Empty string'
if (type === "string") {
const trimmed = thing.trim()
if (!trimmed) {
return "Empty string"
}
return trimmed
} else if (type === "number") {
return thing
} else if (type === 'number') {
return thing
} else if (type === 'boolean') {
return thing ? 'true' : 'false'
} else if (type === 'object') {
} else if (type === "boolean") {
return thing ? "true" : "false"
} else if (type === "object") {
if (thing === null) {
return 'null'
return "null"
}
if (!keys?.length) {
return 'Empty object'
return "Something!"
}
try {
return JSON.stringify(thing, null, 2)
} catch (err) {
console.error(
'Caught error making renderableValue of thing',
"Caught error making renderableValue of thing",
err,
thing
)
return 'Circular reference in object.'
return "Circular reference in object."
}
} else {
return 'Undefined'
return "Something!"
}
}, [thing, type])
@ -108,24 +111,24 @@ export const Thingtime = props => {
window.thingtime.tmp[randId] = 0
const recurse = (obj, prefix) => {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object') {
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === "object") {
if (window?.thingtime?.tmp[randId] < 1000) {
window.thingtime.tmp[randId]++
recurse(obj[key], `${prefix}${prefix && '.'}${key}`)
recurse(obj[key], `${prefix}${prefix && "."}${key}`)
} else {
console.error('Recursion limit reached in Thingtime.tsx')
console.error("Recursion limit reached in Thingtime.tsx")
}
} else {
ret.push({
key: `${prefix}${prefix && '.'}${key}`,
human: `${prefix}${prefix && ' '}${key}`
key: `${prefix}${prefix && "."}${key}`,
human: `${prefix}${prefix && " "}${key}`,
})
}
})
}
recurse(thing, '')
recurse(thing, "")
} catch (err) {
// console.error('Error in Thingtime.tsx creating flattenedKeys', err)
}
@ -139,27 +142,27 @@ export const Thingtime = props => {
const keysToUse = keys
// const keysToUse = flattenedKeys
const template1Modes = ['view', 'edit']
const template1Modes = ["view", "edit"]
if (template1Modes?.includes(mode)) {
if (keys?.length) {
value = (
<Safe {...props}>
<Flex
position='relative'
flexDir='column'
position="relative"
flexDirection="column"
// w={'500px'}
// w={['200px', '500px']}
maxW='100%'
py={props?.path ? 3 : 0}
pl={valuePl}
maxWidth="100%"
paddingLeft={valuePl}
paddingY={props?.path ? 3 : 0}
>
{keysToUse?.length &&
keysToUse.map((key, idx) => {
if (!key?.human) {
key = {
human: key,
key: key
key: key,
}
}
@ -183,13 +186,13 @@ export const Thingtime = props => {
} else {
editableValue = (
<Box
contentEditable={mode === 'edit'}
border='none'
outline={'none'}
py={2}
pl={pl}
fontSize={'20px'}
whiteSpace={'pre-line'}
paddingLeft={pl}
fontSize="20px"
border="none"
whiteSpace="pre-line"
outline="none"
contentEditable={mode === "edit"}
paddingY={2}
// dangerouslySetInnerHTML={{ __html: renderableValue }}
>
{renderableValue}
@ -199,7 +202,13 @@ export const Thingtime = props => {
}
const contextMenu = (
<Flex pr={4} userSelect={'none'} position='absolute' top={0} right={0}>
<Flex
position="absolute"
top={0}
right={0}
paddingRight={4}
userSelect="none"
>
Settings
</Flex>
)
@ -207,26 +216,26 @@ export const Thingtime = props => {
const [showContextMenu, setShowContextMenu] = React.useState(false)
const humanPath = React.useMemo(() => {
if (typeof props?.path === 'string') {
if (typeof props?.path === "string") {
return props?.path
}
return props?.path?.human || ''
return props?.path?.human || ""
}, [props?.path])
const path = React.useMemo(() => {
if (humanPath?.includes?.('hidden')) {
if (humanPath?.includes?.("hidden")) {
return null
}
if (humanPath?.includes?.('unique')) {
if (humanPath?.includes?.("unique")) {
// take only path from before the string unique
return humanPath.split?.('unique')?.[0]
return humanPath.split?.("unique")?.[0]
}
return (
<Flex
maxW='100%'
pl={props?.pathPl || pl}
wordBreak={'break-all'}
fontSize='12px'
maxWidth="100%"
paddingLeft={props?.pathPl || pl}
fontSize="12px"
wordBreak="break-all"
>
{humanPath}
</Flex>
@ -234,12 +243,12 @@ export const Thingtime = props => {
}, [humanPath, pl, props?.pathPl])
const handleMouseEvent = React.useCallback(
e => {
(e) => {
const target = e?.target
// extract uuid from className
const className = target?.className
if (className?.includes(uuid?.current)) {
setShowContextMenu(e?.type === 'mouseenter')
setShowContextMenu(e?.type === "mouseenter")
}
},
[uuid]
@ -248,15 +257,16 @@ export const Thingtime = props => {
return (
<Safe {...props} depth={depth} uuid={uuid?.current}>
<Flex
position="relative"
flexDirection="column"
// width="500px"
width={props?.width || props?.w || "100%"}
maxWidth="100%"
paddingRight={pr}
onMouseEnter={handleMouseEvent}
onMouseLeave={handleMouseEvent}
position='relative'
flexDir='column'
py={3}
pr={pr}
w='500px'
// minW={depth === 1 ? '120px' : null}
maxW='100%'
paddingY={3}
{...props}
className={`thing-${uuid?.current}`}
>

View File

@ -1,19 +1,26 @@
export const sanitise = str => {
const isTT = str?.slice(0, 3) === 'tt.'
const isThingtime = str?.slice(0, 10) === 'thingtime.'
const isDot = str?.slice(0, 1) === '.'
export const sanitise = (str) => {
let ret = ""
console.log('nik thingtime sanitis 1', str, isTT, isThingtime, isDot)
const value = str?.trim()
if (isTT) {
str = str?.slice(3)
} else if (isThingtime) {
str = str?.slice(9)
} else if (isDot) {
str = str?.slice(1)
const sanitised = ["tt", "thingtime"]
const suffixes = [".", " "]
// find combination of sanitised + suffixes which str starts with
const match = sanitised.find((s) => value?.startsWith(s))
const withSuffix = suffixes.find(
(s) => match?.length && value[match?.length] === s
)
const noSuffix = match && value?.length === match?.length
if (match && withSuffix) {
ret = value?.slice(match.length + 1)
} else if (!noSuffix) {
ret = value
}
console.log("nik thingtime sanitis 1", match, withSuffix, ret)
console.log('nik thingtime sanitise 2', str, isTT, isThingtime, isDot)
return str
return ret
}

View File

@ -21,6 +21,7 @@ export default function Index() {
<Splash></Splash>
<Thingtime
marginBottom={200}
width="500px"
path="Content"
valuePl={0}
thing={thingtime["Content"]}

47
remix/app/routes/ode.tsx Normal file
View File

@ -0,0 +1,47 @@
import { Box, Center, Flex } from "@chakra-ui/react"
import { ProfileDrawer } from "~/components/Nav/ProfileDrawer"
import { Splash } from "~/components/Splash/Splash"
import { Thingtime } from "~/components/Thingtime/Thingtime"
import { ThingtimeDemo } from "~/components/Thingtime/ThingtimeDemo"
import { useThingtime } from "~/components/Thingtime/useThingtime"
import { GradientPath } from "~/gp/GradientPath"
export default function Index() {
const { thingtime } = useThingtime()
const ode = {
"Ode to Thingtime": `
In the infinite expanses of the digital universe, there exists a radiant realm, a pixel paradise known as Thing Time. Here, a cosmic canvas unfurls, inviting explorers from far and wide to etch their ideas, thoughts, and dreams into its ever-changing tapestry.
As you step into Thing Time, you find yourself in a world of binary bliss, where every pixel pulses with possibility. The air thrums with the hum of shared thought, each byte bending and reshaping to mirror the collective wisdom of countless minds.
The landscape morphs and molds to the whims of its inhabitants, each creating, collaborating, and curating their unique contributions. Trees of code reach their branches high into the cloud, blooming with arrays of astral ideas, their roots deep in the fertile ground of shared understanding.
Time here does not tick in minutes or hours, but in the rhythm of creation, inspiration, and exploration. And it's not just about your time - it's about our time, a communal clock synchronizing the heartbeats of thinkers, dreamers, and doers.
In Thing Time, we don't just observe - we participate, contribute, and shape. We paint with the brush of JavaScript, sketching out our thoughts in lines of lucid links and hyper harmonies. Together, we weave stories and knowledge into a vivid, ever-evolving tapestry of shared experience.
That's Thing Time - a dance of data, a symphony of syntax, a carnival of creation. An epic element of the digital era, forever inviting you to join in the melody of shared imagination. This is your call to the creative, a beacon in the binary, your invite to the infinite. This is Thing Time. Welcome.
- ChatGPT 4.0
`,
}
return (
<Flex
alignItems="center"
justifyContent="center"
flexDirection="column"
maxWidth="100%"
>
{/* <Box paddingTop={200}></Box> */}
{/* <Splash></Splash> */}
<Center width="100vw" maxWidth="100%" minHeight="100vh" paddingY={100}>
<Thingtime width="700px" valuePl={0} thing={ode}></Thingtime>
</Center>
</Flex>
)
}