feat: 💄 node+ improvements
- Mardown mode (now default as it support most html too) - Added a CSS editor - "Purify" input to avoid script embedding in notes...
This commit is contained in:
@@ -9,6 +9,36 @@
|
||||
|
||||
import { app } from '../../scripts/app.js'
|
||||
|
||||
// - crude uuid
|
||||
export function makeUUID() {
|
||||
let dt = new Date().getTime()
|
||||
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = (dt + Math.random() * 16) % 16 | 0
|
||||
dt = Math.floor(dt / 16)
|
||||
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)
|
||||
})
|
||||
return uuid
|
||||
}
|
||||
|
||||
// - log utilities
|
||||
|
||||
function createLogger(emoji, color, consoleMethod = 'log') {
|
||||
return function (message, ...args) {
|
||||
if (window.MTB?.DEBUG) {
|
||||
console[consoleMethod](
|
||||
`%c${emoji} ${message}`,
|
||||
`color: ${color};`,
|
||||
...args
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const infoLogger = createLogger('ℹ️', 'yellow')
|
||||
export const warnLogger = createLogger('⚠️', 'orange', 'warn')
|
||||
export const errorLogger = createLogger('🔥', 'red', 'error')
|
||||
export const successLogger = createLogger('✅', 'green')
|
||||
|
||||
export const log = (...args) => {
|
||||
if (window.MTB?.DEBUG) {
|
||||
console.debug(...args)
|
||||
@@ -159,6 +189,25 @@ export const dynamic_connection = (
|
||||
}
|
||||
}
|
||||
|
||||
export function calculateTotalChildrenHeight(parentElement) {
|
||||
let totalHeight = 0
|
||||
|
||||
for (const child of parentElement.children) {
|
||||
const style = window.getComputedStyle(child)
|
||||
|
||||
// Get height as an integer (without 'px')
|
||||
const height = parseInt(style.height, 10)
|
||||
|
||||
// Get vertical margin as integers
|
||||
const marginTop = parseInt(style.marginTop, 10)
|
||||
const marginBottom = parseInt(style.marginBottom, 10)
|
||||
|
||||
// Sum up height and vertical margins
|
||||
totalHeight += height + marginTop + marginBottom
|
||||
}
|
||||
|
||||
return totalHeight
|
||||
}
|
||||
/**
|
||||
* Appends a callback to the extra menu options of a given node type.
|
||||
* @param {*} nodeType
|
||||
|
||||
Vendored
+3
File diff suppressed because one or more lines are too long
Vendored
+3
File diff suppressed because one or more lines are too long
+535
-111
@@ -1,181 +1,605 @@
|
||||
import { app } from '../../scripts/app.js'
|
||||
import { api } from '../../scripts/api.js'
|
||||
import * as shared from './comfy_shared.js'
|
||||
import {
|
||||
infoLogger,
|
||||
warnLogger,
|
||||
successLogger,
|
||||
errorLogger,
|
||||
} from './comfy_shared.js'
|
||||
|
||||
const DEFAULT_CSS = ``
|
||||
const DEFAULT_HTML = `<p style='color:red;font-family:monospace'>
|
||||
Note+
|
||||
</p>`
|
||||
const DEFAULT_MD = '## Note+'
|
||||
const DEFAULT_MODE = 'markdown'
|
||||
|
||||
const CSS_RESET = `
|
||||
* {
|
||||
font-family: monospace;
|
||||
line-height: 1.25em;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
p, ul, ol, dl, blockquote {
|
||||
margin: 0.3em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
ul, ol {
|
||||
|
||||
padding-left: 1em;
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
pointer-events: all;
|
||||
color: cyan;
|
||||
}
|
||||
|
||||
img {
|
||||
padding: 1em 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border:none;
|
||||
pointer-events:all;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid #ccc;
|
||||
padding-left: 1em;
|
||||
margin-left: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #000;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border-left: 1px solid #000;
|
||||
border-right: 1px solid #000;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
border: 1px solid #000;
|
||||
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
`
|
||||
class NotePlus extends LiteGraph.LGraphNode {
|
||||
title = 'Note+ (mtb)'
|
||||
category = 'mtb/utils'
|
||||
|
||||
// same values as the comfy note
|
||||
color = LGraphCanvas.node_colors.yellow.color
|
||||
bgcolor = LGraphCanvas.node_colors.yellow.bgcolor
|
||||
groupcolor = LGraphCanvas.node_colors.yellow.groupcolor
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.uuid = shared.makeUUID()
|
||||
|
||||
infoLogger(`Constructing Note+ instance`)
|
||||
// - litegraph settings
|
||||
this.collapsable = true
|
||||
this.isVirtualNode = true
|
||||
this.shape = LiteGraph.BOX_SHAPE
|
||||
this.serialize_widgets = true
|
||||
|
||||
this.editing = false
|
||||
// - default values, serialization is done through widgets
|
||||
this._raw_html = DEFAULT_MODE == 'html' ? DEFAULT_HTML : DEFAULT_MD
|
||||
|
||||
// - mardown converter
|
||||
this.markdownConverter = new showdown.Converter({
|
||||
tables: true,
|
||||
strikethrough: true,
|
||||
emoji: true,
|
||||
ghCodeBlocks: true,
|
||||
tasklists: true,
|
||||
ghMentions: true,
|
||||
smoothLivePreview: true,
|
||||
simplifiedAutoLink: true,
|
||||
parseImgDimensions: true,
|
||||
openLinksInNewWindow: true,
|
||||
})
|
||||
|
||||
// - state
|
||||
this.live = true
|
||||
this.rawVal = "<p style='color:red;font-family:monospace'\n> Note+\n</p>"
|
||||
|
||||
this.calculated_height = 36
|
||||
this.calculated_height = 0
|
||||
|
||||
// - add widgets
|
||||
const inner = document.createElement('div')
|
||||
inner.style.margin = '0'
|
||||
inner.style.padding = '0'
|
||||
inner.style.pointerEvents = 'none'
|
||||
this.html_widget = this.addDOMWidget('HTML', 'html', inner, {
|
||||
setValue: (v) => {
|
||||
// update our widget preview
|
||||
this.html_widget.element.innerHTML = v
|
||||
// calculate height
|
||||
this.calculated_height = this.html_widget.element.scrollHeight + 36
|
||||
setValue: (val) => {
|
||||
this._raw_html = val
|
||||
},
|
||||
getValue: () => this.rawVal,
|
||||
getValue: () => this._raw_html,
|
||||
getMinHeight: () => this.calculated_height, // (the edit button),
|
||||
hideOnZoom: false,
|
||||
})
|
||||
|
||||
// console.log(`Value of HTML: ${this.html_widget.value}`)
|
||||
this.html_widget.element.innerHTML = this.html_widget.value
|
||||
this.setupSerializationWidgets()
|
||||
this.setupDialog()
|
||||
this.loadAceEditor()
|
||||
}
|
||||
|
||||
//- ace based editor
|
||||
this.addWidget('button', 'Edit', 'Edit', () => {
|
||||
const container = document.createElement('div')
|
||||
Object.assign(container.style, {
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
})
|
||||
/**
|
||||
*
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
* @param {LGraphCanvas} graphcanvas
|
||||
* @returns
|
||||
*/
|
||||
|
||||
dialog.show('')
|
||||
dialog.textElement.append(container)
|
||||
onDrawForeground(ctx, graphcanvas) {
|
||||
if (this.flags.collapsed) return
|
||||
|
||||
const value = document.createElement('div')
|
||||
value.id = 'noteplus-editor'
|
||||
Object.assign(value.style, {
|
||||
width: '300px',
|
||||
height: '200px',
|
||||
backgroundColor: 'rgb(30,30,30)',
|
||||
color: 'whitesmoke',
|
||||
})
|
||||
// Define the size and position of the icon
|
||||
const iconSize = 14 // Size of the icon
|
||||
const iconMargin = 8 // Margin from the edges
|
||||
const x = this.size[0] - iconSize - iconMargin
|
||||
const y = iconMargin * 1.5
|
||||
|
||||
container.append(value)
|
||||
// Create a new Path2D object from SVG path data
|
||||
const pencilPath = new Path2D(
|
||||
'M21.28 6.4l-9.54 9.54c-.95.95-3.77 1.39-4.4.76-.63-.63-.2-3.45.75-4.4l9.55-9.55a2.58 2.58 0 1 1 3.64 3.65z'
|
||||
)
|
||||
const folderPath = new Path2D(
|
||||
'M11 4H6a4 4 0 0 0-4 4v10a4 4 0 0 0 4 4h11c2.21 0 3-1.8 3-4v-5'
|
||||
)
|
||||
|
||||
const live_edit = document.createElement('input')
|
||||
live_edit.type = 'checkbox'
|
||||
live_edit.checked = this.live
|
||||
live_edit.onchange = () => {
|
||||
this.live = live_edit.checked
|
||||
// Draw the paths
|
||||
ctx.save()
|
||||
ctx.translate(x, y) // Position the icon on the canvas
|
||||
ctx.scale(iconSize / 32, iconSize / 32) // Scale the icon to the desired size
|
||||
ctx.strokeStyle = 'rgba(255,255,255,0.3)'
|
||||
|
||||
ctx.lineCap = 'round'
|
||||
ctx.lineJoin = 'round'
|
||||
|
||||
ctx.lineWidth = 2.4
|
||||
ctx.stroke(pencilPath)
|
||||
ctx.stroke(folderPath)
|
||||
ctx.restore()
|
||||
}
|
||||
onMouseDown(e, localPos, graphcanvas) {
|
||||
// Check if the click is within the pencil icon bounds
|
||||
const iconSize = 14
|
||||
const iconMargin = 8
|
||||
const iconX = this.size[0] - iconSize - iconMargin
|
||||
const iconY = iconMargin * 1.5
|
||||
|
||||
if (
|
||||
localPos[0] > iconX &&
|
||||
localPos[0] < iconX + iconSize &&
|
||||
localPos[1] > iconY &&
|
||||
localPos[1] < iconY + iconSize
|
||||
) {
|
||||
// Pencil icon was clicked, open the editor
|
||||
this.openEditorDialog()
|
||||
return true // Return true to indicate the event was handled
|
||||
}
|
||||
|
||||
return false // Return false to let the event propagate
|
||||
}
|
||||
|
||||
setupSerializationWidgets() {
|
||||
infoLogger(`Setup Serializing widgets`)
|
||||
|
||||
this.edit_mode_widget = this.addWidget(
|
||||
'combo',
|
||||
'Mode',
|
||||
DEFAULT_MODE,
|
||||
(me) => successLogger(`Updating edit_mode`, me),
|
||||
{
|
||||
values: ['html', 'markdown', 'raw'],
|
||||
}
|
||||
)
|
||||
|
||||
const live_edit_label = document.createElement('label')
|
||||
live_edit_label.textContent = 'Live Edit'
|
||||
live_edit_label.append(live_edit)
|
||||
|
||||
value.after(live_edit_label)
|
||||
|
||||
this.setupEditor()
|
||||
this.editor.setValue(this.html_widget.element.innerHTML)
|
||||
this.css_widget = this.addWidget('text', 'CSS', DEFAULT_CSS, (val) => {
|
||||
successLogger(`Updating css ${val}`)
|
||||
})
|
||||
|
||||
const dialog = new app.ui.dialog.constructor()
|
||||
dialog.element.classList.add('comfy-settings')
|
||||
shared.hideWidgetForGood(this, this.edit_mode_widget)
|
||||
shared.hideWidgetForGood(this, this.css_widget)
|
||||
}
|
||||
setupDialog() {
|
||||
infoLogger(`Setup dialog`)
|
||||
// this.addWidget('button', 'Edit', 'Edit', this.openEditorDialog.bind(this))
|
||||
|
||||
const closeButton = dialog.element.querySelector('button')
|
||||
this.dialog = new app.ui.dialog.constructor()
|
||||
this.dialog.element.classList.add('comfy-settings')
|
||||
|
||||
const closeButton = this.dialog.element.querySelector('button')
|
||||
closeButton.textContent = 'CANCEL'
|
||||
const saveButton = document.createElement('button')
|
||||
saveButton.textContent = 'SAVE'
|
||||
saveButton.onclick = () => {
|
||||
this.updateHTML(this.editor.getValue())
|
||||
this.closeEditorDialog(true)
|
||||
}
|
||||
closeButton.onclick = () => {
|
||||
this.closeEditorDialog(false)
|
||||
}
|
||||
closeButton.before(saveButton)
|
||||
}
|
||||
|
||||
this.editor.destroy()
|
||||
this.editor.container.remove()
|
||||
teardownEditors() {
|
||||
this.css_editor.destroy()
|
||||
this.css_editor.container.remove()
|
||||
|
||||
dialog.close()
|
||||
this.html_editor.destroy()
|
||||
this.html_editor.container.remove()
|
||||
}
|
||||
|
||||
closeEditorDialog(accept) {
|
||||
infoLogger(`Closing editor dialog`, accept)
|
||||
if (accept) {
|
||||
this.updateHTML(this.html_editor.getValue())
|
||||
this.updateCSS(this.css_editor.getValue())
|
||||
}
|
||||
this.teardownEditors()
|
||||
this.dialog.close()
|
||||
}
|
||||
|
||||
openEditorDialog() {
|
||||
infoLogger(`Current edit mode ${this.edit_mode_widget.value}`)
|
||||
const container = document.createElement('div')
|
||||
|
||||
Object.assign(container.style, {
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
flexDirection: 'column',
|
||||
})
|
||||
|
||||
const editorsContainer = document.createElement('div')
|
||||
Object.assign(editorsContainer.style, {
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
flexDirection: 'row',
|
||||
})
|
||||
|
||||
container.append(editorsContainer)
|
||||
|
||||
this.dialog.show('')
|
||||
this.dialog.textElement.append(container)
|
||||
|
||||
const aceHTML = document.createElement('div')
|
||||
aceHTML.id = 'noteplus-html-editor'
|
||||
Object.assign(aceHTML.style, {
|
||||
width: '300px',
|
||||
height: '300px',
|
||||
backgroundColor: 'rgb(30,30,30)',
|
||||
color: 'whitesmoke',
|
||||
})
|
||||
|
||||
editorsContainer.append(aceHTML)
|
||||
|
||||
const aceCSS = document.createElement('div')
|
||||
aceCSS.id = 'noteplus-css-editor'
|
||||
Object.assign(aceCSS.style, {
|
||||
width: '300px',
|
||||
height: '300px',
|
||||
backgroundColor: 'rgb(30,30,30)',
|
||||
color: 'whitesmoke',
|
||||
})
|
||||
|
||||
editorsContainer.append(aceCSS)
|
||||
|
||||
const live_edit = document.createElement('input')
|
||||
live_edit.type = 'checkbox'
|
||||
live_edit.checked = this.live
|
||||
live_edit.onchange = () => {
|
||||
this.live = live_edit.checked
|
||||
}
|
||||
|
||||
closeButton.before(saveButton)
|
||||
//- "Dynamic" elements
|
||||
const firstButton = this.dialog.element.querySelector('button')
|
||||
const syncUI = () => {
|
||||
let convert_to_html =
|
||||
this.dialog.element.querySelector('#convert-to-html')
|
||||
if (this.edit_mode_widget.value == 'markdown') {
|
||||
if (convert_to_html == null) {
|
||||
convert_to_html = document.createElement('button')
|
||||
convert_to_html.textContent = 'Convert to HTML (NO UNDO!)'
|
||||
convert_to_html.id = 'convert-to-html'
|
||||
convert_to_html.onclick = () => {
|
||||
let select_mode = this.dialog.element.querySelector('#edit_mode')
|
||||
|
||||
let md = this.html_editor.getValue()
|
||||
this.edit_mode_widget.value = 'html'
|
||||
select_mode.value = 'html'
|
||||
const html = this.markdownConverter.makeHtml(md)
|
||||
this.html_widget.value = html
|
||||
this.html_editor.setValue(html)
|
||||
this.html_editor.session.setMode('ace/mode/html')
|
||||
this.updateHTML(this.html_widget.value)
|
||||
|
||||
convert_to_html.remove()
|
||||
}
|
||||
|
||||
firstButton.before(convert_to_html)
|
||||
}
|
||||
} else {
|
||||
if (convert_to_html != null) {
|
||||
convert_to_html.remove()
|
||||
convert_to_html = null
|
||||
}
|
||||
}
|
||||
select_mode.value = this.edit_mode_widget.value
|
||||
}
|
||||
//- combobox
|
||||
let select_mode = this.dialog.element.querySelector('#edit_mode')
|
||||
|
||||
if (!select_mode) {
|
||||
infoLogger(`Creating combobox for select`)
|
||||
select_mode = document.createElement('select')
|
||||
select_mode.name = 'mode'
|
||||
select_mode.id = 'edit_mode'
|
||||
|
||||
const addOption = (label) => {
|
||||
const option = document.createElement('option')
|
||||
option.value = label
|
||||
option.textContent = label
|
||||
select_mode.append(option)
|
||||
}
|
||||
addOption('markdown')
|
||||
addOption('html')
|
||||
|
||||
select_mode.addEventListener('change', (event) => {
|
||||
const val = event.target.value
|
||||
this.edit_mode_widget.value = val
|
||||
if (this.html_editor) {
|
||||
this.html_editor.session.setMode(`ace/mode/${val}`)
|
||||
this.updateHTML(this.html_editor.getValue())
|
||||
|
||||
syncUI()
|
||||
}
|
||||
})
|
||||
|
||||
// firstButton.before(select_mode)
|
||||
container.append(select_mode)
|
||||
}
|
||||
select_mode.value = this.edit_mode_widget.value
|
||||
|
||||
syncUI()
|
||||
|
||||
const live_edit_label = document.createElement('label')
|
||||
live_edit_label.textContent = 'Live Edit'
|
||||
|
||||
// add a tooltip
|
||||
live_edit_label.title =
|
||||
'When this is on, the editor will update the note+ whenever you change the text.'
|
||||
|
||||
live_edit_label.append(live_edit)
|
||||
|
||||
// select_mode.before(live_edit_label)
|
||||
container.append(live_edit_label)
|
||||
|
||||
this.setupEditors()
|
||||
}
|
||||
loadAceEditor() {
|
||||
shared
|
||||
.loadScript(
|
||||
'https://cdn.jsdelivr.net/npm/ace-builds@1.16.0/src-min-noconflict/ace.min.js'
|
||||
)
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
errorLogger(e)
|
||||
})
|
||||
}
|
||||
onCreate() {
|
||||
errorLogger('NotePlus onCreate')
|
||||
}
|
||||
configure(info) {
|
||||
super.configure(info)
|
||||
infoLogger('Restoring serialized values', info)
|
||||
// - update view from serialzed data
|
||||
this.html_widget.element.id = `note-plus-${this.uuid}`
|
||||
this.setMode(this.edit_mode_widget.value)
|
||||
this.updateHTML(this.html_widget.value)
|
||||
this.updateCSS(this.css_widget.value)
|
||||
this.setSize(info.size)
|
||||
}
|
||||
onNodeCreated() {
|
||||
infoLogger('Node created', this.uuid)
|
||||
this.html_widget.element.id = `note-plus-${this.uuid}`
|
||||
this.setMode(this.edit_mode_widget.value)
|
||||
this.updateHTML(this.html_widget.value) // widget is populated here since we called super
|
||||
this.updateCSS(this.css_widget.value)
|
||||
}
|
||||
onRemoved() {
|
||||
infoLogger('Node removed', this.uuid)
|
||||
}
|
||||
getExtraMenuOptions() {
|
||||
var options = []
|
||||
// {
|
||||
// content: string;
|
||||
// callback?: ContextMenuEventListener;
|
||||
// /** Used as innerHTML for extra child element */
|
||||
// title?: string;
|
||||
// disabled?: boolean;
|
||||
// has_submenu?: boolean;
|
||||
// submenu?: {
|
||||
// options: ContextMenuItem[];
|
||||
// } & IContextMenuOptions;
|
||||
// className?: string;
|
||||
// }
|
||||
options.push({
|
||||
content: `Set to ${
|
||||
this.edit_mode_widget.value === 'html' ? 'markdown' : 'html'
|
||||
}`,
|
||||
callback: () => {
|
||||
this.edit_mode_widget.value =
|
||||
this.edit_mode_widget.value === 'html' ? 'markdown' : 'html'
|
||||
this.updateHTML(this.html_widget.value)
|
||||
},
|
||||
})
|
||||
|
||||
setupEditor() {
|
||||
this.editor = ace.edit('noteplus-editor')
|
||||
this.editor.setTheme('ace/theme/dracula')
|
||||
this.editor.session.setMode('ace/mode/html')
|
||||
return options
|
||||
}
|
||||
|
||||
this.editor.setShowPrintMargin(false)
|
||||
this.editor.session.setUseWrapMode(true)
|
||||
this.editor.renderer.setShowGutter(false)
|
||||
this.editor.session.setTabSize(4)
|
||||
this.editor.session.setUseSoftTabs(true)
|
||||
this.editor.setFontSize(14)
|
||||
this.editor.setReadOnly(false)
|
||||
this.editor.setHighlightActiveLine(false)
|
||||
this.editor.setShowFoldWidgets(true)
|
||||
_setupEditor(editor) {
|
||||
editor.setTheme('ace/theme/dracula')
|
||||
|
||||
this.editor.session.on('change', (delta) => {
|
||||
editor.setShowPrintMargin(false)
|
||||
editor.session.setUseWrapMode(true)
|
||||
editor.renderer.setShowGutter(false)
|
||||
editor.session.setTabSize(4)
|
||||
editor.session.setUseSoftTabs(true)
|
||||
editor.setFontSize(14)
|
||||
editor.setReadOnly(false)
|
||||
editor.setHighlightActiveLine(false)
|
||||
editor.setShowFoldWidgets(true)
|
||||
return editor
|
||||
}
|
||||
|
||||
setMode(mode) {
|
||||
this.edit_mode_widget.value = mode
|
||||
if (this.html_editor) {
|
||||
this.html_editor.session.setMode(`ace/mode/${mode}`)
|
||||
}
|
||||
|
||||
this.updateHTML(this.html_widget.value)
|
||||
}
|
||||
setupEditors() {
|
||||
infoLogger('NotePlus setupEditor')
|
||||
|
||||
this.html_editor = ace.edit('noteplus-html-editor')
|
||||
|
||||
this.css_editor = ace.edit('noteplus-css-editor')
|
||||
this.css_editor.session.setMode('ace/mode/css')
|
||||
|
||||
this.setMode(DEFAULT_MODE)
|
||||
|
||||
this._setupEditor(this.html_editor)
|
||||
this._setupEditor(this.css_editor)
|
||||
|
||||
this.css_editor.session.on('change', (delta) => {
|
||||
// delta.start, delta.end, delta.lines, delta.action
|
||||
if (this.live) {
|
||||
this.updateHTML(this.editor.getValue())
|
||||
this.updateCSS(this.css_editor.getValue())
|
||||
}
|
||||
})
|
||||
|
||||
this.html_editor.session.on('change', (delta) => {
|
||||
// delta.start, delta.end, delta.lines, delta.action
|
||||
if (this.live) {
|
||||
this.updateHTML(this.html_editor.getValue())
|
||||
}
|
||||
})
|
||||
|
||||
this.html_editor.setValue(this.html_widget.value)
|
||||
this.css_editor.setValue(this.css_widget.value)
|
||||
}
|
||||
|
||||
scopeCss(css, scopeId) {
|
||||
return css
|
||||
.split('}')
|
||||
.map((rule) => {
|
||||
if (rule.trim() === '') {
|
||||
return ''
|
||||
}
|
||||
const scopedRule = rule
|
||||
.split('{')
|
||||
.map((segment, index) => {
|
||||
if (index === 0) {
|
||||
return `#${scopeId} ${segment.trim()}`
|
||||
}
|
||||
return `{${segment.trim()}`
|
||||
})
|
||||
.join(' ')
|
||||
return `${scopedRule}}`
|
||||
})
|
||||
.join('\n')
|
||||
}
|
||||
getCssDom() {
|
||||
const styleTagId = `note-plus-stylesheet-${this.uuid}`
|
||||
|
||||
let styleTag = document.head.querySelector(`#${styleTagId}`)
|
||||
|
||||
if (!styleTag) {
|
||||
styleTag = document.createElement('style')
|
||||
styleTag.type = 'text/css'
|
||||
styleTag.id = styleTagId
|
||||
document.head.appendChild(styleTag)
|
||||
infoLogger(`Creating note-plus-stylesheet-${this.uuid}`, styleTag)
|
||||
}
|
||||
|
||||
return styleTag
|
||||
}
|
||||
calculateHeight() {
|
||||
this.calculated_height = shared.calculateTotalChildrenHeight(
|
||||
this.html_widget.element
|
||||
)
|
||||
this.setDirtyCanvas(true, true)
|
||||
}
|
||||
updateCSS(css) {
|
||||
infoLogger('NotePlus updateCSS')
|
||||
// this.html_widget.element.style = css
|
||||
const scopedCss = this.scopeCss(
|
||||
`${CSS_RESET}\n${css}`,
|
||||
`note-plus-${this.uuid}`
|
||||
)
|
||||
|
||||
const cssDom = this.getCssDom()
|
||||
cssDom.innerHTML = scopedCss
|
||||
|
||||
this.css_widget.value = css
|
||||
this.calculateHeight()
|
||||
infoLogger('NotePlus updateCSS', this.calculated_height)
|
||||
// this.setSize(this.computeSize())
|
||||
}
|
||||
|
||||
updateHTML(val) {
|
||||
// if (CONTAINER_HTML.includes('${html}')) {
|
||||
// console.log('found template')
|
||||
// val = CONTAINER_HTML.replace('${html}', val)
|
||||
// }
|
||||
const cleanHTML = DOMPurify.sanitize(val, { ADD_TAGS: ['iframe'] })
|
||||
this.html_widget.value = cleanHTML
|
||||
|
||||
this.html_widget.value = val
|
||||
this.rawVal = val
|
||||
|
||||
this.calculated_height = this.html_widget.element.scrollHeight
|
||||
|
||||
this.setSize(this.computeSize())
|
||||
// update our widget preview
|
||||
if (this.edit_mode_widget.value === 'html') {
|
||||
this.html_widget.element.innerHTML = cleanHTML
|
||||
} else if (this.edit_mode_widget.value === 'markdown') {
|
||||
this.html_widget.element.innerHTML =
|
||||
this.markdownConverter.makeHtml(cleanHTML)
|
||||
}
|
||||
this.calculateHeight()
|
||||
// this.setSize(this.computeSize())
|
||||
}
|
||||
|
||||
// // onRemoved() {
|
||||
// // console.log('Removing', this)
|
||||
// // for (const w of this.widgets) {
|
||||
// // console.log('Removing', w)
|
||||
// // w.onRemove?.()
|
||||
// // w.onRemoved?.()
|
||||
// // }
|
||||
// // }
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'mtb.noteplus',
|
||||
|
||||
setup() {
|
||||
// app.ui.settings.addSetting({
|
||||
// id: "mtb.noteplus.Container",
|
||||
// name: "📦 HTML container",
|
||||
// type: "text",
|
||||
// defaultValue: "<div>${html}</div>",
|
||||
// tooltip:
|
||||
// "This defines the wrapper for the noteplus html content, use '${html}' to define the location of the placeholder",
|
||||
// attrs: {
|
||||
// style: {
|
||||
// fontFamily: "monospace",
|
||||
// },
|
||||
// },
|
||||
// onChange(value) {
|
||||
// if (!value) {
|
||||
// CONTAINER_HTML = null;
|
||||
// return;
|
||||
// }
|
||||
// console.log(`NOTEPLUS| value changed: ${value}`)
|
||||
// CONTAINER_HTML = value
|
||||
// },
|
||||
// });
|
||||
},
|
||||
|
||||
registerCustomNodes() {
|
||||
LiteGraph.registerNodeType('Note Plus (mtb)', NotePlus)
|
||||
|
||||
NotePlus.title_mode = LiteGraph.NO_TITLE
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user