Updates
This commit is contained in:
parent
d1ae498a2f
commit
57f1ecf5c3
1
.gitignore
vendored
1
.gitignore
vendored
@ -39,3 +39,4 @@ yarn-error.log*
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
dsql-schema-to-typedef.json
|
||||
.data
|
||||
@ -24,6 +24,7 @@ export type TinyMCEEditorProps<KeyType extends string> = {
|
||||
showLabel?: boolean;
|
||||
useParentCSS?: boolean;
|
||||
placeholder?: string;
|
||||
refreshDependencyArray?: any[];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -32,7 +33,7 @@ export type TinyMCEEditorProps<KeyType extends string> = {
|
||||
*/
|
||||
export default function TinyMCEEditor<KeyType extends string>({
|
||||
options,
|
||||
editorRef,
|
||||
editorRef: passedEditorRef,
|
||||
setEditor: passedSetEditor,
|
||||
wrapperProps,
|
||||
defaultValue,
|
||||
@ -43,10 +44,12 @@ export default function TinyMCEEditor<KeyType extends string>({
|
||||
showLabel,
|
||||
useParentCSS,
|
||||
placeholder,
|
||||
refreshDependencyArray,
|
||||
}: TinyMCEEditorProps<KeyType>) {
|
||||
const { tinyMCE } = useTinyMCE();
|
||||
|
||||
const editorComponentRef = React.useRef<HTMLDivElement>(null);
|
||||
const editorRef = passedEditorRef || React.useRef<Editor>(null);
|
||||
|
||||
const EDITOR_VALUE_CHANGE_TIMEOUT = 500;
|
||||
|
||||
@ -97,7 +100,10 @@ export default function TinyMCEEditor<KeyType extends string>({
|
||||
"body { font-family:Helvetica,Arial,sans-serif; font-size:14px; background-color: transparent }",
|
||||
init_instance_callback: (editor) => {
|
||||
setEditor(editor as any);
|
||||
if (editorRef) editorRef.current = editor as any;
|
||||
if (editorRef) {
|
||||
editorRef.current = editor;
|
||||
passedSetEditor?.(editor);
|
||||
}
|
||||
if (defaultValue) editor.setContent(defaultValue);
|
||||
setReady(true);
|
||||
|
||||
@ -140,7 +146,15 @@ export default function TinyMCEEditor<KeyType extends string>({
|
||||
: undefined;
|
||||
instance?.remove();
|
||||
};
|
||||
}, [tinyMCE, themeReady, refresh]);
|
||||
}, [tinyMCE, themeReady, refresh, ...(refreshDependencyArray || [])]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const instance = editorRef.current;
|
||||
|
||||
if (instance) {
|
||||
instance.setContent(defaultValue || "");
|
||||
}
|
||||
}, [defaultValue]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -148,7 +162,7 @@ export default function TinyMCEEditor<KeyType extends string>({
|
||||
className={twMerge(
|
||||
"relative w-full [&_.tox-tinymce]:!border-none",
|
||||
"bg-background-light dark:bg-background-dark",
|
||||
wrapperWrapperProps?.className
|
||||
wrapperWrapperProps?.className,
|
||||
)}
|
||||
onInput={(e) => {
|
||||
console.log(`Input Detected`);
|
||||
@ -159,7 +173,7 @@ export default function TinyMCEEditor<KeyType extends string>({
|
||||
className={twMerge(
|
||||
"absolute z-10 -top-[7px] left-[10px] px-2 text-xs",
|
||||
"bg-background-light dark:bg-background-dark text-gray-500",
|
||||
"dark:text-white/80 rounded"
|
||||
"dark:text-white/80 rounded",
|
||||
)}
|
||||
htmlFor={id}
|
||||
>
|
||||
@ -170,7 +184,7 @@ export default function TinyMCEEditor<KeyType extends string>({
|
||||
{...borderProps}
|
||||
className={twMerge(
|
||||
"dark:border-white/30 p-0 pt-2",
|
||||
borderProps?.className
|
||||
borderProps?.className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@ -183,7 +197,7 @@ export default function TinyMCEEditor<KeyType extends string>({
|
||||
}}
|
||||
className={twMerge(
|
||||
"bg-slate-200 dark:bg-slate-700 rounded-sm w-full",
|
||||
"twui-rte-wrapper"
|
||||
"twui-rte-wrapper",
|
||||
)}
|
||||
id={id}
|
||||
></div>
|
||||
|
||||
@ -3,6 +3,7 @@ import React from "react";
|
||||
type Params = {
|
||||
initialLoading?: boolean;
|
||||
initialReady?: boolean;
|
||||
initialOpen?: boolean;
|
||||
};
|
||||
|
||||
export type UseStatusStatusType = {
|
||||
@ -13,10 +14,11 @@ export type UseStatusStatusType = {
|
||||
export default function useStatus(params?: Params) {
|
||||
const [refresh, setRefresh] = React.useState(0);
|
||||
const [loading, setLoading] = React.useState(
|
||||
params?.initialLoading || false
|
||||
params?.initialLoading || false,
|
||||
);
|
||||
const [status, setStatus] = React.useState<UseStatusStatusType>({});
|
||||
const [ready, setReady] = React.useState(params?.initialReady || false);
|
||||
const [open, setOpen] = React.useState(params?.initialOpen || false);
|
||||
|
||||
return {
|
||||
refresh,
|
||||
@ -27,5 +29,7 @@ export default function useStatus(params?: Params) {
|
||||
setStatus,
|
||||
ready,
|
||||
setReady,
|
||||
open,
|
||||
setOpen,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {
|
||||
import React, {
|
||||
AnchorHTMLAttributes,
|
||||
ButtonHTMLAttributes,
|
||||
ComponentProps,
|
||||
@ -35,8 +35,8 @@ export type TWUIButtonProps = DetailedHTMLProps<
|
||||
AnchorHTMLAttributes<HTMLAnchorElement>,
|
||||
HTMLAnchorElement
|
||||
>;
|
||||
beforeIcon?: TWUILucideIconName | JSX.Element;
|
||||
afterIcon?: TWUILucideIconName | JSX.Element;
|
||||
beforeIcon?: TWUILucideIconName | React.JSX.Element;
|
||||
afterIcon?: TWUILucideIconName | React.JSX.Element;
|
||||
buttonContentProps?: DetailedHTMLProps<
|
||||
HTMLAttributes<HTMLDivElement>,
|
||||
HTMLDivElement
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { ComponentProps } from "react";
|
||||
import React, { ComponentProps, useRef } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import MarkdownEditorPreviewComponent from "./MarkdownEditorPreviewComponent";
|
||||
import MarkdownEditorComponent from "./MarkdownEditorComponent";
|
||||
@ -6,6 +6,7 @@ import MarkdownEditorSelectorButtons from "./MarkdownEditorSelectorButtons";
|
||||
import Row from "../../layout/Row";
|
||||
import Stack from "../../layout/Stack";
|
||||
import AceEditor from "../../editors/AceEditor";
|
||||
import useStatus from "../../hooks/useStatus";
|
||||
|
||||
type Props = {
|
||||
value?: string;
|
||||
@ -29,17 +30,30 @@ export default function MarkdownEditor({
|
||||
const [value, setValue] = React.useState<any>(existingValue || ``);
|
||||
|
||||
const [sideBySide, setSideBySide] = React.useState(
|
||||
defaultSideBySide || false
|
||||
defaultSideBySide || false,
|
||||
);
|
||||
const [preview, setPreview] = React.useState(false);
|
||||
const { refresh, setRefresh } = useStatus();
|
||||
const updatingFromExtValueRef = useRef(false);
|
||||
|
||||
const maxHeight = existingMaxHeight || "600px";
|
||||
|
||||
React.useEffect(() => {
|
||||
if (updatingFromExtValueRef.current) return;
|
||||
setExistingValue?.(value);
|
||||
changeHandler?.(value);
|
||||
}, [value]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!existingValue) return;
|
||||
updatingFromExtValueRef.current = true;
|
||||
setValue(existingValue);
|
||||
setTimeout(() => {
|
||||
updatingFromExtValueRef.current = false;
|
||||
setRefresh((prev) => prev + 1);
|
||||
}, 500);
|
||||
}, [existingValue]);
|
||||
|
||||
return (
|
||||
<Stack className="w-full items-stretch">
|
||||
{!noToggleButtons && (
|
||||
@ -52,13 +66,14 @@ export default function MarkdownEditor({
|
||||
<Row
|
||||
className={twMerge(
|
||||
`w-full grid xl:grid-cols-2 gap-6 max-h-[${maxHeight}]`,
|
||||
"overflow-auto"
|
||||
"overflow-auto",
|
||||
)}
|
||||
>
|
||||
<MarkdownEditorComponent
|
||||
setValue={setValue}
|
||||
value={value}
|
||||
maxHeight={maxHeight}
|
||||
refreshDepArr={[refresh]}
|
||||
{...editorProps}
|
||||
/>
|
||||
<MarkdownEditorPreviewComponent
|
||||
@ -80,6 +95,7 @@ export default function MarkdownEditor({
|
||||
setValue={setValue}
|
||||
value={value}
|
||||
maxHeight={maxHeight}
|
||||
refreshDepArr={[refresh]}
|
||||
{...editorProps}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -25,11 +25,7 @@ export const work = {
|
||||
description: "Mortgage Broker in Utah",
|
||||
href: "https://summitlending.com",
|
||||
image: "/images/work/devops/server-management.png",
|
||||
metrics: [
|
||||
"500+ leads/month",
|
||||
"~10 deploys/week",
|
||||
"99.99% uptime",
|
||||
],
|
||||
metrics: ["500+ leads/month", "99.99% uptime"],
|
||||
technologies: [
|
||||
"Next JS",
|
||||
"Tailwind CSS",
|
||||
@ -142,6 +138,13 @@ export const work = {
|
||||
"Azure",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Bun SQLite",
|
||||
description:
|
||||
"A schema-driven SQLite manager for Bun, featuring automatic schema synchronization, type-safe CRUD operations, vector embedding support, and TypeScript type definition generation.",
|
||||
href: "https://git.tben.me/Moduletrace/bun-sqlite",
|
||||
technologies: ["SQLite", "Bun", "Shell", "Typescript"],
|
||||
},
|
||||
{
|
||||
title: "Turbo Sync",
|
||||
description:
|
||||
|
||||
159
scripts/substack/save-all-notes.ts
Normal file
159
scripts/substack/save-all-notes.ts
Normal file
@ -0,0 +1,159 @@
|
||||
// @ts-ignore
|
||||
const articles_objects = [];
|
||||
|
||||
const substack_href = "https://substack.com/@benjamintoby";
|
||||
|
||||
// @ts-ignore
|
||||
async function sleep(wait) {
|
||||
return new Promise((res) => {
|
||||
setTimeout(() => {
|
||||
res(true);
|
||||
}, wait);
|
||||
});
|
||||
}
|
||||
|
||||
function grabContentHeight() {
|
||||
return document.querySelector(
|
||||
"div[style='max-width: 568px;']",
|
||||
// @ts-ignore
|
||||
)?.offsetHeight;
|
||||
}
|
||||
|
||||
async function scrollToEnd() {
|
||||
let last_content_height = grabContentHeight();
|
||||
|
||||
while (true) {
|
||||
window.scrollTo({
|
||||
top: document.body.scrollHeight,
|
||||
behavior: "smooth",
|
||||
});
|
||||
|
||||
await sleep(5000);
|
||||
|
||||
const current_content_height = grabContentHeight();
|
||||
|
||||
if (current_content_height > last_content_height) {
|
||||
last_content_height = current_content_height;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await scrollToEnd();
|
||||
|
||||
const articles = Array.from(
|
||||
document.querySelectorAll("div[role='article']"),
|
||||
);
|
||||
|
||||
console.log(`Handling ${articles.length} Articles ...`);
|
||||
|
||||
for (let i = 0; i < articles.length; i++) {
|
||||
let present_articles = Array.from(
|
||||
document.querySelectorAll("div[role='article']"),
|
||||
);
|
||||
|
||||
console.log(`Found ${present_articles.length} Present Articles!`);
|
||||
|
||||
while (i > present_articles.length - 1) {
|
||||
window.scrollTo({
|
||||
top: document.body.scrollHeight,
|
||||
behavior: "smooth",
|
||||
});
|
||||
|
||||
console.log(`Searching for Article #${i} ...`);
|
||||
|
||||
await sleep(5000);
|
||||
|
||||
present_articles = Array.from(
|
||||
document.querySelectorAll("div[role='article']"),
|
||||
);
|
||||
}
|
||||
|
||||
const article = present_articles[i];
|
||||
|
||||
console.log(`Handling Article #${i} ...`);
|
||||
|
||||
const content_div = article.querySelector(`.FeedProseMirror`);
|
||||
const date_link = Array.from(article.querySelectorAll("a")).find((a) =>
|
||||
Boolean(a.getAttribute("title")),
|
||||
);
|
||||
|
||||
if (!content_div) continue;
|
||||
|
||||
const content_div_first_paragraph = content_div.querySelector(`p`);
|
||||
|
||||
if (!content_div_first_paragraph) continue;
|
||||
|
||||
let window_url = window.location.href;
|
||||
const initial_text_content = content_div.textContent;
|
||||
|
||||
const article_object = {
|
||||
title: initial_text_content,
|
||||
content: initial_text_content,
|
||||
html: content_div.innerHTML,
|
||||
images: [],
|
||||
date: date_link?.getAttribute("title"),
|
||||
};
|
||||
|
||||
const article_images = Array.from(
|
||||
article.querySelectorAll("picture img"),
|
||||
);
|
||||
|
||||
if (article_images?.[0]) {
|
||||
for (let img = 0; img < article_images.length; img++) {
|
||||
if (img > 0) {
|
||||
const article_image = article_images[img];
|
||||
// @ts-ignore
|
||||
const article_image_srcset = article_image.srcset;
|
||||
const largest_image = article_image_srcset
|
||||
.split(` `)
|
||||
.at(-2);
|
||||
// @ts-ignore
|
||||
article_object.images.push(largest_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const more_content = Array.from(article.querySelectorAll("a"))
|
||||
.find((el) => el.textContent.includes("See more"))
|
||||
?.click();
|
||||
|
||||
await sleep(2000);
|
||||
|
||||
let new_window_url = window.location.href;
|
||||
|
||||
if (new_window_url === window_url) {
|
||||
const new_article_content =
|
||||
article.querySelector(`.FeedProseMirror`);
|
||||
|
||||
if (new_article_content) {
|
||||
article_object.content = new_article_content.textContent;
|
||||
article_object.html = new_article_content.innerHTML;
|
||||
}
|
||||
|
||||
articles_objects.push(article_object);
|
||||
} else {
|
||||
const text_sample = content_div_first_paragraph.textContent;
|
||||
const target_content_div = Array.from(
|
||||
document.querySelectorAll(".ProseMirror.FeedProseMirror"),
|
||||
).find((el) => el.textContent.includes(text_sample));
|
||||
|
||||
if (target_content_div) {
|
||||
article_object.content = target_content_div.textContent;
|
||||
article_object.html = target_content_div.innerHTML;
|
||||
}
|
||||
|
||||
articles_objects.push(article_object);
|
||||
|
||||
window.history.back();
|
||||
await sleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
console.log(articles_objects);
|
||||
}
|
||||
|
||||
main();
|
||||
Loading…
Reference in New Issue
Block a user