a million fixes

master
Dustin Swan 2 weeks ago
parent b1696499e5
commit c294d7fd6a
Signed by: dustinswan
GPG Key ID: 30D46587E2100467

@ -33,7 +33,7 @@ canvas {
<body>
<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>
</body>
</html>

@ -17,7 +17,7 @@ export type Variable = {
kind: 'variable'
name: string
line?: number
column: number
column?: number
start?: number
}
@ -148,24 +148,6 @@ export type Definition = {
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 = {
kind: 'rebind'
target: AST
@ -189,8 +171,6 @@ export type AST =
| List
| ListSpread
| Definition
| TypeDef
| Import
| Rebind
export function prettyPrint(ast: AST, indent = 0): string {

@ -160,7 +160,7 @@ textInput = config \ ui.stateful {
| Focused \ { state = state.{ focused = True }, 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 = [] },
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
# itemHeight
# items = [
@ -30,14 +31,17 @@
# | None \ currentIndex);
paletteState = {
query = ""
};
palette = config \
focusedIndex = 0;
windowHeight = 400;
windowWidth = 600;
results = take 8 (config.search config.state.query);
# results = config.search config.state.query; # once you get scrolling..
results = take 8 (config.search paletteState.query);
# results = config.search paletteState.query; # once you get scrolling..
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 {
x = (config.viewport.width - windowWidth) / 2,
y = (config.viewport.height - windowHeight) / 2,
@ -78,18 +98,13 @@ palette = config \
children = [
textInput {
key = "palette-query",
initialValue = config.state.query,
initialValue = "",
initialFocus = True,
color = "white",
backgroundColor = "rgba(0,0,0,0.2)",
w = contentWidth,
h = textInputHeight,
onChange = text \ batch [config.state.query := text, config.state.focusedIndex := 0],
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
onChange = text \ batch [paletteState.query := text],
},
ui.clip {
@ -111,7 +126,7 @@ palette = config \
child = t,
w = contentWidth,
h = textInputHeight,
selected = (config.state.focusedIndex == i),
selected = (state.focusedIndex == i),
onClick = \ config.onSelect t
}) results)
]
@ -122,4 +137,5 @@ palette = config \
}
]
}
}
};

@ -1,8 +1,6 @@
osState = {
palette = {
visible = True,
query = "",
focusedIndex = 0,
visible = True
},
inspector = {
visible = False,
@ -37,8 +35,9 @@ inspect = item \
init = {};
update = state event \ event
| Key { key = "p", meta = True } \ osState.palette.visible := not (osState.palette.visible)
| _ \ state;
| Key { key = "p", meta = True } \
{ state = state, emit = [osState.palette.visible := not (osState.palette.visible)] }
| _ \ { state = state, emit = [] };
view = state viewport \
ui.stack {
@ -55,13 +54,13 @@ view = state viewport \
# keep palette at the end so it's on top
osState.palette.visible
| True \ palette {
state = osState.palette,
# state = osState.palette,
# query = osState.palette.query,
search = storeSearch,
onSelect = onSelect,
viewport = viewport,
}
| False \ empty,
]
};

@ -85,10 +85,7 @@ export function compile(ast: AST, useStore = true, bound = new Set<string>()): s
const path = getPath(ast.target);
const value = compile(ast.value, useStore, bound);
if (!useStore || !rootName) {
const target = compile(ast.target, useStore, bound);
return `(() => { ${target} = ${value}; return { _tag: "Rerender" }; })()`;
}
if (!rootName) throw new Error('Rebind target must be a variable');
if (path.length === 0) {
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 {
const ops: Record<string, string> = {
'add': '_rt.add', 'sub': '_rt.sub', 'mul': '_rt.mul',
'div': '_rt.div', 'mod': '_rt.mod', 'eq': '_rt.eq',
'cat': '_rt.cat', 'gt': '_rt.gt', 'lt': '_rt.lt',
'gte': '_rt.gte', 'lte': '_rt.lte', 'max': '_rt.max', 'min': '_rt.min',
'add': '_rt.add', 'sub': '_rt.sub', 'mul': '_rt.mul', 'div': '_rt.div',
'mod': '_rt.mod', 'pow': '_rt.pow',
'eq': '_rt.eq', 'gt': '_rt.gt', 'lt': '_rt.lt', 'gte': '_rt.gte', 'lte': '_rt.lte',
'cat': '_rt.cat', 'max': '_rt.max', 'min': '_rt.min',
};
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 (useStore) {

@ -165,7 +165,7 @@ export class Parser {
let expr = this.parseInfix();
// Rebind
if (this.current().kind == 'colon-equals') {
if (this.current().kind === 'colon-equals') {
const token = this.current();
this.advance();
const value = this.parseExpressionNoMatch();

@ -60,7 +60,6 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
handleEvent(e);
}
}
rerender();
} catch(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 }
};
}
return expandStateful(viewUI, path, renderedKeys);
return expandStateful(viewUI, [...path, ui.key], renderedKeys);
}
case 'stack':
@ -186,7 +185,6 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
if (event._tag === 'Rebind') {
rt.rebind(event._0, event._1, event._2);
rerender();
return;
}
@ -198,8 +196,13 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
if (event._tag === 'NoOp')
return;
state = app.update(state)(event);
rerender();
const result = app.update(state)(event);
state = result.state;
if (result.emit && Array.isArray(result.emit)) {
for (const e of result.emit) {
handleEvent(e);
}
}
}
canvas.addEventListener('click', (e) => {
@ -221,6 +224,8 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
handleEvent(event);
}
}
rerender();
});
window.addEventListener('keydown', (e) => {
@ -236,20 +241,37 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
}
};
// always send to OS first
handleEvent(event);
console.log("keydown", e)
console.log("componentInstances", componentInstances)
console.log("focusedComponentKey", focusedComponentKey)
if (focusedComponentKey) {
// send to focused component
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();
rerender();
});
let resizeRAF = 0;
window.addEventListener('resize', () => {
cancelAnimationFrame(resizeRAF);
resizeRAF = requestAnimationFrame(() => {
setupCanvas();
rerender();
})
});
});
rerender();
}

@ -14,7 +14,8 @@ export const _rt = {
mul: (a: number) => (b: number) => a * b,
div: (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),
min: (a: number) => (b: number) => Math.min(a, b),
@ -110,7 +111,6 @@ export const _rt = {
batch: (events: any[]) => ({ _tag: 'Batch', _0: events }),
noOp: { _tag: 'NoOp' },
rerender: { _tag: 'Rerender' },
focus: (key: string) => ({ _tag: 'Focus', _0: key }),
nth: (i: number) => (xs: any[] | string) => i >= 0 && i < xs.length
@ -142,6 +142,7 @@ export const _rt = {
return printed;
},
rebind: (name: string, pathOrValue: any, maybeValue?: any) => {
console.log("rebind", store, name, pathOrValue, maybeValue);
if (maybeValue === undefined) {
store[name] = pathOrValue;
} else {
@ -202,12 +203,12 @@ export const _rt = {
const defs = parser.parse();
const ast = defs[0].body;
// validate free vars
const free = freeVars(ast);
const allowed = new Set([
...Object.keys(store),
...Object.keys(_rt),
'True'
])
// const free = freeVars(ast);
// const allowed = new Set([
// ...Object.keys(store),
// ...Object.keys(_rt),
// 'True'
// ])
const compiled = compile(defs[0].body);
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 {
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 &&
y >= region.y && y < region.y + region.height) {
return {

@ -1,5 +1,3 @@
// import type { UIValue } from './types'
export function valueToUI(value: any): any {
if (!value || !value._kind)
throw new Error(`Expected UI constructor, got: ${JSON.stringify(value)}`);
@ -92,6 +90,6 @@ export function valueToUI(value: any): any {
};
default:
throw new Error(`Unknown UI constructor: ${value._tag}`);
throw new Error(`Unknown UI constructor: ${value._kind}`);
}
}

Loading…
Cancel
Save