sandbox page, multiple sidebars, select field docs

This commit is contained in:
Loren Norman 2025-07-10 14:39:28 -04:00
parent 6d4cb1f96a
commit dd6d8d9c3b
9 changed files with 136 additions and 76 deletions

View file

@ -20,17 +20,13 @@ export default {
fields: {
OPERATION: {
description: `
Round: rounds up if .5 or higher, down otherwise
Floor: rounds down
Ceiling: rounds up
`,
bytecodeProperty: "operation",
description: "Select which rounding operation to perform on the input:",
options: [
["Round", "round"],
["Floor", "floor"],
["Ceiling", "ceiling"],
]
["Round", "round", "if .5 or higher: round up; otherwise round down"],
["Floor", "floor", "rounds down"],
["Ceiling", "ceiling", "rounds up"],
],
bytecodeProperty: "operation",
}
},

View file

@ -39,15 +39,42 @@ export default defineConfig({
},
nav: [
{ text: '🪏', link: '/sandbox' },
{ text: 'Home', link: '/' },
{ text: 'Getting Started', link: '/getting-started' },
{ text: 'The Blocks', link: '/block-index' },
{ text: 'Examples', link: '/automation-examples' }
],
sidebar: [
blocksSidebar
],
sidebar: {
// covers /blocks/* and /block-index
"/block": [ blocksSidebar ],
// devtools for the sandbox
"/sandbox": [
{
"text": "Tools",
"items": [
{
"text": "Custom Feed Names",
"link": "#"
},
{
"text": "Weather Locations",
"link": "#"
},
{
"text": "IO Bytecode Explorer",
"link": "#"
},
{
"text": "Blockly JSON Explorer",
"link": "#"
},
]
}
],
},
socialLinks: [
{ icon: 'github', link: REPO }

View file

@ -1,33 +1,42 @@
<script setup>
import { onMounted, onUnmounted } from 'vue'
import { dispose, inject } from "../blockly/blockly_app.js"
import initialWorkspace from "../blockly/workspace.json"
const
props = defineProps(['block'])
{ block, width="100%", height="200px", toolbox=true } = defineProps(
['block', 'width', 'height', 'toolbox']
),
injectOptions = {},
options = {
injectOptions,
workspaceJson: block
? {
"blocks": {
"languageVersion": 0,
"blocks": [
{
"type": block,
"deletable": false,
"x": 50,
"y": 50
}
]
}
}
: initialWorkspace
}
if(!toolbox) {
injectOptions.toolbox = false
}
onMounted(() => {
console.log('mounted, block:', props.block)
inject("blocklyDiv", {
injectOptions: { toolbox: false },
workspaceJson: {
"blocks": {
"languageVersion": 0,
"blocks": [
{
"type": props.block,
"deletable": false,
"x": 50,
"y": 50
}
]
}
}
})
inject("blocklyDiv", options)
})
// clear workspace
onUnmounted(() => {
console.log('unmounted')
// clear workspace
dispose()
})
</script>
@ -38,8 +47,10 @@
<style>
div#blocklyDiv {
width: 100%;
height: 200px;
/* width: 100%; */
width: v-bind(width);
/* height: 200px; */
height: v-bind(height);
.injectionDiv {
border-radius: 5px;

9
docs/sandbox.md Normal file
View file

@ -0,0 +1,9 @@
---
title: Blockly Sandbox
# layout: page
aside: false
---
# Time to Play!
<BlocklyWorkspace height="800px" />

View file

@ -1,6 +1,6 @@
import { capitalize, find, forEach, map } from 'lodash-es'
import { cleanDir, copyDir, write, totalBytesWritten } from "./export_util.js"
import { cleanDir, write, totalBytesWritten } from "./export_util.js"
import DefinitionSet from '#src/definition_set.js'
import toBlockMarkdown from "#src/docs/render_block.js"
@ -11,24 +11,25 @@ const startTime = Date.now()
console.log("\nStarting Documentation Export")
console.log("=======================")
const definitionSet = await DefinitionSet.load()
cleanDir("docs/blockly")
// TODO: instead, export exactly what we need
copyDir("export", "docs/blockly")
definitionSet.export({ to: "docs/blockly" })
cleanDir("docs/blocks")
const definitionSet = await DefinitionSet.load()
const categories = definitionSet.getCategories()
const
categories = definitionSet.getCategories(),
// INIT SIDEBAR
const blockSidebar = {
text: 'Blocks',
items: map(categories, ({ name }) => ({
text: name,
collapsed: true,
items: []
}))
}
// INIT SIDEBAR
blockSidebar = {
text: 'Blocks',
items: map(categories, ({ name }) => ({
text: name,
collapsed: true,
items: []
}))
}
forEach(definitionSet.blocks, blockDefinition => {
// skip disabled blocks
@ -36,9 +37,10 @@ forEach(definitionSet.blocks, blockDefinition => {
// mirror the blocks/**/*.js path structure
const docPath = blockDefinition.definitionPath.replace(/.js$/, '.md')
write(`docs/blocks/${docPath}`, toBlockMarkdown(blockDefinition)) // EXPORT MARKDOWN
// DEDICATED DOC PAGE FOR BLOCK
write(`docs/blocks/${docPath}`, toBlockMarkdown(blockDefinition))
// APPEND TO SIDEBAR
// BLOCK SIDEBAR ITEM
const
blockSidebarPath = `/blocks/${docPath.slice(0, -3)}`,
sidebarEntry = {
@ -55,6 +57,7 @@ forEach(definitionSet.blocks, blockDefinition => {
throw new Error(`Block category (${ category.name }) not present in sidebar!`)
}
// ADD TO SIDEBAR
sidebarCategory.items.push(sidebarEntry)
})
})

View file

@ -1,25 +1,9 @@
import { capitalize, filter, invokeMap, map } from 'lodash-es'
import { capitalize, filter, invokeMap } from 'lodash-es'
import { toBlockJSON } from '#src/importer/block_processor/index.js'
import { niceTemplate } from '#src/util.js'
const niceTemplate = tplString => {
const
lines = tplString.split("\n"),
firstLineBlank = /^\s*$/.test(lines[0]),
remainingLines = lines.slice(1, -1),
indentCounts = map(remainingLines, line => line.search(/\S/)),
firstLineLeastIndented = indentCounts[0] >= Math.min(...indentCounts.slice(1, -1))
// ensure first line is blank and every other line has at least as much whitespace as the first line
if(firstLineBlank && firstLineLeastIndented) {
// drop the first line, remove X whitespace chars from the rest and join with newline
return map(remainingLines, line => line.slice(indentCounts[0])).join("\n")
}
return tplString
}
class BlockDefinition {
definitionSet = null

View file

@ -16,7 +16,7 @@ const
},
renderWorkspace = definition => {
return `<BlocklyWorkspace block="${ definition.type }" />`
return `<BlocklyWorkspace :toolbox="false" block="${ definition.type }" />`
},
renderOutput = definition => {

View file

@ -1,11 +1,18 @@
import { capitalize, filter, isArray, isObject, isString, map } from 'lodash-es'
import { capitalize, filter, isArray, isObject, isString, map, mapValues, values } from 'lodash-es'
import { niceTemplate } from '#src/util.js'
const
getLineObjects = definition => filter(map(filter(definition.lines, isArray), "[1]"), isObject),
renderFields = definition => {
const fields = filter(getLineObjects(definition), "field")
const fields = filter(getLineObjects(definition), "field").concat(
values(mapValues(definition.fields, (newField, name) => {
newField.field = name
return newField
}))
)
if(!fields.length) { return "This block has no form fields." }
@ -16,9 +23,10 @@ const
const lines = []
// title for this field
lines.push(`### Field: \`${capitalize(field.field)}\``)
lines.push(`### \`${capitalize(field.field)}\`:`)
// add lines based on what properties are present
Object.hasOwn(field, 'description') && lines.push(niceTemplate(field.description).replaceAll("\n", "\n\n"))
Object.hasOwn(field, 'text') && lines.push(renderTextField(field))
Object.hasOwn(field, 'checked') && lines.push(renderCheckboxField(field))
Object.hasOwn(field, 'options') && lines.push(renderSelectField(field))
@ -32,10 +40,12 @@ const
renderSelectField = selectField => {
const
optionLabels = map(selectField.options, 0),
optionList = map(optionLabels, label => ` - ${label}`).join("\n")
optionList = map(selectField.options, ([ label, , doc ]) => doc
? `- \`${label}\`: ${doc}`
: `- \`${label}\``
).join("\n")
return `- Options:\n${optionList}`
return optionList
}

20
src/util.js Normal file
View file

@ -0,0 +1,20 @@
import { map } from 'lodash-es'
export const
niceTemplate = tplString => {
const
lines = tplString.split("\n"),
firstLineBlank = /^\s*$/.test(lines[0]),
remainingLines = lines.slice(1, -1),
indentCounts = map(remainingLines, line => line.search(/\S/)),
firstLineLeastIndented = indentCounts[0] >= Math.min(...indentCounts.slice(1, -1))
// ensure first line is blank and every other line has at least as much whitespace as the first line
if(firstLineBlank && firstLineLeastIndented) {
// drop the first line, remove X whitespace chars from the rest and join with newline
return map(remainingLines, line => line.slice(indentCounts[0])).join("\n")
}
return tplString
}