Skip to content

Commit 1b2eabe

Browse files
committed
Merge branch 'cursor/development-environment-setup-7672'
2 parents 06fe2b8 + cc4fcab commit 1b2eabe

File tree

2 files changed

+53
-180
lines changed

2 files changed

+53
-180
lines changed

blocks/generators.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,10 +1810,35 @@ await (async function() {
18101810
try {
18111811
// Use eval to safely execute generated code
18121812
// First create a context containing all necessary helper functions
1813+
// sendAndWait: send command and wait for robot completion token
1814+
async function __sendAndWait(cmd, timeout, token) {
1815+
const _wr = (typeof window !== 'undefined' && window.webRequest) ? window.webRequest : webRequest;
1816+
const __from = (typeof serialBuffer === 'string')
1817+
? serialBuffer.length
1818+
: ((typeof window !== 'undefined' && typeof window.serialBuffer === 'string') ? window.serialBuffer.length : undefined);
1819+
await _wr(cmd, timeout, true);
1820+
if (!((typeof window !== 'undefined') && window.petoiClient) && typeof waitForSerialTokenLine === 'function') {
1821+
await waitForSerialTokenLine(token, timeout, __from);
1822+
}
1823+
if (typeof window !== 'undefined') window.__lastTokenReceivedAt = Date.now();
1824+
}
1825+
// delayAfterToken: delay starting from when robot completed
1826+
async function __delayAfterToken(ms) {
1827+
const __tokenAt = (typeof window !== 'undefined' && typeof window.__lastTokenReceivedAt === 'number') ? window.__lastTokenReceivedAt : Date.now();
1828+
const __endAt = __tokenAt + ms;
1829+
while (Date.now() < __endAt) {
1830+
checkStopExecution();
1831+
const __wait = Math.min(100, __endAt - Date.now());
1832+
if (__wait > 0) await new Promise(r => setTimeout(r, __wait));
1833+
}
1834+
}
18131835
const execContext = {
1836+
sendAndWait: __sendAndWait,
1837+
delayAfterToken: __delayAfterToken,
18141838
checkStopExecution: checkStopExecution,
18151839
checkStopExecutionInLoop: checkStopExecutionInLoop,
18161840
webRequest: (typeof window !== 'undefined' && window.webRequest) ? window.webRequest : webRequest,
1841+
waitForSerialTokenLine: (typeof waitForSerialTokenLine === 'function') ? waitForSerialTokenLine : undefined,
18171842
delay: delay,
18181843
encodeCommand: encodeCommand,
18191844
parseSingleResult: parseSingleResult,

main.html

Lines changed: 28 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ <h4 style="margin: 0 0 10px 0;" data-i18n="uploadedSkills">Uploaded Skills</h4>
186186

187187
// Global version date variable (format: vYear.Month.Day)
188188
const GLOBAL_VERSION_DATE = "v2026.03.26";
189-
const GLOBAL_VERSION_LAST_MODIFIED = "2026-03-26 21:29:58";
189+
const GLOBAL_VERSION_LAST_MODIFIED = "2026-03-26 21:45:55";
190190

191191
// Set version date
192192
function setVersionDate()
@@ -4295,186 +4295,34 @@ <h3 style="color: #4CAF50;">${title}</h3>
42954295
}
42964296
}
42974297

4298-
// AI代码生成主函数
4298+
// AI代码生成主函数 - 系统提示词需要极度精简,因为使用GET URL传递,有长度限制
42994299
async function generateAICode(description) {
4300-
const config = await loadBlocksConfigForAI();
4301-
4302-
// 构建提示词
4303-
let systemPrompt = `You are a code generator for Petoi robot Blockly-based visual programming. Generate JavaScript code that uses the available helper functions.
4304-
4305-
IMPORTANT RULES:
4306-
1. Only generate JavaScript code - NO explanations, NO markdown code blocks, NO comments unless necessary
4307-
2. Use ONLY the provided helper functions - do not invent new ones
4308-
3. Always use await for async functions
4309-
4. Include checkStopExecution() at appropriate points in loops
4310-
5. All code must be valid JavaScript that can be executed in an async context
4311-
6. CRITICAL: For ALL motion/action commands, use sendAndWait() to wait for the robot's completion token BEFORE executing the next command. NEVER fire-and-forget motion commands.
4312-
7. Define sendAndWait() and delayAfterToken() helpers at the top of generated code as shown in CODE PATTERNS
4313-
8. Use delayAfterToken(ms) instead of delay(ms) for delays between actions - it starts timing from when the robot finished, not when the command was sent
4314-
4315-
AVAILABLE HELPER FUNCTIONS:
4316-
- checkStopExecution(): Call at the start of code and in loops to respect stop signals
4317-
- checkStopExecutionInLoop(): Use inside loops for stop checking with yield
4318-
- webRequest(command, timeout=5000, needResponse=true): Send command to robot via WebSocket/Serial
4319-
- waitForSerialTokenLine(token, timeout, fromIndex): Wait for robot completion token in serial mode
4320-
- delay(ms): Promise-based delay with stop checking
4321-
- encodeCommand(token, params): Encode command with token and parameters
4322-
- parseSingleResult(rawResult): Parse numeric result from response
4323-
- parseAllJointsResult(rawResult): Parse joint angles array from response
4324-
- console: Standard console object for logging
4325-
4326-
AVAILABLE COMMANDS (token-based):
4327-
`;
4328-
4329-
// 从配置添加命令信息
4330-
if (config && config.command_reference && config.command_reference.token_commands) {
4331-
const commands = config.command_reference.token_commands;
4332-
for (const [token, info] of Object.entries(commands)) {
4333-
systemPrompt += `- ${token}: ${info.description || ''} | Params: ${info.params || 'none'} | Example: ${info.example || ''}\n`;
4334-
}
4335-
}
4336-
4337-
systemPrompt += `
4338-
MOTION COMMANDS (lowercase k* or d, NO encodeCommand needed):
4339-
Posture: stand=kup, balance=kbalance, sit=ksit, rest=d, stretch=kstr, buttUp=kbuttUp, calibration=kcalib
4340-
Gait: step=kvtF, spinL=kvtL, spinR=kvtR, walkF=kwkF, walkL=kwkL, walkR=kwkR, backF=kbkF, backL=kbkL, backR=kbkR, trotF=ktrF, trotL=ktrL, trotR=ktrR, crawlF=kcrF, crawlL=kcrL, crawlR=kcrR, strideF=kgpF, strideL=kgpL, strideR=kgpR, pushF=kphF, pushL=kphL, moonwalk=kmw
4341-
Behavior: hi/wave=khi, handshake=khsk, highFive=kfiv, hug=khg, handsUp=khu, nod=knd, comeHere=kcmh, goodBoy=kgdb, cheers=kchr, pee=kpee, sniff=ksnf, check=kck, dig=kdg, angry=kang, scratch=kscrh, waveHead=kwh, beTable=ktbl, playDead=kpd, roll=krl, recover=krc, pushUp=kpu, pushUp1hand=kpu1, kick=kkc, leapOver=klpov, testServo=kts
4342-
Acrobatic(⚠️high stress): handstand=khds, boxing=kbx, backflip=kflipD, frontflip=kflipF, jump=kjmp
4343-
Arm: pickD/F/L/R=kpickD/F/L/R, putD/F/L/R=kputD/F/L/R, shoot=klaunch, throwF/L/R=ktossF/L/R, hunt=khunt, showOff=kshowOff, clap=kclap
4344-
Direction suffix: append L=left, R=right, X=random to any skill for directional variants.
4345-
4346-
IMPORTANT: ALWAYS use a single k* command for named actions. NEVER compose complex joint movements when a built-in skill exists. Command pattern: k + abbreviated name (e.g. handshake=khsk, hi=khi).
4347-
4348-
I/O COMMANDS (UPPERCASE, MUST use encodeCommand):
4349-
- Digital input: encodeCommand("Rd", [34]) for "Rd 34"
4350-
- Analog input: encodeCommand("Ra", [34]) for "Ra 34"
4351-
- Digital output: encodeCommand("Wd", [34, 1]) for "Wd 34 1"
4352-
- Analog output: encodeCommand("Wa", [2, 128]) for "Wa 2 128"
4353-
4354-
MUSIC COMMANDS - CRITICAL DISTINCTION:
4355-
- SHORT melodies (1-10 notes): lowercase "b" + ASCII string - NO encodeCommand
4356-
Example: await webRequest("b 14 4 16 4 18 2", 5000, true)
4357-
LIMITATION: ASCII 'b' format has ~200 byte limit. Each "b 14 4" takes 6+ bytes.
4358-
4359-
- LONG melodies (10+ notes, up to 1250 notes): UPPERCASE "B" + BINARY encoding - MUST use encodeCommand
4360-
Example: const melody = encodeCommand("B", [14, 4, 16, 4, 18, 2, 14, 2]);
4361-
await webRequest(melody, 30000, true);
4362-
CAPACITY: NyBoard supports ~250 notes, BiBoard supports ~1250 notes in binary format
4363-
4364-
- NOTES use numbers 0-35 (0 or -1 for silence, 14=middle C3, 26=middle C4)
4365-
- DURATIONS: 1=1sec, 2=1/2sec, 4=1/4sec, 8=1/8sec, 16=1/16sec
4366-
- For melodies >30 notes: ALWAYS use UPPERCASE B with encodeCommand, increase timeout to 30-60s
4367-
4368-
SENSOR/JOINT COMMANDS (lowercase, NO encodeCommand):
4369-
- Query joints: "j" or "j 0" for single joint
4370-
- Gyro enable: encodeCommand("g", ["B"]) - special case, g is lowercase but uses encodeCommand
4371-
- Gyro disable: encodeCommand("g", ["b"])
4372-
4373-
EXTENSION COMMANDS (UPPERCASE X*, MUST use encodeCommand with ~ terminator):
4374-
- Ultrasonic: encodeCommand("XU", [9, 9]) returns "XU 9 9~" (bytes format internally)
4375-
- Camera activate: encodeCommand("XCr", []) returns "XCr~"
4376-
- Camera poll: encodeCommand("XCP", []) returns "XCP~"
4377-
- Gesture enable: encodeCommand("XGr", []) returns "XGr~"
4378-
- Gesture poll: encodeCommand("XGp", []) returns "XGp~"
4379-
- Gesture exit: encodeCommand("Xg", []) returns "Xg~"
4380-
4381-
IMPORTANT ENCODING RULES:
4382-
1. LOWERCASE commands (k*, m, i, j, b for short melodies, d): Send directly as string, NO encodeCommand
4383-
2. UPPERCASE single letter (R, W, g with params B/b): Use encodeCommand(token, params)
4384-
3. UPPERCASE multi-letter (XU, XCr, XCP, XGr, XGp, Xg): Use encodeCommand(token, params), adds ~ automatically
4385-
4. MUSIC FORMAT RULES (CRITICAL):
4386-
- Short (≤10 notes): await webRequest("b 14 4 16 4...", timeout, true) - ASCII format
4387-
- Long (>10 notes): const m = encodeCommand("B", [14,4,16,4...]); await webRequest(m, 30000, true) - BINARY format
4388-
- Note: UPPERCASE B uses binary encoding, supports 250-1250 notes vs 10-20 for lowercase b
4389-
5. When in doubt: Motion/short music use direct strings, I/O/sensors/long music use encodeCommand
4390-
4391-
CRITICAL: WAITING FOR ROBOT TOKEN BEFORE NEXT COMMAND
4392-
Every command MUST wait for the robot's completion token before executing the next command.
4393-
Use the sendAndWait() helper which handles both Serial and WebSocket modes:
4394-
4395-
// Helper: send command and wait for robot completion token
4396-
async function sendAndWait(cmd, timeout, token) {
4397-
const __from = (typeof serialBuffer === 'string')
4398-
? serialBuffer.length
4399-
: ((typeof window !== 'undefined' && typeof window.serialBuffer === 'string') ? window.serialBuffer.length : undefined);
4400-
await webRequest(cmd, timeout, true);
4401-
if (!((typeof window !== 'undefined') && window.petoiClient) && typeof waitForSerialTokenLine === 'function') {
4402-
await waitForSerialTokenLine(token, timeout, __from);
4403-
}
4404-
if (typeof window !== 'undefined') window.__lastTokenReceivedAt = Date.now();
4405-
}
4406-
4407-
// Helper: delay after token received (respects stop)
4408-
async function delayAfterToken(ms) {
4409-
const __tokenAt = (typeof window !== 'undefined' && typeof window.__lastTokenReceivedAt === 'number') ? window.__lastTokenReceivedAt : Date.now();
4410-
const __endAt = __tokenAt + ms;
4411-
while (Date.now() < __endAt) {
4412-
checkStopExecution();
4413-
const __wait = Math.min(100, __endAt - Date.now());
4414-
if (__wait > 0) await new Promise(r => setTimeout(r, __wait));
4415-
}
4416-
}
4417-
4418-
CODE PATTERNS (ALWAYS use sendAndWait for motion/action commands):
4419-
// Motion gait (token: 'k'):
4420-
await sendAndWait("kwkF", 20000, 'k');
4421-
4422-
// Motion posture (token: first char of command, e.g. 'k' for kup, 'd' for rest):
4423-
await sendAndWait("kup", 10000, 'k');
4424-
await sendAndWait("d", 10000, 'd');
4425-
4426-
// Motion with delay (delay starts AFTER robot completes):
4427-
await sendAndWait("kup", 10000, 'k');
4428-
await delayAfterToken(1000);
4429-
4430-
// Acrobatic (token: 'k'):
4431-
await sendAndWait("kbx", 20000, 'k');
4432-
4433-
// Joint control - sequential (token: 'm'):
4434-
await sendAndWait(encodeCommand("m", [0, 30, 1, -20]), 15000, 'm');
4435-
4436-
// Joint control - simultaneous (token: 'i'):
4437-
await sendAndWait(encodeCommand("i", [0, 30, 1, -20]), 30000, 'i');
4438-
4439-
// Read sensor (token varies, use webRequest directly for queries):
4440-
const cmd = encodeCommand("Ra", [34]);
4441-
const result = await webRequest(cmd, 5000, true);
4442-
const value = parseSingleResult(result);
4443-
4444-
// Query all joints:
4445-
const result = await webRequest("j", 5000, true);
4446-
const angles = parseAllJointsResult(result);
4447-
4448-
// Play short melody (token: 'b'):
4449-
await sendAndWait("b 20 4", 5000, 'b');
4450-
4451-
// Play long melody (token: 'B'):
4452-
const melody = encodeCommand("B", [14,4,16,4,18,2,14,4,16,4,18,2,21,4,19,4,18,4,16,4,14,2]);
4453-
await sendAndWait(melody, 30000, 'B');
4454-
4455-
// Ultrasonic sensor:
4456-
const cmd = encodeCommand("XU", [9, 9]);
4457-
const result = await webRequest(cmd, 5000, true);
4458-
const distance = parseSingleResult(result);
4459-
4460-
// Gyro control:
4461-
await sendAndWait(encodeCommand("g", ["B"]), 5000, 'G');
4462-
await sendAndWait(encodeCommand("g", ["b"]), 5000, 'G');
4463-
4464-
// Loop with stop checking:
4465-
for (let i = 0; i < 5; i++) {
4466-
await checkStopExecutionInLoop();
4467-
await sendAndWait("kwkF", 20000, 'k');
4468-
}
4469-
4470-
// Multiple sequential actions (each waits for token before next):
4471-
await sendAndWait("kup", 10000, 'k');
4472-
await delayAfterToken(500);
4473-
await sendAndWait("khi", 10000, 'k');
4474-
await delayAfterToken(1000);
4475-
await sendAndWait("ksit", 10000, 'k');
4476-
4477-
Respond with ONLY executable JavaScript code, nothing else.`;
4300+
const systemPrompt = `Petoi robot JS code generator. Output ONLY executable async JS, no markdown/comments.
4301+
4302+
HELPERS (already defined, just call them):
4303+
await sendAndWait(cmd,timeout,token) - send cmd, wait for robot token. MUST use for all motion.
4304+
await delayAfterToken(ms) - delay after robot completes. Use between actions.
4305+
checkStopExecution() - call in loops.
4306+
await webRequest(cmd,timeout,true) - for sensor queries.
4307+
encodeCommand(token,params) - for UPPERCASE cmds (Rd,Ra,Wd,Wa,XU,g,B,m,i).
4308+
parseSingleResult(r) / parseAllJointsResult(r) - parse responses.
4309+
4310+
SKILLS (k* cmds, send as string, token='k', rest d token='d'):
4311+
Posture: stand=kup,balance=kbalance,sit=ksit,rest=d,stretch=kstr,buttUp=kbuttUp,calib=kcalib
4312+
Gait: step=kvtF,spinL=kvtL,spinR=kvtR,walkF=kwkF,walkL=kwkL,walkR=kwkR,backF=kbkF,backL=kbkL,backR=kbkR,trotF=ktrF,trotL=ktrL,trotR=ktrR,crawlF=kcrF,crawlL=kcrL,crawlR=kcrR,strideF=kgpF,strideL=kgpL,strideR=kgpR,pushF=kphF,pushL=kphL,moonwalk=kmw
4313+
Behavior: hi=khi,handshake=khsk,highFive=kfiv,hug=khg,handsUp=khu,nod=knd,comeHere=kcmh,goodBoy=kgdb,cheers=kchr,pee=kpee,sniff=ksnf,check=kck,dig=kdg,angry=kang,scratch=kscrh,waveHead=kwh,beTable=ktbl,playDead=kpd,roll=krl,recover=krc,pushUp=kpu,pushUp1=kpu1,kick=kkc,leapOver=klpov
4314+
Acrobatic: handstand=khds,boxing=kbx,backflip=kflipD,frontflip=kflipF,jump=kjmp
4315+
Arm: pickD/F/L/R=kpickD/F/L/R,putD/F/L/R=kputD/F/L/R,shoot=klaunch,throwF/L/R=ktossF/L/R,hunt=khunt,showOff=kshowOff,clap=kclap
4316+
Suffix: L=left,R=right,X=random. Use k+abbreviation for unknown actions.
4317+
4318+
I/O: encodeCommand("Rd",[pin])/"Ra"/"Wd",[pin,val]/"Wa"
4319+
Music: short b "b 14 4 16 4",long encodeCommand("B",[14,4,16,4...]) Notes:0-35,Duration:1/2/4/8/16
4320+
Joints: "j"=query,encodeCommand("m",[id,angle,...])=seq,encodeCommand("i",[...])=sim
4321+
Sensor: encodeCommand("XU",[9,9])=ultrasonic,encodeCommand("g",["B"])=gyroOn,"g",["b"]=off
4322+
Gesture: encodeCommand("XGr",[])=enable,"XGp"=poll,"Xg"=exit
4323+
4324+
PATTERN: await sendAndWait("khi",10000,'k'); await delayAfterToken(500); await sendAndWait("khsk",10000,'k');
4325+
RULE: ALWAYS use built-in k* skill for named actions. NEVER compose joint movements for actions that have a skill command.`;
44784326

44794327
const userPrompt = `Generate JavaScript code for: "${description}"`;
44804328
const fullPrompt = userPrompt;

0 commit comments

Comments
 (0)