strict typechecking
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Cat /dev/Nulo 2023-04-18 14:18:15 -03:00
parent 2d40d7f9a0
commit 3f9a2f0603
16 changed files with 52 additions and 68 deletions

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
export let onClose: () => void; export let onClose: () => void;
function click(event: Event) { function click(this: Element, event: Event) {
if (event.target !== this) return; if (event.target !== this) return;
onClose(); onClose();
} }

View file

@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import type { Readable } from "svelte/store";
import { yDocToProsemirrorJSON } from "y-prosemirror"; import { yDocToProsemirrorJSON } from "y-prosemirror";
import { Node } from "prosemirror-model"; import { Node } from "prosemirror-model";
import type { Doc } from "yjs"; import type { Doc } from "yjs";
@ -29,7 +28,7 @@
} }
}); });
docs.push({ docs.push({
title: titleNode?.textContent, title: titleNode ? (titleNode as Node).textContent : undefined,
id: name.replace(/^page\//, ""), id: name.replace(/^page\//, ""),
name, name,
}); });
@ -45,7 +44,7 @@
$: entries = deriveEntries(ydoc); $: entries = deriveEntries(ydoc);
$: lastU = lastUpdated(ydoc); $: lastU = lastUpdated(ydoc);
$: sortedEntries = $entries $: sortedEntries = $entries
.sort((a, b) => +$lastU.get(b.name) - +$lastU.get(a.name)) .sort((a, b) => +($lastU.get(b.name) || 0) - +($lastU.get(a.name) || 0))
// TODO: FTS // TODO: FTS
.filter((x) => (x.title ?? x.id).includes(filter)); .filter((x) => (x.title ?? x.id).includes(filter));
// $: console.debug($lastU); // $: console.debug($lastU);

View file

@ -19,7 +19,6 @@
getFirstMarkInSelection, getFirstMarkInSelection,
} from "./ps-utils"; } from "./ps-utils";
import SimpleMarkItem from "./bubblemenu/SimpleMarkItem.svelte"; import SimpleMarkItem from "./bubblemenu/SimpleMarkItem.svelte";
import { nanoid } from "nanoid";
import Button from "./bubblemenu/Button.svelte"; import Button from "./bubblemenu/Button.svelte";
import Modal from "../components/Modal.svelte"; import Modal from "../components/Modal.svelte";
import PagePicker from "../components/PagePicker.svelte"; import PagePicker from "../components/PagePicker.svelte";
@ -105,7 +104,7 @@
/* https://wicg.github.io/visual-viewport/examples/fixed-to-keyboard.html */ /* https://wicg.github.io/visual-viewport/examples/fixed-to-keyboard.html */
let barStyle = ""; let barStyle = "";
function updateBar() { function updateBar() {
const viewport = window.visualViewport; const viewport = window.visualViewport!;
// Since the bar is position: fixed we need to offset it by the // Since the bar is position: fixed we need to offset it by the
// visual viewport's offset from the layout viewport origin. // visual viewport's offset from the layout viewport origin.
const offsetY = window.innerHeight - viewport.height - viewport.offsetTop; const offsetY = window.innerHeight - viewport.height - viewport.offsetTop;
@ -118,12 +117,12 @@ transform: scale(${1 / viewport.scale});
} }
onMount(() => { onMount(() => {
window.visualViewport.addEventListener("resize", updateBar); window.visualViewport!.addEventListener("resize", updateBar);
window.visualViewport.addEventListener("scroll", updateBar); window.visualViewport!.addEventListener("scroll", updateBar);
}); });
onDestroy(() => { onDestroy(() => {
window.visualViewport.removeEventListener("resize", updateBar); window.visualViewport!.removeEventListener("resize", updateBar);
window.visualViewport.removeEventListener("scroll", updateBar); window.visualViewport!.removeEventListener("scroll", updateBar);
}); });
</script> </script>

View file

@ -5,14 +5,14 @@
import { dropCursor } from "prosemirror-dropcursor"; import { dropCursor } from "prosemirror-dropcursor";
import { gapCursor } from "prosemirror-gapcursor"; import { gapCursor } from "prosemirror-gapcursor";
import type { XmlFragment } from "yjs"; import type { XmlFragment } from "yjs";
import { ySyncPlugin, yCursorPlugin, yUndoPlugin } from "y-prosemirror"; import { ySyncPlugin, yUndoPlugin } from "y-prosemirror";
import "./editor.css"; import "./editor.css";
import { schema } from "./schema"; import { schema } from "./schema";
import BubbleMenu from "./BubbleMenu.svelte"; import BubbleMenu from "./BubbleMenu.svelte";
import MenuBar from "./MenuBar.svelte"; import MenuBar from "./MenuBar.svelte";
import { placeholderPlugin } from "./upload"; // import { placeholderPlugin } from "./upload";
import { baseKeymap } from "./keymap"; import { baseKeymap } from "./keymap";
import type { WorldY } from "../lib/doc"; import type { WorldY } from "../lib/doc";
@ -26,7 +26,7 @@
schema, schema,
plugins: [ plugins: [
new Plugin({ new Plugin({
view: (editorView) => { view: () => {
// editorView.dom.parentElement?.replaceWith(editorView.dom); // editorView.dom.parentElement?.replaceWith(editorView.dom);
return { return {
update(view, lastState) { update(view, lastState) {
@ -50,7 +50,7 @@
// yCursorPlugin(doc.webrtcProvider.awareness), // yCursorPlugin(doc.webrtcProvider.awareness),
yUndoPlugin(), yUndoPlugin(),
keymap(baseKeymap), keymap(baseKeymap),
placeholderPlugin, // placeholderPlugin,
], ],
}); });
} }

View file

@ -15,7 +15,7 @@
$: isActive = nodeIsActiveFn(type, null, true); $: isActive = nodeIsActiveFn(type, null, true);
$: command = wrapIn(type); $: command = wrapIn(type);
$: isPossible = command(state, null); $: isPossible = command(state);
$: actionListener = commandListener(view, command); $: actionListener = commandListener(view, command);
</script> </script>

View file

@ -10,7 +10,7 @@
const paragraphType = state.schema.nodes.paragraph; const paragraphType = state.schema.nodes.paragraph;
const headingType = state.schema.nodes.heading; const headingType = state.schema.nodes.heading;
$: isPossible = setBlockType(headingType, { level: 1 })(state, null); $: isPossible = setBlockType(headingType, { level: 1 })(state);
$: currentValue = $: currentValue =
state.selection.to <= state.selection.$from.end() && state.selection.to <= state.selection.$from.end() &&
(state.selection.$from.parent.type == headingType (state.selection.$from.parent.type == headingType
@ -19,10 +19,12 @@
? "paragraph" ? "paragraph"
: null); : null);
const onChange = (event) => { const onChange = (
event: Event & { currentTarget: EventTarget & HTMLSelectElement }
) => {
event.preventDefault(); event.preventDefault();
const [type, param] = event.target.value.split(":"); const [type, param] = event.currentTarget.value.split(":");
if (type === "paragraph") { if (type === "paragraph") {
setBlockType(paragraphType, { setBlockType(paragraphType, {
align: state.selection.$from.parent.attrs.align, align: state.selection.$from.parent.attrs.align,

View file

@ -21,7 +21,7 @@
? state.schema.nodes.bullet_list ? state.schema.nodes.bullet_list
: kind === ListKind.Ordered : kind === ListKind.Ordered
? state.schema.nodes.ordered_list ? state.schema.nodes.ordered_list
: null; : (null as never);
const listItemType = state.schema.nodes.list_item; const listItemType = state.schema.nodes.list_item;
$: iconComponent = $: iconComponent =
kind === ListKind.Unordered kind === ListKind.Unordered
@ -32,7 +32,7 @@
$: isActive = nodeIsActiveFn(type, null, true); $: isActive = nodeIsActiveFn(type, null, true);
$: command = chainCommands(liftListItem(listItemType), wrapInList(type)); $: command = chainCommands(liftListItem(listItemType), wrapInList(type));
$: isPossible = command(state, null); $: isPossible = command(state);
$: actionListener = commandListener(view, command); $: actionListener = commandListener(view, command);
</script> </script>

View file

@ -6,11 +6,9 @@ import type {
ResolvedPos, ResolvedPos,
Node as ProsemirrorNode, Node as ProsemirrorNode,
} from "prosemirror-model"; } from "prosemirror-model";
import type { EditorState, Selection } from "prosemirror-state"; import type { EditorState } from "prosemirror-state";
import type { EditorView } from "prosemirror-view"; import type { EditorView } from "prosemirror-view";
import type { Align } from "./schema";
export type Command = ( export type Command = (
state: EditorState, state: EditorState,
dispatch?: EditorView["dispatch"] dispatch?: EditorView["dispatch"]
@ -176,7 +174,7 @@ export function getAttrFn(attrKey: string): (state: EditorState) => any {
return (state) => { return (state) => {
let { from, to } = state.selection; let { from, to } = state.selection;
let value: any = undefined; let value: any = undefined;
state.doc.nodesBetween(from, to, (node, pos) => { state.doc.nodesBetween(from, to, (node) => {
if (value !== undefined) return false; if (value !== undefined) return false;
if (!node.isTextblock) return; if (!node.isTextblock) return;
if (attrKey in node.attrs) value = node.attrs[attrKey]; if (attrKey in node.attrs) value = node.attrs[attrKey];
@ -199,10 +197,10 @@ export interface MarkMatch {
export function getFirstMarkInSelection( export function getFirstMarkInSelection(
state: EditorState, state: EditorState,
type: MarkType type: MarkType
): MarkMatch { ): MarkMatch | null {
const { to, from } = state.selection; const { to, from } = state.selection;
let match: MarkMatch; let match: MarkMatch | null = null;
state.selection.$from.doc.nodesBetween(from, to, (node, position) => { state.selection.$from.doc.nodesBetween(from, to, (node, position) => {
if (!match) { if (!match) {
const mark = type.isInSet(node.marks); const mark = type.isInSet(node.marks);
@ -213,25 +211,3 @@ export function getFirstMarkInSelection(
return match; return match;
} }
export function setAlign(align: Align): Command {
return (state, dispatch) => {
let { from, to } = state.selection;
let node: ProsemirrorNode | null = null;
state.doc.nodesBetween(from, to, (_node, pos) => {
if (node) return false;
if (!_node.isTextblock) return;
if (
_node.type == state.schema.nodes.paragraph ||
_node.type == state.schema.nodes.heading
) {
node = _node;
}
});
if (!node) return false;
if (dispatch)
return setBlockType(node.type, { ...node.attrs, align })(state, dispatch);
return true;
};
}

View file

@ -101,8 +101,9 @@ export const schema = new Schema({
tag: "ol", tag: "ol",
getAttrs(dom) { getAttrs(dom) {
dom = dom as HTMLElement; dom = dom as HTMLElement;
const start = dom.getAttribute("start");
return { return {
order: dom.hasAttribute("start") ? +dom.getAttribute("start") : 1, order: start ? +start : 1,
}; };
}, },
}, },
@ -141,11 +142,12 @@ export const schema = new Schema({
{ {
tag: "figure", tag: "figure",
getAttrs(dom) { getAttrs(dom) {
const child: HTMLElement = const child: HTMLElement | null =
(dom as Element).querySelector("img") || (dom as Element).querySelector("img") ||
(dom as Element).querySelector("video") || (dom as Element).querySelector("video") ||
(dom as Element).querySelector("audio") || (dom as Element).querySelector("audio") ||
(dom as Element).querySelector("iframe"); (dom as Element).querySelector("iframe");
if (!child) return false;
if (child instanceof HTMLImageElement) { if (child instanceof HTMLImageElement) {
return { src: child.src, kind: "img" }; return { src: child.src, kind: "img" };
@ -156,6 +158,7 @@ export const schema = new Schema({
} else if (child instanceof HTMLIFrameElement) { } else if (child instanceof HTMLIFrameElement) {
return { src: child.src, kind: "iframe" }; return { src: child.src, kind: "iframe" };
} }
return false;
}, },
}, },
{ {
@ -308,9 +311,11 @@ export const schema = new Schema({
getAttrs(dom) { getAttrs(dom) {
dom = dom as HTMLElement; dom = dom as HTMLElement;
const href = dom.getAttribute("href"); const href = dom.getAttribute("href");
if (href.startsWith("/w/")) { if (href?.startsWith("/w/")) {
const matches = parse(routes.Page).pattern.exec(href);
if (!matches) return false;
return { return {
id: parse(routes.Page).pattern.exec(href)[1], id: matches[1],
}; };
} else return false; } else return false;
}, },

View file

@ -42,7 +42,7 @@ export const placeholderPlugin = new Plugin({
}); });
export function findPlaceholder(state: EditorState, id: any): number | null { export function findPlaceholder(state: EditorState, id: any): number | null {
const decos: DecorationSet = placeholderPlugin.getState(state); const decos = placeholderPlugin.getState(state);
const found = decos.find(undefined, undefined, (spec) => spec.id == id); const found = decos?.find(undefined, undefined, (spec) => spec.id == id);
return found.length ? found[0].from : null; return found?.length ? found[0].from : null;
} }

View file

@ -2,7 +2,12 @@ import type { Readable } from "svelte/store";
import type { Doc, Transaction } from "yjs"; import type { Doc, Transaction } from "yjs";
export function makeYdocStore<T>( export function makeYdocStore<T>(
handler: (update: Uint8Array, origin: any, ydoc: Doc, tr: Transaction) => T, handler: (
update: Uint8Array | null,
origin: any,
ydoc: Doc,
tr: Transaction | null
) => T,
unhandler?: () => void unhandler?: () => void
) { ) {
return (ydoc: Doc): Readable<T> => { return (ydoc: Doc): Readable<T> => {
@ -10,10 +15,10 @@ export function makeYdocStore<T>(
return { return {
subscribe: (run) => { subscribe: (run) => {
function updateHandler( function updateHandler(
update: Uint8Array, update: Uint8Array | null,
origin: any, origin: any,
ydoc: Doc, ydoc: Doc,
tr: Transaction tr: Transaction | null
) { ) {
run(handler(update, origin, ydoc, tr)); run(handler(update, origin, ydoc, tr));
} }

View file

@ -1,8 +1,8 @@
import './app.css' import "./app.css";
import App from './App.svelte' import App from "./App.svelte";
const app = new App({ const app = new App({
target: document.getElementById('app'), target: document.getElementById("app")!,
}) });
export default app export default app;

View file

@ -4,11 +4,7 @@
import { router } from "../lib/router"; import { router } from "../lib/router";
import { writeWorlds } from "../lib/worldStorage"; import { writeWorlds } from "../lib/worldStorage";
function crear( function crear(event: Event) {
event: Event & { readonly submitter: HTMLElement } & {
currentTarget: EventTarget & HTMLFormElement;
}
) {
event.preventDefault(); event.preventDefault();
writeWorlds((worlds) => [...worlds, generateNewWorld()]); writeWorlds((worlds) => [...worlds, generateNewWorld()]);
router.run(routes.ChooseWorld); router.run(routes.ChooseWorld);

View file

@ -8,6 +8,7 @@
async function joinWorldLink() { async function joinWorldLink() {
const worlds = await loadWorlds(); const worlds = await loadWorlds();
const worldIdentifier = worlds.find((w) => w.room === worldId); const worldIdentifier = worlds.find((w) => w.room === worldId);
if (!worldIdentifier) throw new Error("No tenés ese mundo para compartir.");
return `${location.origin}${inject(routes.JoinWorld, { return `${location.origin}${inject(routes.JoinWorld, {
worldId: worldIdentifier.room, worldId: worldIdentifier.room,
})}#${worldIdentifier.password}`; })}#${worldIdentifier.password}`;

View file

@ -13,6 +13,7 @@
*/ */
"allowJs": true, "allowJs": true,
"checkJs": true, "checkJs": true,
"strict": true,
"isolatedModules": true "isolatedModules": true
}, },
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"], "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],