strict typechecking
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
2d40d7f9a0
commit
3f9a2f0603
16 changed files with 52 additions and 68 deletions
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -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;
|
||||||
},
|
},
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
10
src/main.ts
10
src/main.ts
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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}`;
|
||||||
|
|
|
@ -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"],
|
||||||
|
|
Loading…
Reference in a new issue