Created
May 17, 2026 11:13
-
-
Save Birch-san/a9ec6c1cd3e73f55a6feb38e2dc9e95f to your computer and use it in GitHub Desktop.
Infinite Craft NAIScript JSX
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /*--- | |
| compatibilityVersion: naiscript-1.0 | |
| id: e30350cc-876f-46e5-ba3a-b0f37c642935 | |
| name: Infinite Craft | |
| createdAt: 1775407865919 | |
| updatedAt: 1779012871584 | |
| version: 1.0.0 | |
| author: Unknown | |
| memoryLimit: 8 | |
| ---*/ | |
| export default {} | |
| interface Elem { | |
| emoji: string | |
| name: string | |
| } | |
| interface World { | |
| elemList: string[] | |
| elemDict: Record<string, Elem> | |
| selectedElems: Set<string> | |
| generating: boolean | |
| dupeElem: Elem | undefined | |
| newElem: Elem | undefined | |
| precursors: Elem[] | undefined | |
| craftParseFailure: string | undefined | |
| generationError: string | undefined | |
| } | |
| const getInitialWorld = (): World => { | |
| type ElemTup = [emoji: string, name: string] | |
| const elems: ElemTup[] = [ | |
| ['💧', 'Water'], | |
| ['🔥', 'Fire'], | |
| ['🌬️', 'Wind'], | |
| ['🌍', 'Earth'], | |
| // ['💨', 'Steam'], | |
| ] | |
| return { | |
| elemList: elems.map(([_, name]) => name), | |
| elemDict: Object.fromEntries(elems.map((([emoji, name]) => [name, { | |
| emoji, name | |
| } satisfies Elem]))), | |
| selectedElems: new Set<string>(), | |
| generating: false, | |
| dupeElem: undefined, | |
| newElem: undefined, | |
| precursors: undefined, | |
| craftParseFailure: undefined, | |
| generationError: undefined, | |
| } | |
| } | |
| const fmtElem = ({ emoji, name }: Elem): string => | |
| `<elem>${emoji} ${name}</elem>`; | |
| const fmtEquation = (left: Elem, right: Elem): string => | |
| `${fmtElem(left)} + ${fmtElem(right)} = `; | |
| const fewShot: Message[] = [ | |
| { | |
| role: "user", | |
| content: fmtEquation( | |
| {emoji: '💧', name: 'Water'}, | |
| {emoji: '🔥', name: 'Fire'}, | |
| ), | |
| }, | |
| { | |
| role: "assistant", | |
| content: fmtElem({emoji: '💨', name: 'Steam'}), | |
| }, | |
| { | |
| role: "user", | |
| content: fmtEquation( | |
| {emoji: '💧', name: 'Water'}, | |
| {emoji: '🌬️', name: 'Wind'}, | |
| ), | |
| }, | |
| { | |
| role: "assistant", | |
| content: fmtElem({emoji: '🌧️', name: 'Rain'}), | |
| }, | |
| { | |
| role: "user", | |
| content: fmtEquation( | |
| {emoji: '🔥', name: 'Fire'}, | |
| {emoji: '🌍', name: 'Earth'}, | |
| ), | |
| }, | |
| { | |
| role: "assistant", | |
| content: fmtElem({emoji: 'Lava', name: '🌋'}), | |
| }, | |
| ] | |
| type Reducer<State, Action> = (state: State, action: Action) => State | |
| enum WorldActionType { | |
| PickElem = "PickElem", | |
| AddElem = "AddElem", | |
| SetDupeElem = "SetDupeElem", | |
| Generating = "Generating", | |
| ClearSelections = "ClearSelections", | |
| SetCraftParseFailure = "SetCraftParseFailure", | |
| SetGenerationError = "SetGenerationError" | |
| } | |
| interface AbstractReduceWorldAction<T extends WorldActionType> { | |
| type: T | |
| } | |
| interface PickElemAction extends AbstractReduceWorldAction<WorldActionType.PickElem> { | |
| name: string | |
| } | |
| interface AddElemAction extends AbstractReduceWorldAction<WorldActionType.AddElem> { | |
| elem: Elem | |
| precursors: Elem[] | |
| } | |
| interface SetDupeElemAction extends AbstractReduceWorldAction<WorldActionType.SetDupeElem> { | |
| elem: Elem | |
| precursors: Elem[] | |
| } | |
| interface GeneratingAction extends AbstractReduceWorldAction<WorldActionType.Generating> { | |
| generating: boolean | |
| } | |
| interface SetCraftParseFailureAction extends AbstractReduceWorldAction<WorldActionType.SetCraftParseFailure> { | |
| message: string | |
| } | |
| interface SetGenerationErrorAction extends AbstractReduceWorldAction<WorldActionType.SetGenerationError> { | |
| message: string | |
| } | |
| type ClearSelectionsAction = AbstractReduceWorldAction<WorldActionType.ClearSelections> | |
| type ReduceWorldAction = PickElemAction | GeneratingAction | AddElemAction | ClearSelectionsAction | SetCraftParseFailureAction | SetGenerationErrorAction | SetDupeElemAction | |
| type ElemsReducer<T extends ReduceWorldAction> = Reducer<World, T> | |
| type ReduceElemStrategyMap = { | |
| [K in WorldActionType]: ElemsReducer<Extract<ReduceWorldAction, {type: K}>>; | |
| }; | |
| const pickElemReducer: ElemsReducer<PickElemAction> = (state: World, { name }: PickElemAction): World => { | |
| const selectedElems: Set<string> = new Set(state.selectedElems) | |
| if (selectedElems.has(name)) { | |
| selectedElems.delete(name) | |
| } else{ | |
| selectedElems.add(name) | |
| } | |
| return { | |
| ...state, | |
| selectedElems, | |
| dupeElem: undefined, | |
| newElem: undefined, | |
| precursors: undefined, | |
| craftParseFailure: undefined, | |
| generationError: undefined, | |
| } | |
| } | |
| const addElemReducer: ElemsReducer<AddElemAction> = (state: World, { elem, precursors }: AddElemAction): World => { | |
| const { name } = elem | |
| const { elemList, elemDict } = state | |
| return { | |
| ...state, | |
| elemList: [...elemList, name], | |
| elemDict: { | |
| ...elemDict, | |
| [name]: elem, | |
| }, | |
| newElem: elem, | |
| precursors, | |
| } | |
| } | |
| const setDupeElemReducer: ElemsReducer<SetDupeElemAction> = (state: World, { elem: dupeElem, precursors }: SetDupeElemAction): World => ({ | |
| ...state, | |
| dupeElem, | |
| precursors, | |
| }) | |
| const clearSelectionsReducer: ElemsReducer<ClearSelectionsAction> = (state: World, _: ClearSelectionsAction): World => ({ | |
| ...state, | |
| selectedElems: new Set<string>(), | |
| }) | |
| const generatingReducer: ElemsReducer<GeneratingAction> = (state: World, { generating }: GeneratingAction): World => ({ | |
| ...state, | |
| generating, | |
| }) | |
| const setCraftParseFailureReducer: ElemsReducer<SetCraftParseFailureAction> = (state: World, { message: craftParseFailure }: SetCraftParseFailureAction): World => ({ | |
| ...state, | |
| craftParseFailure, | |
| }) | |
| const setGenerationErrorReducer: ElemsReducer<SetGenerationErrorAction> = (state: World, { message: generationError }: SetGenerationErrorAction): World => ({ | |
| ...state, | |
| generationError, | |
| }) | |
| const worldReducers: ReduceElemStrategyMap = { | |
| [WorldActionType.PickElem]: pickElemReducer, | |
| [WorldActionType.AddElem]: addElemReducer, | |
| [WorldActionType.SetDupeElem]: setDupeElemReducer, | |
| [WorldActionType.ClearSelections]: clearSelectionsReducer, | |
| [WorldActionType.Generating]: generatingReducer, | |
| [WorldActionType.SetCraftParseFailure]: setCraftParseFailureReducer, | |
| [WorldActionType.SetGenerationError]: setGenerationErrorReducer, | |
| } | |
| const applyStrategy = < | |
| State, | |
| Action extends { type: K }, | |
| K extends string | |
| >( | |
| strategies: { [T in K]: Reducer<State, Extract<Action, { type: T }>> }, | |
| state: State, | |
| action: Action | |
| ): State => { | |
| const k = action.type as K | |
| const strategy = strategies[k] as Reducer<State, Action> | |
| return strategy(state, action) | |
| } | |
| const worldReducer = (state: World, action: ReduceWorldAction): World => | |
| applyStrategy(worldReducers, state, action) | |
| const createStore = <S, A>(reducer: (s: S, a: A) => S, initial: S) => { | |
| let state = initial; | |
| const listeners = new Set<() => void>(); | |
| return { | |
| getState: () => state, | |
| dispatch: (action: A) => { | |
| state = reducer(state, action); | |
| listeners.forEach((l) => l()); | |
| }, | |
| subscribe: (listener: () => void) => { | |
| listeners.add(listener); | |
| return () => listeners.delete(listener); | |
| }, | |
| }; | |
| } | |
| const worldStore = createStore(worldReducer, getInitialWorld()); | |
| enum CraftOutcomeType { | |
| NewElement = "NewElement", | |
| Duplicate = "Duplicate", | |
| ParseError = "ParseError", | |
| GenerateError = "GenerateError" | |
| } | |
| interface AbstractCraftOutcome<T extends CraftOutcomeType> { | |
| type: T | |
| } | |
| interface NewElementCraftOutcome extends AbstractCraftOutcome<CraftOutcomeType.NewElement> { | |
| elem: Elem | |
| } | |
| interface DuplicateCraftOutcome extends AbstractCraftOutcome<CraftOutcomeType.Duplicate> { | |
| elem: Elem | |
| } | |
| interface ParseErrorCraftOutcome extends AbstractCraftOutcome<CraftOutcomeType.ParseError> { | |
| message: string | |
| } | |
| interface GenerateErrorCraftOutcome extends AbstractCraftOutcome<CraftOutcomeType.GenerateError> { | |
| message: string | |
| } | |
| type CraftOutcome = NewElementCraftOutcome | DuplicateCraftOutcome | ParseErrorCraftOutcome | GenerateErrorCraftOutcome | |
| // matches '<elem>🌫️ Dust</elem>' | |
| const matcher = /^<elem>(\p{Emoji_Presentation}|\p{Emoji}\ufe0f)\s+(\P{Emoji_Presentation}+)<\/elem>$/u | |
| // matches '<elem>Dust 🌪️</elem>' | |
| const altMatcher = /^<elem>(\P{Emoji_Presentation}+)\s+(\p{Emoji_Presentation}|\p{Emoji}\ufe0f)<\/elem>$/u | |
| async function craftElements(left: Elem, right: Elem, knownElems: Set<string>): Promise<CraftOutcome> { | |
| const prompt = fmtEquation(left, right) | |
| const cancellationSignal: CancellationSignal = await api.v1.createCancellationSignal() | |
| const cbk = (choices: GenerationChoice[], final: boolean): void | string => { | |
| // api.v1.log(choices, final); | |
| const total = choices.map(({text}) => text).join('') | |
| const match = matcher.exec(total) | |
| if (match) { | |
| cancellationSignal.cancel() | |
| } | |
| // it can get confused and generate it in reverse | |
| const altMatch = altMatcher.exec(total) | |
| if (altMatch) { | |
| cancellationSignal.cancel() | |
| } | |
| } | |
| const resp: GenerationResponse = await api.v1.generate( | |
| [ | |
| ...fewShot, | |
| { | |
| role: "user", | |
| content: prompt, | |
| }, | |
| ] satisfies Message[], | |
| { | |
| model: "glm-4-6", | |
| max_tokens: 50, | |
| temperature: 0.8, | |
| } satisfies GenerationParams, | |
| cbk, | |
| "blocking", | |
| cancellationSignal, | |
| ) | |
| const total = resp.choices.map(({text}) => text).join('') | |
| api.v1.log(`Response: ${total}`) | |
| const getMatch = (str: string): Elem | undefined => { | |
| const match = matcher.exec(str) | |
| if (match) { | |
| const [_, emoji, name] = match | |
| return { emoji, name } | |
| } | |
| const altMatch = altMatcher.exec(str) | |
| if (altMatch) { | |
| const [_, name, emoji] = altMatch | |
| return { emoji, name } | |
| } | |
| return undefined | |
| } | |
| const elem: Elem | undefined = getMatch(total) | |
| if (elem) { | |
| const { name } = elem | |
| const again = knownElems.has(name) | |
| api.v1.log(`Crafted ${name} ${again ? "again" : "for the first time!"}`) | |
| if (again) { | |
| return { type: CraftOutcomeType.Duplicate, elem } satisfies DuplicateCraftOutcome | |
| } | |
| return { type: CraftOutcomeType.NewElement, elem } satisfies NewElementCraftOutcome | |
| } | |
| return { type: CraftOutcomeType.ParseError, message: total } satisfies ParseErrorCraftOutcome | |
| } | |
| const assertNever = (value: never): never => { | |
| throw new Error(`Unhandled value: ${value}`); | |
| } | |
| worldStore.subscribe(async () => { | |
| const { selectedElems, generating, elemDict } = worldStore.getState(); | |
| // Guard: only trigger when exactly 2 are selected and not already generating | |
| if (selectedElems.size !== 2 || generating) return; | |
| const [elemAName, elemBName] = Array.from(selectedElems); | |
| worldStore.dispatch({ type: WorldActionType.Generating, generating: true } satisfies GeneratingAction); | |
| try { | |
| const elemA = elemDict[elemAName] | |
| const elemB = elemDict[elemBName] | |
| const outcome: CraftOutcome = await craftElements(elemA, elemB, new Set<string>(Object.keys(elemDict))) | |
| switch (outcome.type) { | |
| case CraftOutcomeType.Duplicate: | |
| { | |
| const { elem } = outcome | |
| const precursors: Elem[] = [elemA, elemB] | |
| worldStore.dispatch({ type: WorldActionType.SetDupeElem, elem, precursors } satisfies SetDupeElemAction); | |
| break; | |
| } | |
| case CraftOutcomeType.NewElement: | |
| { | |
| const { elem } = outcome | |
| const precursors: Elem[] = [elemA, elemB] | |
| worldStore.dispatch({ type: WorldActionType.AddElem, elem, precursors } satisfies AddElemAction); | |
| break; | |
| } | |
| case CraftOutcomeType.GenerateError: | |
| { | |
| const { message } = outcome | |
| api.v1.error(`Failed to generate. Error was: ${message}`) | |
| worldStore.dispatch({ type: WorldActionType.SetGenerationError, message } satisfies SetGenerationErrorAction); | |
| } | |
| case CraftOutcomeType.ParseError: | |
| { | |
| const { message } = outcome | |
| api.v1.log(`Failed to parse craft. Response was: ${message}`) | |
| worldStore.dispatch({ type: WorldActionType.SetCraftParseFailure, message } satisfies SetCraftParseFailureAction); | |
| break; | |
| } | |
| default: | |
| return assertNever(outcome) | |
| } | |
| } catch (err) { | |
| worldStore.dispatch({ type: WorldActionType.SetGenerationError, message: JSON.stringify(err, null, 2) } satisfies SetGenerationErrorAction); | |
| } | |
| worldStore.dispatch({ type: WorldActionType.ClearSelections } satisfies ClearSelectionsAction); | |
| worldStore.dispatch({ type: WorldActionType.Generating, generating: false } satisfies GeneratingAction); | |
| }); | |
| const useWorldSelector = <T,>(selector: (s: World) => T): T => | |
| useSyncExternalStore<T>( | |
| worldStore.subscribe, | |
| () => selector(worldStore.getState()) | |
| ) | |
| const disabledButtonStyles = { cursor: 'not-allowed', pointerEvents: 'none' } as const | |
| const selectedButtonStyles = { color: 'rgb(19, 21, 44)', backgroundColor: '#f4f3c6' } as const | |
| const ElemComponent = ({elem: {emoji, name}}: {elem: Elem}): JSX.Element => { | |
| const isSelected = useWorldSelector(({ selectedElems }: World) => selectedElems.has(name)) | |
| const isDisabled = useWorldSelector(({ generating }: World) => generating) | |
| const style = useMemo(() => ({ | |
| ...(isSelected && selectedButtonStyles), | |
| ...(isDisabled && disabledButtonStyles), | |
| }), [isSelected, isDisabled]) | |
| const handleClick = useCallback(() => { | |
| worldStore.dispatch({ type: WorldActionType.PickElem, name } satisfies PickElemAction) | |
| }, []); | |
| const onMouseEnter = useCallback(({ target }) => { | |
| api.v1.log("onMouseEnter") | |
| }, [isSelected]); | |
| const onMouseLeave = useCallback(({ target }) => { | |
| api.v1.log("onMouseLeave") | |
| }, [isSelected]); | |
| return <li> | |
| <button | |
| style={style} onClick={handleClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} | |
| >{emoji} {name}</button> | |
| </li> | |
| } | |
| const CraftPanel = (): JSX.Element => { | |
| const elemList = useWorldSelector(({ elemList }: World) => elemList) | |
| const elemDict = useWorldSelector(({ elemDict }: World) => elemDict) | |
| const craftParseFailure = useWorldSelector(({ craftParseFailure }: World) => craftParseFailure) | |
| const generationError = useWorldSelector(({ generationError }: World) => generationError) | |
| const generating = useWorldSelector(({ generating }: World) => generating) | |
| const dupeElem = useWorldSelector(({ dupeElem }: World) => dupeElem) | |
| const newElem = useWorldSelector(({ newElem }: World) => newElem) | |
| const precursors = useWorldSelector(({ precursors }: World) => precursors) | |
| return <div> | |
| <h1>Infinite Craft</h1> | |
| <ul style={{ display: 'flex', listStyle: 'none', gap: '0.5em', flexWrap: 'wrap' }}> | |
| { | |
| elemList.map((elemName: string): JSX.Element => { | |
| const elem: Elem = elemDict[elemName] | |
| return <ElemComponent key={`elem-${elemName}`} elem={elem}/> | |
| }) | |
| } | |
| </ul> | |
| {generating && <><div>Crafting...</div><div> </div></>} | |
| {dupeElem && <div>You crafted <span>{dupeElem.emoji} {dupeElem.name}</span> again.</div>} | |
| {newElem && <div>You just crafted <span>{newElem.emoji} {newElem.name}</span>!</div>} | |
| {precursors && <div>From: {precursors.map(({emoji, name}: Elem, ix: number) => <><span key={`precursor-${name}`}>{emoji} {name}</span>{ix < precursors.length-1 && ', '}</>)}</div>} | |
| {craftParseFailure && <div>Failed to parse craft output. Response was: | |
| <pre>{craftParseFailure}</pre> | |
| </div>} | |
| {generationError && <div>Failed to generate. Error was: | |
| <pre>{generationError}</pre> | |
| </div>} | |
| {/* stop layout jumping up and down */} | |
| {(!generating && !dupeElem && !newElem && !craftParseFailure && !generating) && <><div> </div><div> </div></>} | |
| </div> | |
| } | |
| await api.v1.ui.register([{ | |
| type: "scriptPanel", | |
| id: "craftPanel", | |
| name: "Craft", | |
| iconId: "tool", | |
| content: [{ | |
| type: "jsx", | |
| id: "craft-jsx-root", | |
| onMount: (elem: any) => render(<CraftPanel />, elem), | |
| style: { height: "100%" }, | |
| } satisfies UIPartJSX], | |
| }]); | |
| api.v1.log("Craft panel initialized successfully!"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment