Compare commits

..

24 commits

Author SHA1 Message Date
Magnus Åhall
40d1c388fd Bumped to v2 2026-05-26 10:03:49 +02:00
Magnus Åhall
7cde8cf874 Added updated SQL 2026-05-26 10:03:24 +02:00
Magnus Åhall
19d0ce9105 New colors on icons 2026-05-24 16:08:29 +02:00
Magnus Åhall
21d93f0188 Design change. 2026-05-23 22:55:29 +02:00
Magnus Åhall
068e21c962 Rename nodes. 2026-05-23 22:55:16 +02:00
Magnus Åhall
0ae8ef939b Removed old dependency on marked 2026-05-21 18:05:45 +02:00
Magnus Åhall
d4e6c58256 Whitespace OCD 2026-05-20 20:24:06 +02:00
Magnus Åhall
13467c2f28 Fixed adding node with ROOT_NODE parent UUID 2026-05-20 20:22:45 +02:00
Magnus Åhall
12b629756f Disable caching when developing 2026-05-20 20:06:10 +02:00
Magnus Åhall
f37ebc1c41 Offline first 2026-05-20 19:58:34 +02:00
Magnus Åhall
84181858c8 Show markdown only when there is content 2026-05-20 18:49:43 +02:00
Magnus Åhall
45cbd49345 Tree focus 2026-05-20 18:28:16 +02:00
Magnus Åhall
5a0340c226 Added markdown rendering 2026-05-15 08:22:43 +02:00
Magnus Åhall
26ca510785 Fixed tree reset after sync, optimized sync with IndexedDB 2026-05-03 19:45:39 +02:00
Magnus Åhall
454d065baa push/popState is handled better 2026-05-03 09:51:48 +02:00
Magnus Åhall
9fc4a14ce3 Work on sync element, now a custom HTML element 2026-05-03 09:17:20 +02:00
Magnus Åhall
99063d34be Upgraded Crumbs and Crumb to custom HTML elements. 2026-04-29 15:33:04 +02:00
Magnus Åhall
993bbf59f3 Upgraded the TreeNative component to a custom HTML component 2026-04-29 14:48:27 +02:00
Magnus Åhall
e2b20816c2 Updated the native treenode to a custom HTML element 2026-04-29 14:03:08 +02:00
Magnus Åhall
d9c82868ab Better visual sync 2025-11-29 16:45:56 +01:00
Magnus Åhall
40a68d6ad0 Use pure CSS 2025-11-29 09:33:42 +01:00
Magnus Åhall
989542be91 First steps to creating a new node 2025-06-28 09:13:26 +02:00
Magnus Åhall
1ce8e29e37 Tree render and navigation with note rendering 2025-06-25 14:59:21 +02:00
Magnus Åhall
dd27be67b9 Tree expansion and keyboard navigation works 2025-06-16 21:30:46 +02:00
195 changed files with 14354 additions and 9710 deletions

26
main.go
View file

@ -23,9 +23,9 @@ import (
"text/template"
)
const VERSION = "v1"
const VERSION = "v2"
const CONTEXT_USER = 1
const SYNC_PAGINATION = 100
const SYNC_PAGINATION = 200
var (
FlagGenerate bool
@ -132,6 +132,7 @@ func main() { // {{{
http.HandleFunc("/notes2", pageNotes2)
http.HandleFunc("/login", pageLogin)
http.HandleFunc("/sync", pageSync)
http.HandleFunc("/offline", pageOffline)
http.HandleFunc("/user/authenticate", AuthManager.AuthenticationHandler)
@ -226,6 +227,15 @@ func pageServiceWorker(w http.ResponseWriter, r *http.Request) { // {{{
return
}
} // }}}
func pageOffline(w http.ResponseWriter, r *http.Request) { // {{{
page := NewPage("offline")
err := Webengine.Render(page, w, r)
if err != nil {
w.Write([]byte(err.Error()))
return
}
} // }}}
func pageLogin(w http.ResponseWriter, r *http.Request) { // {{{
page := NewPage("login")
@ -269,9 +279,11 @@ func actionSyncFromServer(w http.ResponseWriter, r *http.Request) { // {{{
return
}
/*
Log.Debug("/sync/from_server", "num_nodes", len(nodes), "maxSeq", maxSeq)
foo, _ := json.Marshal(nodes)
os.WriteFile(fmt.Sprintf("/tmp/nodes-%d.json", offset), foo, 0644)
*/
j, _ := json.Marshal(struct {
OK bool
@ -288,7 +300,6 @@ func actionSyncFromServerCount(w http.ResponseWriter, r *http.Request) { // {{{
user := getUser(r)
changedFrom, _ := strconv.Atoi(r.PathValue("sequence"))
Log.Debug("FOO", "UUID", user.ClientUUID, "changedFrom", changedFrom)
count, err := NodesCount(user.UserID, uint64(changedFrom), user.ClientUUID)
if err != nil {
Log.Error("/sync/from_server/count", "error", err)
@ -334,9 +345,14 @@ func actionSyncToServer(w http.ResponseWriter, r *http.Request) { // {{{
return
}
db.Exec(`CALL add_nodes($1, $2, $3::jsonb)`, user.UserID, user.ClientUUID, request.NodeData)
_, err = db.Exec(`CALL add_nodes($1, $2, $3::jsonb)`, user.UserID, user.ClientUUID, request.NodeData)
if err != nil {
Log.Error("sync", "error", err)
httpError(w, err)
return
}
responseData(w, map[string]interface{}{
responseData(w, map[string]any{
"OK": true,
})
} // }}}

View file

@ -1,16 +1 @@
CREATE TABLE public.node_history (
id serial4 NOT NULL,
user_id int4 NOT NULL,
uuid bpchar(36) NOT NULL,
parents varchar[] NULL,
created timestamptz NOT NULL,
updated timestamptz NOT NULL,
name varchar(256) NOT NULL,
"content" text NOT NULL,
content_encrypted text NOT NULL,
markdown bool DEFAULT false NOT NULL,
client bpchar(36) DEFAULT ''::bpchar NOT NULL,
CONSTRAINT node_history_pk PRIMARY KEY (id),
CONSTRAINT node_history_user_fk FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE RESTRICT ON UPDATE RESTRICT
);
CREATE INDEX node_history_uuid_idx ON public.node USING btree (uuid);
DROP INDEX public.node_uuid_idx;

166
sql/00011.sql Normal file
View file

@ -0,0 +1,166 @@
CREATE OR REPLACE PROCEDURE add_nodes(p_user_id int4, p_client_uuid varchar, p_nodes jsonb)
LANGUAGE PLPGSQL AS $$
DECLARE
node_data jsonb;
node_updated timestamptz;
db_updated timestamptz;
db_uuid bpchar;
db_client bpchar;
db_client_seq int;
node_uuid bpchar;
parent_uuid bpchar;
BEGIN
RAISE NOTICE '--------------------------';
FOR node_data IN SELECT * FROM jsonb_array_elements(p_nodes)
LOOP
node_uuid = (node_data->>'UUID')::bpchar;
node_updated = (node_data->>'Updated')::timestamptz;
IF node_data->>'ParentUUID' = '00000000-0000-0000-0000-000000000000' THEN
parent_uuid = NULL;
ELSE
parent_uuid = node_data->>'ParentUUID';
END IF;
/* Retrieve the current modified timestamp for this node from the database. */
SELECT
uuid, updated, client, client_sequence
INTO
db_uuid, db_updated, db_client, db_client_seq
FROM public."node"
WHERE
user_id = p_user_id AND
uuid = node_uuid;
/* Is the node not in database? It needs to be created. */
IF db_uuid IS NULL THEN
RAISE NOTICE '01 New node %', node_uuid;
INSERT INTO public."node" (
user_id, "uuid", parent_uuid, created, updated,
"name", "content", markdown, "content_encrypted",
client, client_sequence
)
VALUES(
p_user_id,
node_uuid,
parent_uuid,
(node_data->>'Created')::timestamptz,
(node_data->>'Updated')::timestamptz,
(node_data->>'Name')::varchar,
(node_data->>'Content')::text,
(node_data->>'Markdown')::bool,
'', /* content_encrypted */
p_client_uuid,
(node_data->>'ClientSequence')::int
);
CONTINUE;
END IF;
/* The client could send a specific node again if it didn't receive the OK from this procedure before. */
IF db_updated = node_updated AND db_client = p_client_uuid AND db_client_seq = (node_data->>'ClientSequence')::int THEN
RAISE NOTICE '04, already recorded, %, %', db_client, db_client_seq;
CONTINUE;
END IF;
/* Determine if the incoming node data is to go into history or replace the current node. */
IF db_updated > node_updated THEN
RAISE NOTICE '02 DB newer, % > % (%))', db_updated, node_updated, node_uuid;
/* Incoming node is going straight to history since it is older than the current node. */
INSERT INTO node_history(
user_id, "uuid", parents, created, updated,
"name", "content", markdown, "content_encrypted",
client, client_sequence
)
VALUES(
p_user_id,
node_uuid,
(jsonb_populate_record(null::json_ancestor_array, node_data))."Ancestors",
(node_data->>'Created')::timestamptz,
(node_data->>'Updated')::timestamptz,
(node_data->>'Name')::varchar,
(node_data->>'Content')::text,
(node_data->>'Markdown')::bool,
'', /* content_encrypted */
p_client_uuid,
(node_data->>'ClientSequence')::int
)
ON CONFLICT (client, client_sequence)
DO NOTHING;
ELSE
RAISE NOTICE '03 Client newer, % > % (%, %)', node_updated, db_updated, node_uuid, (node_data->>'ClientSequence');
/* Incoming node is newer and will replace the current node.
*
* The current node is copied to the node_history table and then modified in place
* with the incoming data. */
INSERT INTO node_history(
user_id, "uuid", parents,
created, updated, "name", "content", markdown, "content_encrypted",
client, client_sequence
)
SELECT
user_id,
"uuid",
(
WITH RECURSIVE nodes AS (
SELECT
uuid,
COALESCE(parent_uuid, '') AS parent_uuid,
name,
0 AS depth
FROM node
WHERE
uuid = node_uuid
UNION
SELECT
n.uuid,
COALESCE(n.parent_uuid, '') AS parent_uuid,
n.name,
nr.depth+1 AS depth
FROM node n
INNER JOIN nodes nr ON n.uuid = nr.parent_uuid
)
SELECT ARRAY (
SELECT name
FROM nodes
ORDER BY depth DESC
OFFSET 1 /* discard itself */
)
),
created,
updated,
name,
content,
markdown,
content_encrypted,
client,
client_sequence
FROM public."node"
WHERE
user_id = p_user_id AND
uuid = node_uuid
ON CONFLICT (client, client_sequence)
DO NOTHING;
/* Current node in database is updated with incoming data. */
UPDATE public."node"
SET
updated = (node_data->>'Updated')::timestamptz,
updated_seq = nextval('node_updates'),
name = (node_data->>'Name')::varchar,
content = (node_data->>'Content')::text,
markdown = (node_data->>'Markdown')::bool,
client = p_client_uuid,
client_sequence = (node_data->>'ClientSequence')::int
WHERE
user_id = p_user_id AND
uuid = node_uuid;
END IF;
END LOOP;
END
$$;

166
sql/00012.sql Normal file
View file

@ -0,0 +1,166 @@
CREATE OR REPLACE PROCEDURE add_nodes(p_user_id int4, p_client_uuid varchar, p_nodes jsonb)
LANGUAGE PLPGSQL AS $$
DECLARE
node_data jsonb;
node_updated timestamptz;
db_updated timestamptz;
db_uuid bpchar;
db_client bpchar;
db_client_seq int;
node_uuid bpchar;
parent_uuid_nullable bpchar;
BEGIN
RAISE NOTICE '--------------------------';
FOR node_data IN SELECT * FROM jsonb_array_elements(p_nodes)
LOOP
node_uuid = (node_data->>'UUID')::bpchar;
node_updated = (node_data->>'Updated')::timestamptz;
IF node_data->>'ParentUUID' = '00000000-0000-0000-0000-000000000000' THEN
parent_uuid_nullable = NULL;
ELSE
parent_uuid_nullable = node_data->>'ParentUUID';
END IF;
/* Retrieve the current modified timestamp for this node from the database. */
SELECT
uuid, updated, client, client_sequence
INTO
db_uuid, db_updated, db_client, db_client_seq
FROM public."node"
WHERE
user_id = p_user_id AND
uuid = node_uuid;
/* Is the node not in database? It needs to be created. */
IF db_uuid IS NULL THEN
RAISE NOTICE '01 New node %', node_uuid;
INSERT INTO public."node" (
user_id, "uuid", parent_uuid, created, updated,
"name", "content", markdown, "content_encrypted",
client, client_sequence
)
VALUES(
p_user_id,
node_uuid,
parent_uuid_nullable,
(node_data->>'Created')::timestamptz,
(node_data->>'Updated')::timestamptz,
(node_data->>'Name')::varchar,
(node_data->>'Content')::text,
(node_data->>'Markdown')::bool,
'', /* content_encrypted */
p_client_uuid,
(node_data->>'ClientSequence')::int
);
CONTINUE;
END IF;
/* The client could send a specific node again if it didn't receive the OK from this procedure before. */
IF db_updated = node_updated AND db_client = p_client_uuid AND db_client_seq = (node_data->>'ClientSequence')::int THEN
RAISE NOTICE '04, already recorded, %, %', db_client, db_client_seq;
CONTINUE;
END IF;
/* Determine if the incoming node data is to go into history or replace the current node. */
IF db_updated > node_updated THEN
RAISE NOTICE '02 DB newer, % > % (%))', db_updated, node_updated, node_uuid;
/* Incoming node is going straight to history since it is older than the current node. */
INSERT INTO node_history(
user_id, "uuid", parents, created, updated,
"name", "content", markdown, "content_encrypted",
client, client_sequence
)
VALUES(
p_user_id,
node_uuid,
(jsonb_populate_record(null::json_ancestor_array, node_data))."Ancestors",
(node_data->>'Created')::timestamptz,
(node_data->>'Updated')::timestamptz,
(node_data->>'Name')::varchar,
(node_data->>'Content')::text,
(node_data->>'Markdown')::bool,
'', /* content_encrypted */
p_client_uuid,
(node_data->>'ClientSequence')::int
)
ON CONFLICT (client, client_sequence)
DO NOTHING;
ELSE
RAISE NOTICE '03 Client newer, % > % (%, %)', node_updated, db_updated, node_uuid, (node_data->>'ClientSequence');
/* Incoming node is newer and will replace the current node.
*
* The current node is copied to the node_history table and then modified in place
* with the incoming data. */
INSERT INTO node_history(
user_id, "uuid", parents,
created, updated, "name", "content", markdown, "content_encrypted",
client, client_sequence
)
SELECT
user_id,
"uuid",
(
WITH RECURSIVE nodes AS (
SELECT
uuid,
COALESCE(parent_uuid, '') AS parent_uuid,
name,
0 AS depth
FROM node
WHERE
uuid = node_uuid
UNION
SELECT
n.uuid,
COALESCE(n.parent_uuid, '') AS parent_uuid,
n.name,
nr.depth+1 AS depth
FROM node n
INNER JOIN nodes nr ON n.uuid = nr.parent_uuid
)
SELECT ARRAY (
SELECT name
FROM nodes
ORDER BY depth DESC
OFFSET 1 /* discard itself */
)
),
created,
updated,
name,
content,
markdown,
content_encrypted,
client,
client_sequence
FROM public."node"
WHERE
user_id = p_user_id AND
uuid = node_uuid
ON CONFLICT (client, client_sequence)
DO NOTHING;
/* Current node in database is updated with incoming data. */
UPDATE public."node"
SET
updated = (node_data->>'Updated')::timestamptz,
updated_seq = nextval('node_updates'),
name = (node_data->>'Name')::varchar,
content = (node_data->>'Content')::text,
markdown = (node_data->>'Markdown')::bool,
client = p_client_uuid,
client_sequence = (node_data->>'ClientSequence')::int
WHERE
user_id = p_user_id AND
uuid = node_uuid;
END IF;
END LOOP;
END
$$;

View file

@ -1,11 +1,15 @@
@import "theme.css";
#app {
display: grid;
justify-items: center;
margin-top: 128px;
}
#logo {
margin-bottom: 48px;
}
#box {
display: grid;
grid-gap: 16px 0;
@ -14,24 +18,27 @@
padding: 48px 0px;
background-color: #fff;
box-shadow: 0px 20px 52px -33px rgba(0,0,0,0.75);
border-left: 8px solid #666;
}
#box input {
border-left: 8px solid var(--color3);
input {
padding: 4px 8px;
font-size: 1em;
width: calc(100% - 64px);
border: 1px solid #aaa;
border-radius: 4px;
}
#box button {
button {
padding: 6px 16px;
font-size: 1em;
border-radius: 4px;
border: none;
background-color: #fe5f55;
background-color: var(--color1);
color: #fff;
}
#box #error {
#error {
color: #c33;
margin-top: 16px;
}
}

View file

@ -1,23 +1,29 @@
@import "theme.css";
html {
box-sizing: border-box;
background: #efede8;
background: var(--color2);
font-family: "Liberation Mono", monospace;
font-size: 14px;
margin: 0px;
padding: 0px;
}
body {
margin: 0px;
padding: 0px;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
*:focus {
outline: none;
}
[onClick] {
cursor: pointer;
}

76
static/css/markdown.css Normal file
View file

@ -0,0 +1,76 @@
.el-node-markdown {
h1 {
border-bottom: 1px solid #ccc;
margin-top: 32px;
margin-bottom: 8px;
display: inline-block;
font-size: 1.25em;
border-radius: 8px;
color: #fff;
background-color: var(--color1);
padding: 4px 12px;
&:first-child {
margin-top: 32px;
}
}
h2 {
font-size: 1.25em;
margin-top: 32px;
margin-bottom: 0px;
color: var(--color1);
}
h3:before {
font-size: 1.0em;
content: "> ";
color: var(--color1);
}
p {
line-height: 150%;
}
img {
max-width: var(--thumbnail-width);
max-height: var(--thumbnail-height);
}
table {
border: 1px solid #ccc;
border-collapse: collapse;
th {
text-align: left;
padding: 8px;
}
th,
td {
border: 1px solid #ccc;
padding: 8px;
}
}
code {
background-color: #f8f8f8;
border: 1px solid #ccc;
padding: 2px 4px;
border-radius: 4px;
}
pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
padding: 8px;
border-radius: 4px;
code {
border: unset;
padding: unset;
}
}
}

View file

@ -1,93 +1,170 @@
@import "theme.css";
:root {
--content-width: 900px;
--thumbnail-width: 300px;
--thumbnail-height: 100px;
}
html {
background-color: #fff;
}
#notes2 {
min-height: 100vh;
display: grid;
grid-template-areas: "tree crumbs" "tree sync" "tree name" "tree content" "tree blank";
grid-template-columns: min-content 1fr;
grid-template-rows: 48px 56px 48px min-content 1fr;
}
grid-template-areas:
"tree hum crumbs crumbs ding"
"tree hum name name ding"
"tree hum sync functions ding"
"tree hum content content ding"
"tree hum blank blank ding"
;
grid-template-columns: min-content minmax(16px, 1fr) minmax(min-content, 820px) 80px minmax(16px, 1fr);
grid-template-rows:
min-content min-content 48px 1fr;
@media only screen and (max-width: 600px) {
#notes2 {
grid-template-areas: "crumbs" "sync" "name" "content" "blank";
grid-template-areas:
"crumbs"
"sync"
"name"
"content"
"blank"
;
grid-template-columns: 1fr;
}
#notes2 #tree {
#tree {
display: none;
}
n2-syncprogress {
.el-count {
top: 4px;
}
}
}
}
#tree {
grid-area: tree;
padding: 16px 32px;
background-color: #333;
color: #ddd;
display: grid;
background-color: #fafafa;
color: #444;
z-index: 100;
border-left: 2px solid #333;
border-right: 1px solid #ddd;
n2-tree {
/*border: 2px solid #f8f8f8;*/
padding: 16px 48px 16px 24px;
}
#tree:focus {
border-left: 2px solid #FE5F55;
&:focus-within {
n2-tree {
/*
border: 2px solid #fe5f55;
*/
}
#tree #logo {
}
#logo {
display: grid;
position: relative;
justify-items: center;
margin-top: 8px;
margin-bottom: 8px;
margin-left: 24px;
margin-right: 24px;
}
#tree #logo img {
cursor: pointer;
img {
width: 128px;
left: -20px;
}
#tree .icons {
}
.icons {
display: flex;
justify-content: center;
margin-bottom: 32px;
gap: 8px;
}
#tree .node {
.node {
display: grid;
grid-template-columns: 24px min-content;
grid-template-rows: min-content 1fr;
grid-template-columns: 40px min-content;
grid-template-rows:
min-content 1fr;
margin-top: 12px;
}
#tree .node .expand-toggle {
align-items: center;
.expand-toggle {
user-select: none;
cursor: pointer;
justify-self: center;
img {
width: auto;
height: 18px;
}
#tree .node .expand-toggle img {
width: 16px;
height: 16px;
}
#tree .node .name {
.name {
white-space: nowrap;
cursor: pointer;
user-select: none;
&:hover {
color: var(--color1);
}
#tree .node .name:hover {
color: #fe5f55;
}
#tree .node .name.selected {
color: #fe5f55;
&.selected {
color: var(--color1);
font-weight: bold;
}
#tree .node .children {
padding-left: 24px;
margin-left: 8px;
border-left: 1px solid #444;
grid-column: 1 / -1;
}
#tree .node .children.collapsed {
.children {
padding-left: 24px;
margin-left: 18px;
border-left: 1px solid #ddd;
grid-column: 1 / -1;
&.collapsed {
display: none;
}
}
}
}
#tree-nodes {
padding: 16px 32px;
/*
border-radius: 8px;
*/
/*
box-shadow: 5px 5px 10px -5px rgba(0, 0, 0, 0.75);
*/
}
#crumbs {
grid-area: crumbs;
display: grid;
align-items: start;
justify-items: center;
margin: 0px 16px;
}
#crumbs .crumbs {
height: min-content;
margin: 0 16px 16px 16px;
n2-crumbs {
background: #e4e4e4;
display: flex;
flex-wrap: wrap;
padding: 8px 16px;
@ -95,170 +172,219 @@ html {
color: #333;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
&.node-modified {
background-color: var(--color1);
color: var(--color2);
.crumb:after {
color: var(--color2);
}
#crumbs .crumbs.node-modified {
background-color: #fe5f55;
color: #efede8;
}
#crumbs .crumbs.node-modified .crumb:after {
color: #efede8;
}
#crumbs .crumbs .crumb {
n2-crumb {
margin-right: 8px;
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
a {
text-decoration: none;
color: inherit;
}
#crumbs .crumbs .crumb:after {
content: "•";
margin-left: 8px;
color: #fe5f55;
}
#crumbs .crumbs .crumb:last-child {
n2-crumb:after {
content: ">";
font-weight: bold;
color: var(--color1)
}
n2-crumb:last-child {
margin-right: 0;
}
#crumbs .crumbs .crumb:last-child:after {
n2-crumb:last-child:after {
content: '';
margin-left: 0px;
}
#sync-progress {
}
}
n2-syncprogress {
--radius: 8px;
display: grid;
grid-area: sync;
display: grid;
justify-items: center;
width: 100%;
height: 56px;
align-items: center;
position: relative;
}
#sync-progress progress {
width: 100%;
padding: 0 7px;
max-width: 900px;
height: 16px;
border-radius: 4px;
}
#sync-progress progress[value]::-webkit-progress-bar {
background-color: #eee;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25) inset;
border-radius: 4px;
}
#sync-progress progress[value]::-moz-progress-bar {
background-color: #eee;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25) inset;
border-radius: 4px;
}
#sync-progress progress[value]::-webkit-progress-value {
background: #ba5f59;
background: linear-gradient(180deg, #ba5f59 0%, #fe5f55 50%, #ba5f59 100%);
border-radius: 4px;
}
#sync-progress progress[value]::-moz-progress-value {
background: #ba5f59;
background: linear-gradient(180deg, #ba5f59 0%, #fe5f55 50%, #ba5f59 100%);
border-radius: 4px;
}
#sync-progress .count {
width: min-content;
white-space: nowrap;
margin-top: 0px;
color: #888;
position: absolute;
top: 22px;
}
#sync-progress.hidden {
visibility: hidden;
opacity: 0;
transition: visibility 0s 500ms, opacity 500ms linear;
transition: height 0s 500ms, opacity 500ms linear, visibility 0s 500ms;
&.show {
opacity: 1;
transition: visibility, height 0s, opacity 500ms linear;
}
#name {
progress {
width: 100%;
height: 24px;
border-radius: 8px;
}
.count {
position: absolute;
top: 16px;
width: 100%;
white-space: nowrap;
color: #888;
text-align: center;
font-size: 12pt;
font-weight: bold;
}
progress[value]::-webkit-progress-bar {
background-color: #eee;
box-shadow: 0 2px var(--radius) rgba(0, 0, 0, 0.25) inset;
border-radius: var(--radius);
}
progress[value]::-moz-progress-bar {
background-color: #eee;
box-shadow: 0 2px var(--radius) rgba(0, 0, 0, 0.25) inset;
border-radius: var(--radius);
}
progress[value]::-webkit-progress-value {
background: rgb(186, 95, 89);
background: linear-gradient(180deg, rgba(186, 95, 89, 1) 0%, rgba(254, 95, 85, 1) 50%, rgba(186, 95, 89, 1) 100%);
border-radius: var(--radius);
}
progress[value]::-moz-progress-value {
background: rgb(186, 95, 89);
background: linear-gradient(180deg, rgba(186, 95, 89, 1) 0%, rgba(254, 95, 85, 1) 50%, rgba(186, 95, 89, 1) 100%);
border-radius: var(--radius);
}
}
/* ============================================================= */
n2-nodeui {
margin-bottom: 32px;
.el-name {
grid-area: name;
color: #333;
font-weight: bold;
text-align: center;
font-size: 1.15em;
margin-top: 0px;
margin-bottom: 16px;
margin-top: 8px;
margin-bottom: 0px;
}
/* ============================================================= *
* Textarea replicates the height of an element expanding height *
* ============================================================= */
.grow-wrap {
/* easy way to plop the elements on top of each other and have them both sized based on the tallest one's height */
display: grid;
.el-functions {
grid-area: functions;
}
.el-node-content {
grid-area: content;
font-size: 1em;
}
.grow-wrap::after {
/* Note the weird space! Needed to preventy jumpy behavior */
content: attr(data-replicated-value) " ";
/* This is how textarea text behaves */
width: calc(100% - 32px);
max-width: 900px;
white-space: pre-wrap;
word-wrap: break-word;
background: rgba(0, 255, 255, 0.5);
justify-self: center;
/* Hidden from view, clicks, and screen readers */
visibility: hidden;
}
.grow-wrap > textarea {
/* You could leave this, but after a user resizes, then it ruins the auto sizing */
resize: none;
/* Firefox shows scrollbar on growth, you can hide like this. */
overflow: hidden;
}
.grow-wrap > textarea,
.grow-wrap::after {
/* Identical styling required!! */
padding: 0.5rem;
font: inherit;
/* Place on top of each other */
grid-area: 1 / 1 / 2 / 2;
}
/* ============================================================= */
#node-content {
justify-self: center;
word-wrap: break-word;
font-family: monospace;
color: #333;
width: calc(100% - 32px);
max-width: 900px;
width: 100%;
max-width: var(--content-width);
field-sizing: content;
resize: none;
border: none;
outline: none;
}
#node-content:invalid {
padding: 32px 0;
border-left: none;
border-right: none;
border-top: 1px solid #e0e0e0;
border-bottom: 1px solid #e0e0e0;
margin-bottom: 32px;
&:invalid {
background: #f5f5f5;
padding-top: 16px;
}
}
.el-node-markdown {
grid-area: content;
display: none;
border-top: 1px solid #e0e0e0;
border-bottom: 1px solid #e0e0e0;
margin-bottom: 32px;
}
&.show-markdown {
.el-node-content {
display: none;
}
.el-node-markdown {
display: block;
}
}
}
#blank {
grid-area: blank;
height: 32px;
}
dialog.op::backdrop {
dialog.op {
&::backdrop {
background: rgba(0, 0, 0, 0.5);
}
dialog.op .header {
.header {
font-weight: bold;
margin-top: 16px;
}
dialog.op .header:first-child {
&:first-child {
margin-top: 0px;
}
#op-search .results {
}
}
#op-search {
.results {
display: grid;
grid-template-columns: min-content min-content;
grid-gap: 6px 16px;
}
#op-search .results div {
div {
white-space: nowrap;
}
#op-search .results .ancestors {
.ancestors {
display: flex;
}
#op-search .results .ancestors .ancestor::after {
.ancestor::after {
content: ">";
margin: 0px 8px;
color: #a00;
}
#op-search .results .ancestors .ancestor:last-child::after {
.ancestor:last-child::after {
content: "";
}
}
}
}

View file

@ -0,0 +1,5 @@
:root {
--color1: #fe5f55;
--color2: #efede8;
--color3: #666;
}

View file

@ -2,73 +2,49 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="399.99997"
height="399.99997"
viewBox="0 0 105.83332 105.83333"
width="24.999989"
height="20.000013"
viewBox="0 0 6.6145802 5.2916702"
version="1.1"
id="svg8"
inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
id="svg1"
sodipodi:docname="collapsed.svg"
xml:space="preserve"
inkscape:version="1.4.2 (ebf0e94, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><defs
id="defs2" /><sodipodi:namedview
id="base"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4142136"
inkscape:cx="304.05591"
inkscape:cy="298.39905"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1916"
inkscape:window-height="1404"
inkscape:window-x="0"
inkscape:window-y="16"
inkscape:window-maximized="0"
inkscape:showpageshadow="true"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d6d6d6"
showborder="true" /><metadata
id="metadata5"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><g
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="4.8373092"
inkscape:cx="6.201795"
inkscape:cy="-12.40359"
inkscape:window-width="1916"
inkscape:window-height="1161"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="false" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-42.756321,-24.613384)"><rect
style="color:#000000;overflow:visible;fill:#537979;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.264583;paint-order:markers stroke fill;stop-color:#000000"
id="rect5470"
width="105.83333"
height="105.83333"
x="42.756321"
y="24.613384"
rx="21.166666"
ry="21.166666" /><rect
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.264583;paint-order:markers stroke fill;stop-color:#000000"
id="rect6360"
width="63.5"
height="18.520834"
x="63.922985"
y="68.26963"
rx="5.2916665"
ry="5.2916665" /><rect
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.264583;paint-order:markers stroke fill;stop-color:#000000"
id="rect10171"
width="63.5"
height="18.520834"
x="-109.28004"
y="86.412567"
rx="5.2916665"
ry="5.2916665"
transform="rotate(-90)" /></g></svg>
transform="translate(-102.39375,-146.31458)">
<title
id="title1">folder-outline</title>
<path
d="m 108.34687,150.94479 h -5.29166 v -3.30729 h 5.29166 m 0,-0.66146 h -2.64584 l -0.66145,-0.66146 h -1.98437 c -0.36711,0 -0.66146,0.29435 -0.66146,0.66146 v 3.96875 a 0.66145729,0.66145729 0 0 0 0.66146,0.66146 h 5.29166 a 0.66145729,0.66145729 0 0 0 0.66146,-0.66146 v -3.30729 c 0,-0.36711 -0.29767,-0.66146 -0.66146,-0.66146 z"
id="path1"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#71c837;fill-opacity:1;stroke-width:0.330728;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Before After
Before After

View file

@ -2,64 +2,43 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="399.99997"
height="399.99997"
viewBox="0 0 105.83332 105.83333"
width="26.499931"
height="20.000013"
viewBox="0 0 7.0114402 5.2916702"
version="1.1"
id="svg8"
inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
id="svg1"
sodipodi:docname="expanded.svg"
inkscape:version="1.4.2 (ebf0e94, 2025-05-08)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><defs
id="defs2" /><sodipodi:namedview
id="base"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4142136"
inkscape:cx="304.05591"
inkscape:cy="298.39905"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1916"
inkscape:window-height="1404"
inkscape:window-x="0"
inkscape:window-y="16"
inkscape:window-maximized="0"
inkscape:showpageshadow="true"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d6d6d6"
showborder="true" /><metadata
id="metadata5"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><g
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="11.17754"
inkscape:cx="20.845374"
inkscape:cy="26.929003"
inkscape:window-width="1916"
inkscape:window-height="1161"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" /><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-42.756321,-24.613384)"><rect
style="color:#000000;overflow:visible;fill:#537979;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.264583;paint-order:markers stroke fill;stop-color:#000000"
id="rect5470"
width="105.83333"
height="105.83333"
x="42.756321"
y="24.613384"
rx="21.166666"
ry="21.166666" /><rect
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.264583;paint-order:markers stroke fill;stop-color:#000000"
id="rect6360"
width="63.5"
height="18.520834"
x="63.922985"
y="68.26963"
rx="5.2916665"
ry="5.2916665" /></g></svg>
transform="translate(-101.33542,-147.10833)"><title
id="title1">folder-open</title><title
id="title1-1">folder-open-outline</title><path
d="m 102.69141,149.0927 -0.69454,2.64584 v -3.30729 h 5.62239 a 0.6614573,0.6614573 0 0 0 -0.66146,-0.66146 h -2.3151 l -0.66146,-0.66146 h -1.98437 a 0.6614573,0.6614573 0 0 0 -0.66145,0.66146 v 3.96875 a 0.6614573,0.6614573 0 0 0 0.66145,0.66146 h 4.96093 c 0.29766,0 0.56224,-0.19844 0.62839,-0.4961 l 0.76067,-2.8112 h -5.65545 m 4.26639,2.64584 h -4.29947 l 0.52916,-1.98438 h 4.29948 z"
id="path1"
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#71c837;fill-opacity:1;stroke-width:0.264583;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1" /></g></svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg
fill="#000000"
width="365.05487"
height="224.67957"
viewBox="0 0 365.05487 224.67956"
role="img"
version="1.1"
id="svg1"
sodipodi:docname="markdown.svg"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="0.70710678"
inkscape:cx="453.96255"
inkscape:cy="60.811183"
inkscape:window-width="2190"
inkscape:window-height="1401"
inkscape:window-x="1463"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<title
id="title1">Markdown icon</title>
<path
d="M 338.73829,224.67957 H 26.316564 A 26.316564,26.316564 0 0 1 0,198.363 V 26.316564 A 26.316564,26.316564 0 0 1 26.316564,0 H 338.73829 a 26.316564,26.316564 0 0 1 26.31657,26.316564 V 198.33258 a 26.316564,26.316564 0 0 1 -26.31657,26.33177 z M 87.742162,172.01601 v -68.45349 l 35.109038,43.8863 35.09382,-43.8863 v 68.45349 h 35.10903 V 52.678763 H 157.94502 L 122.8512,96.565057 87.742162,52.678763 H 52.633128 V 172.04644 Z M 322.94835,112.33978 H 287.83932 V 52.663552 H 252.7455 v 59.676228 h -35.10904 l 52.64834,61.44081 z"
id="path1"
style="stroke-width:15.2119;fill:#fe5f55;fill-opacity:1" />
<metadata
id="metadata1">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>Markdown icon</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="128"
width="208"
viewBox="-31.2 -32 208 128"
version="1.1"
id="svg1"
sodipodi:docname="icon_markdown_hollow.svg"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="5.6568542"
inkscape:cx="109.60155"
inkscape:cy="68.942912"
inkscape:window-width="2190"
inkscape:window-height="1401"
inkscape:window-x="1463"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1"
showgrid="false" />
<rect
fill="none"
stroke-width="10"
stroke="#000000"
ry="10"
y="-27"
x="-26.200001"
height="118"
width="198"
id="rect1"
style="stroke:#cccccc;stroke-opacity:1" />
<path
d="M -1.2000003,66 V -2 H 18.8 l 20,25 20,-25 h 20 v 68 h -20 V 27 l -20,25 -20,-25 V 66 Z M 123.8,66 93.8,33 h 20 V -2 h 20 v 35 h 20 z"
id="path1"
style="fill:#cccccc;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -7,7 +7,7 @@
viewBox="0 0 4.2333398 5.8208399"
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
inkscape:version="1.4.2 (ebf0e94, 2025-05-08)"
sodipodi:docname="icon_refresh.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
@ -23,15 +23,16 @@
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.83651094"
inkscape:cx="7.7703706"
inkscape:cy="11.356695"
inkscape:zoom="23.548693"
inkscape:cx="6.9218279"
inkscape:cy="12.5697"
inkscape:window-width="1916"
inkscape:window-height="1161"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
inkscape:current-layer="layer1"
showgrid="false" />
<defs
id="defs1" />
<g
@ -44,6 +45,6 @@
<path
d="m 104.775,150.01875 a 1.5875,1.5875 0 0 1 -1.5875,-1.5875 c 0,-0.26458 0.0661,-0.52123 0.18521,-0.74083 l -0.38629,-0.3863 c -0.20638,0.32544 -0.32809,0.71173 -0.32809,1.12713 a 2.1166667,2.1166667 0 0 0 2.11667,2.11667 v 0.79375 l 1.05833,-1.05834 -1.05833,-1.05833 m 0,-2.91042 v -0.79375 l -1.05833,1.05834 1.05833,1.05833 v -0.79375 a 1.5875,1.5875 0 0 1 1.5875,1.5875 c 0,0.26458 -0.0661,0.52123 -0.18521,0.74083 l 0.38629,0.38629 c 0.20638,-0.32543 0.32809,-0.71172 0.32809,-1.12712 a 2.1166667,2.1166667 0 0 0 -2.11667,-2.11667 z"
id="path1"
style="stroke-width:0.264583;fill:#f9f9f9" />
style="stroke-width:0.264583;fill:#fe5f55;fill-opacity:1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Before After
Before After

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="4.7624998mm"
height="4.7624998mm"
viewBox="0 0 4.7624998 4.7624998"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="icon_save.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="5.6568542"
inkscape:cx="41.454135"
inkscape:cy="-3.8890873"
inkscape:window-width="1093"
inkscape:window-height="1401"
inkscape:window-x="2560"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-40.48125,-208.22708)">
<title
id="title1">content-save</title>
<path
d="m 43.65625,209.81458 h -2.645833 v -1.05833 h 2.645833 m -0.79375,3.70417 a 0.79375,0.79375 0 0 1 -0.79375,-0.79375 0.79375,0.79375 0 0 1 0.79375,-0.79375 0.79375,0.79375 0 0 1 0.79375,0.79375 0.79375,0.79375 0 0 1 -0.79375,0.79375 m 1.322917,-4.23334 h -3.175 c -0.293688,0 -0.529167,0.23813 -0.529167,0.52917 v 3.70417 a 0.52916667,0.52916667 0 0 0 0.529167,0.52916 h 3.704166 a 0.52916667,0.52916667 0 0 0 0.529167,-0.52916 v -3.175 z"
id="path1"
style="stroke-width:0.264583;fill:#fe5f55;fill-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="4.7624998mm"
height="4.7624998mm"
viewBox="0 0 4.7624998 4.7624998"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="icon_save_disabled.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="5.6568542"
inkscape:cx="41.454135"
inkscape:cy="-3.8890873"
inkscape:window-width="1093"
inkscape:window-height="1401"
inkscape:window-x="2560"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-40.48125,-208.22708)">
<title
id="title1">content-save</title>
<path
d="m 43.65625,209.81458 h -2.645833 v -1.05833 h 2.645833 m -0.79375,3.70417 a 0.79375,0.79375 0 0 1 -0.79375,-0.79375 0.79375,0.79375 0 0 1 0.79375,-0.79375 0.79375,0.79375 0 0 1 0.79375,0.79375 0.79375,0.79375 0 0 1 -0.79375,0.79375 m 1.322917,-4.23334 h -3.175 c -0.293688,0 -0.529167,0.23813 -0.529167,0.52917 v 3.70417 a 0.52916667,0.52916667 0 0 0 0.529167,0.52916 h 3.704166 a 0.52916667,0.52916667 0 0 0 0.529167,-0.52916 v -3.175 z"
id="path1"
style="stroke-width:0.264583;fill:#cccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -7,7 +7,7 @@
viewBox="0 0 109.40056 109.39984"
version="1.1"
id="svg8"
inkscape:version="1.4 (e7c3feb, 2024-10-09)"
inkscape:version="1.4.2 (ebf0e94, 2025-05-08)"
sodipodi:docname="icon_search.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
@ -27,16 +27,16 @@
inkscape:pageshadow="2"
inkscape:zoom="0.70710678"
inkscape:cx="206.47518"
inkscape:cy="207.18229"
inkscape:cy="207.88939"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="2190"
inkscape:window-height="1404"
inkscape:window-x="1463"
inkscape:window-y="16"
inkscape:window-maximized="0"
inkscape:window-width="1916"
inkscape:window-height="1161"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:showpageshadow="true"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d6d6d6"
@ -62,6 +62,6 @@
<path
d="m 78.736803,96.575592 a 40.634474,40.634474 0 0 1 40.634477,40.634438 c 0,10.06486 -3.68838,19.31694 -9.75227,26.44372 l 1.68795,1.68701 h 4.93863 l 31.2573,31.25736 -9.37719,9.37731 -31.25729,-31.25737 v -4.93863 l -1.68795,-1.68701 c -7.126666,6.06378 -16.378815,9.75204 -26.443681,9.75204 A 40.634474,40.634474 0 0 1 38.102322,137.21003 40.634474,40.634474 0 0 1 78.736803,96.575592 m 0,12.502758 c -15.628636,0 -28.131559,12.50299 -28.131559,28.13168 0,15.62868 12.502923,28.13144 28.131559,28.13144 15.628635,0 28.131557,-12.50276 28.131557,-28.13144 0,-15.62869 -12.502922,-28.13168 -28.131557,-28.13168 z"
id="path1"
style="stroke-width:6.25145;fill:#ffffff" />
style="stroke-width:6.25145;fill:#fe5f55;fill-opacity:1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before After
Before After

View file

@ -2,56 +2,51 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="399.99997"
height="399.99997"
viewBox="0 0 105.83332 105.83333"
width="18"
height="18"
viewBox="0 0 4.7625 4.7625"
version="1.1"
id="svg8"
inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
id="svg1"
sodipodi:docname="leaf.svg"
inkscape:version="1.4.2 (ebf0e94, 2025-05-08)"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><defs
id="defs2" /><sodipodi:namedview
id="base"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4142136"
inkscape:cx="303.34881"
inkscape:cy="297.69195"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1916"
inkscape:window-height="1404"
inkscape:window-x="0"
inkscape:window-y="16"
inkscape:window-maximized="0"
inkscape:showpageshadow="true"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d6d6d6"
showborder="true" /><metadata
id="metadata5"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><g
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="11.17754"
inkscape:cx="8.0965937"
inkscape:cy="22.903072"
inkscape:window-width="1916"
inkscape:window-height="1161"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showgrid="false" /><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-42.756321,-24.613384)"><rect
style="color:#000000;overflow:visible;fill:#555555;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.264583;paint-order:markers stroke fill;stop-color:#000000"
id="rect5470"
width="105.83333"
height="105.83333"
x="42.756321"
y="24.613384"
rx="21.166666"
ry="21.166666" /></g></svg>
transform="translate(-107.95,-148.16667)"><title
id="title1">folder-open</title><title
id="title1-1">folder-open-outline</title><title
id="title1-5">notebook-outline</title><title
id="title1-8">text-box-outline</title><path
d="m 108.47917,148.16667 c -0.29369,0 -0.52917,0.23548 -0.52917,0.52917 V 152.4 c 0,0.29369 0.23548,0.52917 0.52917,0.52917 h 3.70416 c 0.29369,0 0.52917,-0.23548 0.52917,-0.52917 v -3.70416 c 0,-0.29369 -0.23548,-0.52917 -0.52917,-0.52917 h -3.70416 m 0,0.52917 h 3.70416 V 152.4 h -3.70416 v -3.70416"
id="path1"
style="fill:#ababab;fill-opacity:1;stroke-width:0.264583"
sodipodi:nodetypes="cssssssscccccc" /><path
d="m 109.00833,149.225 v 0.52917 h 2.64584 V 149.225 h -2.64584 m 0,1.05834 v 0.52916 h 2.64584 v -0.52916 h -2.64584 m 0,1.05833 v 0.52917 h 1.85209 v -0.52917 z"
id="path2"
style="fill:#c7c7c7;fill-opacity:1;stroke-width:0.264583"
sodipodi:nodetypes="ccccccccccccccc" /></g></svg>

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

331
static/js/app.mjs Normal file
View file

@ -0,0 +1,331 @@
import { ROOT_NODE } from 'node_store'
import { CustomHTMLElement } from './lib/custom_html_element.mjs'
import { N2Tree } from 'tree'
import { Node } from 'node'
export class App {
constructor() {// {{{
this.currentNode = null
this.tree = new N2Tree()
this.crumbs = new N2Crumbs()
this.crumbsElement = document.getElementById('crumbs')
this.nodeUI = document.getElementById('note')
_mbus.subscribe('TREE_TRUNK_FETCHED', async () => {
document.getElementById('tree').append(this.tree.render())
document.getElementById('tree-nodes')?.focus()
const startNode = await this.getStartNode()
this.goToNode(startNode.UUID, false, false)
})
_mbus.subscribe('TREE_NODE_SELECTED', event => {
const node = event.detail.data
this.goToNode(node.UUID, false, false)
})
_mbus.subscribe('GO_TO_NODE', event => {
const node = event.detail.data
this.goToNode(node.nodeUUID, node.dontPush, node.dontExpand)
})
window.addEventListener('keydown', event => this.keyHandler(event))
window.addEventListener('popstate', event => this.popState(event))
document.getElementById('notes2').addEventListener('click', event => {
if (event.target.id === 'notes2')
document.getElementById('node-content')?.focus()
})
window._sync = new Sync()
// I think it is uncomfortable having the sync running as soon as the page load.
// I haven't gotten the time to look at the page before stuff jumps around.
// There a slight delay to initiate sync seems reasonable.
setTimeout(() => window._sync.run(), 1000)
}// }}}
keyHandler(event) {//{{{
let handled = true
// All keybindings is Alt+Shift, since the popular browsers at the time (2023) allows to override thees.
// Ctrl+S is the exception to using Alt+Shift, since it is overridable and in such widespread use for saving.
// Thus, the exception is acceptable to consequent use of alt+shift.
if (!(event.shiftKey && event.altKey) && !(event.key.toUpperCase() === 'S' && event.ctrlKey))
return
switch (event.key.toUpperCase()) {
case 'T':
if (document.activeElement.id === 'tree-nodes') {
console.log('take focus')
this.nodeUI.takeFocus()
} else {
this.tree.focus()
}
break
case 'F':
_mbus.dispatch('op-search')
break
/*
case 'C':
this.showPage('node')
break
case 'E':
this.showPage('keys')
break
*/
case 'M':
globalThis._mbus.dispatch('MARKDOWN_TOGGLE')
break
case 'N':
this.createNode()
break
/*
case 'P':
this.showPage('node-properties')
break
*/
case 'S':
this.saveNode()
/*
else if (this.page.value === 'node-properties')
this.nodeProperties.current.save()
*/
break
/*
case 'U':
this.showPage('upload')
break
case 'F':
this.showPage('search')
break
*/
default:
handled = false
}
if (handled) {
event.preventDefault()
event.stopPropagation()
}
}//}}}
popState(event) {// {{{
_mbus.dispatch("GO_TO_NODE", { nodeUUID: event.state.nodeUUID, dontPush: true, dontExpand: true })
}// }}}
async getStartNode() {//{{{
let nodeUUID = ROOT_NODE
// Is a UUID provided on the URI as an anchor?
const parts = document.URL.split('#')
if (parts[1]?.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i))
nodeUUID = parts[1]
return await nodeStore.get(nodeUUID)
}//}}}
async saveNode() {//{{{
if (!this.currentNode.isModified())
return
/* The node history is a local store for node history.
* This could be provisioned from the server or cleared if
* deemed unnecessary.
*
* The send queue is what will be sent back to the server
* to have a recorded history of the notes.
*
* A setting to be implemented in the future could be to
* not save the history locally at all. */
const node = this.currentNode
// The node is still in its old state and will present
// the unmodified content to the node store.
const history = nodeStore.nodesHistory.add(node)
// Prepares the node object for saving.
// Sets Updated value to current date and time.
await node.save()
// Updated node is added to the send queue to be stored on server.
const sendQueue = nodeStore.sendQueue.add(node)
// Updated node is saved to the primary node store.
const nodeStoreAdding = nodeStore.add([node])
await Promise.all([history, sendQueue, nodeStoreAdding])
}//}}}
async createNode() {//{{{
let name = prompt("Name")
if (!name)
return
const nn = Node.create(name, this.currentNode.UUID)
nn.save()
nodeStore.sendQueue.add(nn)
nodeStore.add([nn])
}//}}}
async goToNode(nodeUUID, dontPush, dontExpand) {//{{{
if (nodeUUID === null || nodeUUID === undefined)
return
// Don't switch notes until saved.
if (this.nodeUI.isModified()) {
if (!confirm("Changes not saved. Do you want to discard changes?"))
return
}
if (!dontPush)
history.pushState({ nodeUUID }, '', `/notes2#${nodeUUID}`)
const node = nodeStore.node(nodeUUID)
node.reset() // any modifications are discarded.
this.currentNode = node
this.tree.setSelected(node, dontExpand)
const ancestors = await nodeStore.getNodeAncestry(node)
_mbus.dispatch('CRUMBS_SET', ancestors, () => this.crumbsElement.replaceChildren(this.crumbs.render()))
_mbus.dispatch('NODE_UI_OPEN', node)
_mbus.dispatch('NODE_UNMODIFIED')
// Scrolls node into view.
this.tree.makeVisible(node)
}//}}}
}
class N2Crumbs extends CustomHTMLElement {
static {// {{{
this.tmpl = document.createElement('template')
this.tmpl.innerHTML = `
`
}// }}}
constructor() {// {{{
super()
this.classList.add('crumbs')
this.crumbs = []
_mbus.subscribe('CRUMBS_SET', event => {
this.crumbs = event.detail.data
})
}// }}}
render() {// {{{
const crumbs = this.crumbs.map(node =>
new N2Crumb(
node.get('Name'),
node.UUID,
)
)
const start = new N2Crumb('Start', ROOT_NODE)
crumbs.push(start)
this.replaceChildren(...crumbs.reverse())
return this
}// }}}
}
customElements.define('n2-crumbs', N2Crumbs)
class N2Crumb extends CustomHTMLElement {
static {// {{{
this.tmpl = document.createElement('template')
this.tmpl.innerHTML = `
<a data-el="link"></a>
`
}// }}}
constructor(label, uuid) {// {{{
super()
this.classList.add('crumb')
this.label = label
this.uuid = uuid
this.elLink.href = `/notes2#${this.uuid}`
this.elLink.innerText = this.label
this.elLink.addEventListener('click', () => _mbus.dispatch("GO_TO_NODE", { nodeUUID: this.uuid, dontPush: false, dontExpand: true }))
}// }}}
}
customElements.define('n2-crumb', N2Crumb)
function tmpl(html) {// {{{
const el = document.createElement('template')
el.innerHTML = html
return el.content.children
}// }}}
class Op {
constructor(id) {// {{{
this.id = id
_mbus.subscribe(this.id, p => this.render(p))
}// }}}
render(html) {// {{{
const op = document.getElementById('op')
const t = document.createElement('template')
t.innerHTML = `<dialog id="${this.id}" class="op">${html}</dialog>`
op.replaceChildren(t.content)
document.getElementById(this.id).showModal()
}// }}}
get(selector) {// {{{
return document.querySelector(`#${this.id} ${selector}`)
}// }}}
bind(selector, event, fn) {// {{{
this.get(selector).addEventListener(event, evt => fn(evt))
}// }}}
}
class OpSearch extends Op {
constructor() {// {{{
super('op-search')
}// }}}
render() {// {{{
super.render(`
<div class="header">Search</div>
<div>
<input type="text" />
</div>
<div class="header">Results</div>
<div class="results"></div>
`)
this.bind('input[type="text"]', 'keydown', evt => this.search(evt))
}// }}}
search(event) {// {{{
if (event.key !== 'Enter')
return
const searchFor = document.querySelector('#op-search input').value
nodeStore.search(searchFor, ROOT_NODE)
.then(res => this.displayResults(res))
}// }}}
displayResults(results) {// {{{
const rs = []
for (const r of results) {
const ancestors = r.ancestry.reverse().map(a => {
const div = tmpl(`<div class="ancestor">${a.data.Name}</div>`)
div[0].addEventListener('click', () => _notes2.current.goToNode(a.UUID))
return div[0]
})
const div = tmpl(`<div>${r.name}</div>`)
div[0].addEventListener('click', () => _notes2.current.goToNode(r.uuid))
rs.push(...div)
const ancDev = tmpl('<div class="ancestors"></div>')
ancDev[0].append(...ancestors)
rs.push(ancDev[0])
}
this.get('.results').replaceChildren(...rs)
}// }}}
}
// vim: foldmethod=marker

View file

@ -0,0 +1,57 @@
export class CustomHTMLElement extends HTMLElement {
constructor() {// {{{
super()
this.appendChild(this.constructor.tmpl.content.cloneNode(true))
this.querySelectorAll('*').forEach(el => {
const field = el.dataset.field
if (field !== undefined) {
const fieldName = this.toElementName('field', field)
this[fieldName] = el
}
const name = el.dataset.el
if (name !== undefined) {
const elName = this.toElementName('el', name)
this[elName] = el
el.classList.add('el-' + name)
}
})
}// }}}
toElementName(prefix, str) {// {{{
str = prefix + '-' + str
return str.replace(/-(id|[a-z])/g, match => match.toUpperCase().replace('-', ''))
}// }}}
}
export class StupidPreactCustomHTMLElement extends HTMLElement {
constructor() {// {{{
super()
// Stupid stuff because of Preact.
this.clonedNodes = this.constructor.tmpl.content.cloneNode(true)
this.clonedNodes.querySelectorAll('*').forEach(el => {
const field = el.dataset.field
if (field !== undefined) {
const fieldName = this.toElementName('field', field)
this[fieldName] = el
}
const name = el.dataset.el
if (name !== undefined) {
const elName = this.toElementName('el', name)
this[elName] = el
el.classList.add('el-' + name)
}
})
}// }}}
toElementName(prefix, str) {// {{{
str = prefix + '-' + str
return str.replace(/-(id|[a-z])/g, match => match.toUpperCase().replace('-', ''))
}// }}}
connectedCallback() {// {{{
// Stupid stuff because of Preact.
this.appendChild(this.clonedNodes)
}// }}}
}

View file

@ -4,14 +4,24 @@
"requires": true,
"packages": {
"node_modules/marked": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/marked/-/marked-11.1.1.tgz",
"integrity": "sha512-EgxRjgK9axsQuUa/oKMx5DEY8oXpKJfk61rT5iY3aRlgU6QJtUcxU5OAymdhCvWvhYcd9FKmO5eQoX8m9VGJXg==",
"version": "18.0.3",
"resolved": "https://registry.npmjs.org/marked/-/marked-18.0.3.tgz",
"integrity": "sha512-7VT90JOkDeaRWpfjOReRGPEKn0ecdARBkDGL+tT1wZY0efPPqkUxLUSmzy/C7TIylQYJC9STISEsCHrqb/7VIA==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 18"
"node": ">= 20"
}
},
"node_modules/marked-token-position": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/marked-token-position/-/marked-token-position-2.0.2.tgz",
"integrity": "sha512-IMyr4mR3A5uFReXn7cxLDgDLjefG110ANy0oMGs5+gB7NsdIbv9YoVoJuGxuMSFHWOeIFkAzjdSoFNVKcMPfZw==",
"license": "MIT",
"peerDependencies": {
"marked": ">=16.2.0 <19"
}
},
"node_modules/preact": {

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 @UziTech
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,160 @@
# marked-token-position
Add `position` field for each token.
```ts
interface Position {
/**
* Positions for each line of the token. LinePositions will not include the newline character for the line.
*/
lines: LinePosition[]
/**
* Position at the beginning of token
*/
start: PositionFields;
/**
* Position at the end of token
*/
end: PositionFields;
}
interface LinePosition {
/**
* Position at the beginning of line
*/
start: PositionFields;
/**
* Position at the end of line. Will not include the newline character.
*/
end: PositionFields;
}
interface PositionFields {
/**
* Number of characters from the beginning of the markdown string
*/
offset: number;
/**
* Line number of the token. Starts at line 0.
*/
line: number;
/**
* Column number of the token. Starts at column 0.
*/
column: number;
}
```
# Usage
## Extension
```js
import {Marked} from "marked";
import markedTokenPosition from "marked-token-position";
// or UMD script
// <script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.js"></script>
// <script src="https://cdn.jsdelivr.net/npm/marked-token-position/lib/index.umd.js"></script>
// const Marked = marked.Marked;
const marked = new Marked();
function anotherExtension {
return {
walkTokens(token) {
// token has `position` field
}
hooks: {
processAllTokens(tokens) {
// tokens have `position` field
}
}
};
}
marked.use(anotherExtension(), markedTokenPosition());
marked.parse("# example markdown");
```
The `position` field will be added to the tokens so any other extension can
use the `position` field in a `walkTokens` function or `processAllTokens` hook.
> [!CAUTION]
> The `processAllTokens` hook is used by this extension so any other extension
> using `processAllTokens` that requires the `position` field must be added
> before this extension because marked calls the `processAllTokens` hooks in
> reverse order.
The tokens will look like:
```json
[
{
"type": "heading",
"raw": "# example markdown",
"depth": 1,
"text": "example markdown",
"tokens": [
{
"type": "text",
"raw": "example markdown",
"text": "example markdown",
"escaped": false,
"position": {
"start": {
"offset": 2,
"line": 0,
"column": 2
},
"end": {
"offset": 18,
"line": 0,
"column": 18
}
}
}
],
"position": {
"start": {
"offset": 0,
"line": 0,
"column": 0
},
"end": {
"offset": 18,
"line": 0,
"column": 18
}
}
}
]
```
## addTokenPositions
Calling `marked.lexer()` will not add the `position` field with the extension
since the extension is only called on `marked.parse()` and `marked.parseInline()`.
An `addTokenPositions` function is exported to add the `position` field to the
tokens returned by `marked.lexer()`.
```js
import {Marked} from "marked";
import {addTokenPositions} from "marked-token-position";
// or UMD script
// <script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.js"></script>
// <script src="https://cdn.jsdelivr.net/npm/marked-token-position/lib/index.umd.js"></script>
// const Marked = marked.Marked;
// const addTokenPositions = markedTokenPosition.addTokenPositions;
const marked = new Marked();
const tokens = marked.lexer("# example markdown");
addTokenPositions(tokens);
// tokens now have a `position` field
```

View file

@ -0,0 +1,59 @@
// Generated by dts-bundle-generator v9.5.1
import { MarkedExtension, Token, Tokens } from 'marked';
export interface TokenWithPosition extends Tokens.Generic {
position: Position;
}
export interface Position {
/**
* Positions for each line of the token. LinePositions will not include the newline character for the line.
*/
lines: LinePosition[];
/**
* Position at the beginning of token
*/
start: PositionFields;
/**
* Position at the end of token
*/
end: PositionFields;
}
export interface LinePosition {
/**
* Position at the beginning of line
*/
start: PositionFields;
/**
* Position at the end of line. Will not include the newline character.
*/
end: PositionFields;
}
export interface PositionFields {
/**
* Number of characters from the beginning of the markdown string
*/
offset: number;
/**
* Line number of the token. Starts at line 0.
*/
line: number;
/**
* Column number of the token. Starts at column 0.
*/
column: number;
}
/**
* Add position field to tokens
*/
export declare function addTokenPositions(tokens: Token[]): TokenWithPosition[];
/**
* Marked extension to add position field to tokens
*/
declare function _default(options?: {}): MarkedExtension;
export {
_default as default,
};
export {};

View file

@ -0,0 +1,6 @@
function g(u){let i=u.map(r=>r.raw).join("");return h(u,0,0,0,i).tokens}function b(u={}){return{hooks:{processAllTokens(i){return g(i)}}}}function h(u,i,r,f,l){for(let s of u){let n=s,a=T(i,r,f,l,n.raw);if(n.position=a,n.tokens&&h(n.tokens,i,r,f,l),n.childTokens){let c=i,t=r,e=f,d=l;for(let k of n.childTokens){let o=h(n[k],c,t,e,d);c=o.offset,t=o.line,e=o.column,d=o.markdown}}if(n.type==="list"&&h(n.items,i,r,f,l),n.type==="table"){let c=i,t=r,e=f,d=l;for(let k of n.header){let o=h(k.tokens,c,t,e,d);c=o.offset,t=o.line,e=o.column,d=o.markdown}for(let k of n.rows)for(let o of k){let P=h(o.tokens,c,t,e,d);c=P.offset,t=P.line,e=P.column,d=P.markdown}}let m=a.end.offset-i;i=a.end.offset,r=a.end.line,f=a.end.column,l=l.slice(m)}return{tokens:u,offset:i,line:r,column:f,markdown:l}}function T(u,i,r,f,l){let s=[],n=l.split(`
`),a=f.split(`
`);n:for(let t=0;t<=a.length-n.length;t++){s=[];for(let e=0;e<n.length;e++){let d=a[t+e],k=n[e],o=d.indexOf(k);if(o===-1)continue n;let P=a.slice(0,t+e).join(`
`)+(t+e>0?`
`:""),x={offset:u+P.length+o,line:i+t+e,column:(t+e===0?r:0)+o},p={offset:x.offset+k.length,line:x.line,column:x.column+k.length};s.push({start:x,end:p})}break}if(s.length===0)throw new Error(`Cannot find ${JSON.stringify(l)} in ${JSON.stringify(f)}`);let m=s[0].start,c=s.at(-1).end;return s.length>1&&s.at(-1).start.offset===c.offset&&(s=s.slice(0,-1)),{lines:s,start:m,end:c}}export{g as addTokenPositions,b as default};
//# sourceMappingURL=index.esm.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,9 @@
(function(g,f){if(typeof exports=="object"&&typeof module<"u"){module.exports=f()}else if("function"==typeof define && define.amd){define("markedTokenPosition",f)}else {g["markedTokenPosition"]=f()}}(typeof globalThis < "u" ? globalThis : typeof self < "u" ? self : this,function(){var exports={};var __exports=exports;var module={exports};
var m=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var F=(e,n)=>()=>(e&&(n=e(e=0)),n);var M=(e,n)=>{for(var o in n)m(e,o,{get:n[o],enumerable:!0})},j=(e,n,o,f)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of y(n))!C.call(e,t)&&t!==o&&m(e,t,{get:()=>n[t],enumerable:!(f=O(n,t))||f.enumerable});return e};var b=e=>j(m({},"__esModule",{value:!0}),e);var T={};M(T,{addTokenPositions:()=>L,default:()=>E});function L(e){let n=e.map(o=>o.raw).join("");return x(e,0,0,0,n).tokens}function E(e={}){return{hooks:{processAllTokens(n){return L(n)}}}}function x(e,n,o,f,t){for(let c of e){let i=c,a=S(n,o,f,t,i.raw);if(i.position=a,i.tokens&&x(i.tokens,n,o,f,t),i.childTokens){let d=n,r=o,s=f,u=t;for(let k of i.childTokens){let l=x(i[k],d,r,s,u);d=l.offset,r=l.line,s=l.column,u=l.markdown}}if(i.type==="list"&&x(i.items,n,o,f,t),i.type==="table"){let d=n,r=o,s=f,u=t;for(let k of i.header){let l=x(k.tokens,d,r,s,u);d=l.offset,r=l.line,s=l.column,u=l.markdown}for(let k of i.rows)for(let l of k){let P=x(l.tokens,d,r,s,u);d=P.offset,r=P.line,s=P.column,u=P.markdown}}let p=a.end.offset-n;n=a.end.offset,o=a.end.line,f=a.end.column,t=t.slice(p)}return{tokens:e,offset:n,line:o,column:f,markdown:t}}function S(e,n,o,f,t){let c=[],i=t.split(`
`),a=f.split(`
`);n:for(let r=0;r<=a.length-i.length;r++){c=[];for(let s=0;s<i.length;s++){let u=a[r+s],k=i[s],l=u.indexOf(k);if(l===-1)continue n;let P=a.slice(0,r+s).join(`
`)+(r+s>0?`
`:""),h={offset:e+P.length+l,line:n+r+s,column:(r+s===0?o:0)+l},w={offset:h.offset+k.length,line:h.line,column:h.column+k.length};c.push({start:h,end:w})}break}if(c.length===0)throw new Error(`Cannot find ${JSON.stringify(t)} in ${JSON.stringify(f)}`);let p=c[0].start,d=c.at(-1).end;return c.length>1&&c.at(-1).start.offset===d.offset&&(c=c.slice(0,-1)),{lines:c,start:p,end:d}}var g=F(()=>{"use strict"});module.exports=(g(),b(T)).default;module.exports.addTokenPositions=(g(),b(T)).addTokenPositions;
if(__exports != exports)module.exports = exports;return module.exports}));
//# sourceMappingURL=index.umd.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,66 @@
{
"name": "marked-token-position",
"version": "2.0.2",
"description": "marked extension template",
"main": "./lib/index.esm.js",
"module": "./lib/index.esm.js",
"browser": "./lib/index.umd.js",
"type": "module",
"keywords": [
"marked",
"extension"
],
"files": [
"lib/",
"src/"
],
"exports": {
".": {
"typescript": "./src/index.ts",
"types": "./lib/index.d.ts",
"default": "./lib/index.esm.js"
}
},
"scripts": {
"build": "npm run build:esbuild && npm run build:types",
"build:esbuild": "node esbuild.config.js",
"build:types": "tsc && dts-bundle-generator --export-referenced-types --project tsconfig.json -o lib/index.d.ts src/index.ts",
"format": "eslint --fix",
"lint": "eslint",
"test": "npm run build:esbuild && node --experimental-transform-types ./spec/test.config.js",
"test:cover": "npm run build:esbuild && node --experimental-transform-types --experimental-test-coverage ./spec/test.config.js -- --cover",
"test:only": "npm run build:esbuild && node --experimental-transform-types ./spec/test.config.js -- --only",
"test:types": "npm run build:types && tsc --project tsconfig-test-types.json && attw -P --entrypoints . --profile esm-only",
"test:update": "npm run build:esbuild && node --experimental-transform-types --test-update-snapshots ./spec/test.config.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/UziTech/marked-token-position.git"
},
"author": "Tony Brix <Tony@Brix.ninja> (https://Tony.Brix.ninja)",
"license": "MIT",
"bugs": {
"url": "https://github.com/UziTech/marked-token-position/issues"
},
"homepage": "https://github.com/UziTech/marked-token-position#readme",
"peerDependencies": {
"marked": ">=16.2.0 <19"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.18.2",
"@markedjs/eslint-config": "^1.0.14",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^13.0.1",
"@semantic-release/git": "^10.0.1",
"@semantic-release/github": "^12.0.6",
"@semantic-release/npm": "^13.1.5",
"@semantic-release/release-notes-generator": "^14.1.0",
"dts-bundle-generator": "^9.5.1",
"esbuild": "^0.28.0",
"esbuild-plugin-umd-wrapper": "^3.0.0",
"eslint": "^10.2.0",
"marked": "^18.0.0",
"semantic-release": "^25.0.3",
"typescript": "^6.0.2"
}
}

View file

@ -0,0 +1,192 @@
/* node:coverage ignore next */
import type { MarkedExtension, Token, Tokens } from 'marked';
export interface TokenWithPosition extends Tokens.Generic {
position: Position;
}
interface Position {
/**
* Positions for each line of the token. LinePositions will not include the newline character for the line.
*/
lines: LinePosition[]
/**
* Position at the beginning of token
*/
start: PositionFields;
/**
* Position at the end of token
*/
end: PositionFields;
}
interface LinePosition {
/**
* Position at the beginning of line
*/
start: PositionFields;
/**
* Position at the end of line. Will not include the newline character.
*/
end: PositionFields;
}
interface PositionFields {
/**
* Number of characters from the beginning of the markdown string
*/
offset: number;
/**
* Line number of the token. Starts at line 0.
*/
line: number;
/**
* Column number of the token. Starts at column 0.
*/
column: number;
}
/**
* Add position field to tokens
*/
export function addTokenPositions(tokens: Token[]) {
const markdown = tokens.map(token => token.raw).join('');
return addPosition(tokens, 0, 0, 0, markdown).tokens;
}
/**
* Marked extension to add position field to tokens
*/
export default function(options = {}): MarkedExtension {
return {
hooks: {
processAllTokens(tokens) {
return addTokenPositions(tokens);
},
},
};
}
function addPosition(tokens: Token[], offset: number, line: number, column: number, markdown: string) {
for (const token of tokens) {
const genericToken = token as Tokens.Generic;
const position = getPosition(offset, line, column, markdown, genericToken.raw);
genericToken.position = position;
if (genericToken.tokens) {
addPosition(genericToken.tokens, offset, line, column, markdown);
}
if (genericToken.childTokens) {
let nextOffset = offset;
let nextLine = line;
let nextColumn = column;
let nextMarkdown = markdown;
for (const childToken of genericToken.childTokens) {
const nextPosition = addPosition(genericToken[childToken], nextOffset, nextLine, nextColumn, nextMarkdown);
nextOffset = nextPosition.offset;
nextLine = nextPosition.line;
nextColumn = nextPosition.column;
nextMarkdown = nextPosition.markdown;
}
}
if (genericToken.type === 'list') {
addPosition(genericToken.items, offset, line, column, markdown);
}
if (genericToken.type === 'table') {
let nextOffset = offset;
let nextLine = line;
let nextColumn = column;
let nextMarkdown = markdown;
for (const headerCell of genericToken.header) {
const nextPosition = addPosition(headerCell.tokens, nextOffset, nextLine, nextColumn, nextMarkdown);
nextOffset = nextPosition.offset;
nextLine = nextPosition.line;
nextColumn = nextPosition.column;
nextMarkdown = nextPosition.markdown;
}
for (const row of genericToken.rows) {
for (const rowCell of row) {
const nextPosition = addPosition(rowCell.tokens, nextOffset, nextLine, nextColumn, nextMarkdown);
nextOffset = nextPosition.offset;
nextLine = nextPosition.line;
nextColumn = nextPosition.column;
nextMarkdown = nextPosition.markdown;
}
}
}
const deltaOffset = position.end.offset - offset;
offset = position.end.offset;
line = position.end.line;
column = position.end.column;
markdown = markdown.slice(deltaOffset);
}
return {
tokens: tokens as TokenWithPosition[],
offset,
line,
column,
markdown,
};
}
function getPosition(offset: number, line: number, column: number, markdown: string, raw: string): Position {
let lines: LinePosition[] = [];
const rawLines = raw.split('\n');
const markdownLines = markdown.split('\n');
// eslint-disable-next-line no-labels
md: for (let i = 0; i <= markdownLines.length - rawLines.length; i++) {
lines = [];
for (let j = 0; j < rawLines.length; j++) {
const markdownLine = markdownLines[i + j];
const rawLine = rawLines[j];
const lineStartOffset = markdownLine.indexOf(rawLine);
if (lineStartOffset === -1) {
// eslint-disable-next-line no-labels
continue md;
}
const beforeMarkdownLines = markdownLines.slice(0, i + j).join('\n') + (i + j > 0 ? '\n' : '');
const start = {
offset: offset + beforeMarkdownLines.length + lineStartOffset,
line: line + i + j,
column: (i + j === 0 ? column : 0) + lineStartOffset,
};
const end = {
offset: start.offset + rawLine.length,
line: start.line,
column: start.column + rawLine.length,
};
lines.push({
start,
end,
});
}
break;
}
/* node:coverage ignore next 4 */
if (lines.length === 0) {
// This shouldn't ever happen but if it does it would be nice to have a good error message
throw new Error(`Cannot find ${JSON.stringify(raw)} in ${JSON.stringify(markdown)}`);
}
const start = lines[0].start;
const end = lines.at(-1)!.end;
if (lines.length > 1 && lines.at(-1)!.start.offset === end.offset) {
lines = lines.slice(0, -1);
}
return {
lines,
start,
end,
};
}

View file

@ -5,7 +5,6 @@
# Marked
[![npm](https://badgen.net/npm/v/marked)](https://www.npmjs.com/package/marked)
[![gzip size](https://badgen.net/badgesize/gzip/https://cdn.jsdelivr.net/npm/marked/marked.min.js)](https://cdn.jsdelivr.net/npm/marked/marked.min.js)
[![install size](https://badgen.net/packagephobia/install/marked)](https://packagephobia.now.sh/result?p=marked)
[![downloads](https://badgen.net/npm/dt/marked)](https://www.npmjs.com/package/marked)
[![github actions](https://github.com/markedjs/marked/workflows/Tests/badge.svg)](https://github.com/markedjs/marked/actions)
@ -18,7 +17,7 @@
## Demo
Checkout the [demo page](https://marked.js.org/demo/) to see marked in action ⛹️
Check out the [demo page](https://marked.js.org/demo/) to see Marked in action ⛹️
## Docs
@ -33,7 +32,7 @@ Also read about:
**Node.js:** Only [current and LTS](https://nodejs.org/en/about/releases/) Node.js versions are supported. End of life Node.js versions may become incompatible with Marked at any point in time.
**Browser:** Not IE11 :)
**Browser:** [Baseline Widely Available](https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility)
## Installation
@ -84,7 +83,7 @@ $ marked --help
</head>
<body>
<div id="content"></div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.js"></script>
<script>
document.getElementById('content').innerHTML =
marked.parse('# Marked in the browser\n\nRendered by **marked**.');
@ -104,4 +103,5 @@ or import esm module
## License
Copyright (c) 2011-2022, Christopher Jeffrey. (MIT License)
Copyright (c) 2018+, MarkedJS. (MIT License)
Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License)

View file

@ -2,13 +2,15 @@
/**
* Marked CLI
* Copyright (c) 2011-2013, Christopher Jeffrey (MIT License)
* Copyright (c) 2018+, MarkedJS. (MIT License)
* Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License)
*/
import { promises } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { homedir } from 'node:os';
import { createRequire } from 'node:module';
import { pathToFileURL } from 'node:url';
import { marked } from '../lib/marked.esm.js';
const { access, readFile, writeFile } = promises;
@ -28,16 +30,19 @@ export async function main(nodeProcess) {
const options = {
cwd: nodeProcess.cwd(),
env: nodeProcess.env,
stdio: 'inherit'
stdio: 'inherit',
};
const __dirname = dirname(fileURLToPath(import.meta.url));
const helpText = await readFile(resolve(__dirname, '../man/marked.1.md'), 'utf8');
// eslint-disable-next-line promise/param-names
await new Promise(res => {
spawn('man', [resolve(__dirname, '../man/marked.1')], options)
.on('error', () => {
const manProcess = spawn('man', [resolve(__dirname, '../man/marked.1')], options);
nodeProcess.on('SIGINT', () => {
manProcess.kill('SIGINT');
});
manProcess.on('error', () => {
console.log(helpText);
})
.on('close', res);
@ -128,7 +133,7 @@ export async function main(nodeProcess) {
default:
if (arg.indexOf('--') === 0) {
opt = camelize(arg.replace(/^--(no-)?/, ''));
if (!marked.defaults.hasOwnProperty(opt)) {
if (!(opt in marked.defaults)) {
continue;
}
if (arg.indexOf('--no-') === 0) {
@ -148,11 +153,11 @@ export async function main(nodeProcess) {
}
async function getData() {
if (!input) {
if (files.length <= 2) {
if (string) {
return string;
}
if (!input) {
if (files.length === 0) {
return await getStdin();
}
input = files.pop();
@ -179,7 +184,7 @@ export async function main(nodeProcess) {
throw err;
}
// must import esm
markedConfig = await import('file:///' + configFile);
markedConfig = await import(pathToFileURL(configFile).href);
}
if (markedConfig.default) {
@ -205,7 +210,7 @@ export async function main(nodeProcess) {
const defaultConfig = [
'~/.marked.json',
'~/.marked.js',
'~/.marked/index.js'
'~/.marked/index.js',
];
for (const configFile of defaultConfig) {
@ -222,8 +227,7 @@ export async function main(nodeProcess) {
if (output) {
if (noclobber && await fileExists(output)) {
nodeProcess.stderr.write('marked: output file \'' + output + '\' already exists, disable the \'-n\' / \'--no-clobber\' flag to overwrite\n');
nodeProcess.exit(1);
throw Error('marked: output file \'' + output + '\' already exists, disable the \'-n\' / \'--no-clobber\' flag to overwrite\n');
}
return await writeFile(output, html);
}
@ -271,9 +275,10 @@ export async function main(nodeProcess) {
nodeProcess.exit(0);
} catch(err) {
if (err.code === 'ENOENT') {
nodeProcess.stderr.write('marked: output to ' + err.path + ': No such directory');
nodeProcess.stderr.write('marked: ' + err.path + ': No such file or directory');
} else {
nodeProcess.stderr.write(err.message);
}
nodeProcess.stderr.write(err);
return nodeProcess.exit(1);
}
}

View file

@ -2,7 +2,8 @@
/**
* Marked CLI
* Copyright (c) 2011-2013, Christopher Jeffrey (MIT License)
* Copyright (c) 2018+, MarkedJS. (MIT License)
* Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License)
*/
import { main } from './main.js';

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,638 +0,0 @@
// Generated by dts-bundle-generator v9.0.0
export type Token = (Tokens.Space | Tokens.Code | Tokens.Heading | Tokens.Table | Tokens.Hr | Tokens.Blockquote | Tokens.List | Tokens.ListItem | Tokens.Paragraph | Tokens.HTML | Tokens.Text | Tokens.Def | Tokens.Escape | Tokens.Tag | Tokens.Image | Tokens.Link | Tokens.Strong | Tokens.Em | Tokens.Codespan | Tokens.Br | Tokens.Del | Tokens.Generic);
export declare namespace Tokens {
interface Space {
type: "space";
raw: string;
}
interface Code {
type: "code";
raw: string;
codeBlockStyle?: "indented" | undefined;
lang?: string | undefined;
text: string;
escaped?: boolean;
}
interface Heading {
type: "heading";
raw: string;
depth: number;
text: string;
tokens: Token[];
}
interface Table {
type: "table";
raw: string;
align: Array<"center" | "left" | "right" | null>;
header: TableCell[];
rows: TableCell[][];
}
interface TableCell {
text: string;
tokens: Token[];
}
interface Hr {
type: "hr";
raw: string;
}
interface Blockquote {
type: "blockquote";
raw: string;
text: string;
tokens: Token[];
}
interface List {
type: "list";
raw: string;
ordered: boolean;
start: number | "";
loose: boolean;
items: ListItem[];
}
interface ListItem {
type: "list_item";
raw: string;
task: boolean;
checked?: boolean | undefined;
loose: boolean;
text: string;
tokens: Token[];
}
interface Paragraph {
type: "paragraph";
raw: string;
pre?: boolean | undefined;
text: string;
tokens: Token[];
}
interface HTML {
type: "html";
raw: string;
pre: boolean;
text: string;
block: boolean;
}
interface Text {
type: "text";
raw: string;
text: string;
tokens?: Token[];
}
interface Def {
type: "def";
raw: string;
tag: string;
href: string;
title: string;
}
interface Escape {
type: "escape";
raw: string;
text: string;
}
interface Tag {
type: "text" | "html";
raw: string;
inLink: boolean;
inRawBlock: boolean;
text: string;
block: boolean;
}
interface Link {
type: "link";
raw: string;
href: string;
title?: string | null;
text: string;
tokens: Token[];
}
interface Image {
type: "image";
raw: string;
href: string;
title: string | null;
text: string;
}
interface Strong {
type: "strong";
raw: string;
text: string;
tokens: Token[];
}
interface Em {
type: "em";
raw: string;
text: string;
tokens: Token[];
}
interface Codespan {
type: "codespan";
raw: string;
text: string;
}
interface Br {
type: "br";
raw: string;
}
interface Del {
type: "del";
raw: string;
text: string;
tokens: Token[];
}
interface Generic {
[index: string]: any;
type: string;
raw: string;
tokens?: Token[] | undefined;
}
}
export type Links = Record<string, Pick<Tokens.Link | Tokens.Image, "href" | "title">>;
export type TokensList = Token[] & {
links: Links;
};
declare class _Renderer {
options: MarkedOptions;
constructor(options?: MarkedOptions);
code(code: string, infostring: string | undefined, escaped: boolean): string;
blockquote(quote: string): string;
html(html: string, block?: boolean): string;
heading(text: string, level: number, raw: string): string;
hr(): string;
list(body: string, ordered: boolean, start: number | ""): string;
listitem(text: string, task: boolean, checked: boolean): string;
checkbox(checked: boolean): string;
paragraph(text: string): string;
table(header: string, body: string): string;
tablerow(content: string): string;
tablecell(content: string, flags: {
header: boolean;
align: "center" | "left" | "right" | null;
}): string;
/**
* span level renderer
*/
strong(text: string): string;
em(text: string): string;
codespan(text: string): string;
br(): string;
del(text: string): string;
link(href: string, title: string | null | undefined, text: string): string;
image(href: string, title: string | null, text: string): string;
text(text: string): string;
}
declare class _TextRenderer {
strong(text: string): string;
em(text: string): string;
codespan(text: string): string;
del(text: string): string;
html(text: string): string;
text(text: string): string;
link(href: string, title: string | null | undefined, text: string): string;
image(href: string, title: string | null, text: string): string;
br(): string;
}
declare class _Parser {
options: MarkedOptions;
renderer: _Renderer;
textRenderer: _TextRenderer;
constructor(options?: MarkedOptions);
/**
* Static Parse Method
*/
static parse(tokens: Token[], options?: MarkedOptions): string;
/**
* Static Parse Inline Method
*/
static parseInline(tokens: Token[], options?: MarkedOptions): string;
/**
* Parse Loop
*/
parse(tokens: Token[], top?: boolean): string;
/**
* Parse Inline Tokens
*/
parseInline(tokens: Token[], renderer?: _Renderer | _TextRenderer): string;
}
declare const blockNormal: {
blockquote: RegExp;
code: RegExp;
def: RegExp;
fences: RegExp;
heading: RegExp;
hr: RegExp;
html: RegExp;
lheading: RegExp;
list: RegExp;
newline: RegExp;
paragraph: RegExp;
table: RegExp;
text: RegExp;
};
export type BlockKeys = keyof typeof blockNormal;
declare const inlineNormal: {
_backpedal: RegExp;
anyPunctuation: RegExp;
autolink: RegExp;
blockSkip: RegExp;
br: RegExp;
code: RegExp;
del: RegExp;
emStrongLDelim: RegExp;
emStrongRDelimAst: RegExp;
emStrongRDelimUnd: RegExp;
escape: RegExp;
link: RegExp;
nolink: RegExp;
punctuation: RegExp;
reflink: RegExp;
reflinkSearch: RegExp;
tag: RegExp;
text: RegExp;
url: RegExp;
};
export type InlineKeys = keyof typeof inlineNormal;
/**
* exports
*/
export declare const block: {
normal: {
blockquote: RegExp;
code: RegExp;
def: RegExp;
fences: RegExp;
heading: RegExp;
hr: RegExp;
html: RegExp;
lheading: RegExp;
list: RegExp;
newline: RegExp;
paragraph: RegExp;
table: RegExp;
text: RegExp;
};
gfm: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "heading" | "list" | "paragraph" | "def" | "fences" | "lheading" | "newline", RegExp>;
pedantic: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "heading" | "list" | "paragraph" | "def" | "fences" | "lheading" | "newline", RegExp>;
};
export declare const inline: {
normal: {
_backpedal: RegExp;
anyPunctuation: RegExp;
autolink: RegExp;
blockSkip: RegExp;
br: RegExp;
code: RegExp;
del: RegExp;
emStrongLDelim: RegExp;
emStrongRDelimAst: RegExp;
emStrongRDelimUnd: RegExp;
escape: RegExp;
link: RegExp;
nolink: RegExp;
punctuation: RegExp;
reflink: RegExp;
reflinkSearch: RegExp;
tag: RegExp;
text: RegExp;
url: RegExp;
};
gfm: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
breaks: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
pedantic: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
};
export interface Rules {
block: Record<BlockKeys, RegExp>;
inline: Record<InlineKeys, RegExp>;
}
declare class _Tokenizer {
options: MarkedOptions;
rules: Rules;
lexer: _Lexer;
constructor(options?: MarkedOptions);
space(src: string): Tokens.Space | undefined;
code(src: string): Tokens.Code | undefined;
fences(src: string): Tokens.Code | undefined;
heading(src: string): Tokens.Heading | undefined;
hr(src: string): Tokens.Hr | undefined;
blockquote(src: string): Tokens.Blockquote | undefined;
list(src: string): Tokens.List | undefined;
html(src: string): Tokens.HTML | undefined;
def(src: string): Tokens.Def | undefined;
table(src: string): Tokens.Table | undefined;
lheading(src: string): Tokens.Heading | undefined;
paragraph(src: string): Tokens.Paragraph | undefined;
text(src: string): Tokens.Text | undefined;
escape(src: string): Tokens.Escape | undefined;
tag(src: string): Tokens.Tag | undefined;
link(src: string): Tokens.Link | Tokens.Image | undefined;
reflink(src: string, links: Links): Tokens.Link | Tokens.Image | Tokens.Text | undefined;
emStrong(src: string, maskedSrc: string, prevChar?: string): Tokens.Em | Tokens.Strong | undefined;
codespan(src: string): Tokens.Codespan | undefined;
br(src: string): Tokens.Br | undefined;
del(src: string): Tokens.Del | undefined;
autolink(src: string): Tokens.Link | undefined;
url(src: string): Tokens.Link | undefined;
inlineText(src: string): Tokens.Text | undefined;
}
declare class _Hooks {
options: MarkedOptions;
constructor(options?: MarkedOptions);
static passThroughHooks: Set<string>;
/**
* Process markdown before marked
*/
preprocess(markdown: string): string;
/**
* Process HTML after marked is finished
*/
postprocess(html: string): string;
/**
* Process all tokens before walk tokens
*/
processAllTokens(tokens: Token[] | TokensList): Token[] | TokensList;
}
export interface TokenizerThis {
lexer: _Lexer;
}
export type TokenizerExtensionFunction = (this: TokenizerThis, src: string, tokens: Token[] | TokensList) => Tokens.Generic | undefined;
export type TokenizerStartFunction = (this: TokenizerThis, src: string) => number | void;
export interface TokenizerExtension {
name: string;
level: "block" | "inline";
start?: TokenizerStartFunction | undefined;
tokenizer: TokenizerExtensionFunction;
childTokens?: string[] | undefined;
}
export interface RendererThis {
parser: _Parser;
}
export type RendererExtensionFunction = (this: RendererThis, token: Tokens.Generic) => string | false | undefined;
export interface RendererExtension {
name: string;
renderer: RendererExtensionFunction;
}
export type TokenizerAndRendererExtension = TokenizerExtension | RendererExtension | (TokenizerExtension & RendererExtension);
export type HooksApi = Omit<_Hooks, "constructor" | "options">;
export type HooksObject = {
[K in keyof HooksApi]?: (...args: Parameters<HooksApi[K]>) => ReturnType<HooksApi[K]> | Promise<ReturnType<HooksApi[K]>>;
};
export type RendererApi = Omit<_Renderer, "constructor" | "options">;
export type RendererObject = {
[K in keyof RendererApi]?: (...args: Parameters<RendererApi[K]>) => ReturnType<RendererApi[K]> | false;
};
export type TokenizerApi = Omit<_Tokenizer, "constructor" | "options" | "rules" | "lexer">;
export type TokenizerObject = {
[K in keyof TokenizerApi]?: (...args: Parameters<TokenizerApi[K]>) => ReturnType<TokenizerApi[K]> | false;
};
export interface MarkedExtension {
/**
* True will tell marked to await any walkTokens functions before parsing the tokens and returning an HTML string.
*/
async?: boolean;
/**
* Enable GFM line breaks. This option requires the gfm option to be true.
*/
breaks?: boolean | undefined;
/**
* Add tokenizers and renderers to marked
*/
extensions?: TokenizerAndRendererExtension[] | undefined | null;
/**
* Enable GitHub flavored markdown.
*/
gfm?: boolean | undefined;
/**
* Hooks are methods that hook into some part of marked.
* preprocess is called to process markdown before sending it to marked.
* processAllTokens is called with the TokensList before walkTokens.
* postprocess is called to process html after marked has finished parsing.
*/
hooks?: HooksObject | undefined | null;
/**
* Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior.
*/
pedantic?: boolean | undefined;
/**
* Type: object Default: new Renderer()
*
* An object containing functions to render tokens to HTML.
*/
renderer?: RendererObject | undefined | null;
/**
* Shows an HTML error message when rendering fails.
*/
silent?: boolean | undefined;
/**
* The tokenizer defines how to turn markdown text into tokens.
*/
tokenizer?: TokenizerObject | undefined | null;
/**
* The walkTokens function gets called with every token.
* Child tokens are called before moving on to sibling tokens.
* Each token is passed by reference so updates are persisted when passed to the parser.
* The return value of the function is ignored.
*/
walkTokens?: ((token: Token) => void | Promise<void>) | undefined | null;
}
export interface MarkedOptions extends Omit<MarkedExtension, "hooks" | "renderer" | "tokenizer" | "extensions" | "walkTokens"> {
/**
* Hooks are methods that hook into some part of marked.
*/
hooks?: _Hooks | undefined | null;
/**
* Type: object Default: new Renderer()
*
* An object containing functions to render tokens to HTML.
*/
renderer?: _Renderer | undefined | null;
/**
* The tokenizer defines how to turn markdown text into tokens.
*/
tokenizer?: _Tokenizer | undefined | null;
/**
* Custom extensions
*/
extensions?: null | {
renderers: {
[name: string]: RendererExtensionFunction;
};
childTokens: {
[name: string]: string[];
};
inline?: TokenizerExtensionFunction[];
block?: TokenizerExtensionFunction[];
startInline?: TokenizerStartFunction[];
startBlock?: TokenizerStartFunction[];
};
/**
* walkTokens function returns array of values for Promise.all
*/
walkTokens?: null | ((token: Token) => void | Promise<void> | (void | Promise<void>)[]);
}
declare class _Lexer {
tokens: TokensList;
options: MarkedOptions;
state: {
inLink: boolean;
inRawBlock: boolean;
top: boolean;
};
private tokenizer;
private inlineQueue;
constructor(options?: MarkedOptions);
/**
* Expose Rules
*/
static get rules(): {
block: {
normal: {
blockquote: RegExp;
code: RegExp;
def: RegExp;
fences: RegExp;
heading: RegExp;
hr: RegExp;
html: RegExp;
lheading: RegExp;
list: RegExp;
newline: RegExp;
paragraph: RegExp;
table: RegExp;
text: RegExp;
};
gfm: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "heading" | "list" | "paragraph" | "def" | "fences" | "lheading" | "newline", RegExp>;
pedantic: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "heading" | "list" | "paragraph" | "def" | "fences" | "lheading" | "newline", RegExp>;
};
inline: {
normal: {
_backpedal: RegExp;
anyPunctuation: RegExp;
autolink: RegExp;
blockSkip: RegExp;
br: RegExp;
code: RegExp;
del: RegExp;
emStrongLDelim: RegExp;
emStrongRDelimAst: RegExp;
emStrongRDelimUnd: RegExp;
escape: RegExp;
link: RegExp;
nolink: RegExp;
punctuation: RegExp;
reflink: RegExp;
reflinkSearch: RegExp;
tag: RegExp;
text: RegExp;
url: RegExp;
};
gfm: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
breaks: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
pedantic: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
};
};
/**
* Static Lex Method
*/
static lex(src: string, options?: MarkedOptions): TokensList;
/**
* Static Lex Inline Method
*/
static lexInline(src: string, options?: MarkedOptions): Token[];
/**
* Preprocessing
*/
lex(src: string): TokensList;
/**
* Lexing
*/
blockTokens(src: string, tokens?: Token[]): Token[];
blockTokens(src: string, tokens?: TokensList): TokensList;
inline(src: string, tokens?: Token[]): Token[];
/**
* Lexing/Compiling
*/
inlineTokens(src: string, tokens?: Token[]): Token[];
}
declare function _getDefaults(): MarkedOptions;
declare let _defaults: MarkedOptions;
export type MaybePromise = void | Promise<void>;
export declare class Marked {
#private;
defaults: MarkedOptions;
options: (opt: MarkedOptions) => this;
parse: (src: string, options?: MarkedOptions | undefined | null) => string | Promise<string>;
parseInline: (src: string, options?: MarkedOptions | undefined | null) => string | Promise<string>;
Parser: typeof _Parser;
Renderer: typeof _Renderer;
TextRenderer: typeof _TextRenderer;
Lexer: typeof _Lexer;
Tokenizer: typeof _Tokenizer;
Hooks: typeof _Hooks;
constructor(...args: MarkedExtension[]);
/**
* Run callback for every token
*/
walkTokens(tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]): MaybePromise[];
use(...args: MarkedExtension[]): this;
setOptions(opt: MarkedOptions): this;
lexer(src: string, options?: MarkedOptions): TokensList;
parser(tokens: Token[], options?: MarkedOptions): string;
}
/**
* Compiles markdown to HTML asynchronously.
*
* @param src String of markdown source to be compiled
* @param options Hash of options, having async: true
* @return Promise of string of compiled HTML
*/
export declare function marked(src: string, options: MarkedOptions & {
async: true;
}): Promise<string>;
/**
* Compiles markdown to HTML.
*
* @param src String of markdown source to be compiled
* @param options Optional hash of options
* @return String of compiled HTML. Wil be a Promise of string if async is set to true by any extensions.
*/
export declare function marked(src: string, options?: MarkedOptions): string | Promise<string>;
export declare namespace marked {
var options: (options: MarkedOptions) => typeof marked;
var setOptions: (options: MarkedOptions) => typeof marked;
var getDefaults: typeof _getDefaults;
var defaults: MarkedOptions;
var use: (...args: MarkedExtension[]) => typeof marked;
var walkTokens: (tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]) => MaybePromise[];
var parseInline: (src: string, options?: MarkedOptions | null | undefined) => string | Promise<string>;
var Parser: typeof _Parser;
var parser: typeof _Parser.parse;
var Renderer: typeof _Renderer;
var TextRenderer: typeof _TextRenderer;
var Lexer: typeof _Lexer;
var lexer: typeof _Lexer.lex;
var Tokenizer: typeof _Tokenizer;
var Hooks: typeof _Hooks;
var parse: typeof marked;
}
export declare const options: (options: MarkedOptions) => typeof marked;
export declare const setOptions: (options: MarkedOptions) => typeof marked;
export declare const use: (...args: MarkedExtension[]) => typeof marked;
export declare const walkTokens: (tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]) => MaybePromise[];
export declare const parseInline: (src: string, options?: MarkedOptions | null | undefined) => string | Promise<string>;
export declare const parse: typeof marked;
export declare const parser: typeof _Parser.parse;
export declare const lexer: typeof _Lexer.lex;
export {
_Hooks as Hooks,
_Lexer as Lexer,
_Parser as Parser,
_Renderer as Renderer,
_TextRenderer as TextRenderer,
_Tokenizer as Tokenizer,
_defaults as defaults,
_getDefaults as getDefaults,
};
export {};

View file

@ -1,19 +1,66 @@
// Generated by dts-bundle-generator v9.0.0
// Generated by dts-bundle-generator v9.5.1
export type Token = (Tokens.Space | Tokens.Code | Tokens.Heading | Tokens.Table | Tokens.Hr | Tokens.Blockquote | Tokens.List | Tokens.ListItem | Tokens.Paragraph | Tokens.HTML | Tokens.Text | Tokens.Def | Tokens.Escape | Tokens.Tag | Tokens.Image | Tokens.Link | Tokens.Strong | Tokens.Em | Tokens.Codespan | Tokens.Br | Tokens.Del | Tokens.Generic);
export type MarkedToken = (Tokens.Blockquote | Tokens.Br | Tokens.Checkbox | Tokens.Code | Tokens.Codespan | Tokens.Def | Tokens.Del | Tokens.Em | Tokens.Escape | Tokens.Heading | Tokens.Hr | Tokens.HTML | Tokens.Image | Tokens.Link | Tokens.List | Tokens.ListItem | Tokens.Paragraph | Tokens.Space | Tokens.Strong | Tokens.Table | Tokens.Tag | Tokens.Text);
export type Token = (MarkedToken | Tokens.Generic);
export declare namespace Tokens {
interface Space {
type: "space";
interface Blockquote {
type: "blockquote";
raw: string;
text: string;
tokens: Token[];
}
interface Br {
type: "br";
raw: string;
}
interface Checkbox {
type: "checkbox";
raw: string;
checked: boolean;
}
interface Code {
type: "code";
raw: string;
codeBlockStyle?: "indented" | undefined;
lang?: string | undefined;
codeBlockStyle?: "indented";
lang?: string;
text: string;
escaped?: boolean;
}
interface Codespan {
type: "codespan";
raw: string;
text: string;
}
interface Def {
type: "def";
raw: string;
tag: string;
href: string;
title: string;
}
interface Del {
type: "del";
raw: string;
text: string;
tokens: Token[];
}
interface Em {
type: "em";
raw: string;
text: string;
tokens: Token[];
}
interface Escape {
type: "escape";
raw: string;
text: string;
}
interface Generic {
[index: string]: any;
type: string;
raw: string;
tokens?: Token[];
}
interface Heading {
type: "heading";
raw: string;
@ -21,24 +68,30 @@ export declare namespace Tokens {
text: string;
tokens: Token[];
}
interface Table {
type: "table";
raw: string;
align: Array<"center" | "left" | "right" | null>;
header: TableCell[];
rows: TableCell[][];
}
interface TableCell {
text: string;
tokens: Token[];
}
interface Hr {
type: "hr";
raw: string;
}
interface Blockquote {
type: "blockquote";
interface HTML {
type: "html";
raw: string;
pre: boolean;
text: string;
block: boolean;
}
interface Image {
type: "image";
raw: string;
href: string;
title: string | null;
text: string;
tokens: Token[];
}
interface Link {
type: "link";
raw: string;
href: string;
title?: string | null;
text: string;
tokens: Token[];
}
@ -54,7 +107,7 @@ export declare namespace Tokens {
type: "list_item";
raw: string;
task: boolean;
checked?: boolean | undefined;
checked?: boolean;
loose: boolean;
text: string;
tokens: Token[];
@ -62,14 +115,41 @@ export declare namespace Tokens {
interface Paragraph {
type: "paragraph";
raw: string;
pre?: boolean | undefined;
pre?: boolean;
text: string;
tokens: Token[];
}
interface HTML {
interface Space {
type: "space";
raw: string;
}
interface Strong {
type: "strong";
raw: string;
text: string;
tokens: Token[];
}
interface Table {
type: "table";
raw: string;
align: Array<"center" | "left" | "right" | null>;
header: TableCell[];
rows: TableCell[][];
}
interface TableCell {
text: string;
tokens: Token[];
header: boolean;
align: "center" | "left" | "right" | null;
}
interface TableRow<P = string> {
text: P;
}
interface Tag {
type: "html";
raw: string;
pre: boolean;
inLink: boolean;
inRawBlock: boolean;
text: string;
block: boolean;
}
@ -78,143 +158,145 @@ export declare namespace Tokens {
raw: string;
text: string;
tokens?: Token[];
}
interface Def {
type: "def";
raw: string;
tag: string;
href: string;
title: string;
}
interface Escape {
type: "escape";
raw: string;
text: string;
}
interface Tag {
type: "text" | "html";
raw: string;
inLink: boolean;
inRawBlock: boolean;
text: string;
block: boolean;
}
interface Link {
type: "link";
raw: string;
href: string;
title?: string | null;
text: string;
tokens: Token[];
}
interface Image {
type: "image";
raw: string;
href: string;
title: string | null;
text: string;
}
interface Strong {
type: "strong";
raw: string;
text: string;
tokens: Token[];
}
interface Em {
type: "em";
raw: string;
text: string;
tokens: Token[];
}
interface Codespan {
type: "codespan";
raw: string;
text: string;
}
interface Br {
type: "br";
raw: string;
}
interface Del {
type: "del";
raw: string;
text: string;
tokens: Token[];
}
interface Generic {
[index: string]: any;
type: string;
raw: string;
tokens?: Token[] | undefined;
escaped?: boolean;
}
}
export type Links = Record<string, Pick<Tokens.Link | Tokens.Image, "href" | "title">>;
export type TokensList = Token[] & {
links: Links;
};
declare class _Renderer {
options: MarkedOptions;
constructor(options?: MarkedOptions);
code(code: string, infostring: string | undefined, escaped: boolean): string;
blockquote(quote: string): string;
html(html: string, block?: boolean): string;
heading(text: string, level: number, raw: string): string;
hr(): string;
list(body: string, ordered: boolean, start: number | ""): string;
listitem(text: string, task: boolean, checked: boolean): string;
checkbox(checked: boolean): string;
paragraph(text: string): string;
table(header: string, body: string): string;
tablerow(content: string): string;
tablecell(content: string, flags: {
header: boolean;
align: "center" | "left" | "right" | null;
}): string;
/**
* Renderer
*/
declare class _Renderer<ParserOutput = string, RendererOutput = string> {
options: MarkedOptions<ParserOutput, RendererOutput>;
parser: _Parser<ParserOutput, RendererOutput>;
constructor(options?: MarkedOptions<ParserOutput, RendererOutput>);
space(token: Tokens.Space): RendererOutput;
code({ text, lang, escaped }: Tokens.Code): RendererOutput;
blockquote({ tokens }: Tokens.Blockquote): RendererOutput;
html({ text }: Tokens.HTML | Tokens.Tag): RendererOutput;
def(token: Tokens.Def): RendererOutput;
heading({ tokens, depth }: Tokens.Heading): RendererOutput;
hr(token: Tokens.Hr): RendererOutput;
list(token: Tokens.List): RendererOutput;
listitem(item: Tokens.ListItem): RendererOutput;
checkbox({ checked }: Tokens.Checkbox): RendererOutput;
paragraph({ tokens }: Tokens.Paragraph): RendererOutput;
table(token: Tokens.Table): RendererOutput;
tablerow({ text }: Tokens.TableRow<ParserOutput>): RendererOutput;
tablecell(token: Tokens.TableCell): RendererOutput;
/**
* span level renderer
*/
strong(text: string): string;
em(text: string): string;
codespan(text: string): string;
br(): string;
del(text: string): string;
link(href: string, title: string | null | undefined, text: string): string;
image(href: string, title: string | null, text: string): string;
text(text: string): string;
strong({ tokens }: Tokens.Strong): RendererOutput;
em({ tokens }: Tokens.Em): RendererOutput;
codespan({ text }: Tokens.Codespan): RendererOutput;
br(token: Tokens.Br): RendererOutput;
del({ tokens }: Tokens.Del): RendererOutput;
link({ href, title, tokens }: Tokens.Link): RendererOutput;
image({ href, title, text, tokens }: Tokens.Image): RendererOutput;
text(token: Tokens.Text | Tokens.Escape): RendererOutput;
}
declare class _TextRenderer {
strong(text: string): string;
em(text: string): string;
codespan(text: string): string;
del(text: string): string;
html(text: string): string;
text(text: string): string;
link(href: string, title: string | null | undefined, text: string): string;
image(href: string, title: string | null, text: string): string;
br(): string;
/**
* TextRenderer
* returns only the textual part of the token
*/
declare class _TextRenderer<RendererOutput = string> {
strong({ text }: Tokens.Strong): RendererOutput;
em({ text }: Tokens.Em): RendererOutput;
codespan({ text }: Tokens.Codespan): RendererOutput;
del({ text }: Tokens.Del): RendererOutput;
html({ text }: Tokens.HTML | Tokens.Tag): RendererOutput;
text({ text }: Tokens.Text | Tokens.Escape | Tokens.Tag): RendererOutput;
link({ text }: Tokens.Link): RendererOutput;
image({ text }: Tokens.Image): RendererOutput;
br(): RendererOutput;
checkbox({ raw }: Tokens.Checkbox): RendererOutput;
}
declare class _Parser {
options: MarkedOptions;
renderer: _Renderer;
textRenderer: _TextRenderer;
constructor(options?: MarkedOptions);
/**
* Parsing & Compiling
*/
declare class _Parser<ParserOutput = string, RendererOutput = string> {
options: MarkedOptions<ParserOutput, RendererOutput>;
renderer: _Renderer<ParserOutput, RendererOutput>;
textRenderer: _TextRenderer<RendererOutput>;
constructor(options?: MarkedOptions<ParserOutput, RendererOutput>);
/**
* Static Parse Method
*/
static parse(tokens: Token[], options?: MarkedOptions): string;
static parse<ParserOutput = string, RendererOutput = string>(tokens: Token[], options?: MarkedOptions<ParserOutput, RendererOutput>): ParserOutput;
/**
* Static Parse Inline Method
*/
static parseInline(tokens: Token[], options?: MarkedOptions): string;
static parseInline<ParserOutput = string, RendererOutput = string>(tokens: Token[], options?: MarkedOptions<ParserOutput, RendererOutput>): ParserOutput;
/**
* Parse Loop
*/
parse(tokens: Token[], top?: boolean): string;
parse(tokens: Token[]): ParserOutput;
/**
* Parse Inline Tokens
*/
parseInline(tokens: Token[], renderer?: _Renderer | _TextRenderer): string;
parseInline(tokens: Token[], renderer?: _Renderer<ParserOutput, RendererOutput> | _TextRenderer<RendererOutput>): ParserOutput;
}
declare const other: {
codeRemoveIndent: RegExp;
outputLinkReplace: RegExp;
indentCodeCompensation: RegExp;
beginningSpace: RegExp;
endingHash: RegExp;
startingSpaceChar: RegExp;
endingSpaceChar: RegExp;
nonSpaceChar: RegExp;
newLineCharGlobal: RegExp;
tabCharGlobal: RegExp;
multipleSpaceGlobal: RegExp;
blankLine: RegExp;
doubleBlankLine: RegExp;
blockquoteStart: RegExp;
blockquoteSetextReplace: RegExp;
blockquoteSetextReplace2: RegExp;
listReplaceNesting: RegExp;
listIsTask: RegExp;
listReplaceTask: RegExp;
listTaskCheckbox: RegExp;
anyLine: RegExp;
hrefBrackets: RegExp;
tableDelimiter: RegExp;
tableAlignChars: RegExp;
tableRowBlankLine: RegExp;
tableAlignRight: RegExp;
tableAlignCenter: RegExp;
tableAlignLeft: RegExp;
startATag: RegExp;
endATag: RegExp;
startPreScriptTag: RegExp;
endPreScriptTag: RegExp;
startAngleBracket: RegExp;
endAngleBracket: RegExp;
pedanticHrefTitle: RegExp;
unicodeAlphaNumeric: RegExp;
escapeTest: RegExp;
escapeReplace: RegExp;
escapeTestNoEncode: RegExp;
escapeReplaceNoEncode: RegExp;
caret: RegExp;
percentDecode: RegExp;
findPipe: RegExp;
splitPipe: RegExp;
slashPipe: RegExp;
carriageReturn: RegExp;
spaceLine: RegExp;
notSpaceStart: RegExp;
endingNewline: RegExp;
listItemRegex: (bull: string) => RegExp;
nextBulletRegex: (indent: number) => RegExp;
hrRegex: (indent: number) => RegExp;
fencesBeginRegex: (indent: number) => RegExp;
headingBeginRegex: (indent: number) => RegExp;
htmlBeginRegex: (indent: number) => RegExp;
blockquoteBeginRegex: (indent: number) => RegExp;
};
declare const blockNormal: {
blockquote: RegExp;
code: RegExp;
@ -239,6 +321,8 @@ declare const inlineNormal: {
br: RegExp;
code: RegExp;
del: RegExp;
delLDelim: RegExp;
delRDelim: RegExp;
emStrongLDelim: RegExp;
emStrongRDelimAst: RegExp;
emStrongRDelimUnd: RegExp;
@ -253,63 +337,19 @@ declare const inlineNormal: {
url: RegExp;
};
export type InlineKeys = keyof typeof inlineNormal;
/**
* exports
*/
export declare const block: {
normal: {
blockquote: RegExp;
code: RegExp;
def: RegExp;
fences: RegExp;
heading: RegExp;
hr: RegExp;
html: RegExp;
lheading: RegExp;
list: RegExp;
newline: RegExp;
paragraph: RegExp;
table: RegExp;
text: RegExp;
};
gfm: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "heading" | "list" | "paragraph" | "def" | "fences" | "lheading" | "newline", RegExp>;
pedantic: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "heading" | "list" | "paragraph" | "def" | "fences" | "lheading" | "newline", RegExp>;
};
export declare const inline: {
normal: {
_backpedal: RegExp;
anyPunctuation: RegExp;
autolink: RegExp;
blockSkip: RegExp;
br: RegExp;
code: RegExp;
del: RegExp;
emStrongLDelim: RegExp;
emStrongRDelimAst: RegExp;
emStrongRDelimUnd: RegExp;
escape: RegExp;
link: RegExp;
nolink: RegExp;
punctuation: RegExp;
reflink: RegExp;
reflinkSearch: RegExp;
tag: RegExp;
text: RegExp;
url: RegExp;
};
gfm: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
breaks: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
pedantic: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
};
export interface Rules {
other: typeof other;
block: Record<BlockKeys, RegExp>;
inline: Record<InlineKeys, RegExp>;
}
declare class _Tokenizer {
options: MarkedOptions;
/**
* Tokenizer
*/
declare class _Tokenizer<ParserOutput = string, RendererOutput = string> {
options: MarkedOptions<ParserOutput, RendererOutput>;
rules: Rules;
lexer: _Lexer;
constructor(options?: MarkedOptions);
lexer: _Lexer<ParserOutput, RendererOutput>;
constructor(options?: MarkedOptions<ParserOutput, RendererOutput>);
space(src: string): Tokens.Space | undefined;
code(src: string): Tokens.Code | undefined;
fences(src: string): Tokens.Code | undefined;
@ -330,15 +370,17 @@ declare class _Tokenizer {
emStrong(src: string, maskedSrc: string, prevChar?: string): Tokens.Em | Tokens.Strong | undefined;
codespan(src: string): Tokens.Codespan | undefined;
br(src: string): Tokens.Br | undefined;
del(src: string): Tokens.Del | undefined;
del(src: string, maskedSrc: string, prevChar?: string): Tokens.Del | undefined;
autolink(src: string): Tokens.Link | undefined;
url(src: string): Tokens.Link | undefined;
inlineText(src: string): Tokens.Text | undefined;
}
declare class _Hooks {
options: MarkedOptions;
constructor(options?: MarkedOptions);
declare class _Hooks<ParserOutput = string, RendererOutput = string> {
options: MarkedOptions<ParserOutput, RendererOutput>;
block?: boolean;
constructor(options?: MarkedOptions<ParserOutput, RendererOutput>);
static passThroughHooks: Set<string>;
static passThroughHooksRespectAsync: Set<string>;
/**
* Process markdown before marked
*/
@ -346,11 +388,23 @@ declare class _Hooks {
/**
* Process HTML after marked is finished
*/
postprocess(html: string): string;
postprocess(html: ParserOutput): ParserOutput;
/**
* Process all tokens before walk tokens
*/
processAllTokens(tokens: Token[] | TokensList): Token[] | TokensList;
/**
* Mask contents that should not be interpreted as em/strong delimiters
*/
emStrongMask(src: string): string;
/**
* Provide function to tokenize markdown
*/
provideLexer(block?: boolean | undefined): typeof _Lexer.lexInline;
/**
* Provide function to parse tokens
*/
provideParser(block?: boolean | undefined): (tokens: Token[], options?: MarkedOptions<ParserOutput, RendererOutput> | undefined) => ParserOutput;
}
export interface TokenizerThis {
lexer: _Lexer;
@ -360,32 +414,32 @@ export type TokenizerStartFunction = (this: TokenizerThis, src: string) => numbe
export interface TokenizerExtension {
name: string;
level: "block" | "inline";
start?: TokenizerStartFunction | undefined;
start?: TokenizerStartFunction;
tokenizer: TokenizerExtensionFunction;
childTokens?: string[] | undefined;
childTokens?: string[];
}
export interface RendererThis {
parser: _Parser;
export interface RendererThis<ParserOutput = string, RendererOutput = string> {
parser: _Parser<ParserOutput, RendererOutput>;
}
export type RendererExtensionFunction = (this: RendererThis, token: Tokens.Generic) => string | false | undefined;
export interface RendererExtension {
export type RendererExtensionFunction<ParserOutput = string, RendererOutput = string> = (this: RendererThis<ParserOutput, RendererOutput>, token: Tokens.Generic) => RendererOutput | false | undefined;
export interface RendererExtension<ParserOutput = string, RendererOutput = string> {
name: string;
renderer: RendererExtensionFunction;
renderer: RendererExtensionFunction<ParserOutput, RendererOutput>;
}
export type TokenizerAndRendererExtension = TokenizerExtension | RendererExtension | (TokenizerExtension & RendererExtension);
export type HooksApi = Omit<_Hooks, "constructor" | "options">;
export type HooksObject = {
[K in keyof HooksApi]?: (...args: Parameters<HooksApi[K]>) => ReturnType<HooksApi[K]> | Promise<ReturnType<HooksApi[K]>>;
export type TokenizerAndRendererExtension<ParserOutput = string, RendererOutput = string> = TokenizerExtension | RendererExtension<ParserOutput, RendererOutput> | (TokenizerExtension & RendererExtension<ParserOutput, RendererOutput>);
export type HooksApi<ParserOutput = string, RendererOutput = string> = Omit<_Hooks<ParserOutput, RendererOutput>, "constructor" | "options" | "block">;
export type HooksObject<ParserOutput = string, RendererOutput = string> = {
[K in keyof HooksApi<ParserOutput, RendererOutput>]?: (this: _Hooks<ParserOutput, RendererOutput>, ...args: Parameters<HooksApi<ParserOutput, RendererOutput>[K]>) => ReturnType<HooksApi<ParserOutput, RendererOutput>[K]> | Promise<ReturnType<HooksApi<ParserOutput, RendererOutput>[K]>>;
};
export type RendererApi = Omit<_Renderer, "constructor" | "options">;
export type RendererObject = {
[K in keyof RendererApi]?: (...args: Parameters<RendererApi[K]>) => ReturnType<RendererApi[K]> | false;
export type RendererApi<ParserOutput = string, RendererOutput = string> = Omit<_Renderer<ParserOutput, RendererOutput>, "constructor" | "options" | "parser">;
export type RendererObject<ParserOutput = string, RendererOutput = string> = {
[K in keyof RendererApi<ParserOutput, RendererOutput>]?: (this: _Renderer<ParserOutput, RendererOutput>, ...args: Parameters<RendererApi<ParserOutput, RendererOutput>[K]>) => ReturnType<RendererApi<ParserOutput, RendererOutput>[K]> | false;
};
export type TokenizerApi = Omit<_Tokenizer, "constructor" | "options" | "rules" | "lexer">;
export type TokenizerObject = {
[K in keyof TokenizerApi]?: (...args: Parameters<TokenizerApi[K]>) => ReturnType<TokenizerApi[K]> | false;
export type TokenizerApi<ParserOutput = string, RendererOutput = string> = Omit<_Tokenizer<ParserOutput, RendererOutput>, "constructor" | "options" | "rules" | "lexer">;
export type TokenizerObject<ParserOutput = string, RendererOutput = string> = {
[K in keyof TokenizerApi<ParserOutput, RendererOutput>]?: (this: _Tokenizer<ParserOutput, RendererOutput>, ...args: Parameters<TokenizerApi<ParserOutput, RendererOutput>[K]>) => ReturnType<TokenizerApi<ParserOutput, RendererOutput>[K]> | false;
};
export interface MarkedExtension {
export interface MarkedExtension<ParserOutput = string, RendererOutput = string> {
/**
* True will tell marked to await any walkTokens functions before parsing the tokens and returning an HTML string.
*/
@ -393,69 +447,72 @@ export interface MarkedExtension {
/**
* Enable GFM line breaks. This option requires the gfm option to be true.
*/
breaks?: boolean | undefined;
breaks?: boolean;
/**
* Add tokenizers and renderers to marked
*/
extensions?: TokenizerAndRendererExtension[] | undefined | null;
extensions?: TokenizerAndRendererExtension<ParserOutput, RendererOutput>[] | null;
/**
* Enable GitHub flavored markdown.
*/
gfm?: boolean | undefined;
gfm?: boolean;
/**
* Hooks are methods that hook into some part of marked.
* preprocess is called to process markdown before sending it to marked.
* processAllTokens is called with the TokensList before walkTokens.
* postprocess is called to process html after marked has finished parsing.
* emStrongMask is called to mask contents that should not be interpreted as em/strong delimiters.
* provideLexer is called to provide a function to tokenize markdown.
* provideParser is called to provide a function to parse tokens.
*/
hooks?: HooksObject | undefined | null;
hooks?: HooksObject<ParserOutput, RendererOutput> | null;
/**
* Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior.
*/
pedantic?: boolean | undefined;
pedantic?: boolean;
/**
* Type: object Default: new Renderer()
*
* An object containing functions to render tokens to HTML.
*/
renderer?: RendererObject | undefined | null;
renderer?: RendererObject<ParserOutput, RendererOutput> | null;
/**
* Shows an HTML error message when rendering fails.
*/
silent?: boolean | undefined;
silent?: boolean;
/**
* The tokenizer defines how to turn markdown text into tokens.
*/
tokenizer?: TokenizerObject | undefined | null;
tokenizer?: TokenizerObject | null;
/**
* The walkTokens function gets called with every token.
* Child tokens are called before moving on to sibling tokens.
* Each token is passed by reference so updates are persisted when passed to the parser.
* The return value of the function is ignored.
*/
walkTokens?: ((token: Token) => void | Promise<void>) | undefined | null;
walkTokens?: ((token: Token) => void | Promise<void>) | null;
}
export interface MarkedOptions extends Omit<MarkedExtension, "hooks" | "renderer" | "tokenizer" | "extensions" | "walkTokens"> {
export interface MarkedOptions<ParserOutput = string, RendererOutput = string> extends Omit<MarkedExtension<ParserOutput, RendererOutput>, "hooks" | "renderer" | "tokenizer" | "extensions" | "walkTokens"> {
/**
* Hooks are methods that hook into some part of marked.
*/
hooks?: _Hooks | undefined | null;
hooks?: _Hooks<ParserOutput, RendererOutput> | null;
/**
* Type: object Default: new Renderer()
*
* An object containing functions to render tokens to HTML.
*/
renderer?: _Renderer | undefined | null;
renderer?: _Renderer<ParserOutput, RendererOutput> | null;
/**
* The tokenizer defines how to turn markdown text into tokens.
*/
tokenizer?: _Tokenizer | undefined | null;
tokenizer?: _Tokenizer<ParserOutput, RendererOutput> | null;
/**
* Custom extensions
*/
extensions?: null | {
renderers: {
[name: string]: RendererExtensionFunction;
[name: string]: RendererExtensionFunction<ParserOutput, RendererOutput>;
};
childTokens: {
[name: string]: string[];
@ -470,17 +527,23 @@ export interface MarkedOptions extends Omit<MarkedExtension, "hooks" | "renderer
*/
walkTokens?: null | ((token: Token) => void | Promise<void> | (void | Promise<void>)[]);
}
declare class _Lexer {
/**
* Block Lexer
*/
declare class _Lexer<ParserOutput = string, RendererOutput = string> {
tokens: TokensList;
options: MarkedOptions;
options: MarkedOptions<ParserOutput, RendererOutput>;
state: {
inLink: boolean;
inRawBlock: boolean;
top: boolean;
};
inlineQueue: {
src: string;
tokens: Token[];
}[];
private tokenizer;
private inlineQueue;
constructor(options?: MarkedOptions);
constructor(options?: MarkedOptions<ParserOutput, RendererOutput>);
/**
* Expose Rules
*/
@ -501,8 +564,8 @@ declare class _Lexer {
table: RegExp;
text: RegExp;
};
gfm: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "heading" | "list" | "paragraph" | "def" | "fences" | "lheading" | "newline", RegExp>;
pedantic: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "heading" | "list" | "paragraph" | "def" | "fences" | "lheading" | "newline", RegExp>;
gfm: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "def" | "heading" | "list" | "paragraph" | "fences" | "lheading" | "newline", RegExp>;
pedantic: Record<"code" | "blockquote" | "hr" | "html" | "table" | "text" | "def" | "heading" | "list" | "paragraph" | "fences" | "lheading" | "newline", RegExp>;
};
inline: {
normal: {
@ -513,6 +576,8 @@ declare class _Lexer {
br: RegExp;
code: RegExp;
del: RegExp;
delLDelim: RegExp;
delRDelim: RegExp;
emStrongLDelim: RegExp;
emStrongRDelimAst: RegExp;
emStrongRDelimUnd: RegExp;
@ -526,19 +591,19 @@ declare class _Lexer {
text: RegExp;
url: RegExp;
};
gfm: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
breaks: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
pedantic: Record<"link" | "code" | "url" | "br" | "del" | "text" | "escape" | "tag" | "reflink" | "autolink" | "nolink" | "_backpedal" | "anyPunctuation" | "blockSkip" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
gfm: Record<"link" | "code" | "url" | "br" | "del" | "text" | "tag" | "escape" | "reflink" | "nolink" | "_backpedal" | "anyPunctuation" | "autolink" | "blockSkip" | "delLDelim" | "delRDelim" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
breaks: Record<"link" | "code" | "url" | "br" | "del" | "text" | "tag" | "escape" | "reflink" | "nolink" | "_backpedal" | "anyPunctuation" | "autolink" | "blockSkip" | "delLDelim" | "delRDelim" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
pedantic: Record<"link" | "code" | "url" | "br" | "del" | "text" | "tag" | "escape" | "reflink" | "nolink" | "_backpedal" | "anyPunctuation" | "autolink" | "blockSkip" | "delLDelim" | "delRDelim" | "emStrongLDelim" | "emStrongRDelimAst" | "emStrongRDelimUnd" | "punctuation" | "reflinkSearch", RegExp>;
};
};
/**
* Static Lex Method
*/
static lex(src: string, options?: MarkedOptions): TokensList;
static lex<ParserOutput = string, RendererOutput = string>(src: string, options?: MarkedOptions<ParserOutput, RendererOutput>): TokensList;
/**
* Static Lex Inline Method
*/
static lexInline(src: string, options?: MarkedOptions): Token[];
static lexInline<ParserOutput = string, RendererOutput = string>(src: string, options?: MarkedOptions<ParserOutput, RendererOutput>): Token[];
/**
* Preprocessing
*/
@ -546,38 +611,73 @@ declare class _Lexer {
/**
* Lexing
*/
blockTokens(src: string, tokens?: Token[]): Token[];
blockTokens(src: string, tokens?: TokensList): TokensList;
blockTokens(src: string, tokens?: Token[], lastParagraphClipped?: boolean): Token[];
blockTokens(src: string, tokens?: TokensList, lastParagraphClipped?: boolean): TokensList;
inline(src: string, tokens?: Token[]): Token[];
/**
* Lexing/Compiling
*/
inlineTokens(src: string, tokens?: Token[]): Token[];
private infiniteLoopError;
}
declare function _getDefaults(): MarkedOptions;
declare let _defaults: MarkedOptions;
/**
* Gets the original marked default options.
*/
declare function _getDefaults<ParserOutput = string, RendererOutput = string>(): MarkedOptions<ParserOutput, RendererOutput>;
declare let _defaults: MarkedOptions<any, any>;
export type MaybePromise = void | Promise<void>;
export declare class Marked {
#private;
defaults: MarkedOptions;
options: (opt: MarkedOptions) => this;
parse: (src: string, options?: MarkedOptions | undefined | null) => string | Promise<string>;
parseInline: (src: string, options?: MarkedOptions | undefined | null) => string | Promise<string>;
Parser: typeof _Parser;
Renderer: typeof _Renderer;
TextRenderer: typeof _TextRenderer;
export declare class Marked<ParserOutput = string, RendererOutput = string> {
defaults: MarkedOptions<ParserOutput, RendererOutput>;
options: (opt: MarkedOptions<ParserOutput, RendererOutput>) => this;
parse: {
(src: string, options: MarkedOptions<ParserOutput, RendererOutput> & {
async: true;
}): Promise<ParserOutput>;
(src: string, options: MarkedOptions<ParserOutput, RendererOutput> & {
async: false;
}): ParserOutput;
(src: string, options?: MarkedOptions<ParserOutput, RendererOutput> | null): ParserOutput | Promise<ParserOutput>;
};
parseInline: {
(src: string, options: MarkedOptions<ParserOutput, RendererOutput> & {
async: true;
}): Promise<ParserOutput>;
(src: string, options: MarkedOptions<ParserOutput, RendererOutput> & {
async: false;
}): ParserOutput;
(src: string, options?: MarkedOptions<ParserOutput, RendererOutput> | null): ParserOutput | Promise<ParserOutput>;
};
Parser: {
new (options?: MarkedOptions<ParserOutput, RendererOutput> | undefined): _Parser<ParserOutput, RendererOutput>;
parse<ParserOutput_1 = string, RendererOutput_1 = string>(tokens: Token[], options?: MarkedOptions<ParserOutput_1, RendererOutput_1>): ParserOutput_1;
parseInline<ParserOutput_1 = string, RendererOutput_1 = string>(tokens: Token[], options?: MarkedOptions<ParserOutput_1, RendererOutput_1>): ParserOutput_1;
};
Renderer: {
new (options?: MarkedOptions<ParserOutput, RendererOutput> | undefined): _Renderer<ParserOutput, RendererOutput>;
};
TextRenderer: {
new (): _TextRenderer<RendererOutput>;
};
Lexer: typeof _Lexer;
Tokenizer: typeof _Tokenizer;
Hooks: typeof _Hooks;
constructor(...args: MarkedExtension[]);
Tokenizer: {
new (options?: MarkedOptions<ParserOutput, RendererOutput> | undefined): _Tokenizer<ParserOutput, RendererOutput>;
};
Hooks: {
new (options?: MarkedOptions<ParserOutput, RendererOutput> | undefined): _Hooks<ParserOutput, RendererOutput>;
passThroughHooks: Set<string>;
passThroughHooksRespectAsync: Set<string>;
};
constructor(...args: MarkedExtension<ParserOutput, RendererOutput>[]);
/**
* Run callback for every token
*/
walkTokens(tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]): MaybePromise[];
use(...args: MarkedExtension[]): this;
setOptions(opt: MarkedOptions): this;
lexer(src: string, options?: MarkedOptions): TokensList;
parser(tokens: Token[], options?: MarkedOptions): string;
use(...args: MarkedExtension<ParserOutput, RendererOutput>[]): this;
setOptions(opt: MarkedOptions<ParserOutput, RendererOutput>): this;
lexer(src: string, options?: MarkedOptions<ParserOutput, RendererOutput>): TokensList;
parser(tokens: Token[], options?: MarkedOptions<ParserOutput, RendererOutput>): ParserOutput;
private parseMarkdown;
private onError;
}
/**
* Compiles markdown to HTML asynchronously.
@ -594,17 +694,31 @@ export declare function marked(src: string, options: MarkedOptions & {
*
* @param src String of markdown source to be compiled
* @param options Optional hash of options
* @return String of compiled HTML. Wil be a Promise of string if async is set to true by any extensions.
* @return String of compiled HTML. Will be a Promise of string if async is set to true by any extensions.
*/
export declare function marked(src: string, options?: MarkedOptions): string | Promise<string>;
export declare function marked(src: string, options: MarkedOptions & {
async: false;
}): string;
export declare function marked(src: string, options: MarkedOptions & {
async: true;
}): Promise<string>;
export declare function marked(src: string, options?: MarkedOptions | null): string | Promise<string>;
export declare namespace marked {
var options: (options: MarkedOptions) => typeof marked;
var setOptions: (options: MarkedOptions) => typeof marked;
var getDefaults: typeof _getDefaults;
var defaults: MarkedOptions;
var defaults: MarkedOptions<any, any>;
var use: (...args: MarkedExtension[]) => typeof marked;
var walkTokens: (tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]) => MaybePromise[];
var parseInline: (src: string, options?: MarkedOptions | null | undefined) => string | Promise<string>;
var parseInline: {
(src: string, options: MarkedOptions<string, string> & {
async: true;
}): Promise<string>;
(src: string, options: MarkedOptions<string, string> & {
async: false;
}): string;
(src: string, options?: MarkedOptions<string, string> | null | undefined): string | Promise<string>;
};
var Parser: typeof _Parser;
var parser: typeof _Parser.parse;
var Renderer: typeof _Renderer;
@ -619,7 +733,15 @@ export declare const options: (options: MarkedOptions) => typeof marked;
export declare const setOptions: (options: MarkedOptions) => typeof marked;
export declare const use: (...args: MarkedExtension[]) => typeof marked;
export declare const walkTokens: (tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]) => MaybePromise[];
export declare const parseInline: (src: string, options?: MarkedOptions | null | undefined) => string | Promise<string>;
export declare const parseInline: {
(src: string, options: MarkedOptions<string, string> & {
async: true;
}): Promise<string>;
(src: string, options: MarkedOptions<string, string> & {
async: false;
}): string;
(src: string, options?: MarkedOptions<string, string> | null | undefined): string | Promise<string>;
};
export declare const parse: typeof marked;
export declare const parser: typeof _Parser.parse;
export declare const lexer: typeof _Lexer.lex;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
.TH "MARKED" "1" "December 2023" "11.1.0"
.TH "MARKED" "1" "May 2026" "18.0.2"
.SH "NAME"
\fBmarked\fR \- a javascript markdown parser
.SH SYNOPSIS
@ -104,7 +104,9 @@ marked\.parse('*foo*');
Please report any bugs to https://github.com/markedjs/marked
.SH LICENSE
.P
Copyright (c) 2011\-2014, Christopher Jeffrey (MIT License)\.
Copyright (c) 2018+, MarkedJS\. (MIT License)
.br
Copyright (c) 2011\-2018, Christopher Jeffrey\. (MIT License)
.SH SEE ALSO
.P
markdown(1), nodejs(1)

View file

@ -85,7 +85,8 @@ Please report any bugs to <https://github.com/markedjs/marked>.
## LICENSE
Copyright (c) 2011-2014, Christopher Jeffrey (MIT License).
Copyright (c) 2018+, MarkedJS. (MIT License)
Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License)
## SEE ALSO

File diff suppressed because one or more lines are too long

View file

@ -2,9 +2,9 @@
"name": "marked",
"description": "A markdown parser built for speed",
"author": "Christopher Jeffrey",
"version": "11.1.1",
"version": "18.0.3",
"type": "module",
"main": "./lib/marked.cjs",
"main": "./lib/marked.esm.js",
"module": "./lib/marked.esm.js",
"browser": "./lib/marked.umd.js",
"types": "./lib/marked.d.ts",
@ -15,25 +15,23 @@
"files": [
"bin/",
"lib/",
"man/",
"marked.min.js"
"man/"
],
"exports": {
".": {
"import": {
"types": "./lib/marked.d.ts",
"default": "./lib/marked.esm.js"
},
"default": {
"types": "./lib/marked.d.cts",
"default": "./lib/marked.cjs"
}
},
"./bin/marked": "./bin/marked.js",
"./marked.min.js": "./marked.min.js",
"./package.json": "./package.json"
},
"repository": "git://github.com/markedjs/marked.git",
"publishConfig": {
"provenance": true
},
"repository": {
"type": "git",
"url": "git://github.com/markedjs/marked.git"
},
"homepage": "https://marked.js.org",
"bugs": {
"url": "http://github.com/markedjs/marked/issues"
@ -50,59 +48,56 @@
"html"
],
"devDependencies": {
"@markedjs/testutils": "9.1.5-0",
"@arethetypeswrong/cli": "^0.13.5",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.5",
"@semantic-release/commit-analyzer": "^11.1.0",
"@arethetypeswrong/cli": "^0.18.2",
"@markedjs/eslint-config": "^1.0.14",
"@markedjs/testutils": "18.0.0-1",
"@semantic-release/commit-analyzer": "^13.0.1",
"@semantic-release/git": "^10.0.1",
"@semantic-release/github": "^9.2.6",
"@semantic-release/npm": "^11.0.2",
"@semantic-release/release-notes-generator": "^12.1.0",
"@typescript-eslint/eslint-plugin": "^6.15.0",
"@typescript-eslint/parser": "^6.13.2",
"cheerio": "^1.0.0-rc.12",
"commonmark": "0.30.0",
"cross-env": "^7.0.3",
"dts-bundle-generator": "^9.0.0",
"eslint": "^8.56.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-n": "^16.5.0",
"eslint-plugin-promise": "^6.1.1",
"highlight.js": "^11.9.0",
"markdown-it": "13.0.2",
"marked-highlight": "^2.1.0",
"marked-man": "^2.0.0",
"node-fetch": "^3.3.2",
"recheck": "^4.4.5",
"rollup": "^4.9.1",
"semantic-release": "^22.0.12",
"@semantic-release/github": "^12.0.6",
"@semantic-release/npm": "^13.1.5",
"@semantic-release/release-notes-generator": "^14.1.0",
"cheerio": "1.2.0",
"commonmark": "0.31.2",
"cross-env": "^10.1.0",
"dts-bundle-generator": "^9.5.1",
"esbuild": "^0.28.0",
"esbuild-plugin-umd-wrapper": "^3.0.0",
"eslint": "^10.2.1",
"highlight.js": "^11.11.1",
"markdown-it": "14.1.1",
"marked-highlight": "^2.2.4",
"marked-man": "^2.1.0",
"recheck": "^4.5.0",
"rimraf": "^6.1.3",
"semantic-release": "^25.0.3",
"titleize": "^4.0.0",
"ts-expect": "^1.3.0",
"typescript": "5.3.3"
"tslib": "^2.8.1",
"typescript": "6.0.3"
},
"scripts": {
"test": "npm run build && npm run test:specs && npm run test:unit",
"test:all": "npm test && npm run test:umd && npm run test:types && npm run test:lint",
"test:unit": "node --test --test-reporter=spec test/unit",
"test:specs": "node --test --test-reporter=spec test/run-spec-tests.js",
"test:lint": "eslint .",
"test:redos": "node test/recheck.js > vuln.js",
"test:types": "tsc --project tsconfig-type-test.json && attw -P --exclude-entrypoints ./bin/marked ./marked.min.js",
"test:umd": "node test/umd-test.js",
"test:update": "node test/update-specs.js",
"rules": "node test/rules.js",
"bench": "npm run build && node test/bench.js",
"lint": "eslint --fix .",
"build:reset": "git checkout upstream/master lib/marked.cjs lib/marked.umd.js lib/marked.esm.js marked.min.js",
"build": "npm run rollup && npm run build:types && npm run build:man",
"build": "npm run build:esbuild && npm run build:types && npm run build:man",
"build:docs": "npm run build && node docs/build.js",
"build:types": "tsc && dts-bundle-generator --project tsconfig.json -o lib/marked.d.ts src/marked.ts && dts-bundle-generator --project tsconfig.json -o lib/marked.d.cts src/marked.ts",
"build:esbuild": "node esbuild.config.js",
"build:man": "marked-man man/marked.1.md > man/marked.1",
"rollup": "rollup -c rollup.config.js"
"build:reset": "rimraf ./lib ./public",
"build:types": "tsc && dts-bundle-generator --export-referenced-types --project tsconfig.json -o lib/marked.d.ts src/marked.ts",
"lint": "eslint --fix",
"rules": "node test/rules.js",
"test": "npm run build:reset && npm run build:docs && npm run test:specs && npm run test:unit && npm run test:umd && npm run test:cjs && npm run test:types && npm run test:lint",
"test:cjs": "node test/cjs-test.cjs",
"test:lint": "eslint",
"test:only": "npm run build && npm run test:specs:only && npm run test:unit:only",
"test:redos": "node test/recheck.ts > vuln.js",
"test:specs:only": "node --test --test-only --test-reporter=spec test/run-spec-tests.js",
"test:specs": "node --test --test-reporter=spec test/run-spec-tests.js",
"test:types": "tsc --project tsconfig-type-test.json && attw -P --entrypoints . --profile esm-only",
"test:umd": "node test/umd-test.js",
"test:unit:only": "node --test --test-only --test-reporter=spec test/unit/*.test.js",
"test:unit": "node --test --test-reporter=spec test/unit/*.test.js",
"test:update": "node test/update-specs.js"
},
"engines": {
"node": ">= 18"
"node": ">= 20"
}
}

21
static/js/lib/node_modules/preact/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-present Jason Miller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

185
static/js/lib/node_modules/preact/README.md generated vendored Normal file
View file

@ -0,0 +1,185 @@
<p align="center">
<a href="https://preactjs.com" target="_blank">
![Preact](https://raw.githubusercontent.com/preactjs/preact/8b0bcc927995c188eca83cba30fbc83491cc0b2f/logo.svg?sanitize=true 'Preact')
</a>
</p>
<p align="center">Fast <b>3kB</b> alternative to React with the same modern API.</p>
**All the power of Virtual DOM components, without the overhead:**
- Familiar React API & patterns: ES6 Class, hooks, and Functional Components
- Extensive React compatibility via a simple [preact/compat] alias
- Everything you need: JSX, <abbr title="Virtual DOM">VDOM</abbr>, [DevTools], <abbr title="Hot Module Replacement">HMR</abbr>, <abbr title="Server-Side Rendering">SSR</abbr>.
- Highly optimized diff algorithm and seamless hydration from Server Side Rendering
- Supports all modern browsers and IE11
- Transparent asynchronous rendering with a pluggable scheduler
### 💁 More information at the [Preact Website ➞](https://preactjs.com)
<table border="0">
<tbody>
<tr>
<td>
[![npm](https://img.shields.io/npm/v/preact.svg)](http://npm.im/preact)
[![Preact Slack Community](https://img.shields.io/badge/Slack%20Community-preact.slack.com-blue)](https://chat.preactjs.com)
[![OpenCollective Backers](https://opencollective.com/preact/backers/badge.svg)](#backers)
[![OpenCollective Sponsors](https://opencollective.com/preact/sponsors/badge.svg)](#sponsors)
[![coveralls](https://img.shields.io/coveralls/preactjs/preact/main.svg)](https://coveralls.io/github/preactjs/preact)
[![gzip size](http://img.badgesize.io/https://unpkg.com/preact/dist/preact.min.js?compression=gzip&label=gzip)](https://unpkg.com/preact/dist/preact.min.js)
[![brotli size](http://img.badgesize.io/https://unpkg.com/preact/dist/preact.min.js?compression=brotli&label=brotli)](https://unpkg.com/preact/dist/preact.min.js)
</td>
</tr>
</tbody>
</table>
You can find some awesome libraries in the [awesome-preact list](https://github.com/preactjs/awesome-preact) :sunglasses:
---
## Getting Started
> 💁 _**Note:** You [don't need ES2015 to use Preact](https://github.com/developit/preact-in-es3)... but give it a try!_
#### Tutorial: Building UI with Preact
With Preact, you create user interfaces by assembling trees of components and elements. Components are functions or classes that return a description of what their tree should output. These descriptions are typically written in [JSX](https://facebook.github.io/jsx/) (shown underneath), or [HTM](https://github.com/developit/htm) which leverages standard JavaScript Tagged Templates. Both syntaxes can express trees of elements with "props" (similar to HTML attributes) and children.
To get started using Preact, first look at the render() function. This function accepts a tree description and creates the structure described. Next, it appends this structure to a parent DOM element provided as the second argument. Future calls to render() will reuse the existing tree and update it in-place in the DOM. Internally, render() will calculate the difference from previous outputted structures in an attempt to perform as few DOM operations as possible.
```js
import { h, render } from 'preact';
// Tells babel to use h for JSX. It's better to configure this globally.
// See https://babeljs.io/docs/en/babel-plugin-transform-react-jsx#usage
// In tsconfig you can specify this with the jsxFactory
/** @jsx h */
// create our tree and append it to document.body:
render(
<main>
<h1>Hello</h1>
</main>,
document.body
);
// update the tree in-place:
render(
<main>
<h1>Hello World!</h1>
</main>,
document.body
);
// ^ this second invocation of render(...) will use a single DOM call to update the text of the <h1>
```
Hooray! render() has taken our structure and output a User Interface! This approach demonstrates a simple case, but would be difficult to use as an application grows in complexity. Each change would be forced to calculate the difference between the current and updated structure for the entire application. Components can help here by dividing the User Interface into nested Components each can calculate their difference from their mounted point. Here's an example:
```js
import { render, h } from 'preact';
import { useState } from 'preact/hooks';
/** @jsx h */
const App = () => {
const [input, setInput] = useState('');
return (
<div>
<p>Do you agree to the statement: "Preact is awesome"?</p>
<input value={input} onInput={e => setInput(e.target.value)} />
</div>
);
};
render(<App />, document.body);
```
---
## Sponsors
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor](https://opencollective.com/preact#sponsor)]
<a href="https://opencollective.com/preact/sponsor/0/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/1/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/2/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/3/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/4/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/4/avatar.svg"></a>
<a href="https://snyk.co/preact" target="_blank"><img src="https://res.cloudinary.com/snyk/image/upload/snyk-marketingui/brand-logos/wordmark-logo-color.svg" width="192" height="64"></a>
<a href="https://opencollective.com/preact/sponsor/5/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/6/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/7/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/8/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/9/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/10/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/11/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/12/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/13/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/14/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/15/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/15/avatar.svg"></a>
<a href="https://github.com/guardian" target="_blank"> &nbsp; &nbsp; &nbsp; <img src="https://github.com/guardian.png" width="64" height="64"> &nbsp; &nbsp; &nbsp; </a>
<a href="https://opencollective.com/preact/sponsor/16/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/17/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/18/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/19/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/20/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/21/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/22/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/23/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/24/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/25/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/26/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/27/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/28/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/preact/sponsor/29/website" target="_blank"><img src="https://opencollective.com/preact/sponsor/29/avatar.svg"></a>
## Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/preact#backer)]
<a href="https://opencollective.com/preact/backer/0/website" target="_blank"><img src="https://opencollective.com/preact/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/1/website" target="_blank"><img src="https://opencollective.com/preact/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/2/website" target="_blank"><img src="https://opencollective.com/preact/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/3/website" target="_blank"><img src="https://opencollective.com/preact/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/4/website" target="_blank"><img src="https://opencollective.com/preact/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/5/website" target="_blank"><img src="https://opencollective.com/preact/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/6/website" target="_blank"><img src="https://opencollective.com/preact/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/7/website" target="_blank"><img src="https://opencollective.com/preact/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/8/website" target="_blank"><img src="https://opencollective.com/preact/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/9/website" target="_blank"><img src="https://opencollective.com/preact/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/10/website" target="_blank"><img src="https://opencollective.com/preact/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/11/website" target="_blank"><img src="https://opencollective.com/preact/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/12/website" target="_blank"><img src="https://opencollective.com/preact/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/13/website" target="_blank"><img src="https://opencollective.com/preact/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/14/website" target="_blank"><img src="https://opencollective.com/preact/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/15/website" target="_blank"><img src="https://opencollective.com/preact/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/16/website" target="_blank"><img src="https://opencollective.com/preact/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/17/website" target="_blank"><img src="https://opencollective.com/preact/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/18/website" target="_blank"><img src="https://opencollective.com/preact/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/19/website" target="_blank"><img src="https://opencollective.com/preact/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/20/website" target="_blank"><img src="https://opencollective.com/preact/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/21/website" target="_blank"><img src="https://opencollective.com/preact/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/22/website" target="_blank"><img src="https://opencollective.com/preact/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/23/website" target="_blank"><img src="https://opencollective.com/preact/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/24/website" target="_blank"><img src="https://opencollective.com/preact/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/25/website" target="_blank"><img src="https://opencollective.com/preact/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/26/website" target="_blank"><img src="https://opencollective.com/preact/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/27/website" target="_blank"><img src="https://opencollective.com/preact/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/28/website" target="_blank"><img src="https://opencollective.com/preact/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/preact/backer/29/website" target="_blank"><img src="https://opencollective.com/preact/backer/29/avatar.svg"></a>
---
## License
MIT
[![Preact](https://i.imgur.com/YqCHvEW.gif)](https://preactjs.com)
[preact/compat]: https://github.com/preactjs/preact/tree/main/compat
[hyperscript]: https://github.com/dominictarr/hyperscript
[DevTools]: https://github.com/preactjs/preact-devtools

21
static/js/lib/node_modules/preact/compat/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-present Jason Miller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

13
static/js/lib/node_modules/preact/compat/client.d.ts generated vendored Normal file
View file

@ -0,0 +1,13 @@
// Intentionally not using a relative path to take advantage of
// the TS version resolution mechanism
import * as preact from 'preact';
export function createRoot(container: preact.ContainerNode): {
render(children: preact.ComponentChild): void;
unmount(): void;
};
export function hydrateRoot(
container: preact.ContainerNode,
children: preact.ComponentChild
): typeof createRoot;

21
static/js/lib/node_modules/preact/compat/client.js generated vendored Normal file
View file

@ -0,0 +1,21 @@
const { render, hydrate, unmountComponentAtNode } = require('preact/compat');
function createRoot(container) {
return {
// eslint-disable-next-line
render: function (children) {
render(children, container);
},
// eslint-disable-next-line
unmount: function () {
unmountComponentAtNode(container);
}
};
}
exports.createRoot = createRoot;
exports.hydrateRoot = function (container, children) {
hydrate(children, container);
return createRoot(container);
};

24
static/js/lib/node_modules/preact/compat/client.mjs generated vendored Normal file
View file

@ -0,0 +1,24 @@
import { render, hydrate, unmountComponentAtNode } from 'preact/compat';
export function createRoot(container) {
return {
// eslint-disable-next-line
render: function (children) {
render(children, container);
},
// eslint-disable-next-line
unmount: function () {
unmountComponentAtNode(container);
}
};
}
export function hydrateRoot(container, children) {
hydrate(children, container);
return createRoot(container);
}
export default {
createRoot,
hydrateRoot
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
require('preact/compat');
module.exports = require('preact/jsx-runtime');

View file

@ -0,0 +1,3 @@
import 'preact/compat';
export * from 'preact/jsx-runtime';

View file

@ -0,0 +1,3 @@
require('preact/compat');
module.exports = require('preact/jsx-runtime');

View file

@ -0,0 +1,3 @@
import 'preact/compat';
export * from 'preact/jsx-runtime';

51
static/js/lib/node_modules/preact/compat/package.json generated vendored Normal file
View file

@ -0,0 +1,51 @@
{
"name": "preact-compat",
"amdName": "preactCompat",
"version": "4.0.0",
"private": true,
"description": "A React compatibility layer for Preact",
"main": "dist/compat.js",
"module": "dist/compat.module.js",
"umd:main": "dist/compat.umd.js",
"source": "src/index.js",
"types": "src/index.d.ts",
"license": "MIT",
"mangle": {
"regex": "^_"
},
"peerDependencies": {
"preact": "^10.0.0"
},
"exports": {
".": {
"types": "./src/index.d.ts",
"browser": "./dist/compat.module.js",
"umd": "./dist/compat.umd.js",
"import": "./dist/compat.mjs",
"require": "./dist/compat.js"
},
"./client": {
"types": "./client.d.ts",
"import": "./client.mjs",
"require": "./client.js"
},
"./server": {
"browser": "./server.browser.js",
"import": "./server.mjs",
"require": "./server.js"
},
"./jsx-runtime": {
"import": "./jsx-runtime.mjs",
"require": "./jsx-runtime.js"
},
"./jsx-dev-runtime": {
"import": "./jsx-dev-runtime.mjs",
"require": "./jsx-dev-runtime.js"
},
"./scheduler": {
"import": "./scheduler.mjs",
"require": "./scheduler.js"
},
"./package.json": "./package.json"
}
}

15
static/js/lib/node_modules/preact/compat/scheduler.js generated vendored Normal file
View file

@ -0,0 +1,15 @@
// see scheduler.mjs
function unstable_runWithPriority(priority, callback) {
return callback();
}
module.exports = {
unstable_ImmediatePriority: 1,
unstable_UserBlockingPriority: 2,
unstable_NormalPriority: 3,
unstable_LowPriority: 4,
unstable_IdlePriority: 5,
unstable_runWithPriority,
unstable_now: performance.now.bind(performance)
};

23
static/js/lib/node_modules/preact/compat/scheduler.mjs generated vendored Normal file
View file

@ -0,0 +1,23 @@
/* eslint-disable */
// This file includes experimental React APIs exported from the "scheduler"
// npm package. Despite being explicitely marked as unstable some libraries
// already make use of them. This file is not a full replacement for the
// scheduler package, but includes the necessary shims to make those libraries
// work with Preact.
export var unstable_ImmediatePriority = 1;
export var unstable_UserBlockingPriority = 2;
export var unstable_NormalPriority = 3;
export var unstable_LowPriority = 4;
export var unstable_IdlePriority = 5;
/**
* @param {number} priority
* @param {() => void} callback
*/
export function unstable_runWithPriority(priority, callback) {
return callback();
}
export var unstable_now = performance.now.bind(performance);

View file

@ -0,0 +1,11 @@
import { renderToString } from 'preact-render-to-string';
export {
renderToString,
renderToString as renderToStaticMarkup
} from 'preact-render-to-string';
export default {
renderToString,
renderToStaticMarkup: renderToString
};

26
static/js/lib/node_modules/preact/compat/server.js generated vendored Normal file
View file

@ -0,0 +1,26 @@
/* eslint-disable */
var renderToString;
try {
const mod = require('preact-render-to-string');
renderToString = mod.default || mod.renderToString || mod;
} catch (e) {
throw Error(
'renderToString() error: missing "preact-render-to-string" dependency.'
);
}
var renderToPipeableStream;
try {
const mod = require('preact-render-to-string/stream-node');
renderToPipeableStream = mod.default || mod.renderToPipeableStream || mod;
} catch (e) {
throw Error(
'renderToPipeableStream() error: update "preact-render-to-string" dependency to at least 6.5.0.'
);
}
module.exports = {
renderToString: renderToString,
renderToStaticMarkup: renderToString,
renderToPipeableStream: renderToPipeableStream
};

15
static/js/lib/node_modules/preact/compat/server.mjs generated vendored Normal file
View file

@ -0,0 +1,15 @@
import { renderToString } from 'preact-render-to-string';
import { renderToPipeableStream } from 'preact-render-to-string/stream-node';
export {
renderToString,
renderToString as renderToStaticMarkup
} from 'preact-render-to-string';
export { renderToPipeableStream } from 'preact-render-to-string/stream-node';
export default {
renderToString,
renderToStaticMarkup: renderToString,
renderToPipeableStream
};

View file

@ -0,0 +1,21 @@
import { toChildArray } from 'preact';
const mapFn = (children, fn) => {
if (children == null) return null;
return toChildArray(toChildArray(children).map(fn));
};
// This API is completely unnecessary for Preact, so it's basically passthrough.
export const Children = {
map: mapFn,
forEach: mapFn,
count(children) {
return children ? toChildArray(children).length : 0;
},
only(children) {
const normalized = toChildArray(children);
if (normalized.length !== 1) throw 'Children.only';
return normalized[0];
},
toArray: toChildArray
};

View file

@ -0,0 +1,16 @@
import { Component } from 'preact';
import { shallowDiffers } from './util';
/**
* Component class with a predefined `shouldComponentUpdate` implementation
*/
export function PureComponent(p, c) {
this.props = p;
this.context = c;
}
PureComponent.prototype = new Component();
// Some third-party libraries check if this property is present
PureComponent.prototype.isPureReactComponent = true;
PureComponent.prototype.shouldComponentUpdate = function (props, state) {
return shallowDiffers(this.props, props) || shallowDiffers(this.state, state);
};

View file

@ -0,0 +1,47 @@
import { options } from 'preact';
let oldDiffHook = options._diff;
options._diff = vnode => {
if (vnode.type && vnode.type._forwarded && vnode.ref) {
vnode.props.ref = vnode.ref;
vnode.ref = null;
}
if (oldDiffHook) oldDiffHook(vnode);
};
export const REACT_FORWARD_SYMBOL =
(typeof Symbol != 'undefined' &&
Symbol.for &&
Symbol.for('react.forward_ref')) ||
0xf47;
/**
* Pass ref down to a child. This is mainly used in libraries with HOCs that
* wrap components. Using `forwardRef` there is an easy way to get a reference
* of the wrapped component instead of one of the wrapper itself.
* @param {import('./index').ForwardFn} fn
* @returns {import('./internal').FunctionComponent}
*/
export function forwardRef(fn) {
function Forwarded(props) {
if (!('ref' in props)) return fn(props, null);
let ref = props.ref;
delete props.ref;
const result = fn(props, ref);
props.ref = ref;
return result;
}
// mobx-react checks for this being present
Forwarded.$$typeof = REACT_FORWARD_SYMBOL;
// mobx-react heavily relies on implementation details.
// It expects an object here with a `render` property,
// and prototype.render will fail. Without this
// mobx-react throws.
Forwarded.render = Forwarded;
Forwarded.prototype.isReactComponent = Forwarded._forwarded = true;
Forwarded.displayName = 'ForwardRef(' + (fn.displayName || fn.name) + ')';
return Forwarded;
}

70
static/js/lib/node_modules/preact/compat/src/hooks.js generated vendored Normal file
View file

@ -0,0 +1,70 @@
import { useState, useLayoutEffect, useEffect } from 'preact/hooks';
import { is } from './util';
/**
* This is taken from https://github.com/facebook/react/blob/main/packages/use-sync-external-store/src/useSyncExternalStoreShimClient.js#L84
* on a high level this cuts out the warnings, ... and attempts a smaller implementation
* @typedef {{ _value: any; _getSnapshot: () => any }} Store
*/
export function useSyncExternalStore(subscribe, getSnapshot) {
const value = getSnapshot();
/**
* @typedef {{ _instance: Store }} StoreRef
* @type {[StoreRef, (store: StoreRef) => void]}
*/
const [{ _instance }, forceUpdate] = useState({
_instance: { _value: value, _getSnapshot: getSnapshot }
});
useLayoutEffect(() => {
_instance._value = value;
_instance._getSnapshot = getSnapshot;
if (didSnapshotChange(_instance)) {
forceUpdate({ _instance });
}
}, [subscribe, value, getSnapshot]);
useEffect(() => {
if (didSnapshotChange(_instance)) {
forceUpdate({ _instance });
}
return subscribe(() => {
if (didSnapshotChange(_instance)) {
forceUpdate({ _instance });
}
});
}, [subscribe]);
return value;
}
/** @type {(inst: Store) => boolean} */
function didSnapshotChange(inst) {
const latestGetSnapshot = inst._getSnapshot;
const prevValue = inst._value;
try {
const nextValue = latestGetSnapshot();
return !is(prevValue, nextValue);
} catch (error) {
return true;
}
}
export function startTransition(cb) {
cb();
}
export function useDeferredValue(val) {
return val;
}
export function useTransition() {
return [false, startTransition];
}
// TODO: in theory this should be done after a VNode is diffed as we want to insert
// styles/... before it attaches
export const useInsertionEffect = useLayoutEffect;

346
static/js/lib/node_modules/preact/compat/src/index.d.ts generated vendored Normal file
View file

@ -0,0 +1,346 @@
import * as _hooks from '../../hooks';
// Intentionally not using a relative path to take advantage of
// the TS version resolution mechanism
import * as preact from 'preact';
import { JSXInternal } from '../../src/jsx';
import * as _Suspense from './suspense';
import * as _SuspenseList from './suspense-list';
interface SignalLike<T> {
value: T;
peek(): T;
subscribe(fn: (value: T) => void): () => void;
}
type Signalish<T> = T | SignalLike<T>;
// export default React;
export = React;
export as namespace React;
declare namespace React {
// Export JSX
export import JSX = JSXInternal;
// Hooks
export import CreateHandle = _hooks.CreateHandle;
export import EffectCallback = _hooks.EffectCallback;
export import Inputs = _hooks.Inputs;
export import PropRef = _hooks.PropRef;
export import Reducer = _hooks.Reducer;
export import Dispatch = _hooks.Dispatch;
export import SetStateAction = _hooks.StateUpdater;
export import useCallback = _hooks.useCallback;
export import useContext = _hooks.useContext;
export import useDebugValue = _hooks.useDebugValue;
export import useEffect = _hooks.useEffect;
export import useImperativeHandle = _hooks.useImperativeHandle;
export import useId = _hooks.useId;
export import useLayoutEffect = _hooks.useLayoutEffect;
export import useMemo = _hooks.useMemo;
export import useReducer = _hooks.useReducer;
export import useRef = _hooks.useRef;
export import useState = _hooks.useState;
// React 18 hooks
export import useInsertionEffect = _hooks.useLayoutEffect;
export function useTransition(): [false, typeof startTransition];
export function useDeferredValue<T = any>(val: T): T;
export function useSyncExternalStore<T>(
subscribe: (flush: () => void) => () => void,
getSnapshot: () => T
): T;
// Preact Defaults
export import Context = preact.Context;
export import ContextType = preact.ContextType;
export import RefObject = preact.RefObject;
export import Component = preact.Component;
export import FunctionComponent = preact.FunctionComponent;
export import ComponentType = preact.ComponentType;
export import ComponentClass = preact.ComponentClass;
export import FC = preact.FunctionComponent;
export import createContext = preact.createContext;
export import Ref = preact.Ref;
export import createRef = preact.createRef;
export import Fragment = preact.Fragment;
export import createElement = preact.createElement;
export import cloneElement = preact.cloneElement;
export import ComponentProps = preact.ComponentProps;
export import ReactNode = preact.ComponentChild;
export import ReactElement = preact.VNode;
export import Consumer = preact.Consumer;
export import ErrorInfo = preact.ErrorInfo;
// Suspense
export import Suspense = _Suspense.Suspense;
export import lazy = _Suspense.lazy;
export import SuspenseList = _SuspenseList.SuspenseList;
// Compat
export import StrictMode = preact.Fragment;
export const version: string;
export function startTransition(cb: () => void): void;
// HTML
export interface HTMLAttributes<T extends EventTarget>
extends JSXInternal.HTMLAttributes<T> {}
export interface HTMLProps<T extends EventTarget>
extends JSXInternal.HTMLAttributes<T>,
preact.ClassAttributes<T> {}
export interface AllHTMLAttributes<T extends EventTarget>
extends JSXInternal.AllHTMLAttributes<T> {}
export import DetailedHTMLProps = JSXInternal.DetailedHTMLProps;
export import CSSProperties = JSXInternal.CSSProperties;
export interface SVGProps<T extends EventTarget>
extends JSXInternal.SVGAttributes<T>,
preact.ClassAttributes<T> {}
interface SVGAttributes extends JSXInternal.SVGAttributes {}
interface ReactSVG extends JSXInternal.IntrinsicSVGElements {}
export import HTMLAttributeReferrerPolicy = JSXInternal.HTMLAttributeReferrerPolicy;
export import HTMLAttributeAnchorTarget = JSXInternal.HTMLAttributeAnchorTarget;
export import HTMLInputTypeAttribute = JSXInternal.HTMLInputTypeAttribute;
export import HTMLAttributeCrossOrigin = JSXInternal.HTMLAttributeCrossOrigin;
export import AnchorHTMLAttributes = JSXInternal.AnchorHTMLAttributes;
export import AudioHTMLAttributes = JSXInternal.AudioHTMLAttributes;
export import AreaHTMLAttributes = JSXInternal.AreaHTMLAttributes;
export import BaseHTMLAttributes = JSXInternal.BaseHTMLAttributes;
export import BlockquoteHTMLAttributes = JSXInternal.BlockquoteHTMLAttributes;
export import ButtonHTMLAttributes = JSXInternal.ButtonHTMLAttributes;
export import CanvasHTMLAttributes = JSXInternal.CanvasHTMLAttributes;
export import ColHTMLAttributes = JSXInternal.ColHTMLAttributes;
export import ColgroupHTMLAttributes = JSXInternal.ColgroupHTMLAttributes;
export import DataHTMLAttributes = JSXInternal.DataHTMLAttributes;
export import DetailsHTMLAttributes = JSXInternal.DetailsHTMLAttributes;
export import DelHTMLAttributes = JSXInternal.DelHTMLAttributes;
export import DialogHTMLAttributes = JSXInternal.DialogHTMLAttributes;
export import EmbedHTMLAttributes = JSXInternal.EmbedHTMLAttributes;
export import FieldsetHTMLAttributes = JSXInternal.FieldsetHTMLAttributes;
export import FormHTMLAttributes = JSXInternal.FormHTMLAttributes;
export import IframeHTMLAttributes = JSXInternal.IframeHTMLAttributes;
export import ImgHTMLAttributes = JSXInternal.ImgHTMLAttributes;
export import InsHTMLAttributes = JSXInternal.InsHTMLAttributes;
export import InputHTMLAttributes = JSXInternal.InputHTMLAttributes;
export import KeygenHTMLAttributes = JSXInternal.KeygenHTMLAttributes;
export import LabelHTMLAttributes = JSXInternal.LabelHTMLAttributes;
export import LiHTMLAttributes = JSXInternal.LiHTMLAttributes;
export import LinkHTMLAttributes = JSXInternal.LinkHTMLAttributes;
export import MapHTMLAttributes = JSXInternal.MapHTMLAttributes;
export import MenuHTMLAttributes = JSXInternal.MenuHTMLAttributes;
export import MediaHTMLAttributes = JSXInternal.MediaHTMLAttributes;
export import MetaHTMLAttributes = JSXInternal.MetaHTMLAttributes;
export import MeterHTMLAttributes = JSXInternal.MeterHTMLAttributes;
export import QuoteHTMLAttributes = JSXInternal.QuoteHTMLAttributes;
export import ObjectHTMLAttributes = JSXInternal.ObjectHTMLAttributes;
export import OlHTMLAttributes = JSXInternal.OlHTMLAttributes;
export import OptgroupHTMLAttributes = JSXInternal.OptgroupHTMLAttributes;
export import OptionHTMLAttributes = JSXInternal.OptionHTMLAttributes;
export import OutputHTMLAttributes = JSXInternal.OutputHTMLAttributes;
export import ParamHTMLAttributes = JSXInternal.ParamHTMLAttributes;
export import ProgressHTMLAttributes = JSXInternal.ProgressHTMLAttributes;
export import SlotHTMLAttributes = JSXInternal.SlotHTMLAttributes;
export import ScriptHTMLAttributes = JSXInternal.ScriptHTMLAttributes;
export import SelectHTMLAttributes = JSXInternal.SelectHTMLAttributes;
export import SourceHTMLAttributes = JSXInternal.SourceHTMLAttributes;
export import StyleHTMLAttributes = JSXInternal.StyleHTMLAttributes;
export import TableHTMLAttributes = JSXInternal.TableHTMLAttributes;
export import TextareaHTMLAttributes = JSXInternal.TextareaHTMLAttributes;
export import TdHTMLAttributes = JSXInternal.TdHTMLAttributes;
export import ThHTMLAttributes = JSXInternal.ThHTMLAttributes;
export import TimeHTMLAttributes = JSXInternal.TimeHTMLAttributes;
export import TrackHTMLAttributes = JSXInternal.TrackHTMLAttributes;
export import VideoHTMLAttributes = JSXInternal.VideoHTMLAttributes;
// Events
export import TargetedEvent = JSXInternal.TargetedEvent;
export import ChangeEvent = JSXInternal.TargetedEvent;
export import ClipboardEvent = JSXInternal.TargetedClipboardEvent;
export import CompositionEvent = JSXInternal.TargetedCompositionEvent;
export import DragEvent = JSXInternal.TargetedDragEvent;
export import PointerEvent = JSXInternal.TargetedPointerEvent;
export import FocusEvent = JSXInternal.TargetedFocusEvent;
export import FormEvent = JSXInternal.TargetedEvent;
export import InvalidEvent = JSXInternal.TargetedEvent;
export import KeyboardEvent = JSXInternal.TargetedKeyboardEvent;
export import MouseEvent = JSXInternal.TargetedMouseEvent;
export import TouchEvent = JSXInternal.TargetedTouchEvent;
export import UIEvent = JSXInternal.TargetedUIEvent;
export import AnimationEvent = JSXInternal.TargetedAnimationEvent;
export import TransitionEvent = JSXInternal.TargetedTransitionEvent;
// Event Handler Types
export import ChangeEventHandler = JSXInternal.GenericEventHandler;
export import ClipboardEventHandler = JSXInternal.ClipboardEventHandler;
export import CompositionEventHandler = JSXInternal.CompositionEventHandler;
export import DragEventHandler = JSXInternal.DragEventHandler;
export import PointerEventHandler = JSXInternal.PointerEventHandler;
export import FocusEventHandler = JSXInternal.FocusEventHandler;
export import FormEventHandler = JSXInternal.GenericEventHandler;
export import InvalidEventHandler = JSXInternal.GenericEventHandler;
export import KeyboardEventHandler = JSXInternal.KeyboardEventHandler;
export import MouseEventHandler = JSXInternal.MouseEventHandler;
export import TouchEventHandler = JSXInternal.TouchEventHandler;
export import UIEventHandler = JSXInternal.UIEventHandler;
export import AnimationEventHandler = JSXInternal.AnimationEventHandler;
export import TransitionEventHandler = JSXInternal.TransitionEventHandler;
export function createPortal(
vnode: preact.ComponentChildren,
container: preact.ContainerNode
): preact.VNode<any>;
export function render(
vnode: preact.ComponentChild,
parent: preact.ContainerNode,
callback?: () => void
): Component | null;
export function hydrate(
vnode: preact.ComponentChild,
parent: preact.ContainerNode,
callback?: () => void
): Component | null;
export function unmountComponentAtNode(
container: preact.ContainerNode
): boolean;
export function createFactory(
type: preact.VNode<any>['type']
): (
props?: any,
...children: preact.ComponentChildren[]
) => preact.VNode<any>;
export function isValidElement(element: any): boolean;
export function isFragment(element: any): boolean;
export function isMemo(element: any): boolean;
export function findDOMNode(
component: preact.Component | Element
): Element | null;
export abstract class PureComponent<
P = {},
S = {},
SS = any
> extends preact.Component<P, S> {
isPureReactComponent: boolean;
}
export type MemoExoticComponent<C extends preact.FunctionalComponent<any>> =
preact.FunctionComponent<ComponentProps<C>> & {
readonly type: C;
};
export function memo<P = {}>(
component: preact.FunctionalComponent<P>,
comparer?: (prev: P, next: P) => boolean
): preact.FunctionComponent<P>;
export function memo<C extends preact.FunctionalComponent<any>>(
component: C,
comparer?: (
prev: preact.ComponentProps<C>,
next: preact.ComponentProps<C>
) => boolean
): C;
export interface RefAttributes<R> extends preact.Attributes {
ref?: preact.Ref<R> | undefined;
}
export interface ForwardFn<P = {}, T = any> {
(props: P, ref: ForwardedRef<T>): preact.ComponentChild;
displayName?: string;
}
export interface ForwardRefExoticComponent<P>
extends preact.FunctionComponent<P> {
defaultProps?: Partial<P> | undefined;
}
export function forwardRef<R, P = {}>(
fn: ForwardFn<P, R>
): preact.FunctionalComponent<PropsWithoutRef<P> & { ref?: preact.Ref<R> }>;
export type PropsWithoutRef<P> = Omit<P, 'ref'>;
interface MutableRefObject<T> {
current: T;
}
export type ForwardedRef<T> =
| ((instance: T | null) => void)
| MutableRefObject<T | null>
| null;
export type ElementType<
P = any,
Tag extends keyof JSX.IntrinsicElements = keyof JSX.IntrinsicElements
> =
| { [K in Tag]: P extends JSX.IntrinsicElements[K] ? K : never }[Tag]
| ComponentType<P>;
export type ComponentPropsWithoutRef<T extends ElementType> = PropsWithoutRef<
ComponentProps<T>
>;
export type ComponentPropsWithRef<C extends ElementType> = C extends new (
props: infer P
) => Component<any, any>
? PropsWithoutRef<P> & RefAttributes<InstanceType<C>>
: ComponentProps<C>;
export type ElementRef<
C extends
| ForwardRefExoticComponent<any>
| { new (props: any): Component<any, any> }
| ((props: any) => ReactNode)
| keyof JSXInternal.IntrinsicElements
> = 'ref' extends keyof ComponentPropsWithRef<C>
? NonNullable<ComponentPropsWithRef<C>['ref']> extends RefAttributes<
infer Instance
>['ref']
? Instance
: never
: never;
export function flushSync<R>(fn: () => R): R;
export function flushSync<A, R>(fn: (a: A) => R, a: A): R;
export function unstable_batchedUpdates(
callback: (arg?: any) => void,
arg?: any
): void;
export type PropsWithChildren<P = unknown> = P & {
children?: preact.ComponentChildren | undefined;
};
export const Children: {
map<T extends preact.ComponentChild, R>(
children: T | T[],
fn: (child: T, i: number) => R
): R[];
forEach<T extends preact.ComponentChild>(
children: T | T[],
fn: (child: T, i: number) => void
): void;
count: (children: preact.ComponentChildren) => number;
only: (children: preact.ComponentChildren) => preact.ComponentChild;
toArray: (children: preact.ComponentChildren) => preact.VNode<{}>[];
};
// scheduler
export const unstable_ImmediatePriority: number;
export const unstable_UserBlockingPriority: number;
export const unstable_NormalPriority: number;
export const unstable_LowPriority: number;
export const unstable_IdlePriority: number;
export function unstable_runWithPriority(
priority: number,
callback: () => void
): void;
export const unstable_now: () => number;
}

238
static/js/lib/node_modules/preact/compat/src/index.js generated vendored Normal file
View file

@ -0,0 +1,238 @@
import {
createElement,
render as preactRender,
cloneElement as preactCloneElement,
createRef,
Component,
createContext,
Fragment
} from 'preact';
import {
useState,
useId,
useReducer,
useEffect,
useLayoutEffect,
useRef,
useImperativeHandle,
useMemo,
useCallback,
useContext,
useDebugValue
} from 'preact/hooks';
import {
useInsertionEffect,
startTransition,
useDeferredValue,
useSyncExternalStore,
useTransition
} from './hooks';
import { PureComponent } from './PureComponent';
import { memo } from './memo';
import { forwardRef } from './forwardRef';
import { Children } from './Children';
import { Suspense, lazy } from './suspense';
import { SuspenseList } from './suspense-list';
import { createPortal } from './portals';
import {
hydrate,
render,
REACT_ELEMENT_TYPE,
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
} from './render';
const version = '18.3.1'; // trick libraries to think we are react
/**
* Legacy version of createElement.
* @param {import('./internal').VNode["type"]} type The node name or Component constructor
*/
function createFactory(type) {
return createElement.bind(null, type);
}
/**
* Check if the passed element is a valid (p)react node.
* @param {*} element The element to check
* @returns {boolean}
*/
function isValidElement(element) {
return !!element && element.$$typeof === REACT_ELEMENT_TYPE;
}
/**
* Check if the passed element is a Fragment node.
* @param {*} element The element to check
* @returns {boolean}
*/
function isFragment(element) {
return isValidElement(element) && element.type === Fragment;
}
/**
* Check if the passed element is a Memo node.
* @param {*} element The element to check
* @returns {boolean}
*/
function isMemo(element) {
return (
!!element &&
!!element.displayName &&
(typeof element.displayName === 'string' ||
element.displayName instanceof String) &&
element.displayName.startsWith('Memo(')
);
}
/**
* Wrap `cloneElement` to abort if the passed element is not a valid element and apply
* all vnode normalizations.
* @param {import('./internal').VNode} element The vnode to clone
* @param {object} props Props to add when cloning
* @param {Array<import('./internal').ComponentChildren>} rest Optional component children
*/
function cloneElement(element) {
if (!isValidElement(element)) return element;
return preactCloneElement.apply(null, arguments);
}
/**
* Remove a component tree from the DOM, including state and event handlers.
* @param {import('./internal').PreactElement} container
* @returns {boolean}
*/
function unmountComponentAtNode(container) {
if (container._children) {
preactRender(null, container);
return true;
}
return false;
}
/**
* Get the matching DOM node for a component
* @param {import('./internal').Component} component
* @returns {import('./internal').PreactElement | null}
*/
function findDOMNode(component) {
return (
(component &&
(component.base || (component.nodeType === 1 && component))) ||
null
);
}
/**
* Deprecated way to control batched rendering inside the reconciler, but we
* already schedule in batches inside our rendering code
* @template Arg
* @param {(arg: Arg) => void} callback function that triggers the updated
* @param {Arg} [arg] Optional argument that can be passed to the callback
*/
// eslint-disable-next-line camelcase
const unstable_batchedUpdates = (callback, arg) => callback(arg);
/**
* In React, `flushSync` flushes the entire tree and forces a rerender. It's
* implmented here as a no-op.
* @template Arg
* @template Result
* @param {(arg: Arg) => Result} callback function that runs before the flush
* @param {Arg} [arg] Optional argument that can be passed to the callback
* @returns
*/
const flushSync = (callback, arg) => callback(arg);
/**
* Strict Mode is not implemented in Preact, so we provide a stand-in for it
* that just renders its children without imposing any restrictions.
*/
const StrictMode = Fragment;
// compat to react-is
export const isElement = isValidElement;
export * from 'preact/hooks';
export {
version,
Children,
render,
hydrate,
unmountComponentAtNode,
createPortal,
createElement,
createContext,
createFactory,
cloneElement,
createRef,
Fragment,
isValidElement,
isFragment,
isMemo,
findDOMNode,
Component,
PureComponent,
memo,
forwardRef,
flushSync,
useInsertionEffect,
startTransition,
useDeferredValue,
useSyncExternalStore,
useTransition,
// eslint-disable-next-line camelcase
unstable_batchedUpdates,
StrictMode,
Suspense,
SuspenseList,
lazy,
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
};
// React copies the named exports to the default one.
export default {
useState,
useId,
useReducer,
useEffect,
useLayoutEffect,
useInsertionEffect,
useTransition,
useDeferredValue,
useSyncExternalStore,
startTransition,
useRef,
useImperativeHandle,
useMemo,
useCallback,
useContext,
useDebugValue,
version,
Children,
render,
hydrate,
unmountComponentAtNode,
createPortal,
createElement,
createContext,
createFactory,
cloneElement,
createRef,
Fragment,
isValidElement,
isElement,
isFragment,
isMemo,
findDOMNode,
Component,
PureComponent,
memo,
forwardRef,
flushSync,
unstable_batchedUpdates,
StrictMode,
Suspense,
SuspenseList,
lazy,
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
};

View file

@ -0,0 +1,48 @@
import {
Component as PreactComponent,
VNode as PreactVNode,
FunctionComponent as PreactFunctionComponent,
PreactElement
} from '../../src/internal';
import { SuspenseProps } from './suspense';
export { ComponentChildren } from '../..';
export { PreactElement };
export interface Component<P = {}, S = {}> extends PreactComponent<P, S> {
isReactComponent?: object;
isPureReactComponent?: true;
_patchedLifecycles?: true;
// Suspense internal properties
_childDidSuspend?(error: Promise<void>, suspendingVNode: VNode): void;
_suspended: (vnode: VNode) => (unsuspend: () => void) => void;
_onResolve?(): void;
// Portal internal properties
_temp: any;
_container: PreactElement;
}
export interface FunctionComponent<P = {}> extends PreactFunctionComponent<P> {
shouldComponentUpdate?(nextProps: Readonly<P>): boolean;
_forwarded?: boolean;
_patchedLifecycles?: true;
}
export interface VNode<T = any> extends PreactVNode<T> {
$$typeof?: symbol | string;
preactCompatNormalized?: boolean;
}
export interface SuspenseState {
_suspended?: null | VNode<any>;
}
export interface SuspenseComponent
extends PreactComponent<SuspenseProps, SuspenseState> {
_pendingSuspensionCount: number;
_suspenders: Component[];
_detachOnNextRender: null | VNode<any>;
}

34
static/js/lib/node_modules/preact/compat/src/memo.js generated vendored Normal file
View file

@ -0,0 +1,34 @@
import { createElement } from 'preact';
import { shallowDiffers } from './util';
/**
* Memoize a component, so that it only updates when the props actually have
* changed. This was previously known as `React.pure`.
* @param {import('./internal').FunctionComponent} c functional component
* @param {(prev: object, next: object) => boolean} [comparer] Custom equality function
* @returns {import('./internal').FunctionComponent}
*/
export function memo(c, comparer) {
function shouldUpdate(nextProps) {
let ref = this.props.ref;
let updateRef = ref == nextProps.ref;
if (!updateRef && ref) {
ref.call ? ref(null) : (ref.current = null);
}
if (!comparer) {
return shallowDiffers(this.props, nextProps);
}
return !comparer(this.props, nextProps) || !updateRef;
}
function Memoed(props) {
this.shouldComponentUpdate = shouldUpdate;
return createElement(c, props);
}
Memoed.displayName = 'Memo(' + (c.displayName || c.name) + ')';
Memoed.prototype.isReactComponent = true;
Memoed._forwarded = true;
return Memoed;
}

View file

@ -0,0 +1,75 @@
import { createElement, render } from 'preact';
/**
* @param {import('../../src/index').RenderableProps<{ context: any }>} props
*/
function ContextProvider(props) {
this.getChildContext = () => props.context;
return props.children;
}
/**
* Portal component
* @this {import('./internal').Component}
* @param {object | null | undefined} props
*
* TODO: use createRoot() instead of fake root
*/
function Portal(props) {
const _this = this;
let container = props._container;
_this.componentWillUnmount = function () {
render(null, _this._temp);
_this._temp = null;
_this._container = null;
};
// When we change container we should clear our old container and
// indicate a new mount.
if (_this._container && _this._container !== container) {
_this.componentWillUnmount();
}
if (!_this._temp) {
_this._container = container;
// Create a fake DOM parent node that manages a subset of `container`'s children:
_this._temp = {
nodeType: 1,
parentNode: container,
childNodes: [],
contains: () => true,
// Technically this isn't needed
appendChild(child) {
this.childNodes.push(child);
_this._container.appendChild(child);
},
insertBefore(child, before) {
this.childNodes.push(child);
_this._container.insertBefore(child, before);
},
removeChild(child) {
this.childNodes.splice(this.childNodes.indexOf(child) >>> 1, 1);
_this._container.removeChild(child);
}
};
}
// Render our wrapping element into temp.
render(
createElement(ContextProvider, { context: _this.context }, props._vnode),
_this._temp
);
}
/**
* Create a `Portal` to continue rendering the vnode tree at a different DOM node
* @param {import('./internal').VNode} vnode The vnode to render
* @param {import('./internal').PreactElement} container The DOM node to continue rendering in to.
*/
export function createPortal(vnode, container) {
const el = createElement(Portal, { _vnode: vnode, _container: container });
el.containerInfo = container;
return el;
}

313
static/js/lib/node_modules/preact/compat/src/render.js generated vendored Normal file
View file

@ -0,0 +1,313 @@
import {
render as preactRender,
hydrate as preactHydrate,
options,
toChildArray,
Component
} from 'preact';
import {
useCallback,
useContext,
useDebugValue,
useEffect,
useId,
useImperativeHandle,
useLayoutEffect,
useMemo,
useReducer,
useRef,
useState
} from 'preact/hooks';
import {
useDeferredValue,
useInsertionEffect,
useSyncExternalStore,
useTransition
} from './index';
export const REACT_ELEMENT_TYPE =
(typeof Symbol != 'undefined' && Symbol.for && Symbol.for('react.element')) ||
0xeac7;
const CAMEL_PROPS =
/^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image(!S)|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/;
const ON_ANI = /^on(Ani|Tra|Tou|BeforeInp|Compo)/;
const CAMEL_REPLACE = /[A-Z0-9]/g;
const IS_DOM = typeof document !== 'undefined';
// Input types for which onchange should not be converted to oninput.
// type="file|checkbox|radio", plus "range" in IE11.
// (IE11 doesn't support Symbol, which we use here to turn `rad` into `ra` which matches "range")
const onChangeInputType = type =>
(typeof Symbol != 'undefined' && typeof Symbol() == 'symbol'
? /fil|che|rad/
: /fil|che|ra/
).test(type);
// Some libraries like `react-virtualized` explicitly check for this.
Component.prototype.isReactComponent = {};
// `UNSAFE_*` lifecycle hooks
// Preact only ever invokes the unprefixed methods.
// Here we provide a base "fallback" implementation that calls any defined UNSAFE_ prefixed method.
// - If a component defines its own `componentDidMount()` (including via defineProperty), use that.
// - If a component defines `UNSAFE_componentDidMount()`, `componentDidMount` is the alias getter/setter.
// - If anything assigns to an `UNSAFE_*` property, the assignment is forwarded to the unprefixed property.
// See https://github.com/preactjs/preact/issues/1941
[
'componentWillMount',
'componentWillReceiveProps',
'componentWillUpdate'
].forEach(key => {
Object.defineProperty(Component.prototype, key, {
configurable: true,
get() {
return this['UNSAFE_' + key];
},
set(v) {
Object.defineProperty(this, key, {
configurable: true,
writable: true,
value: v
});
}
});
});
/**
* Proxy render() since React returns a Component reference.
* @param {import('./internal').VNode} vnode VNode tree to render
* @param {import('./internal').PreactElement} parent DOM node to render vnode tree into
* @param {() => void} [callback] Optional callback that will be called after rendering
* @returns {import('./internal').Component | null} The root component reference or null
*/
export function render(vnode, parent, callback) {
// React destroys any existing DOM nodes, see #1727
// ...but only on the first render, see #1828
if (parent._children == null) {
parent.textContent = '';
}
preactRender(vnode, parent);
if (typeof callback == 'function') callback();
return vnode ? vnode._component : null;
}
export function hydrate(vnode, parent, callback) {
preactHydrate(vnode, parent);
if (typeof callback == 'function') callback();
return vnode ? vnode._component : null;
}
let oldEventHook = options.event;
options.event = e => {
if (oldEventHook) e = oldEventHook(e);
e.persist = empty;
e.isPropagationStopped = isPropagationStopped;
e.isDefaultPrevented = isDefaultPrevented;
return (e.nativeEvent = e);
};
function empty() {}
function isPropagationStopped() {
return this.cancelBubble;
}
function isDefaultPrevented() {
return this.defaultPrevented;
}
const classNameDescriptorNonEnumberable = {
enumerable: false,
configurable: true,
get() {
return this.class;
}
};
function handleDomVNode(vnode) {
let props = vnode.props,
type = vnode.type,
normalizedProps = {};
let isNonDashedType = type.indexOf('-') === -1;
for (let i in props) {
let value = props[i];
if (
(i === 'value' && 'defaultValue' in props && value == null) ||
// Emulate React's behavior of not rendering the contents of noscript tags on the client.
(IS_DOM && i === 'children' && type === 'noscript') ||
i === 'class' ||
i === 'className'
) {
// Skip applying value if it is null/undefined and we already set
// a default value
continue;
}
let lowerCased = i.toLowerCase();
if (i === 'defaultValue' && 'value' in props && props.value == null) {
// `defaultValue` is treated as a fallback `value` when a value prop is present but null/undefined.
// `defaultValue` for Elements with no value prop is the same as the DOM defaultValue property.
i = 'value';
} else if (i === 'download' && value === true) {
// Calling `setAttribute` with a truthy value will lead to it being
// passed as a stringified value, e.g. `download="true"`. React
// converts it to an empty string instead, otherwise the attribute
// value will be used as the file name and the file will be called
// "true" upon downloading it.
value = '';
} else if (lowerCased === 'translate' && value === 'no') {
value = false;
} else if (lowerCased[0] === 'o' && lowerCased[1] === 'n') {
if (lowerCased === 'ondoubleclick') {
i = 'ondblclick';
} else if (
lowerCased === 'onchange' &&
(type === 'input' || type === 'textarea') &&
!onChangeInputType(props.type)
) {
lowerCased = i = 'oninput';
} else if (lowerCased === 'onfocus') {
i = 'onfocusin';
} else if (lowerCased === 'onblur') {
i = 'onfocusout';
} else if (ON_ANI.test(i)) {
i = lowerCased;
}
} else if (isNonDashedType && CAMEL_PROPS.test(i)) {
i = i.replace(CAMEL_REPLACE, '-$&').toLowerCase();
} else if (value === null) {
value = undefined;
}
// Add support for onInput and onChange, see #3561
// if we have an oninput prop already change it to oninputCapture
if (lowerCased === 'oninput') {
i = lowerCased;
if (normalizedProps[i]) {
i = 'oninputCapture';
}
}
normalizedProps[i] = value;
}
// Add support for array select values: <select multiple value={[]} />
if (
type == 'select' &&
normalizedProps.multiple &&
Array.isArray(normalizedProps.value)
) {
// forEach() always returns undefined, which we abuse here to unset the value prop.
normalizedProps.value = toChildArray(props.children).forEach(child => {
child.props.selected =
normalizedProps.value.indexOf(child.props.value) != -1;
});
}
// Adding support for defaultValue in select tag
if (type == 'select' && normalizedProps.defaultValue != null) {
normalizedProps.value = toChildArray(props.children).forEach(child => {
if (normalizedProps.multiple) {
child.props.selected =
normalizedProps.defaultValue.indexOf(child.props.value) != -1;
} else {
child.props.selected =
normalizedProps.defaultValue == child.props.value;
}
});
}
if (props.class && !props.className) {
normalizedProps.class = props.class;
Object.defineProperty(
normalizedProps,
'className',
classNameDescriptorNonEnumberable
);
} else if (props.className && !props.class) {
normalizedProps.class = normalizedProps.className = props.className;
} else if (props.class && props.className) {
normalizedProps.class = normalizedProps.className = props.className;
}
vnode.props = normalizedProps;
}
let oldVNodeHook = options.vnode;
options.vnode = vnode => {
// only normalize props on Element nodes
if (typeof vnode.type === 'string') {
handleDomVNode(vnode);
}
vnode.$$typeof = REACT_ELEMENT_TYPE;
if (oldVNodeHook) oldVNodeHook(vnode);
};
// Only needed for react-relay
let currentComponent;
const oldBeforeRender = options._render;
options._render = function (vnode) {
if (oldBeforeRender) {
oldBeforeRender(vnode);
}
currentComponent = vnode._component;
};
const oldDiffed = options.diffed;
/** @type {(vnode: import('./internal').VNode) => void} */
options.diffed = function (vnode) {
if (oldDiffed) {
oldDiffed(vnode);
}
const props = vnode.props;
const dom = vnode._dom;
if (
dom != null &&
vnode.type === 'textarea' &&
'value' in props &&
props.value !== dom.value
) {
dom.value = props.value == null ? '' : props.value;
}
currentComponent = null;
};
// This is a very very private internal function for React it
// is used to sort-of do runtime dependency injection.
export const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
ReactCurrentDispatcher: {
current: {
readContext(context) {
return currentComponent._globalContext[context._id].props.value;
},
useCallback,
useContext,
useDebugValue,
useDeferredValue,
useEffect,
useId,
useImperativeHandle,
useInsertionEffect,
useLayoutEffect,
useMemo,
// useMutableSource, // experimental-only and replaced by uSES, likely not worth supporting
useReducer,
useRef,
useState,
useSyncExternalStore,
useTransition
}
}
};

View file

@ -0,0 +1,16 @@
// Intentionally not using a relative path to take advantage of
// the TS version resolution mechanism
import { Component, ComponentChild, ComponentChildren } from 'preact';
//
// SuspenseList
// -----------------------------------
export interface SuspenseListProps {
children?: ComponentChildren;
revealOrder?: 'forwards' | 'backwards' | 'together';
}
export class SuspenseList extends Component<SuspenseListProps> {
render(): ComponentChild;
}

View file

@ -0,0 +1,127 @@
import { Component, toChildArray } from 'preact';
import { suspended } from './suspense.js';
// Indexes to linked list nodes (nodes are stored as arrays to save bytes).
const SUSPENDED_COUNT = 0;
const RESOLVED_COUNT = 1;
const NEXT_NODE = 2;
// Having custom inheritance instead of a class here saves a lot of bytes.
export function SuspenseList() {
this._next = null;
this._map = null;
}
// Mark one of child's earlier suspensions as resolved.
// Some pending callbacks may become callable due to this
// (e.g. the last suspended descendant gets resolved when
// revealOrder === 'together'). Process those callbacks as well.
const resolve = (list, child, node) => {
if (++node[RESOLVED_COUNT] === node[SUSPENDED_COUNT]) {
// The number a child (or any of its descendants) has been suspended
// matches the number of times it's been resolved. Therefore we
// mark the child as completely resolved by deleting it from ._map.
// This is used to figure out when *all* children have been completely
// resolved when revealOrder is 'together'.
list._map.delete(child);
}
// If revealOrder is falsy then we can do an early exit, as the
// callbacks won't get queued in the node anyway.
// If revealOrder is 'together' then also do an early exit
// if all suspended descendants have not yet been resolved.
if (
!list.props.revealOrder ||
(list.props.revealOrder[0] === 't' && list._map.size)
) {
return;
}
// Walk the currently suspended children in order, calling their
// stored callbacks on the way. Stop if we encounter a child that
// has not been completely resolved yet.
node = list._next;
while (node) {
while (node.length > 3) {
node.pop()();
}
if (node[RESOLVED_COUNT] < node[SUSPENDED_COUNT]) {
break;
}
list._next = node = node[NEXT_NODE];
}
};
// Things we do here to save some bytes but are not proper JS inheritance:
// - call `new Component()` as the prototype
// - do not set `Suspense.prototype.constructor` to `Suspense`
SuspenseList.prototype = new Component();
SuspenseList.prototype._suspended = function (child) {
const list = this;
const delegated = suspended(list._vnode);
let node = list._map.get(child);
node[SUSPENDED_COUNT]++;
return unsuspend => {
const wrappedUnsuspend = () => {
if (!list.props.revealOrder) {
// Special case the undefined (falsy) revealOrder, as there
// is no need to coordinate a specific order or unsuspends.
unsuspend();
} else {
node.push(unsuspend);
resolve(list, child, node);
}
};
if (delegated) {
delegated(wrappedUnsuspend);
} else {
wrappedUnsuspend();
}
};
};
SuspenseList.prototype.render = function (props) {
this._next = null;
this._map = new Map();
const children = toChildArray(props.children);
if (props.revealOrder && props.revealOrder[0] === 'b') {
// If order === 'backwards' (or, well, anything starting with a 'b')
// then flip the child list around so that the last child will be
// the first in the linked list.
children.reverse();
}
// Build the linked list. Iterate through the children in reverse order
// so that `_next` points to the first linked list node to be resolved.
for (let i = children.length; i--; ) {
// Create a new linked list node as an array of form:
// [suspended_count, resolved_count, next_node]
// where suspended_count and resolved_count are numeric counters for
// keeping track how many times a node has been suspended and resolved.
//
// Note that suspended_count starts from 1 instead of 0, so we can block
// processing callbacks until componentDidMount has been called. In a sense
// node is suspended at least until componentDidMount gets called!
//
// Pending callbacks are added to the end of the node:
// [suspended_count, resolved_count, next_node, callback_0, callback_1, ...]
this._map.set(children[i], (this._next = [1, 0, this._next]));
}
return props.children;
};
SuspenseList.prototype.componentDidUpdate =
SuspenseList.prototype.componentDidMount = function () {
// Iterate through all children after mounting for two reasons:
// 1. As each node[SUSPENDED_COUNT] starts from 1, this iteration increases
// each node[RELEASED_COUNT] by 1, therefore balancing the counters.
// The nodes can now be completely consumed from the linked list.
// 2. Handle nodes that might have gotten resolved between render and
// componentDidMount.
this._map.forEach((node, child) => {
resolve(this, child, node);
});
};

View file

@ -0,0 +1,19 @@
// Intentionally not using a relative path to take advantage of
// the TS version resolution mechanism
import { Component, ComponentChild, ComponentChildren } from 'preact';
//
// Suspense/lazy
// -----------------------------------
export function lazy<T>(
loader: () => Promise<{ default: T } | T>
): T extends { default: infer U } ? U : T;
export interface SuspenseProps {
children?: ComponentChildren;
fallback: ComponentChildren;
}
export class Suspense extends Component<SuspenseProps> {
render(): ComponentChild;
}

View file

@ -0,0 +1,273 @@
import { Component, createElement, options, Fragment } from 'preact';
import { MODE_HYDRATE } from '../../src/constants';
import { assign } from './util';
const oldCatchError = options._catchError;
options._catchError = function (error, newVNode, oldVNode, errorInfo) {
if (error.then) {
/** @type {import('./internal').Component} */
let component;
let vnode = newVNode;
for (; (vnode = vnode._parent); ) {
if ((component = vnode._component) && component._childDidSuspend) {
if (newVNode._dom == null) {
newVNode._dom = oldVNode._dom;
newVNode._children = oldVNode._children;
}
// Don't call oldCatchError if we found a Suspense
return component._childDidSuspend(error, newVNode);
}
}
}
oldCatchError(error, newVNode, oldVNode, errorInfo);
};
const oldUnmount = options.unmount;
options.unmount = function (vnode) {
/** @type {import('./internal').Component} */
const component = vnode._component;
if (component && component._onResolve) {
component._onResolve();
}
// if the component is still hydrating
// most likely it is because the component is suspended
// we set the vnode.type as `null` so that it is not a typeof function
// so the unmount will remove the vnode._dom
if (component && vnode._flags & MODE_HYDRATE) {
vnode.type = null;
}
if (oldUnmount) oldUnmount(vnode);
};
function detachedClone(vnode, detachedParent, parentDom) {
if (vnode) {
if (vnode._component && vnode._component.__hooks) {
vnode._component.__hooks._list.forEach(effect => {
if (typeof effect._cleanup == 'function') effect._cleanup();
});
vnode._component.__hooks = null;
}
vnode = assign({}, vnode);
if (vnode._component != null) {
if (vnode._component._parentDom === parentDom) {
vnode._component._parentDom = detachedParent;
}
vnode._component = null;
}
vnode._children =
vnode._children &&
vnode._children.map(child =>
detachedClone(child, detachedParent, parentDom)
);
}
return vnode;
}
function removeOriginal(vnode, detachedParent, originalParent) {
if (vnode && originalParent) {
vnode._original = null;
vnode._children =
vnode._children &&
vnode._children.map(child =>
removeOriginal(child, detachedParent, originalParent)
);
if (vnode._component) {
if (vnode._component._parentDom === detachedParent) {
if (vnode._dom) {
originalParent.appendChild(vnode._dom);
}
vnode._component._force = true;
vnode._component._parentDom = originalParent;
}
}
}
return vnode;
}
// having custom inheritance instead of a class here saves a lot of bytes
export function Suspense() {
// we do not call super here to golf some bytes...
this._pendingSuspensionCount = 0;
this._suspenders = null;
this._detachOnNextRender = null;
}
// Things we do here to save some bytes but are not proper JS inheritance:
// - call `new Component()` as the prototype
// - do not set `Suspense.prototype.constructor` to `Suspense`
Suspense.prototype = new Component();
/**
* @this {import('./internal').SuspenseComponent}
* @param {Promise} promise The thrown promise
* @param {import('./internal').VNode<any, any>} suspendingVNode The suspending component
*/
Suspense.prototype._childDidSuspend = function (promise, suspendingVNode) {
const suspendingComponent = suspendingVNode._component;
/** @type {import('./internal').SuspenseComponent} */
const c = this;
if (c._suspenders == null) {
c._suspenders = [];
}
c._suspenders.push(suspendingComponent);
const resolve = suspended(c._vnode);
let resolved = false;
const onResolved = () => {
if (resolved) return;
resolved = true;
suspendingComponent._onResolve = null;
if (resolve) {
resolve(onSuspensionComplete);
} else {
onSuspensionComplete();
}
};
suspendingComponent._onResolve = onResolved;
const onSuspensionComplete = () => {
if (!--c._pendingSuspensionCount) {
// If the suspension was during hydration we don't need to restore the
// suspended children into the _children array
if (c.state._suspended) {
const suspendedVNode = c.state._suspended;
c._vnode._children[0] = removeOriginal(
suspendedVNode,
suspendedVNode._component._parentDom,
suspendedVNode._component._originalParentDom
);
}
c.setState({ _suspended: (c._detachOnNextRender = null) });
let suspended;
while ((suspended = c._suspenders.pop())) {
suspended.forceUpdate();
}
}
};
/**
* We do not set `suspended: true` during hydration because we want the actual markup
* to remain on screen and hydrate it when the suspense actually gets resolved.
* While in non-hydration cases the usual fallback -> component flow would occour.
*/
if (
!c._pendingSuspensionCount++ &&
!(suspendingVNode._flags & MODE_HYDRATE)
) {
c.setState({ _suspended: (c._detachOnNextRender = c._vnode._children[0]) });
}
promise.then(onResolved, onResolved);
};
Suspense.prototype.componentWillUnmount = function () {
this._suspenders = [];
};
/**
* @this {import('./internal').SuspenseComponent}
* @param {import('./internal').SuspenseComponent["props"]} props
* @param {import('./internal').SuspenseState} state
*/
Suspense.prototype.render = function (props, state) {
if (this._detachOnNextRender) {
// When the Suspense's _vnode was created by a call to createVNode
// (i.e. due to a setState further up in the tree)
// it's _children prop is null, in this case we "forget" about the parked vnodes to detach
if (this._vnode._children) {
const detachedParent = document.createElement('div');
const detachedComponent = this._vnode._children[0]._component;
this._vnode._children[0] = detachedClone(
this._detachOnNextRender,
detachedParent,
(detachedComponent._originalParentDom = detachedComponent._parentDom)
);
}
this._detachOnNextRender = null;
}
// Wrap fallback tree in a VNode that prevents itself from being marked as aborting mid-hydration:
/** @type {import('./internal').VNode} */
const fallback =
state._suspended && createElement(Fragment, null, props.fallback);
if (fallback) fallback._flags &= ~MODE_HYDRATE;
return [
createElement(Fragment, null, state._suspended ? null : props.children),
fallback
];
};
/**
* Checks and calls the parent component's _suspended method, passing in the
* suspended vnode. This is a way for a parent (e.g. SuspenseList) to get notified
* that one of its children/descendants suspended.
*
* The parent MAY return a callback. The callback will get called when the
* suspension resolves, notifying the parent of the fact.
* Moreover, the callback gets function `unsuspend` as a parameter. The resolved
* child descendant will not actually get unsuspended until `unsuspend` gets called.
* This is a way for the parent to delay unsuspending.
*
* If the parent does not return a callback then the resolved vnode
* gets unsuspended immediately when it resolves.
*
* @param {import('./internal').VNode} vnode
* @returns {((unsuspend: () => void) => void)?}
*/
export function suspended(vnode) {
/** @type {import('./internal').Component} */
let component = vnode._parent._component;
return component && component._suspended && component._suspended(vnode);
}
export function lazy(loader) {
let prom;
let component;
let error;
function Lazy(props) {
if (!prom) {
prom = loader();
prom.then(
exports => {
component = exports.default || exports;
},
e => {
error = e;
}
);
}
if (error) {
throw error;
}
if (!component) {
throw prom;
}
return createElement(component, props);
}
Lazy.displayName = 'Lazy';
Lazy._forwarded = true;
return Lazy;
}

33
static/js/lib/node_modules/preact/compat/src/util.js generated vendored Normal file
View file

@ -0,0 +1,33 @@
/**
* Assign properties from `props` to `obj`
* @template O, P The obj and props types
* @param {O} obj The object to copy properties to
* @param {P} props The object to copy properties from
* @returns {O & P}
*/
export function assign(obj, props) {
for (let i in props) obj[i] = props[i];
return /** @type {O & P} */ (obj);
}
/**
* Check if two objects have a different shape
* @param {object} a
* @param {object} b
* @returns {boolean}
*/
export function shallowDiffers(a, b) {
for (let i in a) if (i !== '__source' && !(i in b)) return true;
for (let i in b) if (i !== '__source' && a[i] !== b[i]) return true;
return false;
}
/**
* Check if two values are the same value
* @param {*} x
* @param {*} y
* @returns {boolean}
*/
export function is(x, y) {
return (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y);
}

View file

@ -0,0 +1 @@
module.exports = require('preact/test-utils');

21
static/js/lib/node_modules/preact/debug/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-present Jason Miller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

27
static/js/lib/node_modules/preact/debug/package.json generated vendored Normal file
View file

@ -0,0 +1,27 @@
{
"name": "preact-debug",
"amdName": "preactDebug",
"version": "1.0.0",
"private": true,
"description": "Preact extensions for development",
"main": "dist/debug.js",
"module": "dist/debug.module.js",
"umd:main": "dist/debug.umd.js",
"source": "src/index.js",
"license": "MIT",
"mangle": {
"regex": "^(?!_renderer)^_"
},
"peerDependencies": {
"preact": "^10.0.0"
},
"exports": {
".": {
"types": "./src/index.d.ts",
"browser": "./dist/debug.module.js",
"umd": "./dist/debug.umd.js",
"import": "./dist/debug.mjs",
"require": "./dist/debug.js"
}
}
}

View file

@ -0,0 +1,54 @@
const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
let loggedTypeFailures = {};
/**
* Reset the history of which prop type warnings have been logged.
*/
export function resetPropWarnings() {
loggedTypeFailures = {};
}
/**
* Assert that the values match with the type specs.
* Error messages are memorized and will only be shown once.
*
* Adapted from https://github.com/facebook/prop-types/blob/master/checkPropTypes.js
*
* @param {object} typeSpecs Map of name to a ReactPropType
* @param {object} values Runtime values that need to be type-checked
* @param {string} location e.g. "prop", "context", "child context"
* @param {string} componentName Name of the component for error messages.
* @param {?Function} getStack Returns the component stack.
*/
export function checkPropTypes(
typeSpecs,
values,
location,
componentName,
getStack
) {
Object.keys(typeSpecs).forEach(typeSpecName => {
let error;
try {
error = typeSpecs[typeSpecName](
values,
typeSpecName,
componentName,
location,
null,
ReactPropTypesSecret
);
} catch (e) {
error = e;
}
if (error && !(error.message in loggedTypeFailures)) {
loggedTypeFailures[error.message] = true;
console.error(
`Failed ${location} type: ${error.message}${
(getStack && `\n${getStack()}`) || ''
}`
);
}
});
}

View file

@ -0,0 +1,146 @@
import { options, Fragment } from 'preact';
/**
* Get human readable name of the component/dom node
* @param {import('./internal').VNode} vnode
* @param {import('./internal').VNode} vnode
* @returns {string}
*/
export function getDisplayName(vnode) {
if (vnode.type === Fragment) {
return 'Fragment';
} else if (typeof vnode.type == 'function') {
return vnode.type.displayName || vnode.type.name;
} else if (typeof vnode.type == 'string') {
return vnode.type;
}
return '#text';
}
/**
* Used to keep track of the currently rendered `vnode` and print it
* in debug messages.
*/
let renderStack = [];
/**
* Keep track of the current owners. An owner describes a component
* which was responsible to render a specific `vnode`. This exclude
* children that are passed via `props.children`, because they belong
* to the parent owner.
*
* ```jsx
* const Foo = props => <div>{props.children}</div> // div's owner is Foo
* const Bar = props => {
* return (
* <Foo><span /></Foo> // Foo's owner is Bar, span's owner is Bar
* )
* }
* ```
*
* Note: A `vnode` may be hoisted to the root scope due to compiler
* optimiztions. In these cases the `_owner` will be different.
*/
let ownerStack = [];
/**
* Get the currently rendered `vnode`
* @returns {import('./internal').VNode | null}
*/
export function getCurrentVNode() {
return renderStack.length > 0 ? renderStack[renderStack.length - 1] : null;
}
/**
* If the user doesn't have `@babel/plugin-transform-react-jsx-source`
* somewhere in his tool chain we can't print the filename and source
* location of a component. In that case we just omit that, but we'll
* print a helpful message to the console, notifying the user of it.
*/
let showJsxSourcePluginWarning = true;
/**
* Check if a `vnode` is a possible owner.
* @param {import('./internal').VNode} vnode
*/
function isPossibleOwner(vnode) {
return typeof vnode.type == 'function' && vnode.type != Fragment;
}
/**
* Return the component stack that was captured up to this point.
* @param {import('./internal').VNode} vnode
* @returns {string}
*/
export function getOwnerStack(vnode) {
const stack = [vnode];
let next = vnode;
while (next._owner != null) {
stack.push(next._owner);
next = next._owner;
}
return stack.reduce((acc, owner) => {
acc += ` in ${getDisplayName(owner)}`;
const source = owner.__source;
if (source) {
acc += ` (at ${source.fileName}:${source.lineNumber})`;
} else if (showJsxSourcePluginWarning) {
console.warn(
'Add @babel/plugin-transform-react-jsx-source to get a more detailed component stack. Note that you should not add it to production builds of your App for bundle size reasons.'
);
}
showJsxSourcePluginWarning = false;
return (acc += '\n');
}, '');
}
/**
* Setup code to capture the component trace while rendering. Note that
* we cannot simply traverse `vnode._parent` upwards, because we have some
* debug messages for `this.setState` where the `vnode` is `undefined`.
*/
export function setupComponentStack() {
let oldDiff = options._diff;
let oldDiffed = options.diffed;
let oldRoot = options._root;
let oldVNode = options.vnode;
let oldRender = options._render;
options.diffed = vnode => {
if (isPossibleOwner(vnode)) {
ownerStack.pop();
}
renderStack.pop();
if (oldDiffed) oldDiffed(vnode);
};
options._diff = vnode => {
if (isPossibleOwner(vnode)) {
renderStack.push(vnode);
}
if (oldDiff) oldDiff(vnode);
};
options._root = (vnode, parent) => {
ownerStack = [];
if (oldRoot) oldRoot(vnode, parent);
};
options.vnode = vnode => {
vnode._owner =
ownerStack.length > 0 ? ownerStack[ownerStack.length - 1] : null;
if (oldVNode) oldVNode(vnode);
};
options._render = vnode => {
if (isPossibleOwner(vnode)) {
ownerStack.push(vnode);
}
if (oldRender) oldRender(vnode);
};
}

View file

@ -0,0 +1,3 @@
export const ELEMENT_NODE = 1;
export const DOCUMENT_NODE = 9;
export const DOCUMENT_FRAGMENT_NODE = 11;

594
static/js/lib/node_modules/preact/debug/src/debug.js generated vendored Normal file
View file

@ -0,0 +1,594 @@
import { checkPropTypes } from './check-props';
import { options, Component } from 'preact';
import {
ELEMENT_NODE,
DOCUMENT_NODE,
DOCUMENT_FRAGMENT_NODE
} from './constants';
import {
getOwnerStack,
setupComponentStack,
getCurrentVNode,
getDisplayName
} from './component-stack';
import { assign, isNaN } from './util';
const isWeakMapSupported = typeof WeakMap == 'function';
/**
* @param {import('./internal').VNode} vnode
* @returns {Array<string>}
*/
function getDomChildren(vnode) {
let domChildren = [];
if (!vnode._children) return domChildren;
vnode._children.forEach(child => {
if (child && typeof child.type === 'function') {
domChildren.push.apply(domChildren, getDomChildren(child));
} else if (child && typeof child.type === 'string') {
domChildren.push(child.type);
}
});
return domChildren;
}
/**
* @param {import('./internal').VNode} parent
* @returns {string}
*/
function getClosestDomNodeParentName(parent) {
if (!parent) return '';
if (typeof parent.type == 'function') {
if (parent._parent == null) {
if (parent._dom != null && parent._dom.parentNode != null) {
return parent._dom.parentNode.localName;
}
return '';
}
return getClosestDomNodeParentName(parent._parent);
}
return /** @type {string} */ (parent.type);
}
export function initDebug() {
setupComponentStack();
let hooksAllowed = false;
/* eslint-disable no-console */
let oldBeforeDiff = options._diff;
let oldDiffed = options.diffed;
let oldVnode = options.vnode;
let oldRender = options._render;
let oldCatchError = options._catchError;
let oldRoot = options._root;
let oldHook = options._hook;
const warnedComponents = !isWeakMapSupported
? null
: {
useEffect: new WeakMap(),
useLayoutEffect: new WeakMap(),
lazyPropTypes: new WeakMap()
};
const deprecations = [];
options._catchError = (error, vnode, oldVNode, errorInfo) => {
let component = vnode && vnode._component;
if (component && typeof error.then == 'function') {
const promise = error;
error = new Error(
`Missing Suspense. The throwing component was: ${getDisplayName(vnode)}`
);
let parent = vnode;
for (; parent; parent = parent._parent) {
if (parent._component && parent._component._childDidSuspend) {
error = promise;
break;
}
}
// We haven't recovered and we know at this point that there is no
// Suspense component higher up in the tree
if (error instanceof Error) {
throw error;
}
}
try {
errorInfo = errorInfo || {};
errorInfo.componentStack = getOwnerStack(vnode);
oldCatchError(error, vnode, oldVNode, errorInfo);
// when an error was handled by an ErrorBoundary we will nonetheless emit an error
// event on the window object. This is to make up for react compatibility in dev mode
// and thus make the Next.js dev overlay work.
if (typeof error.then != 'function') {
setTimeout(() => {
throw error;
});
}
} catch (e) {
throw e;
}
};
options._root = (vnode, parentNode) => {
if (!parentNode) {
throw new Error(
'Undefined parent passed to render(), this is the second argument.\n' +
'Check if the element is available in the DOM/has the correct id.'
);
}
let isValid;
switch (parentNode.nodeType) {
case ELEMENT_NODE:
case DOCUMENT_FRAGMENT_NODE:
case DOCUMENT_NODE:
isValid = true;
break;
default:
isValid = false;
}
if (!isValid) {
let componentName = getDisplayName(vnode);
throw new Error(
`Expected a valid HTML node as a second argument to render. Received ${parentNode} instead: render(<${componentName} />, ${parentNode});`
);
}
if (oldRoot) oldRoot(vnode, parentNode);
};
options._diff = vnode => {
let { type } = vnode;
hooksAllowed = true;
if (type === undefined) {
throw new Error(
'Undefined component passed to createElement()\n\n' +
'You likely forgot to export your component or might have mixed up default and named imports' +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
} else if (type != null && typeof type == 'object') {
if (type._children !== undefined && type._dom !== undefined) {
throw new Error(
`Invalid type passed to createElement(): ${type}\n\n` +
'Did you accidentally pass a JSX literal as JSX twice?\n\n' +
` let My${getDisplayName(vnode)} = ${serializeVNode(type)};\n` +
` let vnode = <My${getDisplayName(vnode)} />;\n\n` +
'This usually happens when you export a JSX literal and not the component.' +
`\n\n${getOwnerStack(vnode)}`
);
}
throw new Error(
'Invalid type passed to createElement(): ' +
(Array.isArray(type) ? 'array' : type)
);
}
if (
vnode.ref !== undefined &&
typeof vnode.ref != 'function' &&
typeof vnode.ref != 'object' &&
!('$$typeof' in vnode) // allow string refs when preact-compat is installed
) {
throw new Error(
`Component's "ref" property should be a function, or an object created ` +
`by createRef(), but got [${typeof vnode.ref}] instead\n` +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
}
if (typeof vnode.type == 'string') {
for (const key in vnode.props) {
if (
key[0] === 'o' &&
key[1] === 'n' &&
typeof vnode.props[key] != 'function' &&
vnode.props[key] != null
) {
throw new Error(
`Component's "${key}" property should be a function, ` +
`but got [${typeof vnode.props[key]}] instead\n` +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
}
}
}
// Check prop-types if available
if (typeof vnode.type == 'function' && vnode.type.propTypes) {
if (
vnode.type.displayName === 'Lazy' &&
warnedComponents &&
!warnedComponents.lazyPropTypes.has(vnode.type)
) {
const m =
'PropTypes are not supported on lazy(). Use propTypes on the wrapped component itself. ';
try {
const lazyVNode = vnode.type();
warnedComponents.lazyPropTypes.set(vnode.type, true);
console.warn(
m + `Component wrapped in lazy() is ${getDisplayName(lazyVNode)}`
);
} catch (promise) {
console.warn(
m + "We will log the wrapped component's name once it is loaded."
);
}
}
let values = vnode.props;
if (vnode.type._forwarded) {
values = assign({}, values);
delete values.ref;
}
checkPropTypes(
vnode.type.propTypes,
values,
'prop',
getDisplayName(vnode),
() => getOwnerStack(vnode)
);
}
if (oldBeforeDiff) oldBeforeDiff(vnode);
};
let renderCount = 0;
let currentComponent;
options._render = vnode => {
if (oldRender) {
oldRender(vnode);
}
hooksAllowed = true;
const nextComponent = vnode._component;
if (nextComponent === currentComponent) {
renderCount++;
} else {
renderCount = 1;
}
if (renderCount >= 25) {
throw new Error(
`Too many re-renders. This is limited to prevent an infinite loop ` +
`which may lock up your browser. The component causing this is: ${getDisplayName(
vnode
)}`
);
}
currentComponent = nextComponent;
};
options._hook = (comp, index, type) => {
if (!comp || !hooksAllowed) {
throw new Error('Hook can only be invoked from render methods.');
}
if (oldHook) oldHook(comp, index, type);
};
// Ideally we'd want to print a warning once per component, but we
// don't have access to the vnode that triggered it here. As a
// compromise and to avoid flooding the console with warnings we
// print each deprecation warning only once.
const warn = (property, message) => ({
get() {
const key = 'get' + property + message;
if (deprecations && deprecations.indexOf(key) < 0) {
deprecations.push(key);
console.warn(`getting vnode.${property} is deprecated, ${message}`);
}
},
set() {
const key = 'set' + property + message;
if (deprecations && deprecations.indexOf(key) < 0) {
deprecations.push(key);
console.warn(`setting vnode.${property} is not allowed, ${message}`);
}
}
});
const deprecatedAttributes = {
nodeName: warn('nodeName', 'use vnode.type'),
attributes: warn('attributes', 'use vnode.props'),
children: warn('children', 'use vnode.props.children')
};
const deprecatedProto = Object.create({}, deprecatedAttributes);
options.vnode = vnode => {
const props = vnode.props;
if (
vnode.type !== null &&
props != null &&
('__source' in props || '__self' in props)
) {
const newProps = (vnode.props = {});
for (let i in props) {
const v = props[i];
if (i === '__source') vnode.__source = v;
else if (i === '__self') vnode.__self = v;
else newProps[i] = v;
}
}
// eslint-disable-next-line
vnode.__proto__ = deprecatedProto;
if (oldVnode) oldVnode(vnode);
};
options.diffed = vnode => {
const { type, _parent: parent } = vnode;
// Check if the user passed plain objects as children. Note that we cannot
// move this check into `options.vnode` because components can receive
// children in any shape they want (e.g.
// `<MyJSONFormatter>{{ foo: 123, bar: "abc" }}</MyJSONFormatter>`).
// Putting this check in `options.diffed` ensures that
// `vnode._children` is set and that we only validate the children
// that were actually rendered.
if (vnode._children) {
vnode._children.forEach(child => {
if (typeof child === 'object' && child && child.type === undefined) {
const keys = Object.keys(child).join(',');
throw new Error(
`Objects are not valid as a child. Encountered an object with the keys {${keys}}.` +
`\n\n${getOwnerStack(vnode)}`
);
}
});
}
if (vnode._component === currentComponent) {
renderCount = 0;
}
if (
typeof type === 'string' &&
(isTableElement(type) ||
type === 'p' ||
type === 'a' ||
type === 'button')
) {
// Avoid false positives when Preact only partially rendered the
// HTML tree. Whilst we attempt to include the outer DOM in our
// validation, this wouldn't work on the server for
// `preact-render-to-string`. There we'd otherwise flood the terminal
// with false positives, which we'd like to avoid.
let domParentName = getClosestDomNodeParentName(parent);
if (domParentName !== '' && isTableElement(type)) {
if (
type === 'table' &&
// Tables can be nested inside each other if it's inside a cell.
// See https://developer.mozilla.org/en-US/docs/Learn/HTML/Tables/Advanced#nesting_tables
domParentName !== 'td' &&
isTableElement(domParentName)
) {
console.log(domParentName, parent._dom);
console.error(
'Improper nesting of table. Your <table> should not have a table-node parent.' +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
} else if (
(type === 'thead' || type === 'tfoot' || type === 'tbody') &&
domParentName !== 'table'
) {
console.error(
'Improper nesting of table. Your <thead/tbody/tfoot> should have a <table> parent.' +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
} else if (
type === 'tr' &&
domParentName !== 'thead' &&
domParentName !== 'tfoot' &&
domParentName !== 'tbody'
) {
console.error(
'Improper nesting of table. Your <tr> should have a <thead/tbody/tfoot> parent.' +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
} else if (type === 'td' && domParentName !== 'tr') {
console.error(
'Improper nesting of table. Your <td> should have a <tr> parent.' +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
} else if (type === 'th' && domParentName !== 'tr') {
console.error(
'Improper nesting of table. Your <th> should have a <tr>.' +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
}
} else if (type === 'p') {
let illegalDomChildrenTypes = getDomChildren(vnode).filter(childType =>
ILLEGAL_PARAGRAPH_CHILD_ELEMENTS.test(childType)
);
if (illegalDomChildrenTypes.length) {
console.error(
'Improper nesting of paragraph. Your <p> should not have ' +
illegalDomChildrenTypes.join(', ') +
' as child-elements.' +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
}
} else if (type === 'a' || type === 'button') {
if (getDomChildren(vnode).indexOf(type) !== -1) {
console.error(
`Improper nesting of interactive content. Your <${type}>` +
` should not have other ${type === 'a' ? 'anchor' : 'button'}` +
' tags as child-elements.' +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
}
}
}
hooksAllowed = false;
if (oldDiffed) oldDiffed(vnode);
if (vnode._children != null) {
const keys = [];
for (let i = 0; i < vnode._children.length; i++) {
const child = vnode._children[i];
if (!child || child.key == null) continue;
const key = child.key;
if (keys.indexOf(key) !== -1) {
console.error(
'Following component has two or more children with the ' +
`same key attribute: "${key}". This may cause glitches and misbehavior ` +
'in rendering process. Component: \n\n' +
serializeVNode(vnode) +
`\n\n${getOwnerStack(vnode)}`
);
// Break early to not spam the console
break;
}
keys.push(key);
}
}
if (vnode._component != null && vnode._component.__hooks != null) {
// Validate that none of the hooks in this component contain arguments that are NaN.
// This is a common mistake that can be hard to debug, so we want to catch it early.
const hooks = vnode._component.__hooks._list;
if (hooks) {
for (let i = 0; i < hooks.length; i += 1) {
const hook = hooks[i];
if (hook._args) {
for (let j = 0; j < hook._args.length; j++) {
const arg = hook._args[j];
if (isNaN(arg)) {
const componentName = getDisplayName(vnode);
console.warn(
`Invalid argument passed to hook. Hooks should not be called with NaN in the dependency array. Hook index ${i} in component ${componentName} was called with NaN.`
);
}
}
}
}
}
}
};
}
const setState = Component.prototype.setState;
Component.prototype.setState = function (update, callback) {
if (this._vnode == null) {
// `this._vnode` will be `null` during componentWillMount. But it
// is perfectly valid to call `setState` during cWM. So we
// need an additional check to verify that we are dealing with a
// call inside constructor.
if (this.state == null) {
console.warn(
`Calling "this.setState" inside the constructor of a component is a ` +
`no-op and might be a bug in your application. Instead, set ` +
`"this.state = {}" directly.\n\n${getOwnerStack(getCurrentVNode())}`
);
}
}
return setState.call(this, update, callback);
};
function isTableElement(type) {
return (
type === 'table' ||
type === 'tfoot' ||
type === 'tbody' ||
type === 'thead' ||
type === 'td' ||
type === 'tr' ||
type === 'th'
);
}
const ILLEGAL_PARAGRAPH_CHILD_ELEMENTS =
/^(address|article|aside|blockquote|details|div|dl|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|main|menu|nav|ol|p|pre|search|section|table|ul)$/;
const forceUpdate = Component.prototype.forceUpdate;
Component.prototype.forceUpdate = function (callback) {
if (this._vnode == null) {
console.warn(
`Calling "this.forceUpdate" inside the constructor of a component is a ` +
`no-op and might be a bug in your application.\n\n${getOwnerStack(
getCurrentVNode()
)}`
);
} else if (this._parentDom == null) {
console.warn(
`Can't call "this.forceUpdate" on an unmounted component. This is a no-op, ` +
`but it indicates a memory leak in your application. To fix, cancel all ` +
`subscriptions and asynchronous tasks in the componentWillUnmount method.` +
`\n\n${getOwnerStack(this._vnode)}`
);
}
return forceUpdate.call(this, callback);
};
/**
* Serialize a vnode tree to a string
* @param {import('./internal').VNode} vnode
* @returns {string}
*/
export function serializeVNode(vnode) {
let { props } = vnode;
let name = getDisplayName(vnode);
let attrs = '';
for (let prop in props) {
if (props.hasOwnProperty(prop) && prop !== 'children') {
let value = props[prop];
// If it is an object but doesn't have toString(), use Object.toString
if (typeof value == 'function') {
value = `function ${value.displayName || value.name}() {}`;
}
value =
Object(value) === value && !value.toString
? Object.prototype.toString.call(value)
: value + '';
attrs += ` ${prop}=${JSON.stringify(value)}`;
}
}
let children = props.children;
return `<${name}${attrs}${
children && children.length ? '>..</' + name + '>' : ' />'
}`;
}
options._hydrationMismatch = (newVNode, excessDomChildren) => {
const { type } = newVNode;
const availableTypes = excessDomChildren
.map(child => child && child.localName)
.filter(Boolean);
console.error(
`Expected a DOM node of type ${type} but found ${availableTypes.join(', ')}as available DOM-node(s), this is caused by the SSR'd HTML containing different DOM-nodes compared to the hydrated one.\n\n${getOwnerStack(newVNode)}`
);
};

View file

@ -0,0 +1,4 @@
/**
* Reset the history of which prop type warnings have been logged.
*/
export function resetPropWarnings(): void;

6
static/js/lib/node_modules/preact/debug/src/index.js generated vendored Normal file
View file

@ -0,0 +1,6 @@
import { initDebug } from './debug';
import 'preact/devtools';
initDebug();
export { resetPropWarnings } from './check-props';

Some files were not shown because too many files have changed in this diff Show more