Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 48 additions & 13 deletions src/json-crdt-extensions/peritext/block/Inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {stringify} from '../../../json-text/stringify';
import {SliceBehavior, SliceTypeName} from '../slice/constants';
import {Range} from '../rga/Range';
import {ChunkSlice} from '../util/ChunkSlice';
import {MarkerOverlayPoint} from '../overlay/MarkerOverlayPoint';
import {Cursor} from '../editor/Cursor';
import {hashId} from '../../../json-crdt/hash';
import {formatType} from '../slice/util';
Expand All @@ -15,34 +14,70 @@ import type {Peritext} from '../Peritext';
import type {Slice} from '../slice/types';
import type {PeritextMlAttributes, PeritextMlNode} from './types';

/** The attribute started before this inline and ends after this inline. */
export class InlineAttrPassing {
export abstract class AbstractInlineAttr {
constructor(public slice: Slice) {}

/** @returns Whether the attribute starts at the start of the inline. */
isStart(): boolean {
return false;
}

/** @returns Whether the attribute ends at the end of the inline. */
isEnd(): boolean {
return false;
}

/** @returns Whether the attribute is collapsed to a point. */
isCollapsed(): boolean {
return false;
}
}

/** The attribute started before this inline and ends after this inline. */
export class InlineAttrPassing extends AbstractInlineAttr {}

/** The attribute starts at the beginning of this inline. */
export class InlineAttrStart {
constructor(public slice: Slice) {}
export class InlineAttrStart extends AbstractInlineAttr {
isStart(): boolean {
return true;
}
}

/** The attribute ends at the end of this inline. */
export class InlineAttrEnd {
constructor(public slice: Slice) {}
export class InlineAttrEnd extends AbstractInlineAttr {
isEnd(): boolean {
return true;
}
}

/** The attribute starts and ends in this inline, exactly contains it. */
export class InlineAttrContained {
constructor(public slice: Slice) {}
export class InlineAttrContained extends AbstractInlineAttr {
isStart(): boolean {
return true;
}
isEnd(): boolean {
return true;
}
}

/** The attribute is collapsed at start of this inline. */
export class InlineAttrStartPoint {
constructor(public slice: Slice) {}
export class InlineAttrStartPoint extends AbstractInlineAttr {
isStart(): boolean {
return true;
}
isCollapsed(): boolean {
return true;
}
}

/** The attribute is collapsed at end of this inline. */
export class InlineAttrEndPoint {
constructor(public slice: Slice) {}
export class InlineAttrEndPoint extends AbstractInlineAttr {
isEnd(): boolean {
return true;
}
isCollapsed(): boolean {
return true;
}
}

export type InlineAttr =
Expand Down
2 changes: 1 addition & 1 deletion src/json-crdt-extensions/peritext/block/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export {Block, IBlock} from './Block';
export {LeafBlock} from './LeafBlock';
export {Inline} from './Inline';
export * from './Inline';
8 changes: 5 additions & 3 deletions src/json-crdt-extensions/peritext/slice/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const enum SliceTypeCon {
collapselist = 26, // Collapsible list - > List item
collapse = 27, // Collapsible block
note = 28, // Note block
mathblock = 29, // <math> block

// ------------------------------------------------ inline slices (-64 to -1)
Cursor = -1,
Expand All @@ -56,12 +57,12 @@ export const enum SliceTypeCon {
ins = -12, // <ins>
sup = -13, // <sup>
sub = -14, // <sub>
math = -15, // <math>
math = -15, // <math> inline
font = -16, // <span style="font-family: ...">
col = -17, // <span style="color: ...">
bg = -18, // <span style="background: ...">
kbd = -19, // <kbd>
hidden = -20, // <span style="color: transparent; background: black">
spoiler = -20, // <span style="color: transparent; background: black">
q = -21, // <q> (inline quote)
cite = -22, // <cite> (inline citation)
footnote = -23, // <sup> or <a> with href="#footnote-..." and title="Footnote ..."
Expand Down Expand Up @@ -106,6 +107,7 @@ export enum SliceTypeName {
collapselist = SliceTypeCon.collapselist,
collapse = SliceTypeCon.collapse,
note = SliceTypeCon.note,
mathblock = SliceTypeCon.mathblock,

Cursor = SliceTypeCon.Cursor,
RemoteCursor = SliceTypeCon.RemoteCursor,
Expand All @@ -126,7 +128,7 @@ export enum SliceTypeName {
col = SliceTypeCon.col,
bg = SliceTypeCon.bg,
kbd = SliceTypeCon.kbd,
hidden = SliceTypeCon.hidden,
spoiler = SliceTypeCon.spoiler,
footnote = SliceTypeCon.footnote,
ref = SliceTypeCon.ref,
iaside = SliceTypeCon.iaside,
Expand Down
7 changes: 7 additions & 0 deletions src/json-crdt-peritext-ui/plugins/cursor/RenderCaret.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ const innerClass = rule({
an: moveAnimation + ' .25s ease-out forwards',
});

const innerClass2 = rule({
'mix-blend-mode': 'hard-light',
});

export interface RenderCaretProps extends CaretViewProps {
children: React.ReactNode;
}
Expand Down Expand Up @@ -84,6 +88,9 @@ export const RenderCaret: React.FC<RenderCaretProps> = ({italic, children}) => {
}}
/>
)}

{/* Two carets overlay, so that they look good, both, on white and black backgrounds. */}
<span className={innerClass + innerClass2} style={style} />
<span className={innerClass} style={style} />
</span>
);
Expand Down
9 changes: 8 additions & 1 deletion src/json-crdt-peritext-ui/plugins/cursor/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
export enum DefaultRendererColors {
ActiveCursor = '#07f',
InactiveCursor = 'rgba(127,127,127,.7)',
ActiveSelection = '#d7e9fd',

/**
* Derived from #d7e9fd. 80% opacity used so that
* any inline formatting underneath the selection
* is still visible.
*/
ActiveSelection = 'rgba(196,223,253,.8)',

InactiveSelection = 'rgba(127,127,127,.2)',
}
23 changes: 23 additions & 0 deletions src/json-crdt-peritext-ui/plugins/minimal/RenderInline/Spoiler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// biome-ignore lint: React is used for JSX
import * as React from 'react';
import {rule} from 'nano-theme';

const blockClass = rule({
bg: '#222',
col: 'transparent',
bdrad: '2px',
'&:hover': {
bg: '#222',
col: 'rgba(255, 255, 255, 0.2)',
},
});

export interface SpoilerProps {
children: React.ReactNode;
}

export const Spoiler: React.FC<SpoilerProps> = (props) => {
const {children} = props;

return <span className={blockClass}>{children}</span>;
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// biome-ignore lint: React is used for JSX
import * as React from 'react';
import {usePeritext} from '../../react';
import {useSyncStoreOpt} from '../../react/hooks';
import {DefaultRendererColors} from './constants';
import type {InlineViewProps} from '../../react/InlineView';
import {CommonSliceType} from '../../../json-crdt-extensions';
import {usePeritext} from '../../../react';
import {useSyncStoreOpt} from '../../../react/hooks';
import {DefaultRendererColors} from '../constants';
import {CommonSliceType} from '../../../../json-crdt-extensions';
import {Spoiler} from './Spoiler';
import type {InlineViewProps} from '../../../react/InlineView';

interface RenderInlineSelectionProps extends RenderInlineProps {
selection: [left: 'anchor' | 'focus' | '', right: 'anchor' | 'focus' | ''];
Expand Down Expand Up @@ -44,8 +45,7 @@ export const RenderInline: React.FC<RenderInlineProps> = (props) => {
if (attr[CommonSliceType.sub]) element = <sub>{element}</sub>;
if (attr[CommonSliceType.math]) element = <code>{element}</code>;
if (attr[CommonSliceType.kbd]) element = <kbd>{element}</kbd>;
if (attr[CommonSliceType.hidden])
element = <span style={{color: 'transparent', background: 'black'}}>{element}</span>;
if (attr[CommonSliceType.spoiler]) element = <Spoiler>{element}</Spoiler>;

if (selection) {
element = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const TopToolbar: React.FC<TopToolbarProps> = ({ctx}) => {
{inlineGroupButton(CommonSliceType.b, 'Bold')}
{inlineGroupButton(CommonSliceType.i, 'Italic')}
{inlineGroupButton(CommonSliceType.u, 'Underline')}
{inlineGroupButton(CommonSliceType.overline, 'Overline')}
{inlineGroupButton(CommonSliceType.s, 'Strikethrough')}
{inlineGroupButton(CommonSliceType.code, 'Code')}
{inlineGroupButton(CommonSliceType.mark, 'Mark')}
Expand All @@ -60,7 +61,7 @@ export const TopToolbar: React.FC<TopToolbarProps> = ({ctx}) => {
{inlineGroupButton(CommonSliceType.sub, 'Subscript')}
{inlineGroupButton(CommonSliceType.math, 'Math')}
{inlineGroupButton(CommonSliceType.kbd, 'Key')}
{inlineGroupButton(CommonSliceType.hidden, 'Spoiler')}
{inlineGroupButton(CommonSliceType.spoiler, 'Spoiler')}
{inlineGroupButton(CommonSliceType.bookmark, 'Bookmark')}
<ButtonSeparator />
{button('Blue', () => {
Expand Down
1 change: 1 addition & 0 deletions src/json-crdt-peritext-ui/plugins/minimal/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const text: PeritextPlugin['text'] = (props, inline) => {
if (attrs[CommonSliceType.b]) style.fontWeight = 'bold';
if (attrs[CommonSliceType.i]) style.fontStyle = 'italic';
if (attrs[CommonSliceType.u]) textDecoration = 'underline';
if (attrs[CommonSliceType.overline]) textDecoration = textDecoration ? textDecoration + ' overline' : 'overline';
if (attrs[CommonSliceType.s]) textDecoration = textDecoration ? textDecoration + ' line-through' : 'line-through';
if ((attr = attrs[CommonSliceType.col])) style.color = attr[0].slice.data() + '';

Expand Down
2 changes: 1 addition & 1 deletion src/json-crdt-peritext-ui/plugins/toolbar/RenderBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// biome-ignore lint: React is used for JSX
import * as React from 'react';
import type {BlockViewProps} from '../../react/BlockView';
import {CommonSliceType} from '../../../json-crdt-extensions';
import type {BlockViewProps} from '../../react/BlockView';

export interface RenderBlockProps extends BlockViewProps {
children: React.ReactNode;
Expand Down
25 changes: 0 additions & 25 deletions src/json-crdt-peritext-ui/plugins/toolbar/RenderInline.tsx

This file was deleted.

41 changes: 41 additions & 0 deletions src/json-crdt-peritext-ui/plugins/toolbar/RenderInline/Code.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// biome-ignore lint: React is used for JSX
import * as React from 'react';
import {rule, drule, theme, useTheme} from 'nano-theme';
import type {InlineAttr} from '../../../../json-crdt-extensions';

const blockClass = drule({
...theme.font.mono.mid,
fz: '.9em',
pdt: '.05em',
pdb: '.05em',
});

const startClass = rule({
borderTopLeftRadius: '.3em',
borderBottomLeftRadius: '.3em',
pdl: '.24em',
});

const endClass = rule({
borderTopRightRadius: '.3em',
borderBottomRightRadius: '.3em',
pdr: '.24em',
});

export interface CodeProps {
attr: InlineAttr;
children: React.ReactNode;
}

export const Code: React.FC<CodeProps> = (props) => {
const {children, attr} = props;
const theme = useTheme();
const className =
blockClass({
bg: theme.g(0.2, 0.1),
}) +
(attr.isStart() ? startClass : '') +
(attr.isEnd() ? endClass : '');

return <span className={className}>{children}</span>;
};
19 changes: 19 additions & 0 deletions src/json-crdt-peritext-ui/plugins/toolbar/RenderInline/Del.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// biome-ignore lint: React is used for JSX
import * as React from 'react';
import {rule} from 'nano-theme';

const delClass = rule({
bg: '#ffebe9',
bxsh: '0 2px 0 0 #ffcecb',
col: 'red',
});

export interface DelProps {
children: React.ReactNode;
}

export const Del: React.FC<DelProps> = (props) => {
const {children} = props;

return <del className={delClass}>{children}</del>;
};
19 changes: 19 additions & 0 deletions src/json-crdt-peritext-ui/plugins/toolbar/RenderInline/Ins.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// biome-ignore lint: React is used for JSX
import * as React from 'react';
import {rule} from 'nano-theme';

const blockClass = rule({
bg: '#dafbe1',
bxsh: '0 2px 0 0 #aceebb',
td: 'none',
});

export interface InsProps {
children: React.ReactNode;
}

export const Ins: React.FC<InsProps> = (props) => {
const {children} = props;

return <ins className={blockClass}>{children}</ins>;
};
Loading