1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-06-24 06:03:28 +00:00

Add more eldritch horrors to the build system

- Add a basic data exporter to the test mod, run via a /ccexport
   command. This dumps all of CC's recipes, and the item icons needed to
   display those recipes.

 - Post-process our illuaminate HTML, applying several transforms:
    - Apply syntax highlighting to code blocks. We previously did this
      at runtime, so this shaves some bytes off the bundle.

    - Convert a mc-recipe custom element into a recipe grid using
      react/react-dom.

 - Add a recipe to the speaker page. I'll probably clean this up in the
   future (though someone else is free to too!), but it's a nice
   start and proof-of-concept.

I tried so hard here to use next.js and MDX instead of rolling our own
solution again, but it's so hard to make it play well with "normal"
Markdown, which isn't explicitly written for MDX.
This commit is contained in:
Jonathan Coates 2022-06-01 00:47:00 +01:00
parent cd76425877
commit 178126725e
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
69 changed files with 3051 additions and 17 deletions

View File

@ -158,7 +158,7 @@ accessTransformer file('src/testMod/resources/META-INF/accesstransformer.cfg')
testModImplementation sourceSets.main.output
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.5'
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.6'
}
// Compile tasks
@ -298,7 +298,7 @@ commandLine mkCommand('"node_modules/.bin/rollup" --config rollup.config.js')
task illuaminateDocs(type: Exec, dependsOn: [rollup, luaJavadoc]) {
group = "build"
description = "Bundles JS into rollup"
description = "Generates docs using Illuaminate"
inputs.files(fileTree("doc")).withPropertyName("docs")
inputs.files(fileTree("src/main/resources/data/computercraft/lua/rom")).withPropertyName("lua rom")
@ -311,7 +311,20 @@ task illuaminateDocs(type: Exec, dependsOn: [rollup, luaJavadoc]) {
commandLine mkCommand('"bin/illuaminate" doc-gen')
}
task docWebsite(type: Copy, dependsOn: [illuaminateDocs]) {
task jsxDocs(type: Exec, dependsOn: [illuaminateDocs]) {
group = "build"
description = "Post-processes documentation to statically render some dynamic content."
inputs.files(fileTree("src/web")).withPropertyName("sources")
inputs.file("package-lock.json").withPropertyName("package-lock.json")
inputs.file("tsconfig.json").withPropertyName("Typescript config")
inputs.files(fileTree("$buildDir/docs/lua"))
outputs.dir("$buildDir/docs/site")
commandLine mkCommand("'node_modules/.bin/ts-node' --esm src/web/transform.tsx")
}
task docWebsite(type: Copy, dependsOn: [jsxDocs]) {
from('doc') {
include 'logo.png'
include 'images/**'
@ -319,7 +332,14 @@ task docWebsite(type: Copy, dependsOn: [illuaminateDocs]) {
from("$buildDir/rollup") {
exclude 'index.js'
}
into "${project.docsDir}/lua"
from("$buildDir/docs/lua") {
exclude '**/*.html'
}
from("src/web/export/items") {
into("images/items")
}
into "${project.docsDir}/site"
}
// Check tasks

View File

@ -76,7 +76,9 @@
<module name="JavadocBlockTagLocation" />
<module name="JavadocMethod"/>
<module name="JavadocType"/>
<module name="JavadocStyle" />
<module name="JavadocStyle">
<property name="checkHtml" value="false" />
</module>
<module name="NonEmptyAtclauseDescription" />
<module name="SingleLineJavadoc" />
<module name="SummaryJavadocCheck"/>

1719
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@
"description": "Website additions for tweaked.cc",
"author": "SquidDev",
"license": "BSD-3-Clause",
"type": "module",
"dependencies": {
"preact": "^10.5.5",
"tslib": "^2.0.3"
@ -11,9 +12,18 @@
"devDependencies": {
"@rollup/plugin-typescript": "^8.2.5",
"@rollup/plugin-url": "^6.1.0",
"@types/glob": "^7.2.0",
"@types/react-dom": "^18.0.5",
"glob": "^8.0.3",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"rehype": "^12.0.1",
"rehype-highlight": "^5.0.2",
"rehype-react": "^7.1.1",
"requirejs": "^2.3.6",
"rollup": "^2.33.1",
"rollup-plugin-terser": "^7.0.2",
"ts-node": "^10.8.0",
"typescript": "^4.0.5"
}
}

View File

@ -41,6 +41,9 @@
* - {@link #playSound} plays any built-in Minecraft sound, such as block sounds or mob noises.
* - {@link #playAudio} can play arbitrary audio.
*
* <h2>Recipe</h2>
* <McRecipe recipe="computercraft:speaker"></McRecipe>
*
* @cc.module speaker
* @cc.since 1.80pr1
*/

View File

@ -0,0 +1,153 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.export;
import com.google.common.io.MoreFiles;
import com.google.common.io.RecursiveDeleteOption;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.ingame.mod.TestMod;
import net.minecraft.client.Minecraft;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.*;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ClientChatEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.registries.ForgeRegistries;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Set;
/**
* Provides a {@literal /ccexport <path>} command which exports icons and recipes for all ComputerCraft items.
*/
@Mod.EventBusSubscriber( modid = TestMod.MOD_ID, value = Dist.CLIENT )
public class Exporter
{
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
@SubscribeEvent
public static void onClientCommands( ClientChatEvent event )
{
String prefix = "/ccexport";
if( !event.getMessage().startsWith( prefix ) ) return;
event.setCanceled( true );
Path output = new File( event.getMessage().substring( prefix.length() ).trim() ).getAbsoluteFile().toPath();
if( !Files.isDirectory( output ) )
{
Minecraft.getInstance().gui.getChat().addMessage( new StringTextComponent( "Output path does not exist" ) );
return;
}
RenderSystem.assertThread( RenderSystem::isOnRenderThread );
try( ImageRenderer renderer = new ImageRenderer() )
{
export( output, renderer );
}
catch( IOException e )
{
throw new UncheckedIOException( e );
}
Minecraft.getInstance().gui.getChat().addMessage( new StringTextComponent( "Export finished!" ) );
}
private static void export( Path root, ImageRenderer renderer ) throws IOException
{
JsonDump dump = new JsonDump();
Set<Item> items = new HashSet<>();
// First find all CC items
for( Item item : ForgeRegistries.ITEMS.getValues() )
{
if( item.getRegistryName().getNamespace().equals( ComputerCraft.MOD_ID ) ) items.add( item );
}
// Now find all CC recipes.
for( ICraftingRecipe recipe : Minecraft.getInstance().level.getRecipeManager().getAllRecipesFor( IRecipeType.CRAFTING ) )
{
ItemStack result = recipe.getResultItem();
if( !result.getItem().getRegistryName().getNamespace().equals( ComputerCraft.MOD_ID ) ) continue;
if( result.hasTag() )
{
ComputerCraft.log.warn( "Skipping recipe {} as it has NBT", recipe.getId() );
continue;
}
if( recipe instanceof ShapedRecipe )
{
JsonDump.Recipe converted = new JsonDump.Recipe( result );
ShapedRecipe shaped = (ShapedRecipe) recipe;
for( int x = 0; x < shaped.getWidth(); x++ )
{
for( int y = 0; y < shaped.getHeight(); y++ )
{
Ingredient ingredient = shaped.getIngredients().get( x + y * shaped.getWidth() );
if( ingredient.isEmpty() ) continue;
converted.setInput( x + y * 3, ingredient, items );
}
}
dump.recipes.put( recipe.getId().toString(), converted );
}
else if( recipe instanceof ShapelessRecipe )
{
JsonDump.Recipe converted = new JsonDump.Recipe( result );
ShapelessRecipe shapeless = (ShapelessRecipe) recipe;
NonNullList<Ingredient> ingredients = shapeless.getIngredients();
for( int i = 0; i < ingredients.size(); i++ )
{
converted.setInput( i, ingredients.get( i ), items );
}
dump.recipes.put( recipe.getId().toString(), converted );
}
else
{
ComputerCraft.log.info( "Don't know how to handle recipe {}", recipe );
}
}
Path itemDir = root.resolve( "items" );
if( Files.exists( itemDir ) ) MoreFiles.deleteRecursively( itemDir, RecursiveDeleteOption.ALLOW_INSECURE );
renderer.setupState();
for( Item item : items )
{
ItemStack stack = new ItemStack( item );
dump.itemNames.put( item.getRegistryName().toString(), stack.getHoverName().getString() );
ResourceLocation location = item.getRegistryName();
renderer.captureRender( itemDir.resolve( location.getNamespace() ).resolve( location.getPath() + ".png" ),
() -> Minecraft.getInstance().getItemRenderer().renderAndDecorateFakeItem( stack, 0, 0 )
);
}
renderer.clearState();
try( Writer writer = Files.newBufferedWriter( root.resolve( "index.json" ) ) )
{
GSON.toJson( dump, writer );
}
}
}

View File

@ -0,0 +1,84 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.export;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.client.shader.Framebuffer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Utilities for saving OpenGL output to an image rather than displaying it on the screen.
*/
public class ImageRenderer implements AutoCloseable
{
public static final int WIDTH = 64;
public static final int HEIGHT = 64;
private final Framebuffer framebuffer = new Framebuffer( WIDTH, HEIGHT, true, Minecraft.ON_OSX );
private final NativeImage image = new NativeImage( WIDTH, HEIGHT, Minecraft.ON_OSX );
public ImageRenderer()
{
framebuffer.setClearColor( 0, 0, 0, 0 );
framebuffer.clear( Minecraft.ON_OSX );
}
public void setupState()
{
RenderSystem.matrixMode( GL11.GL_PROJECTION );
RenderSystem.pushMatrix();
RenderSystem.loadIdentity();
RenderSystem.ortho( 0, 16, 16, 0, 1000, 3000 );
RenderSystem.matrixMode( GL11.GL_MODELVIEW );
RenderSystem.pushMatrix();
RenderSystem.loadIdentity();
RenderSystem.translatef( 0, 0, -2000f );
FogRenderer.setupNoFog();
}
public void clearState()
{
RenderSystem.matrixMode( GL11.GL_PROJECTION );
RenderSystem.popMatrix();
RenderSystem.matrixMode( GL11.GL_MODELVIEW );
RenderSystem.popMatrix();
}
public void captureRender( Path output, Runnable render ) throws IOException
{
Files.createDirectories( output.getParent() );
framebuffer.bindWrite( true );
RenderSystem.clear( GL12.GL_COLOR_BUFFER_BIT | GL12.GL_DEPTH_BUFFER_BIT, Minecraft.ON_OSX );
render.run();
framebuffer.unbindWrite();
framebuffer.bindRead();
image.downloadTexture( 0, false );
image.flipY();
framebuffer.unbindRead();
image.writeToFile( output );
}
@Override
public void close()
{
image.close();
framebuffer.destroyBuffers();
}
}

View File

@ -0,0 +1,65 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.export;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.crafting.Ingredient;
import java.util.*;
public class JsonDump
{
public Map<String, String> itemNames = new TreeMap<>();
public Map<String, Recipe> recipes = new TreeMap<>();
public static class Recipe
{
public final String[][] inputs = new String[9][];
public String output;
public int count;
public Recipe( ItemStack output )
{
this.output = output.getItem().getRegistryName().toString();
count = output.getCount();
}
public void setInput( int pos, Ingredient ingredient, Set<Item> trackedItems )
{
if( ingredient.isEmpty() ) return;
ItemStack[] items = ingredient.getItems();
// First try to simplify some tags to something easier.
for( ItemStack stack : items )
{
Item item = stack.getItem();
if( !canonicalItem.contains( item ) ) continue;
trackedItems.add( item );
inputs[pos] = new String[] { item.getRegistryName().toString() };
return;
}
String[] itemIds = new String[items.length];
for( int i = 0; i < items.length; i++ )
{
Item item = items[i].getItem();
trackedItems.add( item );
itemIds[i] = item.getRegistryName().toString();
}
Arrays.sort( itemIds );
inputs[pos] = itemIds;
}
private static final Set<Item> canonicalItem = new HashSet<>( Arrays.asList(
Items.GLASS_PANE, Items.STONE, Items.CHEST
) );
}
}

View File

@ -0,0 +1,44 @@
import type { FunctionComponent } from "react";
import { createElement as h } from "react";
import useExport from "./WithExport.js";
const Item: FunctionComponent<{ item: string }> = ({ item }) => {
const data = useExport();
const itemName = data.itemNames[item];
return <img
src={`/images/items/${item.replace(":", "/")}.png`}
alt={itemName}
title={itemName}
/>
};
const Arrow: FunctionComponent<JSX.IntrinsicElements["svg"]> = (props) => <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 45.513 45.512" {...props}>
<g>
<path d="M44.275,19.739L30.211,5.675c-0.909-0.909-2.275-1.18-3.463-0.687c-1.188,0.493-1.959,1.654-1.956,2.938l0.015,5.903
l-21.64,0.054C1.414,13.887-0.004,15.312,0,17.065l0.028,11.522c0.002,0.842,0.338,1.648,0.935,2.242s1.405,0.927,2.247,0.925
l21.64-0.054l0.014,5.899c0.004,1.286,0.781,2.442,1.971,2.931c1.189,0.487,2.557,0.21,3.46-0.703L44.29,25.694
C45.926,24.043,45.92,21.381,44.275,19.739z" fill="var(--recipe-hover)" />
</g>
</svg>;
const Recipe: FunctionComponent<{ recipe: string }> = ({ recipe }) => {
const data = useExport();
const recipeInfo = data.recipes[recipe];
if (!recipeInfo) throw Error("Cannot find recipe for " + recipe);
return <div className="recipe-container">
<div className="recipe">
<strong className="recipe-title">{data.itemNames[recipeInfo.output]}</strong>
<div className="recipe-inputs">
{recipeInfo.inputs.map((items, i) => <div className="recipe-item recipe-input" key={i}>{items && <Item item={items[0]} />}</div>)}
</div>
<Arrow className="recipe-arrow" />
<div className="recipe-item recipe-output">
<Item item={recipeInfo.output} />
{recipeInfo.count > 1 && <span className="recipe-count">{recipeInfo.count}</span>}
</div>
</div>
</div>;
}
export default Recipe;

View File

@ -0,0 +1,23 @@
import { createElement as h, useContext, createContext, FunctionComponent, ReactNode } from "react";
export type DataExport = {
readonly itemNames: Record<string, string>,
readonly recipes: Record<string, Recipe>,
};
export type Recipe = {
readonly inputs: Array<Array<string>>,
readonly output: string,
readonly count: number,
};
const DataExport = createContext<DataExport>({
itemNames: {},
recipes: {},
});
export const useExport = () => useContext(DataExport);
export default useExport;
export const WithExport: FunctionComponent<{ data: DataExport, children: ReactNode }> =
({ data, children }) => <DataExport.Provider value={data}> {children}</DataExport.Provider >;

View File

@ -0,0 +1,25 @@
import type { FunctionComponent } from "react";
/**
* Wrap a component and ensure that no children are passed to it.
*
* Our custom tags *must* be explicitly closed, as <foo /> will be parsed as
* <foo>(rest of the document)</foo>. This ensures you've not forgotten to do
* that.
*
* @param component The component to wrap
* @returns A new functional component identical to the previous one
*/
export const noChildren = function <T>(component: FunctionComponent<T>): FunctionComponent<T> {
// I hope that our few remaining friends
// Give up on trying to save us
const name = component.displayName ?? component.name;
const wrapped: FunctionComponent<T> = props => {
if ((props as any).children) throw Error("Unexpected children in " + name);
return component(props);
};
wrapped.displayName = name;
return wrapped;
}

760
src/web/export/index.json Normal file
View File

@ -0,0 +1,760 @@
{
"itemNames": {
"computercraft:cable": "Networking Cable",
"computercraft:computer_advanced": "Advanced Computer",
"computercraft:computer_command": "Command Computer",
"computercraft:computer_normal": "Computer",
"computercraft:disk": "Floppy Disk",
"computercraft:disk_drive": "Disk Drive",
"computercraft:monitor_advanced": "Advanced Monitor",
"computercraft:monitor_normal": "Monitor",
"computercraft:pocket_computer_advanced": "Advanced Pocket Computer",
"computercraft:pocket_computer_normal": "Pocket Computer",
"computercraft:printed_book": "Printed Book",
"computercraft:printed_page": "Printed Page",
"computercraft:printed_pages": "Printed Pages",
"computercraft:printer": "Printer",
"computercraft:speaker": "Speaker",
"computercraft:treasure_disk": "Floppy Disk",
"computercraft:turtle_advanced": "Advanced Turtle",
"computercraft:turtle_normal": "Turtle",
"computercraft:wired_modem": "Wired Modem",
"computercraft:wired_modem_full": "Wired Modem",
"computercraft:wireless_modem_advanced": "Ender Modem",
"computercraft:wireless_modem_normal": "Wireless Modem",
"minecraft:black_dye": "Black Dye",
"minecraft:blue_dye": "Blue Dye",
"minecraft:brown_dye": "Brown Dye",
"minecraft:chest": "Chest",
"minecraft:command_block": "Command Block",
"minecraft:cyan_dye": "Cyan Dye",
"minecraft:ender_eye": "Eye of Ender",
"minecraft:ender_pearl": "Ender Pearl",
"minecraft:glass_pane": "Glass Pane",
"minecraft:gold_block": "Block of Gold",
"minecraft:gold_ingot": "Gold Ingot",
"minecraft:golden_apple": "Golden Apple",
"minecraft:gray_dye": "Gray Dye",
"minecraft:green_dye": "Green Dye",
"minecraft:iron_ingot": "Iron Ingot",
"minecraft:leather": "Leather",
"minecraft:light_blue_dye": "Light Blue Dye",
"minecraft:light_gray_dye": "Light Gray Dye",
"minecraft:lime_dye": "Lime Dye",
"minecraft:magenta_dye": "Magenta Dye",
"minecraft:note_block": "Note Block",
"minecraft:orange_dye": "Orange Dye",
"minecraft:pink_dye": "Pink Dye",
"minecraft:purple_dye": "Purple Dye",
"minecraft:red_dye": "Red Dye",
"minecraft:redstone": "Redstone Dust",
"minecraft:stone": "Stone",
"minecraft:string": "String",
"minecraft:white_dye": "White Dye",
"minecraft:yellow_dye": "Yellow Dye"
},
"recipes": {
"computercraft:cable": {
"inputs": [
null,
[
"minecraft:stone"
],
null,
[
"minecraft:stone"
],
[
"minecraft:redstone"
],
[
"minecraft:stone"
],
null,
[
"minecraft:stone"
],
null
],
"output": "computercraft:cable",
"count": 6
},
"computercraft:computer_advanced": {
"inputs": [
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:redstone"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:glass_pane"
],
[
"minecraft:gold_ingot"
]
],
"output": "computercraft:computer_advanced",
"count": 1
},
"computercraft:computer_advanced_upgrade": {
"inputs": [
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"computercraft:computer_normal"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
null,
[
"minecraft:gold_ingot"
]
],
"output": "computercraft:computer_advanced",
"count": 1
},
"computercraft:computer_command": {
"inputs": [
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:command_block"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:glass_pane"
],
[
"minecraft:gold_ingot"
]
],
"output": "computercraft:computer_command",
"count": 1
},
"computercraft:computer_normal": {
"inputs": [
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:redstone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:glass_pane"
],
[
"minecraft:stone"
]
],
"output": "computercraft:computer_normal",
"count": 1
},
"computercraft:disk_drive": {
"inputs": [
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:redstone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:redstone"
],
[
"minecraft:stone"
]
],
"output": "computercraft:disk_drive",
"count": 1
},
"computercraft:monitor_advanced": {
"inputs": [
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:glass_pane"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
]
],
"output": "computercraft:monitor_advanced",
"count": 4
},
"computercraft:monitor_normal": {
"inputs": [
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:glass_pane"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
]
],
"output": "computercraft:monitor_normal",
"count": 1
},
"computercraft:pocket_computer_advanced": {
"inputs": [
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:golden_apple"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:glass_pane"
],
[
"minecraft:gold_ingot"
]
],
"output": "computercraft:pocket_computer_advanced",
"count": 1
},
"computercraft:pocket_computer_advanced_upgrade": {
"inputs": [
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"computercraft:pocket_computer_normal"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
null,
[
"minecraft:gold_ingot"
]
],
"output": "computercraft:pocket_computer_advanced",
"count": 1
},
"computercraft:pocket_computer_normal": {
"inputs": [
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:golden_apple"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:glass_pane"
],
[
"minecraft:stone"
]
],
"output": "computercraft:pocket_computer_normal",
"count": 1
},
"computercraft:printed_book": {
"inputs": [
[
"minecraft:leather"
],
[
"computercraft:printed_page"
],
[
"minecraft:string"
],
null,
null,
null,
null,
null,
null
],
"output": "computercraft:printed_book",
"count": 1
},
"computercraft:printed_pages": {
"inputs": [
[
"computercraft:printed_page"
],
[
"computercraft:printed_page"
],
[
"minecraft:string"
],
null,
null,
null,
null,
null,
null
],
"output": "computercraft:printed_pages",
"count": 1
},
"computercraft:printer": {
"inputs": [
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:redstone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:black_dye",
"minecraft:blue_dye",
"minecraft:brown_dye",
"minecraft:cyan_dye",
"minecraft:gray_dye",
"minecraft:green_dye",
"minecraft:light_blue_dye",
"minecraft:light_gray_dye",
"minecraft:lime_dye",
"minecraft:magenta_dye",
"minecraft:orange_dye",
"minecraft:pink_dye",
"minecraft:purple_dye",
"minecraft:red_dye",
"minecraft:white_dye",
"minecraft:yellow_dye"
],
[
"minecraft:stone"
]
],
"output": "computercraft:printer",
"count": 1
},
"computercraft:speaker": {
"inputs": [
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:note_block"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:redstone"
],
[
"minecraft:stone"
]
],
"output": "computercraft:speaker",
"count": 1
},
"computercraft:turtle_advanced": {
"inputs": [
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"computercraft:computer_advanced"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:chest"
],
[
"minecraft:gold_ingot"
]
],
"output": "computercraft:turtle_advanced",
"count": 1
},
"computercraft:turtle_advanced_upgrade": {
"inputs": [
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"computercraft:turtle_normal"
],
[
"minecraft:gold_ingot"
],
null,
[
"minecraft:gold_block"
],
null
],
"output": "computercraft:turtle_advanced",
"count": 1
},
"computercraft:turtle_normal": {
"inputs": [
[
"minecraft:iron_ingot"
],
[
"minecraft:iron_ingot"
],
[
"minecraft:iron_ingot"
],
[
"minecraft:iron_ingot"
],
[
"computercraft:computer_normal"
],
[
"minecraft:iron_ingot"
],
[
"minecraft:iron_ingot"
],
[
"minecraft:chest"
],
[
"minecraft:iron_ingot"
]
],
"output": "computercraft:turtle_normal",
"count": 1
},
"computercraft:wired_modem": {
"inputs": [
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:redstone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
]
],
"output": "computercraft:wired_modem",
"count": 1
},
"computercraft:wired_modem_full_from": {
"inputs": [
[
"computercraft:wired_modem"
],
null,
null,
null,
null,
null,
null,
null,
null
],
"output": "computercraft:wired_modem_full",
"count": 1
},
"computercraft:wired_modem_full_to": {
"inputs": [
[
"computercraft:wired_modem_full"
],
null,
null,
null,
null,
null,
null,
null,
null
],
"output": "computercraft:wired_modem",
"count": 1
},
"computercraft:wireless_modem_advanced": {
"inputs": [
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:ender_eye"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
],
[
"minecraft:gold_ingot"
]
],
"output": "computercraft:wireless_modem_advanced",
"count": 1
},
"computercraft:wireless_modem_normal": {
"inputs": [
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:ender_pearl"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
],
[
"minecraft:stone"
]
],
"output": "computercraft:wireless_modem_normal",
"count": 1
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

View File

@ -9,8 +9,6 @@ import exampleNft from "./mount/example.nft";
import exampleAudioLicense from "./mount/example.dfpwm.LICENSE";
import exampleAudioUrl from "./mount/example.dfpwm";
import "./prism.js";
const defaultFiles: { [filename: string]: string } = {
".settings": settingsFile,
"startup.lua": startupFile,

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,8 @@ table {
width: 100%;
}
table td, table th {
table td,
table th {
border: 1px solid #cccccc;
padding: 2px 4px;
}
@ -38,7 +39,8 @@ pre.highlight {
position: fixed;
z-index: 200;
top: 0px;
top: 0px;;
top: 0px;
;
}
/* Behold, the most cursed CSS! copy-cat's resizing algorithm is a weird, in
@ -78,7 +80,9 @@ pre.highlight {
font-size: 15px;
}
.titlebar-close:hover { background: #cc4c4c; }
.titlebar-close:hover {
background: #cc4c4c;
}
@media (max-width: 700px) {
.computer-container {
@ -86,6 +90,79 @@ pre.highlight {
height: calc(179px + 40px);
}
.titlebar { height: 20px; }
.titlebar-close { font-size: 7px; }
.titlebar {
height: 20px;
}
.titlebar-close {
font-size: 7px;
}
}
:root {
--recipe-bg: #ddd;
--recipe-fg: #bbb;
--recipe-hover: #aaa;
--recipe-padding: 4px;
--recipe-size: 32px;
}
.recipe-container {
display: flex;
justify-content: center;
margin: 1em 0;
}
.recipe {
display: inline-grid;
padding: var(--recipe-padding);
background: var(--recipe-bg);
column-gap: var(--recipe-padding);
row-gap: var(--recipe-padding);
grid-template-rows: auto auto;
grid-template-columns: max-content 1fr max-content;
}
.recipe-title {
grid-column-start: span 3;
}
.recipe-inputs {
display: inline-grid;
column-gap: var(--recipe-padding);
row-gap: var(--recipe-padding);
align-items: center;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
}
.recipe-item {
padding: var(--recipe-padding);
background-color: var(--recipe-fg);
}
.recipe-item:hover {
background-color: var(--recipe-hover);
}
.recipe-item > img {
display: block;
width: var(--recipe-size);
height: var(--recipe-size);
max-width: 10vw;
max-height: 10vw;
}
.recipe-arrow {
width: var(--recipe-size);
max-width: 10vw;
align-self: center;
}
.recipe-output {
/* Hrm! */
align-self: center;
}

53
src/web/transform.tsx Normal file
View File

@ -0,0 +1,53 @@
/**
* Find all HTML files generated by illuaminate and pipe them through a remark.
*
* This performs compile-time syntax highlighting and expands our custom
* components using React SSR.
*
* Yes, this would be so much nicer with next.js.
*/
import * as fs from "fs/promises";
import globModule from "glob";
import * as path from "path";
import { createElement, createElement as h, Fragment } from 'react';
import { renderToStaticMarkup } from "react-dom/server";
import rehypeHighlight from "rehype-highlight";
import rehypeParse from 'rehype-parse';
import rehypeReact from 'rehype-react';
import { unified } from 'unified';
import { promisify } from "util";
// Our components
import Recipe from "./components/Recipe.js";
import { noChildren } from "./components/support.js";
import { DataExport, WithExport } from "./components/WithExport.js";
const glob = promisify(globModule);
(async () => {
const base = "build/docs/lua";
const processor = unified()
.use(rehypeParse, { emitParseErrors: true })
.use(rehypeHighlight, { prefix: "" })
.use(rehypeReact, {
createElement,
Fragment,
passNode: false,
components: {
['mc-recipe']: noChildren(Recipe),
['mcrecipe']: noChildren(Recipe),
} as any
});
const dataExport = JSON.parse(await fs.readFile("src/web/export/index.json", "utf-8")) as DataExport;
for (const file of await glob(base + "/**/*.html")) {
const contents = await fs.readFile(file, "utf-8");
const { result } = await processor.process(contents);
const outputPath = path.resolve("build/docs/site", path.relative(base, file));
await fs.mkdir(path.dirname(outputPath), { recursive: true });
await fs.writeFile(outputPath, "<!doctype HTML>" + renderToStaticMarkup(<WithExport data={dataExport}>{result}</WithExport>));
}
})();

View File

@ -22,6 +22,9 @@
"noFallthroughCasesInSwitch": true,
"importsNotUsedAsValues": "error",
"forceConsistentCasingInFileNames": true,
// Needed for some of our internal stuff.
"allowSyntheticDefaultImports": true,
},
"include": [
"src/web",