-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Put together inspector view using observablehq
- Inspector document at the moment just contains `targetURL` field of the document being inspected, but in the future it will be extended to store state like (un)folded substructures and possibly more. - At the moment view initially renders title editor to enter `targetURL` but ideally it would possibly have dropdown like omnibar to just choose document from the active board. I also would like to have long press on Option/Alt key to activate overlay over widgets with a button to launch an inspector for any of the widgets.
- Loading branch information
Showing
5 changed files
with
282 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
:root { | ||
--syntax_normal: #1b1e23; | ||
--syntax_comment: #a9b0bc; | ||
--syntax_number: #20a5ba; | ||
--syntax_keyword: #c30771; | ||
--syntax_atom: #10a778; | ||
--syntax_string: #008ec4; | ||
--syntax_error: #ffbedc; | ||
--syntax_unknown_variable: #838383; | ||
--syntax_known_variable: #005f87; | ||
--syntax_matchbracket: #20bbfc; | ||
--syntax_key: #6636b4; | ||
--mono_fonts: 82%/1.5 Menlo, Consolas, monospace; | ||
} | ||
|
||
.Inspector { | ||
display: flex; | ||
background-color: white; | ||
width: 100%; | ||
overflow: auto; | ||
height: 100%; | ||
padding: 1px 1px 0px 1px; | ||
} | ||
|
||
.observablehq--expanded, | ||
.observablehq--collapsed, | ||
.observablehq--function, | ||
.observablehq--import, | ||
.observablehq--string:before, | ||
.observablehq--string:after, | ||
.observablehq--gray { | ||
color: var(--syntax_normal); | ||
} | ||
|
||
.observablehq--collapsed, | ||
.observablehq--inspect a { | ||
cursor: pointer; | ||
} | ||
|
||
.observablehq--field { | ||
text-indent: -1em; | ||
margin-left: 1em; | ||
} | ||
|
||
.observablehq--empty { | ||
color: var(--syntax_comment); | ||
} | ||
|
||
.observablehq--keyword, | ||
.observablehq--blue { | ||
color: #3182bd; | ||
} | ||
|
||
.observablehq--forbidden, | ||
.observablehq--pink { | ||
color: #e377c2; | ||
} | ||
|
||
.observablehq--orange { | ||
color: #e6550d; | ||
} | ||
|
||
.observablehq--null, | ||
.observablehq--undefined, | ||
.observablehq--boolean { | ||
color: var(--syntax_atom); | ||
} | ||
|
||
.observablehq--number, | ||
.observablehq--bigint, | ||
.observablehq--date, | ||
.observablehq--regexp, | ||
.observablehq--symbol, | ||
.observablehq--green { | ||
color: var(--syntax_number); | ||
} | ||
|
||
.observablehq--index, | ||
.observablehq--key { | ||
color: var(--syntax_key); | ||
} | ||
|
||
.observablehq--empty { | ||
font-style: oblique; | ||
} | ||
|
||
.observablehq--string, | ||
.observablehq--purple { | ||
color: var(--syntax_string); | ||
} | ||
|
||
.observablehq--error, | ||
.observablehq--red { | ||
color: #e7040f; | ||
} | ||
|
||
.observablehq--inspect { | ||
font: var(--mono_fonts); | ||
overflow-x: auto; | ||
display: block; | ||
white-space: pre; | ||
} | ||
|
||
.observablehq--error .observablehq--inspect { | ||
word-break: break-all; | ||
white-space: pre-wrap; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import React, { useEffect, useRef } from 'react' | ||
import { Handle } from 'hypermerge' | ||
|
||
import * as Observable from '@observablehq/inspector' | ||
import * as ContentTypes from '../../ContentTypes' | ||
import { ContentProps } from '../Content' | ||
import { HypermergeUrl, parts } from '../../ShareLink' | ||
import { useDocument } from '../../Hooks' | ||
import Badge from '../Badge' | ||
import TitleEditor from '../TitleEditor' | ||
import * as ContentData from '../../ContentData' | ||
// import '@observablehq/inspector/src/style.css' | ||
import './Inspector.css' | ||
|
||
interface InspectorDoc { | ||
targetURL: HypermergeUrl | ||
} | ||
|
||
interface Props extends ContentProps { | ||
uniquelySelected?: boolean | ||
} | ||
|
||
interface InspectorOpts { | ||
targetURL: HypermergeUrl | null | ||
change: (cb: (doc: InspectorDoc) => void) => void | ||
} | ||
|
||
Inspector.minWidth = 6 | ||
Inspector.minHeight = 2 | ||
Inspector.defaultWidth = 12 | ||
Inspector.defaultHeight = 18 | ||
|
||
const decodeTargetURL = (url: string) => { | ||
try { | ||
const { scheme, docId } = parts(url) | ||
if (scheme !== 'hypermerge') { | ||
throw new Error(`Invalid url scheme: ${scheme} (expected hypermerge)`) | ||
} | ||
|
||
if (!docId) { | ||
throw new Error(`Missing docId in ${url}`) | ||
} | ||
|
||
return `hypermerge:/${docId}` as HypermergeUrl | ||
} catch (_) { | ||
return null | ||
} | ||
} | ||
|
||
export default function Inspector<d>(props: Props) { | ||
const [doc, changeDoc] = useDocument<InspectorDoc>(props.hypermergeUrl) | ||
const targetURL = doc && decodeTargetURL(doc.targetURL) | ||
|
||
const [ref] = useInspector({ | ||
targetURL, | ||
change(fn: (doc: InspectorDoc) => void) { | ||
changeDoc((doc) => fn(doc)) | ||
}, | ||
}) | ||
const placeholder = `URL of the document to inspect e.g ${props.hypermergeUrl}` | ||
|
||
return ( | ||
<div className="Inspector" onPaste={stopPropagation}> | ||
<TitleEditor field="targetURL" url={props.hypermergeUrl} placeholder={placeholder} /> | ||
<div ref={ref} /> | ||
</div> | ||
) | ||
} | ||
|
||
function useInspector<d>({ | ||
targetURL, | ||
change, | ||
}: InspectorOpts): [React.Ref<HTMLDivElement>, Observable.Inspector | null] { | ||
const ref = useRef<HTMLDivElement>(null) | ||
const inspector = useRef<Observable.Inspector | null>(null) | ||
const [targetDoc] = useDocument(targetURL) | ||
// const makeChange = useStaticCallback(change) | ||
|
||
useEffect(() => { | ||
if (!ref.current) return () => {} | ||
|
||
const container = ref.current | ||
inspector.current = new Observable.Inspector(container) | ||
inspector.current.pending() | ||
|
||
return () => { | ||
// inspector.current = null | ||
container.textContent = '' | ||
|
||
inspector.current = new Observable.Inspector(container) | ||
inspector.current.pending() | ||
} | ||
}, [ref.current]) // eslint-disable-line | ||
|
||
useEffect(() => { | ||
if (!inspector.current) return () => {} | ||
|
||
if (targetDoc) { | ||
inspector.current.fulfilled(targetDoc) | ||
} | ||
|
||
return () => { | ||
if (ref.current) { | ||
ref.current.textContent = '' | ||
} | ||
} | ||
}, [targetDoc, targetURL]) | ||
|
||
return [ref, inspector.current] | ||
} | ||
|
||
function stopPropagation(e: React.SyntheticEvent) { | ||
e.stopPropagation() | ||
e.nativeEvent.stopImmediatePropagation() | ||
} | ||
|
||
async function createFrom(contentData: ContentData.ContentData, handle: Handle<InspectorDoc>) { | ||
handle.change((doc) => {}) | ||
} | ||
|
||
function create({ text }, handle: Handle<InspectorDoc>) { | ||
handle.change((doc) => {}) | ||
} | ||
|
||
function InspectorInList(props: ContentProps) { | ||
const [doc] = useDocument<InspectorDoc>(props.hypermergeUrl) | ||
function onDragStart(e: React.DragEvent) { | ||
e.dataTransfer.setData('application/pushpin-url', props.url) | ||
} | ||
|
||
if (!doc) return null | ||
|
||
return ( | ||
<div className="DocLink"> | ||
<span draggable onDragStart={onDragStart}> | ||
<Badge icon="sticky-note" /> | ||
</span> | ||
<TitleEditor | ||
field="targetURL" | ||
url={props.hypermergeUrl} | ||
placeholder="URL of the document to inpsect" | ||
/> | ||
</div> | ||
) | ||
} | ||
|
||
const supportsMimeType = (mimeType) => true | ||
|
||
ContentTypes.register({ | ||
type: 'inspect', | ||
name: 'Inspector', | ||
icon: 'cogs', | ||
contexts: { | ||
board: Inspector, | ||
workspace: Inspector, | ||
list: InspectorInList, | ||
}, | ||
create, | ||
createFrom, | ||
supportsMimeType, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters