Skip to content

HTML Documents Extension - #628

Open
ScrTwPm wants to merge 45 commits into
PenguinMod:mainfrom
ScrTwPm:main
Open

HTML Documents Extension#628
ScrTwPm wants to merge 45 commits into
PenguinMod:mainfrom
ScrTwPm:main

Conversation

@ScrTwPm

@ScrTwPm ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown

Create HTML webpages and display them on the stage
htmldocs
I'll make a documentation when I know what you think of this

@sakurabuilder

sakurabuilder Bot commented Aug 22, 2026

Copy link
Copy Markdown

❌ Preview build failed


added 61 packages, and audited 62 packages in 5s

6 packages are looking for funding
  run `npm fund` for details

11 vulnerabilities (1 low, 2 moderate, 8 high)

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

> penguinmod-extensionsgallery@0.0.1 build
> vite build

vite v6.3.5 building SSR bundle for production...
transforming...
✓ 54 modules transformed.

npm notice
npm notice New major version of npm available! 10.8.2 -> 12.0.2
npm notice Changelog: https://github.com/npm/cli/releases/tag/v12.0.2
npm notice To update run: npm install -g npm@12.0.2
npm notice
12:04:48 AM [vite-plugin-svelte] src/routes/+page.svelte:503:4 Unused CSS selector ".extension-list-filters-label"
https://svelte.dev/e/css_unused_selector
501:         display: initial;
502:     }
503:     .extension-list-filters-label {
                                      ^
504:         display: block;
505:         margin-top: 8px;
✗ Build failed in 725ms
error during build:
src/lib/Documentation/pages.js (90:4): Expected ',', got 'string literal (HTMLDocuments, "HTMLDocuments")'
file: /app/src/lib/Documentation/pages.js:90:4

88: 
89:     // HTML Documents
90:     "HTMLDocuments": HTMLDocuments,
        ^
91: };

    at getRollupError (file:///app/node_modules/rollup/dist/es/shared/parseAst.js:401:41)
    at ParseError.initialise (file:///app/node_modules/rollup/dist/es/shared/node-entry.js:14433:28)
    at convertNode (file:///app/node_modules/rollup/dist/es/shared/node-entry.js:16313:10)
    at convertProgram (file:///app/node_modules/rollup/dist/es/shared/node-entry.js:15553:12)
    at Module.setSource (file:///app/node_modules/rollup/dist/es/shared/node-entry.js:17308:24)
    at async ModuleLoader.addModuleSource (file:///app/node_modules/rollup/dist/es/shared/node-entry.js:21321:13)

Failed on 453b87f

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

But you were right about the Python extension it didnt really add anything new. I think this does add more features that that does. I'm checking right now to see if something like this already exists but i'm pretty sure it isn't

@Steve0Greatness

Copy link
Copy Markdown
Contributor

Doesn't appear to scale appropriately for full screened usage

image

Additionally, although the extension still believes the currently displayed page is still the page when changing view modes (ie. into/out of full screen), it does actually get removed.

image

@Steve0Greatness

Steve0Greatness commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Doesn't appear to scale appropriately for full screened usage
image removed

I see you had added a block that helps to scale appropriately (id spw). Second one still applies, though.

@Steve0Greatness

Copy link
Copy Markdown
Contributor

Could you make it so that multiple pages can be displayed at once?

@Steve0Greatness Steve0Greatness left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit more of a general concern, but you should put this through Prettier, because this is formatted, in my opinion, a bit badly.

width: "470",
},
"ids": [],
"code":""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would probably require a bit of a restructure, but there are far safer and more efficient ways to handle HTML code. Storing it as a DOM would actually be a good idea, here.

class HTMLtoCanvas {
constructor(runtime) {
// Initialize an array holding your default dropdown menu options
this.pages = {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll probably want to use a Map instead of just an object, since maps are more optimized to change frequently.

Comment on lines +4 to +13
if (Scratch.gui) {
Scratch.gui.getBlockly().then(ScratchBlocks => {
ScratchBlocks.BlockSvg.registerCustomNotch("htmldocuments-coolshape",
"c 2 0 3 1 4 2 l 4 4 c 1 1 4 -7 4 2 h 3 c 1 -1 2 -2 3 -3 c 1 1 2 2 3 3 l 3 0 c 0 -9 3 -1 4 -2 l 4 -4 c 1 -1 2 -2 4 -2"
)
ScratchBlocks.BlockSvg.registerCustomNotch("htmldocuments-coolshape2",
"c 0 2 0 8 4 8 c 6 0 10 -7 14 -5 c 4 -2 8 5 14 5 c 4 0 4 -6 4 -8"
)
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These currently go unused.

Comment on lines +661 to +662
const el = document.createElement("div");
el.innerHTML = this.pages[args.PAGE].code;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the purposes of sandboxing, please use iframe instead of a div. You can include HTML inside an iframe using the srcdoc attribute.

]
},
attr: {
acceptReporters: false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be set to true, instead of false. This would necessitate the use of a DOM, however, as the way you're currently setting attributes by their name is unsafe if you allow direct input.

{
opcode: 'eve',
blockType: Scratch.BlockType.HAT,
text: 'When listener for [ID] activated in [PAGE]',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, this should start with a lowercase letter.

Comment on lines +855 to +857
this.pages[args.PAGE].code = `${this.pages[args.PAGE].code}<!--begin my style--><style>`
this.pages[page].code = `${this.pages[page].code}.htmldocumentelement${args.TYPE}element${page}${args.NAME}{${args.PROPERTY}:${value}};`
this.pages[args.PAGE].code = `${this.pages[args.PAGE].code}</style><!--end my style-->`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cases like these, it's easier to read if you use += `...` instead of = `${...}...`

this.pages[args.PAGE].data.x = 5
this.pages[args.PAGE].data.y = 5
console.log(this.viewing)
console.log(document.querySelector("#htmlpage"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few spare console logs here and there in this extension, I'd recommend removing them before this gets merged.

Comment on lines +1023 to +1060
vm.runtime.targets.forEach(target => {
const blocks = target.blocks;
const scripts = blocks.getScripts();

scripts.forEach(rootBlockId => {
const block = blocks.getBlock(rootBlockId);

if (block && block.opcode === targetOpcode) {
let hatValue = '';
let hatValuea = '';

// 1. Check if it's a Field (dropdown/fixed text)
if (block.fields && block.fields.ID && block.fields.PAGE) {
console.log(block.fields.ID.value)
hatValue = block.fields.ID.value;
hatValuea = block.fields.PAGE.value;
}
// 2. Check if it's an Input (text bubble)
else if (block.inputs && block.inputs.ID) {
const input = block.inputs.ID;
const inputa = block.inputs.PAGE;
// Dig into the 'shadow' block which holds the text value
const shadowBlock = blocks.getBlock(input.shadow);
const shadowBlocka = blocks.getBlock(inputa.shadow);
if (shadowBlock && shadowBlock.fields && shadowBlock.fields.TEXT && shadowBlocka && shadowBlocka.fields && shadowBlocka.fields.TEXT) {
hatValue = shadowBlock.fields.TEXT.value;
hatValuea = shadowBlocka.fields.TEXT.value;
}
}

// Compare and trigger
if (hatValue === triggerText && hatValuea === triggerTexta) {
vm.runtime._pushThread(rootBlockId, target);
}
}
});
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runtime.startHats has the functionality to start hat blocks dependent on their values, already.

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

thanks for the feedback! i'll get to it.

@Steve0Greatness

Copy link
Copy Markdown
Contributor

But you were right about the Python extension it didnt really add anything new. I think this does add more features that that does. I'm checking right now to see if something like this already exists but i'm pretty sure it isn't

Well, there's Project Interfaces, which is a similar idea, but it doesn't allow for the creation of documents made up of HTML.

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

i was thinking about that exact thing while i was making this. I wanted to give users more control and the ability to display and build their own webpage documents.

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

or i can use this function

function prettierInText(html) {
  // Clean up existing whitespace and collapse it
  let clean = html.replace(/\s*([<>])\s*/g, '$1').replace(/\s+/g, ' ');
  let reg = /(<[^>]+>)/g;
  let matches = clean.split(reg).filter(Boolean);
  
  let formatted = '';
  let pad = 0;

  matches.forEach((token) => {
    // Check if the token is a closing tag
    if (token.match( /<\/\w+/ )) {
      pad--;
    }
    
    // Add current line indentation
    formatted += '  '.repeat(Math.max(0, pad)) + token + '\n';
    
    // Check if the token is an opening tag (and not self-closing)
    if (token.match( /<[^\/][^>]*[^>\/]>/ ) && !token.match(/<(input|img|br|hr|meta|link)/)) {
      pad++;
    }
  });

  return formatted.trim();
}

// Example Usage
const raw = '<div><h1>Title</h1><p>Text</p><img src="img.jpg"/></div>';
console.log(prettierInText(raw));

@Gen1xLol

Copy link
Copy Markdown
Contributor

or i can use this function

function prettierInText(html) {
  // Clean up existing whitespace and collapse it
  let clean = html.replace(/\s*([<>])\s*/g, '$1').replace(/\s+/g, ' ');
  let reg = /(<[^>]+>)/g;
  let matches = clean.split(reg).filter(Boolean);
  
  let formatted = '';
  let pad = 0;

  matches.forEach((token) => {
    // Check if the token is a closing tag
    if (token.match( /<\/\w+/ )) {
      pad--;
    }
    
    // Add current line indentation
    formatted += '  '.repeat(Math.max(0, pad)) + token + '\n';
    
    // Check if the token is an opening tag (and not self-closing)
    if (token.match( /<[^\/][^>]*[^>\/]>/ ) && !token.match(/<(input|img|br|hr|meta|link)/)) {
      pad++;
    }
  });

  return formatted.trim();
}

// Example Usage
const raw = '<div><h1>Title</h1><p>Text</p><img src="img.jpg"/></div>';
console.log(prettierInText(raw));

This seems to be very clearly AI generated. I don't know if you vibecoded this extension but if you did that's a biiiig no-no

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

I typed this myself. But the function i just pasted was AI generated because I wanted to see if you would or would not want me to import the prettier.
I typed the full extension by hand. I've been working on it since May.

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

I don't paln on using the script I sent you but I want to verify if i'm ok to import perttier from https://unpkg.com/html-dom-parser@latest/dist/html-dom-parser.min.js'

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

This seems to be very clearly AI generated. I don't know if you vibecoded this extension but if you did that's a biiiig no-no

I built this off of my previous html to canvas extension that I made last year.
It is very unsafe and uses cst1229's extendable blocks (i never made a pr for it)

htmltocanvasold.js

@Steve0Greatness

Copy link
Copy Markdown
Contributor

I don't paln on using the script I sent you but I want to verify if i'm ok to import perttier from https://unpkg.com/html-dom-parser@latest/dist/html-dom-parser.min.js'

Uh, I don't think that's Prettier, that's a DOM parser. Prettier is a code formatter, I was requesting that you reformat your code.

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

OH. ok. it does have a html parser. You wnated me to format my code

@Steve0Greatness

Copy link
Copy Markdown
Contributor

OH. ok. it does have a html parser. You wnated me to format my code

Yes. The JavaScript of the extension, that's what I want you to format.

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

I'll do that when i post my new revided version that replies to your suggestions

(I'm a good coder but a horrible formatter. I'm downloading prettier on vscode right now)

@Steve0Greatness

Copy link
Copy Markdown
Contributor

I'll do that when i post my new revided version that replies to your suggestions

(I'm a good coder but a horrible formatter. I'm downloading prettier on vscode right now)

Alright, you actually don't need to open a new PR, just push to the same branch that this PR is pulling from (main).

@Steve0Greatness

Copy link
Copy Markdown
Contributor

Additionally, you can just use the builtin DOMParser provided by the browser, rather than needing an external package..

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

I got you.

@ScrTwPm

ScrTwPm commented Aug 22, 2026

Copy link
Copy Markdown
Author

should i use that to insert elements into my iframe or change the entire

this.pages[page].code

to a HTML DOM

@ScrTwPm

ScrTwPm commented Aug 30, 2026

Copy link
Copy Markdown
Author

or are you talking about changing the elements while the page is showing without having to display it again to reload the changes

@ScrTwPm

ScrTwPm commented Aug 30, 2026

Copy link
Copy Markdown
Author
image

@Steve0Greatness

Copy link
Copy Markdown
Contributor

or are you talking about changing the elements while the page is showing without having to display it again to reload the changes

Yes.

I also made custom reporter types...
@ScrTwPm

ScrTwPm commented Aug 30, 2026

Copy link
Copy Markdown
Author

Done

@ScrTwPm

ScrTwPm commented Sep 1, 2026

Copy link
Copy Markdown
Author

all changes made

@RedMan13

RedMan13 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

if you could please fix the merge conflicts github lists that would be perfect

@jwklong

jwklong commented Sep 1, 2026

Copy link
Copy Markdown
Member

your custom types need serialization/deserialization support or projects may not be able to safe if for example a custom type is stored inside a variable

@ScrTwPm

ScrTwPm commented Sep 1, 2026

Copy link
Copy Markdown
Author

if you could please fix the merge conflicts github lists that would be perfect

Can i make a new PR? this one has too many commits anyway.

@ScrTwPm

ScrTwPm commented Sep 1, 2026

Copy link
Copy Markdown
Author

your custom types need serialization/deserialization support or projects may not be able to safe if for example a custom type is stored inside a variable

I'll get on it

@ScrTwPm

ScrTwPm commented Sep 2, 2026

Copy link
Copy Markdown
Author

ill finish the serilizzaton by the end of the week

@ScrTwPm

ScrTwPm commented Sep 5, 2026

Copy link
Copy Markdown
Author

added

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants