mirror of
https://github.com/outline/outline.git
synced 2026-06-13 03:14:59 +03:00
fc01deeefd
* chore(deps-dev): bump oxlint-tsgolint from 0.14.2 to 0.22.1 Bumps [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) from 0.14.2 to 0.22.1. - [Release notes](https://github.com/oxc-project/tsgolint/releases) - [Commits](https://github.com/oxc-project/tsgolint/compare/v0.14.2...v0.22.1) --- updated-dependencies: - dependency-name: oxlint-tsgolint dependency-version: 0.22.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * chore: Switch tsconfig to bundler resolution for tsgolint 0.22.1 oxlint-tsgolint 0.22.1 removed support for moduleResolution=node10 (the alias for "node"). Switch to "bundler" with resolvePackageJsonExports disabled so packages whose exports field omits a types condition still resolve. Update markdown-it type imports to sub-paths since the package's .d.mts entry only re-exports a subset of named types. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: Resolve type-aware lint errors caught by tsgolint 0.22.1 oxlint-tsgolint 0.22.1 catches several await-thenable, no-floating-promises, and no-meaningless-void-operator cases the prior 0.14.2 missed: - Drop redundant inner `await` from Promise.all([await x, await y]) call sites so the array entries are real Promises rather than already-resolved values. - Replace Promise.all wrappers around synchronous presenters (presentEvent, presentTemplate, presentPublicTeam) with plain map / direct calls. - Wrap non-promise branches of ternaries inside Promise.all with Promise.resolve so the array remains thenable across both arms. - Add `void` to the unawaited provider.connect() in the auth-failed retry chain, and remove `void` from the disconnect() call which returns void. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tom Moor <tom@getoutline.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
183 lines
5.4 KiB
TypeScript
183 lines
5.4 KiB
TypeScript
import type Token from "markdown-it/lib/token.mjs";
|
|
import { WarningIcon, InfoIcon, StarredIcon, DoneIcon } from "outline-icons";
|
|
import { wrappingInputRule } from "prosemirror-inputrules";
|
|
import type {
|
|
NodeSpec,
|
|
Node as ProsemirrorNode,
|
|
NodeType,
|
|
} from "prosemirror-model";
|
|
import type { Command, EditorState, Transaction } from "prosemirror-state";
|
|
import * as React from "react";
|
|
import ReactDOM from "react-dom";
|
|
import type { Primitive } from "utility-types";
|
|
import toggleWrap from "../commands/toggleWrap";
|
|
import type { MarkdownSerializerState } from "../lib/markdown/serializer";
|
|
import noticesRule from "../rules/notices";
|
|
import Node from "./Node";
|
|
|
|
export enum NoticeTypes {
|
|
Info = "info",
|
|
Success = "success",
|
|
Tip = "tip",
|
|
Warning = "warning",
|
|
}
|
|
|
|
export default class Notice extends Node {
|
|
get name() {
|
|
return "container_notice";
|
|
}
|
|
|
|
get rulePlugins() {
|
|
return [noticesRule];
|
|
}
|
|
|
|
get schema(): NodeSpec {
|
|
return {
|
|
attrs: {
|
|
style: {
|
|
default: NoticeTypes.Info,
|
|
},
|
|
},
|
|
content:
|
|
"(list | blockquote | hr | paragraph | heading | code_block | code_fence | attachment)+",
|
|
group: "block",
|
|
defining: true,
|
|
draggable: true,
|
|
parseDOM: [
|
|
{
|
|
tag: "div.notice-block",
|
|
preserveWhitespace: "full",
|
|
contentElement: (node: HTMLDivElement) =>
|
|
node.querySelector("div.content") || node,
|
|
getAttrs: (dom: HTMLDivElement) => ({
|
|
style: dom.className.includes(NoticeTypes.Tip)
|
|
? NoticeTypes.Tip
|
|
: dom.className.includes(NoticeTypes.Warning)
|
|
? NoticeTypes.Warning
|
|
: dom.className.includes(NoticeTypes.Success)
|
|
? NoticeTypes.Success
|
|
: undefined,
|
|
}),
|
|
},
|
|
// Quill editor parsing
|
|
{
|
|
tag: "div.ql-hint",
|
|
preserveWhitespace: "full",
|
|
getAttrs: (dom: HTMLDivElement) => ({
|
|
style: dom.dataset.hint,
|
|
}),
|
|
},
|
|
// GitBook parsing
|
|
{
|
|
tag: "div.alert.theme-admonition",
|
|
preserveWhitespace: "full",
|
|
getAttrs: (dom: HTMLDivElement) => ({
|
|
style: dom.className.includes(NoticeTypes.Warning)
|
|
? NoticeTypes.Warning
|
|
: dom.className.includes(NoticeTypes.Success)
|
|
? NoticeTypes.Success
|
|
: undefined,
|
|
}),
|
|
},
|
|
// Confluence parsing
|
|
{
|
|
tag: "div.confluence-information-macro",
|
|
preserveWhitespace: "full",
|
|
getAttrs: (dom: HTMLDivElement) => ({
|
|
style: dom.className.includes("confluence-information-macro-tip")
|
|
? NoticeTypes.Success
|
|
: dom.className.includes("confluence-information-macro-note")
|
|
? NoticeTypes.Tip
|
|
: dom.className.includes("confluence-information-macro-warning")
|
|
? NoticeTypes.Warning
|
|
: undefined,
|
|
}),
|
|
},
|
|
],
|
|
toDOM: (node) => {
|
|
let icon;
|
|
if (typeof document !== "undefined") {
|
|
let component;
|
|
|
|
if (node.attrs.style === NoticeTypes.Tip) {
|
|
component = <StarredIcon />;
|
|
} else if (node.attrs.style === NoticeTypes.Warning) {
|
|
component = <WarningIcon />;
|
|
} else if (node.attrs.style === NoticeTypes.Success) {
|
|
component = <DoneIcon />;
|
|
} else {
|
|
component = <InfoIcon />;
|
|
}
|
|
|
|
icon = document.createElement("div");
|
|
icon.className = "icon";
|
|
ReactDOM.render(component, icon);
|
|
}
|
|
|
|
return [
|
|
"div",
|
|
{ class: `notice-block ${node.attrs.style}` },
|
|
...(icon ? [icon] : []),
|
|
["div", { class: "content" }, 0],
|
|
];
|
|
},
|
|
};
|
|
}
|
|
|
|
commands({ type }: { type: NodeType }) {
|
|
return {
|
|
container_notice: (attrs: Record<string, Primitive>) =>
|
|
toggleWrap(type, attrs),
|
|
info: (): Command => (state, dispatch) =>
|
|
this.handleStyleChange(state, dispatch, NoticeTypes.Info),
|
|
warning: (): Command => (state, dispatch) =>
|
|
this.handleStyleChange(state, dispatch, NoticeTypes.Warning),
|
|
success: (): Command => (state, dispatch) =>
|
|
this.handleStyleChange(state, dispatch, NoticeTypes.Success),
|
|
tip: (): Command => (state, dispatch) =>
|
|
this.handleStyleChange(state, dispatch, NoticeTypes.Tip),
|
|
};
|
|
}
|
|
|
|
handleStyleChange = (
|
|
state: EditorState,
|
|
dispatch: ((tr: Transaction) => void) | undefined,
|
|
style: NoticeTypes
|
|
): boolean => {
|
|
const { tr, selection } = state;
|
|
const { $from } = selection;
|
|
const node = $from.node(-1);
|
|
|
|
if (node?.type.name === this.name) {
|
|
if (dispatch) {
|
|
const transaction = tr.setNodeMarkup($from.before(-1), undefined, {
|
|
...node.attrs,
|
|
style,
|
|
});
|
|
dispatch(transaction);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
inputRules({ type }: { type: NodeType }) {
|
|
return [wrappingInputRule(/^:::$/, type)];
|
|
}
|
|
|
|
toMarkdown(state: MarkdownSerializerState, node: ProsemirrorNode) {
|
|
state.write("\n:::" + (node.attrs.style || "info") + "\n");
|
|
state.renderContent(node);
|
|
state.ensureNewLine();
|
|
state.write(":::");
|
|
state.closeBlock(node);
|
|
}
|
|
|
|
parseMarkdown() {
|
|
return {
|
|
block: "container_notice",
|
|
getAttrs: (tok: Token) => ({ style: tok.info }),
|
|
};
|
|
}
|
|
}
|