1
0
Fork 0
forked from mirror/cinny

Compare commits

...

2 commits

Author SHA1 Message Date
2007d635e6
remove markdown editor 2024-09-09 13:25:11 -07:00
a430a6af35
fix flake.nix 2024-09-09 13:16:00 -07:00
6 changed files with 19 additions and 353 deletions

3
.gitignore vendored
View file

@ -4,4 +4,5 @@ node_modules
devAssets
.DS_Store
.idea
.idea
result

View file

@ -7,16 +7,17 @@
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system: let
pkgs = import nixpkgs { inherit system; };
der = pkgs.stdenv.mkDerivation {
cinny = pkgs.buildNpmPackage {
name = "cinny";
src = ./.;
buildInputs = [pkgs.nodejs_latest pkgs.nodePackages.pnpm];
unpackPhase = "true";
configurePhase = "pnpm install";
buildPhase = "pnpm run build";
installPhase = "mv dist $out";
npmDepsHash = "sha256-E48xV3Lil7n4xxEeUpuiF8LEGkJc+GXRRK1TSbkOcgk=";
nativeBuildInputs = with pkgs; [ python3 pkg-config ];
buildInputs = with pkgs; [ pixman cairo pango ];
installPhase = ''
cp -r dist $out
'';
};
in {
packages.default = der;
packages.default = cinny;
});
}

View file

@ -8,7 +8,7 @@ import React, {
useState,
} from 'react';
import { Box, Scroll, Text } from 'folds';
import { Text as SlateText, Descendant, Editor, createEditor, NodeEntry } from 'slate';
import { Descendant, Editor, createEditor } from 'slate';
import {
Slate,
Editable,
@ -19,13 +19,10 @@ import {
} from 'slate-react';
import { withHistory } from 'slate-history';
import { BlockType } from './types';
import { RenderElement } from './Elements';
import { RenderElement, RenderLeaf } from './Elements';
import { CustomElement } from './slate';
import { mdParser } from '../../../util/markdown';
import type { SingleASTNode } from '@khanacademy/simple-markdown';
import '../../molecules/markdown-input/MarkdownInput.scss';
import * as css from './Editor.css';
// import { toggleKeyboardShortcut } from './keyboard';
import { toggleKeyboardShortcut } from './keyboard';
const initialValue: CustomElement[] = [
{
@ -60,24 +57,6 @@ export const useEditor = (): Editor => {
return editor;
};
function Leaf({
attributes,
children,
leaf,
}: {
attributes: string[];
children: string[];
leaf: {
classes?: string[];
};
}) {
return (
<span {...attributes} className={leaf.classes?.join(' ')}>
{children}
</span>
);
}
export type EditorChangeHandler = (value: Descendant[]) => void;
type CustomEditorProps = {
editableName?: string;
@ -116,10 +95,14 @@ export const CustomEditor = forwardRef<HTMLDivElement, CustomEditorProps>(
[]
);
const renderLeaf = useCallback((props: RenderLeafProps) => <Leaf {...props} />, []);
const renderLeaf = useCallback((props: RenderLeafProps) => <RenderLeaf {...props} />, []);
const handleKeydown: KeyboardEventHandler = useCallback(
(evt) => onKeyDown?.(evt),
(evt) => {
onKeyDown?.(evt);
const shortcutToggled = toggleKeyboardShortcut(editor, evt);
if (shortcutToggled) evt.preventDefault();
},
[editor, onKeyDown]
);
@ -140,89 +123,6 @@ export const CustomEditor = forwardRef<HTMLDivElement, CustomEditorProps>(
);
}, []);
const decorate = useCallback(([node, path]: NodeEntry) => {
const ranges = [];
if (!SlateText.isText(node)) {
return ranges;
}
let position = 0;
// let bold = false;
// let italic = false;
const enabledStyles = new Set<string>();
function readSyntax(text: string) {
const start = position;
// read bytes until the first character of item.content is found
let prefix = '';
while (position < node.text.length && node.text[position] !== text[0]) {
prefix += node.text[position];
position += 1;
}
if (start !== position) {
ranges.push({
anchor: { path, offset: start },
focus: { path, offset: start + prefix.length },
classes: ['syntax'],
});
}
}
function addRanges(content: SingleASTNode[] | string) {
if (typeof content === 'string') {
const start = position;
readSyntax(content);
const prefixLength = position - start;
position += content.length;
if (enabledStyles.size > 0) {
ranges.push({
anchor: { path, offset: start + prefixLength },
focus: { path, offset: position },
classes: [...enabledStyles],
});
}
return;
}
for (let i = 0; i < content.length; i += 1) {
const item = content[i];
if (item.content) {
let style: string | undefined;
if (item.type === 'strong') style = 'bold';
if (item.type === 'em') style = 'italic';
if (item.type === 'inlineCode') style = 'inline-code';
if (item.type === 'del') style = 'strikethrough';
if (item.type === 'u') style = 'underline';
if (style) enabledStyles.add(style);
addRanges(item.content);
if (style) enabledStyles.delete(style);
}
}
}
// const emojis = getShortcodeToEmoji(this.matrixClient, [room, ...parentRooms]);
const content = mdParser(node.text, {
userNames: [],
emojis: {},
});
addRanges(content);
// add final syntax range
if (position < node.text.length) {
ranges.push({
anchor: { path, offset: position },
focus: { path, offset: node.text.length },
classes: ['syntax'],
});
}
return ranges;
}, []);
return (
<div className={css.Editor} ref={ref}>
<Slate editor={editor} initialValue={initialValue} onChange={onChange}>
@ -242,7 +142,6 @@ export const CustomEditor = forwardRef<HTMLDivElement, CustomEditorProps>(
hideTrack
>
<Editable
decorate={decorate}
data-editable-name={editableName}
className={css.EditorTextarea}
placeholder={placeholder}

View file

@ -320,7 +320,6 @@ const useTimelinePagination = (
return async (backwards: boolean) => {
if (fetching) return;
const targetTimeline = timelineRef.current;
const { linkedTimelines: lTimelines } = timelineRef.current;
const timelinesEventsCount = lTimelines.map(timelineToEventsCount);
@ -360,7 +359,6 @@ const useTimelinePagination = (
}
fetching = false;
if (targetTimeline !== timelineRef.current) return;
if (alive()) {
recalibratePagination(lTimelines, timelinesEventsCount, backwards);
}

View file

@ -1,48 +0,0 @@
.markdown-input {
display: block;
width: 100%;
min-width: 0px;
height: 100%;
font-size: var(--fs-b1);
background-color: var(--bg-surface-low);
color: var(--tc-surface-normal);
border-radius: var(--bo-radius);
border: 1px solid var(--bg-surface-border);
display: flex;
align-items: center;
}
.markdown-input__editable {
padding: var(--sp-ultra-tight) calc(var(--sp-tight) - 2px);
width: 100%;
// the outline adds a blue border on firefox when selected which looks ugly
outline: none;
}
.bold {
font-weight: bold;
}
.italic {
font-style: italic;
}
.strikethrough {
text-decoration: line-through;
}
.underline {
text-decoration: underline;
}
.inline-code {
font-family: monospace;
color: var(--tc-code) !important;
}
.syntax {
color: var(--tc-surface-low);
}
.read-only {
cursor: not-allowed;
}

View file

@ -1,185 +0,0 @@
import React, { useCallback, useMemo, useState } from 'react';
import { Editable, ReactEditor, Slate, withReact } from 'slate-react';
import { withHistory } from 'slate-history';
import { Text, Descendant, createEditor } from 'slate';
import './MarkdownInput.scss';
import { SingleASTNode } from '@khanacademy/simple-markdown';
import PropTypes from 'prop-types';
import { mdParser } from '../../../util/markdown';
import { getShortcodeToEmoji } from '../../organisms/emoji-board/custom-emoji';
function Leaf({
attributes,
children,
leaf,
}: {
attributes: string[];
children: string[];
leaf: {
classes?: string[];
};
}) {
return (
<span {...attributes} className={leaf.classes?.join(' ')}>
{children}
</span>
);
}
Leaf.propTypes = {
attributes: PropTypes.arrayOf(PropTypes.string),
children: PropTypes.arrayOf(PropTypes.string),
leaf: PropTypes.shape({
classes: PropTypes.arrayOf(PropTypes.string),
}),
};
/**
* Flatten Slate nodes into a single string.
* @param nodes Slate nodes, you can get this from ReactEditor.children
* @returns The flattened string
*/
export function flattenNodes(nodes: Descendant[]): string {
const flat = nodes
.map((node) => {
if (Text.isText(node)) {
return node.text;
}
return flattenNodes(node.children);
})
.join('\n');
return flat;
}
export function MarkdownInput({
onChange,
onPaste,
onKeyDown,
placeholder,
onCreateEditor,
readOnly,
}: {
onChange: (value: Descendant[]) => void;
onPaste: (event: React.ClipboardEvent<HTMLDivElement>) => void;
onKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void;
placeholder: string;
onCreateEditor: (editor: ReactEditor) => void;
readOnly?: boolean;
}) {
const renderLeaf = useCallback((props) => <Leaf {...props} />, []);
const editor = useMemo(() => withHistory(withReact(createEditor())), []);
onCreateEditor(editor);
const decorate = useCallback(([node, path]) => {
const ranges = [];
if (!Text.isText(node)) {
return ranges;
}
let position = 0;
// let bold = false;
// let italic = false;
const enabledStyles = new Set<string>();
function readSyntax(text: string) {
const start = position;
// read bytes until the first character of item.content is found
let prefix = '';
while (position < node.text.length && node.text[position] !== text[0]) {
prefix += node.text[position];
position += 1;
}
if (start !== position) {
ranges.push({
anchor: { path, offset: start },
focus: { path, offset: start + prefix.length },
classes: ['syntax'],
});
}
}
function addRanges(content: SingleASTNode[] | string) {
if (typeof content === 'string') {
const start = position;
readSyntax(content);
const prefixLength = position - start;
position += content.length;
if (enabledStyles.size > 0) {
ranges.push({
anchor: { path, offset: start + prefixLength },
focus: { path, offset: position },
classes: [...enabledStyles],
});
}
return;
}
for (let i = 0; i < content.length; i += 1) {
const item = content[i];
if (item.content) {
let style: string | undefined;
if (item.type === 'strong') style = 'bold';
if (item.type === 'em') style = 'italic';
if (item.type === 'inlineCode') style = 'inline-code';
if (item.type === 'del') style = 'strikethrough';
if (item.type === 'u') style = 'underline';
if (style) enabledStyles.add(style);
addRanges(item.content);
if (style) enabledStyles.delete(style);
}
}
}
// const emojis = getShortcodeToEmoji(this.matrixClient, [room, ...parentRooms]);
const content = mdParser(node.text, {
userNames: [],
emojis: {},
});
addRanges(content);
// add final syntax range
if (position < node.text.length) {
ranges.push({
anchor: { path, offset: position },
focus: { path, offset: node.text.length },
classes: ['syntax'],
});
}
return ranges;
}, []);
const initialValue: Descendant[] = [
{
children: [{ text: '' }],
},
];
const [isEmpty, setIsEmpty] = useState(true);
const onChangeInternal = (value: Descendant[]) => {
const text = flattenNodes(value);
setIsEmpty(text.length === 0);
if (onChange) onChange(value);
};
return (
<div className={`markdown-input${readOnly ? ' read-only' : ''}`}>
<Slate editor={editor} initialValue={initialValue} onChange={onChangeInternal}>
<Editable
decorate={decorate}
renderLeaf={renderLeaf}
placeholder={placeholder}
onKeyDown={onKeyDown}
onPaste={onPaste}
readOnly={readOnly}
className={`markdown-input__editable${isEmpty ? ' empty' : ''}`}
/>
</Slate>
</div>
);
}