a million fixes
This commit is contained in:
parent
b1696499e5
commit
c294d7fd6a
12 changed files with 140 additions and 168 deletions
|
|
@ -33,7 +33,7 @@ canvas {
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<button id="clear-storage">Clear Storage</button>
|
<!-- <button id="clear-storage">Clear Storage</button> -->
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
22
src/ast.ts
22
src/ast.ts
|
|
@ -17,7 +17,7 @@ export type Variable = {
|
||||||
kind: 'variable'
|
kind: 'variable'
|
||||||
name: string
|
name: string
|
||||||
line?: number
|
line?: number
|
||||||
column: number
|
column?: number
|
||||||
start?: number
|
start?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,24 +148,6 @@ export type Definition = {
|
||||||
start?: number
|
start?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TypeDef = {
|
|
||||||
kind: 'typedef'
|
|
||||||
name: string
|
|
||||||
variants: Array<{ name: string, args: string[] }>
|
|
||||||
line?: number
|
|
||||||
column?: number
|
|
||||||
start?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Import = {
|
|
||||||
kind: 'import'
|
|
||||||
module: string
|
|
||||||
items: string[] | 'all'
|
|
||||||
line?: number
|
|
||||||
column?: number
|
|
||||||
start?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Rebind = {
|
export type Rebind = {
|
||||||
kind: 'rebind'
|
kind: 'rebind'
|
||||||
target: AST
|
target: AST
|
||||||
|
|
@ -189,8 +171,6 @@ export type AST =
|
||||||
| List
|
| List
|
||||||
| ListSpread
|
| ListSpread
|
||||||
| Definition
|
| Definition
|
||||||
| TypeDef
|
|
||||||
| Import
|
|
||||||
| Rebind
|
| Rebind
|
||||||
|
|
||||||
export function prettyPrint(ast: AST, indent = 0): string {
|
export function prettyPrint(ast: AST, indent = 0): string {
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@ textInput = config \ ui.stateful {
|
||||||
|
|
||||||
| Focused \ { state = state.{ focused = True }, emit = [] }
|
| Focused \ { state = state.{ focused = True }, emit = [] }
|
||||||
| Blurred \ { state = state.{ focused = False }, emit = [] }
|
| Blurred \ { state = state.{ focused = False }, emit = [] }
|
||||||
| Key { key = k } \ { state = state, emit = [\ config.onKeyDown k ] }
|
# | Key { key = k } \ { state = state, emit = [\ config.onKeyDown k ] }
|
||||||
| _ \ { state = state, emit = [] },
|
| _ \ { state = state, emit = [] },
|
||||||
|
|
||||||
view = state \
|
view = state \
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
# standard library testing
|
|
||||||
|
|
||||||
list = [1, 2, 3, 4, 5];
|
|
||||||
add1 = x \ x + 1;
|
|
||||||
mapped = map add1 list;
|
|
||||||
isEven = x \ x % 2 == 0;
|
|
||||||
sumList = list \ fold add 0 list;
|
|
||||||
filtered = filter isEven mapped;
|
|
||||||
summed = sumList mapped;
|
|
||||||
listLen = len list;
|
|
||||||
reversed = reverse list;
|
|
||||||
take3 = take 3 list;
|
|
||||||
drop3 = drop 3 list;
|
|
||||||
list2 = [6, 7, 8];
|
|
||||||
zipped = zipWith add list list2;
|
|
||||||
anded = and True False;
|
|
||||||
ored = or True False;
|
|
||||||
notted = not False;
|
|
||||||
ranged = range 3 14;
|
|
||||||
anyEven = any isEven list;
|
|
||||||
allEven = all isEven list;
|
|
||||||
flattened = flatten [[1, 2, 3], [4, 5, 6]];
|
|
||||||
contains3 = contains 3 list;
|
|
||||||
findEven = find isEven list;
|
|
||||||
|
|
||||||
stdlibTestResult = {
|
|
||||||
filtered = filtered,
|
|
||||||
summed = summed,
|
|
||||||
listLen = listLen,
|
|
||||||
reversed = reversed,
|
|
||||||
take3 = take3,
|
|
||||||
drop3 = drop3,
|
|
||||||
zipped = zipped,
|
|
||||||
anded = anded,
|
|
||||||
ored = ored,
|
|
||||||
notted = notted,
|
|
||||||
ranged = ranged,
|
|
||||||
anyEven = anyEven,
|
|
||||||
allEven = allEven,
|
|
||||||
flattened = flattened,
|
|
||||||
contains3 = contains3,
|
|
||||||
findEven = findEven
|
|
||||||
};
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# TODO: Section labels - flat list with Section/Item types,
|
# TODO
|
||||||
|
# Section labels - flat list with Section/Item types,
|
||||||
# skip sections in keyboard nav, when scrolling with keyboard
|
# skip sections in keyboard nav, when scrolling with keyboard
|
||||||
# itemHeight
|
# itemHeight
|
||||||
# items = [
|
# items = [
|
||||||
|
|
@ -30,14 +31,17 @@
|
||||||
# | None \ currentIndex);
|
# | None \ currentIndex);
|
||||||
|
|
||||||
|
|
||||||
|
paletteState = {
|
||||||
|
query = ""
|
||||||
|
};
|
||||||
|
|
||||||
palette = config \
|
palette = config \
|
||||||
focusedIndex = 0;
|
focusedIndex = 0;
|
||||||
windowHeight = 400;
|
windowHeight = 400;
|
||||||
windowWidth = 600;
|
windowWidth = 600;
|
||||||
|
|
||||||
results = take 8 (config.search config.state.query);
|
results = take 8 (config.search paletteState.query);
|
||||||
# results = config.search config.state.query; # once you get scrolling..
|
# results = config.search paletteState.query; # once you get scrolling..
|
||||||
|
|
||||||
dialogPadding = 0;
|
dialogPadding = 0;
|
||||||
|
|
||||||
|
|
@ -64,6 +68,22 @@ palette = config \
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ui.stateful {
|
||||||
|
key = "palette",
|
||||||
|
focusable = False,
|
||||||
|
|
||||||
|
init = {
|
||||||
|
focusedIndex = 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
update = state event \ event
|
||||||
|
| Key { printable = True } \ { state = state.{ focusedIndex = 0 }, emit = [] }
|
||||||
|
| Key { key = "ArrowUp" } \ { state = state.{ focusedIndex = max 0 (state.focusedIndex - 1) }, emit = [] }
|
||||||
|
| Key { key = "ArrowDown" } \ { state = state.{ focusedIndex = min (len results - 1) (state.focusedIndex + 1) }, emit = [] }
|
||||||
|
| Key { key = "Enter" } \ _ = debug "Enter" []; { state = state, emit = [config.onSelect (unwrapOr "" (nth state.focusedIndex results))] }
|
||||||
|
| e \ _ = debug "event" e; { state = state, emit = [] },
|
||||||
|
|
||||||
|
view = state \
|
||||||
ui.positioned {
|
ui.positioned {
|
||||||
x = (config.viewport.width - windowWidth) / 2,
|
x = (config.viewport.width - windowWidth) / 2,
|
||||||
y = (config.viewport.height - windowHeight) / 2,
|
y = (config.viewport.height - windowHeight) / 2,
|
||||||
|
|
@ -78,18 +98,13 @@ palette = config \
|
||||||
children = [
|
children = [
|
||||||
textInput {
|
textInput {
|
||||||
key = "palette-query",
|
key = "palette-query",
|
||||||
initialValue = config.state.query,
|
initialValue = "",
|
||||||
initialFocus = True,
|
initialFocus = True,
|
||||||
color = "white",
|
color = "white",
|
||||||
backgroundColor = "rgba(0,0,0,0.2)",
|
backgroundColor = "rgba(0,0,0,0.2)",
|
||||||
w = contentWidth,
|
w = contentWidth,
|
||||||
h = textInputHeight,
|
h = textInputHeight,
|
||||||
onChange = text \ batch [config.state.query := text, config.state.focusedIndex := 0],
|
onChange = text \ batch [paletteState.query := text],
|
||||||
onKeyDown = key \ key
|
|
||||||
| "ArrowUp" \ config.state.focusedIndex := max 0 (config.state.focusedIndex - 1)
|
|
||||||
| "ArrowDown" \ config.state.focusedIndex := min (len results - 1) (config.state.focusedIndex + 1)
|
|
||||||
| "Enter" \ (\ config.onSelect (unwrapOr "" (nth config.state.focusedIndex results)))
|
|
||||||
| _ \ noOp
|
|
||||||
},
|
},
|
||||||
|
|
||||||
ui.clip {
|
ui.clip {
|
||||||
|
|
@ -111,7 +126,7 @@ palette = config \
|
||||||
child = t,
|
child = t,
|
||||||
w = contentWidth,
|
w = contentWidth,
|
||||||
h = textInputHeight,
|
h = textInputHeight,
|
||||||
selected = (config.state.focusedIndex == i),
|
selected = (state.focusedIndex == i),
|
||||||
onClick = \ config.onSelect t
|
onClick = \ config.onSelect t
|
||||||
}) results)
|
}) results)
|
||||||
]
|
]
|
||||||
|
|
@ -122,4 +137,5 @@ palette = config \
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
osState = {
|
osState = {
|
||||||
palette = {
|
palette = {
|
||||||
visible = True,
|
visible = True
|
||||||
query = "",
|
|
||||||
focusedIndex = 0,
|
|
||||||
},
|
},
|
||||||
inspector = {
|
inspector = {
|
||||||
visible = False,
|
visible = False,
|
||||||
|
|
@ -37,8 +35,9 @@ inspect = item \
|
||||||
init = {};
|
init = {};
|
||||||
|
|
||||||
update = state event \ event
|
update = state event \ event
|
||||||
| Key { key = "p", meta = True } \ osState.palette.visible := not (osState.palette.visible)
|
| Key { key = "p", meta = True } \
|
||||||
| _ \ state;
|
{ state = state, emit = [osState.palette.visible := not (osState.palette.visible)] }
|
||||||
|
| _ \ { state = state, emit = [] };
|
||||||
|
|
||||||
view = state viewport \
|
view = state viewport \
|
||||||
ui.stack {
|
ui.stack {
|
||||||
|
|
@ -55,13 +54,13 @@ view = state viewport \
|
||||||
# keep palette at the end so it's on top
|
# keep palette at the end so it's on top
|
||||||
osState.palette.visible
|
osState.palette.visible
|
||||||
| True \ palette {
|
| True \ palette {
|
||||||
state = osState.palette,
|
# state = osState.palette,
|
||||||
|
# query = osState.palette.query,
|
||||||
search = storeSearch,
|
search = storeSearch,
|
||||||
onSelect = onSelect,
|
onSelect = onSelect,
|
||||||
viewport = viewport,
|
viewport = viewport,
|
||||||
}
|
}
|
||||||
| False \ empty,
|
| False \ empty,
|
||||||
|
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,10 +85,7 @@ export function compile(ast: AST, useStore = true, bound = new Set<string>()): s
|
||||||
const path = getPath(ast.target);
|
const path = getPath(ast.target);
|
||||||
const value = compile(ast.value, useStore, bound);
|
const value = compile(ast.value, useStore, bound);
|
||||||
|
|
||||||
if (!useStore || !rootName) {
|
if (!rootName) throw new Error('Rebind target must be a variable');
|
||||||
const target = compile(ast.target, useStore, bound);
|
|
||||||
return `(() => { ${target} = ${value}; return { _tag: "Rerender" }; })()`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.length === 0) {
|
if (path.length === 0) {
|
||||||
return `({ _tag: "Rebind", _0: "${rootName}", _1: ${value} })`;
|
return `({ _tag: "Rebind", _0: "${rootName}", _1: ${value} })`;
|
||||||
|
|
@ -105,15 +102,15 @@ export function compile(ast: AST, useStore = true, bound = new Set<string>()): s
|
||||||
|
|
||||||
function sanitize(name: string, useStore = true): string {
|
function sanitize(name: string, useStore = true): string {
|
||||||
const ops: Record<string, string> = {
|
const ops: Record<string, string> = {
|
||||||
'add': '_rt.add', 'sub': '_rt.sub', 'mul': '_rt.mul',
|
'add': '_rt.add', 'sub': '_rt.sub', 'mul': '_rt.mul', 'div': '_rt.div',
|
||||||
'div': '_rt.div', 'mod': '_rt.mod', 'eq': '_rt.eq',
|
'mod': '_rt.mod', 'pow': '_rt.pow',
|
||||||
'cat': '_rt.cat', 'gt': '_rt.gt', 'lt': '_rt.lt',
|
'eq': '_rt.eq', 'gt': '_rt.gt', 'lt': '_rt.lt', 'gte': '_rt.gte', 'lte': '_rt.lte',
|
||||||
'gte': '_rt.gte', 'lte': '_rt.lte', 'max': '_rt.max', 'min': '_rt.min',
|
'cat': '_rt.cat', 'max': '_rt.max', 'min': '_rt.min',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ops[name]) return ops[name];
|
if (ops[name]) return ops[name];
|
||||||
|
|
||||||
const natives = ['eval', 'measure', 'measureText', 'storeSearch', 'getSource', 'debug', 'nth', 'len', 'slice', 'join', 'chars', 'split', 'str', 'redefine', 'undefine', 'stateful', 'batch', 'noOp', 'rerender', 'focus', 'ui'];
|
const natives = ['eval', 'measure', 'measureText', 'storeSearch', 'getSource', 'debug', 'nth', 'len', 'slice', 'join', 'chars', 'split', 'str', 'redefine', 'undefine', 'batch', 'noOp', 'focus', 'ui'];
|
||||||
if (natives.includes(name)) return `_rt.${name}`;
|
if (natives.includes(name)) return `_rt.${name}`;
|
||||||
|
|
||||||
if (useStore) {
|
if (useStore) {
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ export class Parser {
|
||||||
let expr = this.parseInfix();
|
let expr = this.parseInfix();
|
||||||
|
|
||||||
// Rebind
|
// Rebind
|
||||||
if (this.current().kind == 'colon-equals') {
|
if (this.current().kind === 'colon-equals') {
|
||||||
const token = this.current();
|
const token = this.current();
|
||||||
this.advance();
|
this.advance();
|
||||||
const value = this.parseExpressionNoMatch();
|
const value = this.parseExpressionNoMatch();
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
||||||
handleEvent(e);
|
handleEvent(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rerender();
|
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.error('Component event error:', error);
|
console.error('Component event error:', error);
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +101,7 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
||||||
event: { _tag: 'FocusAndClick', _0: fullKey }
|
event: { _tag: 'FocusAndClick', _0: fullKey }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return expandStateful(viewUI, path, renderedKeys);
|
return expandStateful(viewUI, [...path, ui.key], renderedKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'stack':
|
case 'stack':
|
||||||
|
|
@ -186,7 +185,6 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
||||||
|
|
||||||
if (event._tag === 'Rebind') {
|
if (event._tag === 'Rebind') {
|
||||||
rt.rebind(event._0, event._1, event._2);
|
rt.rebind(event._0, event._1, event._2);
|
||||||
rerender();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,8 +196,13 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
||||||
if (event._tag === 'NoOp')
|
if (event._tag === 'NoOp')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
state = app.update(state)(event);
|
const result = app.update(state)(event);
|
||||||
rerender();
|
state = result.state;
|
||||||
|
if (result.emit && Array.isArray(result.emit)) {
|
||||||
|
for (const e of result.emit) {
|
||||||
|
handleEvent(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.addEventListener('click', (e) => {
|
canvas.addEventListener('click', (e) => {
|
||||||
|
|
@ -221,6 +224,8 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
||||||
handleEvent(event);
|
handleEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rerender();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('keydown', (e) => {
|
window.addEventListener('keydown', (e) => {
|
||||||
|
|
@ -236,20 +241,37 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// always send to OS first
|
console.log("keydown", e)
|
||||||
handleEvent(event);
|
console.log("componentInstances", componentInstances)
|
||||||
|
console.log("focusedComponentKey", focusedComponentKey)
|
||||||
|
|
||||||
if (focusedComponentKey) {
|
if (focusedComponentKey) {
|
||||||
|
// send to focused component
|
||||||
handleComponentEvent(focusedComponentKey, event);
|
handleComponentEvent(focusedComponentKey, event);
|
||||||
|
|
||||||
|
// bubble up to ancestors
|
||||||
|
for (const key of componentInstances.keys()) {
|
||||||
|
if (key !== focusedComponentKey && focusedComponentKey.startsWith(key + '.')) {
|
||||||
|
handleComponentEvent(key, event);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OS root
|
||||||
|
handleEvent(event);
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
rerender();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let resizeRAF = 0;
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
|
cancelAnimationFrame(resizeRAF);
|
||||||
|
resizeRAF = requestAnimationFrame(() => {
|
||||||
setupCanvas();
|
setupCanvas();
|
||||||
rerender();
|
rerender();
|
||||||
})
|
});
|
||||||
|
});
|
||||||
|
|
||||||
rerender();
|
rerender();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ export const _rt = {
|
||||||
mul: (a: number) => (b: number) => a * b,
|
mul: (a: number) => (b: number) => a * b,
|
||||||
div: (a: number) => (b: number) => a / b,
|
div: (a: number) => (b: number) => a / b,
|
||||||
mod: (a: number) => (b: number) => a % b,
|
mod: (a: number) => (b: number) => a % b,
|
||||||
cat: (a: string) => (b: string) => a + b,
|
pow: (a: number) => (b: number) => a ** b,
|
||||||
|
cat: (a: any) => (b: any) => Array.isArray(a) ? [...a, ...b] : a + b,
|
||||||
max: (a: number) => (b: number) => Math.max(a, b),
|
max: (a: number) => (b: number) => Math.max(a, b),
|
||||||
min: (a: number) => (b: number) => Math.min(a, b),
|
min: (a: number) => (b: number) => Math.min(a, b),
|
||||||
|
|
||||||
|
|
@ -110,7 +111,6 @@ export const _rt = {
|
||||||
|
|
||||||
batch: (events: any[]) => ({ _tag: 'Batch', _0: events }),
|
batch: (events: any[]) => ({ _tag: 'Batch', _0: events }),
|
||||||
noOp: { _tag: 'NoOp' },
|
noOp: { _tag: 'NoOp' },
|
||||||
rerender: { _tag: 'Rerender' },
|
|
||||||
focus: (key: string) => ({ _tag: 'Focus', _0: key }),
|
focus: (key: string) => ({ _tag: 'Focus', _0: key }),
|
||||||
|
|
||||||
nth: (i: number) => (xs: any[] | string) => i >= 0 && i < xs.length
|
nth: (i: number) => (xs: any[] | string) => i >= 0 && i < xs.length
|
||||||
|
|
@ -142,6 +142,7 @@ export const _rt = {
|
||||||
return printed;
|
return printed;
|
||||||
},
|
},
|
||||||
rebind: (name: string, pathOrValue: any, maybeValue?: any) => {
|
rebind: (name: string, pathOrValue: any, maybeValue?: any) => {
|
||||||
|
console.log("rebind", store, name, pathOrValue, maybeValue);
|
||||||
if (maybeValue === undefined) {
|
if (maybeValue === undefined) {
|
||||||
store[name] = pathOrValue;
|
store[name] = pathOrValue;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -202,12 +203,12 @@ export const _rt = {
|
||||||
const defs = parser.parse();
|
const defs = parser.parse();
|
||||||
const ast = defs[0].body;
|
const ast = defs[0].body;
|
||||||
// validate free vars
|
// validate free vars
|
||||||
const free = freeVars(ast);
|
// const free = freeVars(ast);
|
||||||
const allowed = new Set([
|
// const allowed = new Set([
|
||||||
...Object.keys(store),
|
// ...Object.keys(store),
|
||||||
...Object.keys(_rt),
|
// ...Object.keys(_rt),
|
||||||
'True'
|
// 'True'
|
||||||
])
|
// ])
|
||||||
|
|
||||||
const compiled = compile(defs[0].body);
|
const compiled = compile(defs[0].body);
|
||||||
const fn = new Function('_rt', 'store', `return ${compiled}`);
|
const fn = new Function('_rt', 'store', `return ${compiled}`);
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,9 @@ export function measure(ui: UIValue): { width: number, height: number } {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hitTest(x: number, y: number): { event: any, relativeX: number, relativeY: number } | null {
|
export function hitTest(x: number, y: number): { event: any, relativeX: number, relativeY: number } | null {
|
||||||
for (const region of clickRegions) {
|
for (let i = clickRegions.length - 1; i >= 0; i--) {
|
||||||
|
const region = clickRegions[i];
|
||||||
|
|
||||||
if (x >= region.x && x < region.x + region.width &&
|
if (x >= region.x && x < region.x + region.width &&
|
||||||
y >= region.y && y < region.y + region.height) {
|
y >= region.y && y < region.y + region.height) {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
// import type { UIValue } from './types'
|
|
||||||
|
|
||||||
export function valueToUI(value: any): any {
|
export function valueToUI(value: any): any {
|
||||||
if (!value || !value._kind)
|
if (!value || !value._kind)
|
||||||
throw new Error(`Expected UI constructor, got: ${JSON.stringify(value)}`);
|
throw new Error(`Expected UI constructor, got: ${JSON.stringify(value)}`);
|
||||||
|
|
@ -92,6 +90,6 @@ export function valueToUI(value: any): any {
|
||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown UI constructor: ${value._tag}`);
|
throw new Error(`Unknown UI constructor: ${value._kind}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue