Adds provisional logger
This commit is contained in:
parent
48c659a68a
commit
8180146eb3
14 changed files with 3583 additions and 204 deletions
|
@ -120,16 +120,16 @@ Array [
|
|||
"id": "bend",
|
||||
"index": 2,
|
||||
"point": Array [
|
||||
0,
|
||||
0,
|
||||
50,
|
||||
50,
|
||||
],
|
||||
},
|
||||
"end": Object {
|
||||
"id": "end",
|
||||
"index": 1,
|
||||
"point": Array [
|
||||
0,
|
||||
0,
|
||||
100,
|
||||
100,
|
||||
],
|
||||
},
|
||||
"start": Object {
|
||||
|
|
|
@ -7,9 +7,14 @@ import { CodeError, CodeFile, CodeResult } from 'types'
|
|||
import CodeDocs from './code-docs'
|
||||
import { generateFromCode } from 'state/code/generate'
|
||||
import * as Panel from '../panel'
|
||||
import { IconButton } from '../shared'
|
||||
import { Code, PlayCircle, ChevronUp, ChevronDown } from 'react-feather'
|
||||
import { Cross2Icon } from '@radix-ui/react-icons'
|
||||
import { breakpoints, IconButton } from '../shared'
|
||||
import {
|
||||
Cross2Icon,
|
||||
CodeIcon,
|
||||
PlayIcon,
|
||||
ChevronUpIcon,
|
||||
ChevronDownIcon,
|
||||
} from '@radix-ui/react-icons'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { ReaderIcon } from '@radix-ui/react-icons'
|
||||
const CodeEditor = dynamic(() => import('./code-editor'))
|
||||
|
@ -17,6 +22,7 @@ const CodeEditor = dynamic(() => import('./code-editor'))
|
|||
const increaseCodeSize = () => state.send('INCREASED_CODE_FONT_SIZE')
|
||||
const decreaseCodeSize = () => state.send('DECREASED_CODE_FONT_SIZE')
|
||||
const toggleCodePanel = () => state.send('TOGGLED_CODE_PANEL_OPEN')
|
||||
const handleWheel = (e: React.WheelEvent) => e.stopPropagation()
|
||||
|
||||
export default function CodePanel(): JSX.Element {
|
||||
const rContainer = useRef<HTMLDivElement>(null)
|
||||
|
@ -132,56 +138,48 @@ export default function CodePanel(): JSX.Element {
|
|||
return (
|
||||
<Panel.Root
|
||||
dir="ltr"
|
||||
bp={{ '@initial': 'mobile', '@sm': 'small' }}
|
||||
bp={breakpoints}
|
||||
data-bp-desktop
|
||||
ref={rContainer}
|
||||
isOpen={isOpen}
|
||||
variant="code"
|
||||
onWheel={(e) => e.stopPropagation()}
|
||||
onWheel={handleWheel}
|
||||
>
|
||||
{isOpen ? (
|
||||
<Panel.Layout>
|
||||
<Panel.Header side="left">
|
||||
<IconButton
|
||||
bp={{ '@initial': 'mobile', '@sm': 'small' }}
|
||||
size="small"
|
||||
onClick={toggleCodePanel}
|
||||
>
|
||||
<IconButton bp={breakpoints} size="small" onClick={toggleCodePanel}>
|
||||
<Cross2Icon />
|
||||
</IconButton>
|
||||
<h3>Code</h3>
|
||||
<ButtonsGroup>
|
||||
<FontSizeButtons>
|
||||
<IconButton
|
||||
bp={{ '@initial': 'mobile', '@sm': 'small' }}
|
||||
bp={breakpoints}
|
||||
size="small"
|
||||
disabled={!local.isIn('editingCode')}
|
||||
onClick={increaseCodeSize}
|
||||
>
|
||||
<ChevronUp />
|
||||
<ChevronUpIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
disabled={!local.isIn('editingCode')}
|
||||
onClick={decreaseCodeSize}
|
||||
>
|
||||
<ChevronDown />
|
||||
<ChevronDownIcon />
|
||||
</IconButton>
|
||||
</FontSizeButtons>
|
||||
<IconButton
|
||||
bp={{ '@initial': 'mobile', '@sm': 'small' }}
|
||||
size="small"
|
||||
onClick={toggleDocs}
|
||||
>
|
||||
<IconButton bp={breakpoints} size="small" onClick={toggleDocs}>
|
||||
<ReaderIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
bp={{ '@initial': 'mobile', '@sm': 'small' }}
|
||||
bp={breakpoints}
|
||||
size="small"
|
||||
disabled={!local.isIn('editingCode')}
|
||||
onClick={handleSave}
|
||||
>
|
||||
<PlayCircle />
|
||||
<PlayIcon />
|
||||
</IconButton>
|
||||
</ButtonsGroup>
|
||||
</Panel.Header>
|
||||
|
@ -201,12 +199,8 @@ export default function CodePanel(): JSX.Element {
|
|||
{error && <Panel.Footer>{error.message}</Panel.Footer>}
|
||||
</Panel.Layout>
|
||||
) : (
|
||||
<IconButton
|
||||
bp={{ '@initial': 'mobile', '@sm': 'small' }}
|
||||
size="small"
|
||||
onClick={toggleCodePanel}
|
||||
>
|
||||
<Code />
|
||||
<IconButton bp={breakpoints} size="small" onClick={toggleCodePanel}>
|
||||
<CodeIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Panel.Root>
|
||||
|
|
200
components/debug-panel/debug-panel.tsx
Normal file
200
components/debug-panel/debug-panel.tsx
Normal file
|
@ -0,0 +1,200 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import styled from 'styles'
|
||||
import React, { useRef } from 'react'
|
||||
import state, { useSelector } from 'state'
|
||||
import * as Panel from '../panel'
|
||||
import { breakpoints, IconButton, RowButton, IconWrapper } from '../shared'
|
||||
import {
|
||||
Cross2Icon,
|
||||
PlayIcon,
|
||||
CrumpledPaperIcon,
|
||||
StopIcon,
|
||||
ClipboardCopyIcon,
|
||||
} from '@radix-ui/react-icons'
|
||||
import logger from 'state/logger'
|
||||
import { useStateDesigner } from '@state-designer/react'
|
||||
|
||||
const stopPropagation = (e: React.KeyboardEvent) => e.stopPropagation()
|
||||
const toggleDebugPanel = () => state.send('TOGGLED_DEBUG_PANEL')
|
||||
|
||||
export default function CodePanel(): JSX.Element {
|
||||
const rContainer = useRef<HTMLDivElement>(null)
|
||||
const isDebugging = useSelector((s) => s.data.settings.isDebugMode)
|
||||
const isOpen = useSelector((s) => s.data.settings.isDebugOpen)
|
||||
|
||||
const rTextArea = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
const local = useStateDesigner({
|
||||
initial: 'stopped',
|
||||
data: {
|
||||
log: '',
|
||||
},
|
||||
states: {
|
||||
stopped: {
|
||||
on: {
|
||||
CHANGED_LOG: 'setLog',
|
||||
COPIED_LOG: { if: 'hasLog', do: 'copyLog' },
|
||||
PLAYED_BACK_LOG: { if: 'hasLog', do: 'playbackLog' },
|
||||
STARTED_LOGGING: { do: 'startLogger', to: 'logging' },
|
||||
},
|
||||
},
|
||||
logging: {
|
||||
on: {
|
||||
STOPPED_LOGGING: { do: 'stopLogger', to: 'stopped' },
|
||||
},
|
||||
},
|
||||
},
|
||||
conditions: {
|
||||
hasLog(data) {
|
||||
return data.log !== ''
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setLog(data, payload: { value: string }) {
|
||||
data.log = payload.value
|
||||
},
|
||||
startLogger(data) {
|
||||
logger.start(state.data)
|
||||
data.log = ''
|
||||
},
|
||||
stopLogger(data) {
|
||||
logger.stop(state.data)
|
||||
data.log = logger.copyToJson()
|
||||
},
|
||||
playbackLog(data) {
|
||||
logger.playback(state.data, data.log)
|
||||
},
|
||||
copyLog() {
|
||||
logger.copyToJson()
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (!isDebugging) return null
|
||||
|
||||
const handleLoggingStop = () => local.send('STOPPED_LOGGING')
|
||||
|
||||
const handlePlayback = () =>
|
||||
local.send('PLAYED_BACK_LOG', { log: rTextArea.current?.value })
|
||||
|
||||
const handleLoggingStart = () => local.send('STARTED_LOGGING')
|
||||
|
||||
const handleLoggingCopy = () => local.send('COPIED_DEBUG_LOG')
|
||||
|
||||
return (
|
||||
<StylePanelRoot
|
||||
dir="ltr"
|
||||
bp={breakpoints}
|
||||
data-bp-desktop
|
||||
ref={rContainer}
|
||||
variant="code"
|
||||
onWheel={(e) => e.stopPropagation()}
|
||||
>
|
||||
{isOpen ? (
|
||||
<Panel.Layout onKeyDown={stopPropagation}>
|
||||
<Panel.Header side="left">
|
||||
<IconButton
|
||||
bp={breakpoints}
|
||||
size="small"
|
||||
onClick={toggleDebugPanel}
|
||||
>
|
||||
<Cross2Icon />
|
||||
</IconButton>
|
||||
<span>Debugging</span>
|
||||
<div />
|
||||
</Panel.Header>
|
||||
<Panel.Content>
|
||||
<hr />
|
||||
{local.isIn('stopped') ? (
|
||||
<RowButton bp={breakpoints} onClick={handleLoggingStart}>
|
||||
<span>Start Logger</span>
|
||||
<IconWrapper size="small">
|
||||
<PlayIcon />
|
||||
</IconWrapper>
|
||||
</RowButton>
|
||||
) : (
|
||||
<RowButton bp={breakpoints} onClick={handleLoggingStop}>
|
||||
<span>Stop Logger</span>
|
||||
<IconWrapper size="small">
|
||||
<StopIcon />
|
||||
</IconWrapper>
|
||||
</RowButton>
|
||||
)}
|
||||
{
|
||||
<RowButton
|
||||
bp={breakpoints}
|
||||
onClick={handleLoggingCopy}
|
||||
disabled={!local.can('COPIED_LOG')}
|
||||
>
|
||||
<span>Copy Log</span>
|
||||
<IconWrapper size="small">
|
||||
<ClipboardCopyIcon />
|
||||
</IconWrapper>
|
||||
</RowButton>
|
||||
}
|
||||
<JSONTextAreaWrapper>
|
||||
<JSONTextArea
|
||||
ref={rTextArea}
|
||||
value={local.data.log}
|
||||
onChange={(e) =>
|
||||
local.send('CHANGED_LOG', { value: e.currentTarget.value })
|
||||
}
|
||||
/>
|
||||
</JSONTextAreaWrapper>
|
||||
<RowButton
|
||||
bp={breakpoints}
|
||||
onClick={handlePlayback}
|
||||
disabled={!local.can('PLAYED_BACK_LOG')}
|
||||
>
|
||||
<span>Play Back Log</span>
|
||||
</RowButton>
|
||||
</Panel.Content>
|
||||
</Panel.Layout>
|
||||
) : (
|
||||
<IconButton bp={breakpoints} size="small" onClick={toggleDebugPanel}>
|
||||
<CrumpledPaperIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</StylePanelRoot>
|
||||
)
|
||||
}
|
||||
|
||||
const StylePanelRoot = styled(Panel.Root, {
|
||||
marginRight: '8px',
|
||||
width: 'fit-content',
|
||||
maxWidth: 'fit-content',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
border: '1px solid $panel',
|
||||
boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
pointerEvents: 'all',
|
||||
padding: 2,
|
||||
|
||||
'& hr': {
|
||||
marginTop: 2,
|
||||
marginBottom: 2,
|
||||
marginLeft: '-2px',
|
||||
border: 'none',
|
||||
height: 1,
|
||||
backgroundColor: '$brushFill',
|
||||
width: 'calc(100% + 4px)',
|
||||
},
|
||||
})
|
||||
|
||||
const JSONTextAreaWrapper = styled('div', {
|
||||
padding: '4px',
|
||||
})
|
||||
|
||||
const JSONTextArea = styled('textarea', {
|
||||
minHeight: '100px',
|
||||
width: '100%',
|
||||
font: '$mono',
|
||||
backgroundColor: '$panel',
|
||||
border: '1px solid $border',
|
||||
borderRadius: '4px',
|
||||
padding: '4px',
|
||||
outline: 'none',
|
||||
})
|
|
@ -7,6 +7,7 @@ import StylePanel from './style-panel/style-panel'
|
|||
import styled from 'styles'
|
||||
import PagePanel from './page-panel/page-panel'
|
||||
import CodePanel from './code-panel/code-panel'
|
||||
import DebugPanel from './debug-panel/debug-panel'
|
||||
import ControlsPanel from './controls-panel/controls-panel'
|
||||
|
||||
export default function Editor({ roomId }: { roomId?: string }): JSX.Element {
|
||||
|
@ -15,6 +16,7 @@ export default function Editor({ roomId }: { roomId?: string }): JSX.Element {
|
|||
|
||||
return (
|
||||
<Layout>
|
||||
<DebugPanel />
|
||||
<CodePanel />
|
||||
<PagePanel />
|
||||
<ControlsPanel />
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
IconWrapper,
|
||||
ButtonsRow,
|
||||
RowButton,
|
||||
breakpoints,
|
||||
} from 'components/shared'
|
||||
import ShapesFunctions from './shapes-functions'
|
||||
import AlignDistribute from './align-distribute'
|
||||
|
@ -24,9 +25,12 @@ import {
|
|||
Cross2Icon,
|
||||
} from '@radix-ui/react-icons'
|
||||
|
||||
const breakpoints = { '@initial': 'mobile', '@sm': 'small' } as any
|
||||
|
||||
const handleStylePanelOpen = () => state.send('TOGGLED_STYLE_PANEL_OPEN')
|
||||
const handleCopy = () => state.send('COPIED')
|
||||
const handlePaste = () => state.send('PASTED')
|
||||
const handleCopyToSvg = () => state.send('COPIED_TO_SVG')
|
||||
const handleSave = () => state.send('SAVED')
|
||||
const handleLoad = () => state.send('LOADED_FROM_FILE_STSTEM')
|
||||
|
||||
export default function StylePanel(): JSX.Element {
|
||||
const rContainer = useRef<HTMLDivElement>(null)
|
||||
|
@ -72,14 +76,14 @@ function SelectedShapeContent(): JSX.Element {
|
|||
<RowButton
|
||||
bp={breakpoints}
|
||||
disabled={selectedShapesCount === 0}
|
||||
onClick={() => state.send('COPIED')}
|
||||
onClick={handleCopy}
|
||||
>
|
||||
<span>Copy</span>
|
||||
<IconWrapper size="small">
|
||||
<ClipboardCopyIcon />
|
||||
</IconWrapper>
|
||||
</RowButton>
|
||||
<RowButton bp={breakpoints} onClick={() => state.send('PASTED')}>
|
||||
<RowButton bp={breakpoints} onClick={handlePaste}>
|
||||
<span>Paste</span>
|
||||
<IconWrapper size="small">
|
||||
<ClipboardIcon />
|
||||
|
@ -88,7 +92,7 @@ function SelectedShapeContent(): JSX.Element {
|
|||
<RowButton
|
||||
bp={breakpoints}
|
||||
disabled={selectedShapesCount === 0}
|
||||
onClick={() => state.send('COPIED_TO_SVG')}
|
||||
onClick={handleCopyToSvg}
|
||||
>
|
||||
<span>Copy to SVG</span>
|
||||
<IconWrapper size="small">
|
||||
|
@ -96,13 +100,10 @@ function SelectedShapeContent(): JSX.Element {
|
|||
</IconWrapper>
|
||||
</RowButton>
|
||||
<hr />
|
||||
<RowButton bp={breakpoints} onClick={() => state.send('SAVED')}>
|
||||
<RowButton bp={breakpoints} onClick={handleSave}>
|
||||
<span>Save</span>
|
||||
</RowButton>
|
||||
<RowButton
|
||||
bp={breakpoints}
|
||||
onClick={() => state.send('LOADED_FROM_FILE_STSTEM')}
|
||||
>
|
||||
<RowButton bp={breakpoints} onClick={handleLoad}>
|
||||
<span>Load</span>
|
||||
</RowButton>
|
||||
</>
|
||||
|
|
|
@ -195,7 +195,11 @@ export default function useKeyboardEvents() {
|
|||
}
|
||||
case 'd': {
|
||||
if (metaKey(e)) {
|
||||
state.send('DUPLICATED', info)
|
||||
if (e.shiftKey) {
|
||||
state.send('TOGGLED_DEBUG_MODE')
|
||||
} else {
|
||||
state.send('DUPLICATED', info)
|
||||
}
|
||||
} else {
|
||||
state.send('SELECTED_DRAW_TOOL', info)
|
||||
}
|
||||
|
@ -228,6 +232,8 @@ export default function useKeyboardEvents() {
|
|||
case 'l': {
|
||||
if (metaKey(e)) {
|
||||
if (e.shiftKey) {
|
||||
state.send('TOGGLED_LOGGER')
|
||||
} else {
|
||||
state.send('LOADED_FROM_FILE_STSTEM', info)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -5,16 +5,19 @@ import coopState from 'state/coop/coop-state'
|
|||
|
||||
export default function useLoadOnMount(roomId: string = undefined) {
|
||||
useEffect(() => {
|
||||
const fonts = (document as any).fonts
|
||||
if ('fonts' in document) {
|
||||
const fonts = (document as any).fonts
|
||||
fonts.load('12px Verveine Regular', 'Fonts are loaded!').then(() => {
|
||||
state.send('MOUNTED')
|
||||
|
||||
fonts.load('12px Verveine Regular', 'Fonts are loaded!').then(() => {
|
||||
state.send('MOUNTED')
|
||||
|
||||
if (roomId !== undefined) {
|
||||
state.send('RT_LOADED_ROOM', { id: roomId })
|
||||
coopState.send('JOINED_ROOM', { id: roomId })
|
||||
}
|
||||
})
|
||||
if (roomId !== undefined) {
|
||||
state.send('RT_LOADED_ROOM', { id: roomId })
|
||||
coopState.send('JOINED_ROOM', { id: roomId })
|
||||
}
|
||||
})
|
||||
} else {
|
||||
setTimeout(() => state.send('MOUNTED'), 1000)
|
||||
}
|
||||
|
||||
return () => {
|
||||
state.send('UNMOUNTED').send('RT_UNLOADED_ROOM', { id: roomId })
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
"@sentry/react": "^6.7.2",
|
||||
"@sentry/tracing": "^6.7.2",
|
||||
"@sentry/webpack-plugin": "^1.15.1",
|
||||
"@state-designer/react": "^1.7.32",
|
||||
"@state-designer/react": "^1.7.4",
|
||||
"@stitches/react": "^0.2.2",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"browser-fs-access": "^0.17.3",
|
||||
|
|
|
@ -15,10 +15,36 @@ import * as Session from './sessions'
|
|||
* directly to the state using `forceData`.
|
||||
* @param info
|
||||
*/
|
||||
|
||||
export function fastTranslate(info: PointerInfo): void {
|
||||
const data = { ...state.data }
|
||||
|
||||
session.update<Session.TranslateSession>(
|
||||
data,
|
||||
tld.screenToWorld(info.point, data),
|
||||
info.shiftKey,
|
||||
info.altKey
|
||||
)
|
||||
|
||||
state.forceData(freeze(data))
|
||||
}
|
||||
|
||||
export function fastTransform(info: PointerInfo): void {
|
||||
const data = { ...state.data }
|
||||
|
||||
session.update<Session.TransformSession | Session.TransformSingleSession>(
|
||||
data,
|
||||
tld.screenToWorld(info.point, data),
|
||||
info.shiftKey
|
||||
)
|
||||
|
||||
state.forceData(freeze(data))
|
||||
}
|
||||
|
||||
export function fastDrawUpdate(info: PointerInfo): void {
|
||||
const data = { ...state.data }
|
||||
|
||||
coopState.send('MOVED_CURSOSR', {
|
||||
coopState.send('MOVED_CURSOR', {
|
||||
pageId: data.currentPageId,
|
||||
point: info.point,
|
||||
})
|
||||
|
@ -102,28 +128,3 @@ export function fastBrushSelect(point: number[]): void {
|
|||
|
||||
state.forceData(freeze(data))
|
||||
}
|
||||
|
||||
export function fastTranslate(info: PointerInfo): void {
|
||||
const data = { ...state.data }
|
||||
|
||||
session.update<Session.TranslateSession>(
|
||||
data,
|
||||
tld.screenToWorld(info.point, data),
|
||||
info.shiftKey,
|
||||
info.altKey
|
||||
)
|
||||
|
||||
state.forceData(freeze(data))
|
||||
}
|
||||
|
||||
export function fastTransform(info: PointerInfo): void {
|
||||
const data = { ...state.data }
|
||||
|
||||
session.update<Session.TransformSession | Session.TransformSingleSession>(
|
||||
data,
|
||||
tld.screenToWorld(info.point, data),
|
||||
info.shiftKey
|
||||
)
|
||||
|
||||
state.forceData(freeze(data))
|
||||
}
|
||||
|
|
|
@ -84,8 +84,8 @@ class Inputs {
|
|||
|
||||
this.points[e.pointerId] = info
|
||||
this.activePointerId = e.pointerId
|
||||
|
||||
this.pointer = info
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
|
@ -169,12 +169,17 @@ class Inputs {
|
|||
return { point: getPoint(e), shiftKey, ctrlKey, metaKey, altKey }
|
||||
}
|
||||
|
||||
canAccept = (pointerId: PointerEvent['pointerId']) => {
|
||||
return (
|
||||
this.activePointerId === undefined || this.activePointerId === pointerId
|
||||
)
|
||||
canAccept = (pointerId: PointerEvent['pointerId']): boolean => {
|
||||
pointerId
|
||||
return true
|
||||
}
|
||||
|
||||
// canAccept = (pointerId: PointerEvent['pointerId']) => {
|
||||
// return (
|
||||
// this.activePointerId === undefined || this.activePointerId === pointerId
|
||||
// )
|
||||
// }
|
||||
|
||||
isDoubleClick() {
|
||||
if (!this.pointer) return
|
||||
|
||||
|
@ -225,6 +230,17 @@ class Inputs {
|
|||
altKey,
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.activePointerId = undefined
|
||||
this.pointerUpTime = 0
|
||||
|
||||
this.pointer = undefined
|
||||
this.points = {}
|
||||
|
||||
this.keyboard = undefined
|
||||
this.keys = {}
|
||||
}
|
||||
}
|
||||
|
||||
export default new Inputs()
|
||||
|
|
272
state/logger.ts
Normal file
272
state/logger.ts
Normal file
|
@ -0,0 +1,272 @@
|
|||
import { Data } from 'types'
|
||||
import clipboard from './clipboard'
|
||||
import state from './state'
|
||||
import { isDraft, current } from 'immer'
|
||||
import { setToArray } from 'utils'
|
||||
import tld from 'utils/tld'
|
||||
import inputs from './inputs'
|
||||
|
||||
interface LogEntry {
|
||||
eventName: string
|
||||
payload: any
|
||||
time: number
|
||||
didCauseUpdate: boolean
|
||||
}
|
||||
|
||||
class Logger {
|
||||
filters = new Set([
|
||||
// 'MOVED_OVER_SHAPE',
|
||||
// 'RESIZED_WINDOW',
|
||||
// 'HOVERED_SHAPE',
|
||||
// 'UNHOVERED_SHAPE',
|
||||
// 'PANNED_CAMERA',
|
||||
'STARTED_LOGGING',
|
||||
'STOPPED_LOGGING',
|
||||
])
|
||||
|
||||
snapshotStart: Data
|
||||
snapshotEnd: Data
|
||||
|
||||
log: LogEntry[] = []
|
||||
|
||||
isSimulating = false
|
||||
|
||||
isRunning = false
|
||||
|
||||
speed = 0
|
||||
|
||||
startTime = 0
|
||||
|
||||
/**
|
||||
* Start the logger.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
*```ts
|
||||
* logger.start()
|
||||
*```
|
||||
*/
|
||||
start = (data: Data): Logger => {
|
||||
if (this.isRunning) return
|
||||
|
||||
this.isRunning = true
|
||||
|
||||
this.snapshotStart = isDraft(data) ? current(data) : data
|
||||
|
||||
this.log = []
|
||||
|
||||
this.startTime = Date.now()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.snapshotStart.pageStates[data.currentPageId].selectedIds = setToArray(
|
||||
tld.getSelectedIds(data)
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the logger.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
*```ts
|
||||
* logger.stop()
|
||||
*```
|
||||
*/
|
||||
stop = (data: Data): Logger => {
|
||||
if (!this.isRunning) return
|
||||
|
||||
this.isRunning = false
|
||||
|
||||
this.snapshotEnd = isDraft(data) ? current(data) : data
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.snapshotEnd.pageStates[data.currentPageId].selectedIds = setToArray(
|
||||
tld.getSelectedIds(data)
|
||||
)
|
||||
|
||||
// if (window.confirm('Stopped logging. Copy to clipboard?')) {
|
||||
// this.copyToJson()
|
||||
// }
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event and payload to the log.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
*```ts
|
||||
* logger.addToLog(eventName, payload)
|
||||
*```
|
||||
*/
|
||||
addToLog(event: string, payload: any, didCauseUpdate = false) {
|
||||
if (!this.isRunning) return
|
||||
|
||||
if (this.filters.has(event)) return
|
||||
|
||||
didCauseUpdate
|
||||
// if (!didCauseUpdate) return
|
||||
|
||||
this.log.push({
|
||||
eventName: event,
|
||||
payload: payload,
|
||||
time: Date.now() - this.startTime,
|
||||
didCauseUpdate: true,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Play back a log entry.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
*```ts
|
||||
* logger.playback(log)
|
||||
*```
|
||||
*/
|
||||
playback = (data: Data, log: string): Logger => {
|
||||
const parsed: { start: Data; end: Data; events: LogEntry[] } =
|
||||
JSON.parse(log)
|
||||
|
||||
const { start, events } = parsed
|
||||
|
||||
this.isSimulating = true
|
||||
|
||||
try {
|
||||
data.pageStates[data.currentPageId].selectedIds = new Set(
|
||||
start.pageStates[start.currentPageId].selectedIds
|
||||
)
|
||||
|
||||
state.send('RESET_DOCUMENT_STATE').forceData(start)
|
||||
|
||||
const pointerDowns = [
|
||||
'POINTED_CANVAS',
|
||||
'POINTED_SHAPE',
|
||||
'POINTED_BOUNDS',
|
||||
'DOUBLE_POINTED_SHAPE',
|
||||
'POINTED_HANDLE',
|
||||
'RIGHT_POINTED',
|
||||
]
|
||||
|
||||
const pointerChanges = [
|
||||
...pointerDowns,
|
||||
'MOVED_POINTER',
|
||||
'MOVED_OVER_SHAPE',
|
||||
'STOPPED_POINTING',
|
||||
'DOUBLE_POINTED_CANVAS',
|
||||
]
|
||||
|
||||
const pointerUps = ['STOPPED_POINTING']
|
||||
|
||||
for (const event of events) {
|
||||
if (pointerDowns.includes(event.eventName)) {
|
||||
inputs.pointer.origin = event.payload.point
|
||||
}
|
||||
|
||||
if (pointerChanges.includes(event.eventName)) {
|
||||
inputs.activePointerId = event.payload.pointerId
|
||||
inputs.pointer = { ...inputs.pointer, ...event.payload }
|
||||
inputs.points[event.payload.pointerId] = inputs.pointer
|
||||
}
|
||||
|
||||
if (pointerUps.includes(event.eventName)) {
|
||||
delete inputs.points[event.payload.pointerId]
|
||||
delete inputs.activePointerId
|
||||
|
||||
// TODO: Double pointing
|
||||
}
|
||||
|
||||
state.send(event.eventName, event.payload)
|
||||
}
|
||||
|
||||
setTimeout(
|
||||
() => (this.isSimulating = false),
|
||||
events[events.length - 1].time + 1
|
||||
)
|
||||
} catch (e) {
|
||||
console.warn('Could not play back that state.')
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the log as JSON.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
*```ts
|
||||
* logger.toJson()
|
||||
*```
|
||||
*/
|
||||
copyToJson = () => {
|
||||
const logAsString = JSON.stringify({
|
||||
start: this.snapshotStart,
|
||||
end: this.snapshotEnd,
|
||||
events: this.log,
|
||||
})
|
||||
|
||||
clipboard.copyStringToClipboard(logAsString)
|
||||
|
||||
return logAsString
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new event filter. Filtered events will not be logged.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
*```ts
|
||||
* logger.addFilter("SOME_EVENT")
|
||||
*```
|
||||
*/
|
||||
addFilter(eventName: string) {
|
||||
this.filters.add(eventName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an event from the filters. Filtered events will not be logged.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
*```ts
|
||||
* logger.removeFilter("SOME_EVENT")
|
||||
*```
|
||||
*/
|
||||
removeFilter(eventName: string) {
|
||||
this.filters.delete(eventName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all of the filtered events.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
*```ts
|
||||
* logger.setFilters(["SOME_EVENT", "SOME_OTHER_EVENT"])
|
||||
*```
|
||||
*/
|
||||
setFilters(eventNames: string[]) {
|
||||
this.filters = new Set(eventNames)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the current set of event filters.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
*```ts
|
||||
* logger.clear()
|
||||
*```
|
||||
*/
|
||||
clearFilters() {
|
||||
this.filters.clear()
|
||||
}
|
||||
}
|
||||
|
||||
export default new Logger()
|
|
@ -37,6 +37,7 @@ import {
|
|||
ColorStyle,
|
||||
} from 'types'
|
||||
import { getFontSize } from './shape-styles'
|
||||
import logger from './logger'
|
||||
|
||||
const initialData: Data = {
|
||||
isReadOnly: false,
|
||||
|
@ -44,6 +45,8 @@ const initialData: Data = {
|
|||
fontSize: 13,
|
||||
isDarkMode: false,
|
||||
isCodeOpen: false,
|
||||
isDebugMode: false,
|
||||
isDebugOpen: false,
|
||||
isStyleOpen: false,
|
||||
isToolLocked: false,
|
||||
isPenLocked: false,
|
||||
|
@ -141,12 +144,22 @@ for (let i = 0; i < count; i++) {
|
|||
|
||||
const state = createState({
|
||||
data: initialData,
|
||||
on: {
|
||||
TOGGLED_DEBUG_PANEL: 'toggleDebugPanel',
|
||||
TOGGLED_DEBUG_MODE: 'toggleDebugMode',
|
||||
TOGGLED_LOGGER: 'toggleLogger',
|
||||
COPIED_DEBUG_LOG: 'copyDebugLog',
|
||||
LOADED_FROM_SNAPSHOT: {
|
||||
unless: 'isInSession',
|
||||
do: ['loadDocumentFromJson', 'resetHistory'],
|
||||
},
|
||||
},
|
||||
initial: 'loading',
|
||||
states: {
|
||||
loading: {
|
||||
on: {
|
||||
MOUNTED: {
|
||||
do: 'restoredPreviousDocument',
|
||||
do: ['resetHistory', 'restoredPreviousDocument'],
|
||||
to: 'ready',
|
||||
},
|
||||
},
|
||||
|
@ -160,13 +173,18 @@ const state = createState({
|
|||
},
|
||||
on: {
|
||||
UNMOUNTED: {
|
||||
do: ['saveAppState', 'saveDocumentState', 'resetDocumentState'],
|
||||
do: [
|
||||
'saveAppState',
|
||||
'saveDocumentState',
|
||||
'resetDocumentState',
|
||||
'resetHistory',
|
||||
],
|
||||
to: 'loading',
|
||||
},
|
||||
// Network-Related
|
||||
RT_LOADED_ROOM: [
|
||||
'clearRoom',
|
||||
{ if: 'hasRoom', do: 'resetDocumentState' },
|
||||
{ if: 'hasRoom', do: ['resetDocumentState', 'resetHistory'] },
|
||||
],
|
||||
// RT_UNLOADED_ROOM: ['clearRoom', 'resetDocumentState'],
|
||||
// RT_DISCONNECTED_ROOM: ['clearRoom', 'resetDocumentState'],
|
||||
|
@ -176,7 +194,11 @@ const state = createState({
|
|||
// RT_EDITED_SHAPE: 'editRtShape',
|
||||
// Client
|
||||
RESIZED_WINDOW: 'resetPageState',
|
||||
RESET_DOCUMENT_STATE: 'resetDocumentState',
|
||||
RESET_DOCUMENT_STATE: [
|
||||
'resetHistory',
|
||||
'resetDocumentState',
|
||||
{ to: 'selecting' },
|
||||
],
|
||||
TOGGLED_READ_ONLY: 'toggleReadOnly',
|
||||
LOADED_FONTS: 'resetShapes',
|
||||
USED_PEN_DEVICE: 'enablePenLock',
|
||||
|
@ -729,6 +751,9 @@ const state = createState({
|
|||
do: 'createShape',
|
||||
to: 'draw.editing',
|
||||
},
|
||||
STOPPED_POINTING: {
|
||||
to: 'draw.creating',
|
||||
},
|
||||
},
|
||||
},
|
||||
editing: {
|
||||
|
@ -745,8 +770,11 @@ const state = createState({
|
|||
},
|
||||
PRESSED_SHIFT: 'keyUpdateDrawSession',
|
||||
RELEASED_SHIFT: 'keyUpdateDrawSession',
|
||||
// MOVED_POINTER: 'updateDrawSession',
|
||||
PANNED_CAMERA: 'updateDrawSession',
|
||||
MOVED_POINTER: {
|
||||
if: 'isSimulating',
|
||||
do: 'updateDrawSession',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1041,7 +1069,7 @@ const state = createState({
|
|||
to: 'selecting',
|
||||
},
|
||||
},
|
||||
initial: 'drawingShapeBounds',
|
||||
initial: 'bounds',
|
||||
states: {
|
||||
bounds: {
|
||||
onEnter: 'startDrawTransformSession',
|
||||
|
@ -1096,6 +1124,9 @@ const state = createState({
|
|||
},
|
||||
},
|
||||
conditions: {
|
||||
isSimulating() {
|
||||
return logger.isSimulating
|
||||
},
|
||||
hasRoom(_, payload: { id?: string }) {
|
||||
return payload.id !== undefined
|
||||
},
|
||||
|
@ -1203,6 +1234,30 @@ const state = createState({
|
|||
},
|
||||
},
|
||||
actions: {
|
||||
/* ---------------------- Debug --------------------- */
|
||||
|
||||
closeDebugPanel(data) {
|
||||
data.settings.isDebugOpen = false
|
||||
},
|
||||
openDebugPanel(data) {
|
||||
data.settings.isDebugOpen = true
|
||||
},
|
||||
toggleDebugMode(data) {
|
||||
data.settings.isDebugMode = !data.settings.isDebugMode
|
||||
},
|
||||
toggleDebugPanel(data) {
|
||||
data.settings.isDebugOpen = !data.settings.isDebugOpen
|
||||
},
|
||||
toggleLogger(data) {
|
||||
if (logger.isRunning) {
|
||||
logger.stop(data)
|
||||
} else {
|
||||
logger.start(data)
|
||||
}
|
||||
},
|
||||
copyDebugLog() {
|
||||
logger.copyToJson()
|
||||
},
|
||||
// Networked Room
|
||||
addRtShape(data, payload: { pageId: string; shape: Shape }) {
|
||||
const { pageId, shape } = payload
|
||||
|
@ -1227,6 +1282,8 @@ const state = createState({
|
|||
|
||||
session.cancel(data)
|
||||
|
||||
inputs.reset()
|
||||
|
||||
const newId = 'page1'
|
||||
|
||||
data.currentPageId = newId
|
||||
|
@ -1315,6 +1372,7 @@ const state = createState({
|
|||
|
||||
tld.setSelectedIds(data, [shape.id])
|
||||
},
|
||||
|
||||
/* -------------------- Sessions -------------------- */
|
||||
|
||||
// Shared
|
||||
|
@ -2091,6 +2149,11 @@ const state = createState({
|
|||
return commonStyle
|
||||
},
|
||||
},
|
||||
options: {
|
||||
onSend(eventName, payload, didCauseUpdate) {
|
||||
logger.addToLog(eventName, payload, didCauseUpdate)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default state
|
||||
|
|
2
types.ts
2
types.ts
|
@ -8,6 +8,8 @@ export interface Data {
|
|||
fontSize: number
|
||||
isDarkMode: boolean
|
||||
isCodeOpen: boolean
|
||||
isDebugOpen: boolean
|
||||
isDebugMode: boolean
|
||||
isStyleOpen: boolean
|
||||
nudgeDistanceSmall: number
|
||||
nudgeDistanceLarge: number
|
||||
|
|
Loading…
Reference in a new issue