How a JSON or JSON-like language enables the next generation of safe human and AI-generated UIs 소개 에서 , 나는 AI 생성 코드에 의해 생성 된 원자 JavaScript가 보안 문제이며 프레임워크가없는 JavaScript의 유연성은 관리하기 어려운 코드를 초래할 수 있다고 지적했다. 또는 A (cDOM) with (JPRX 경우), 우리가 우리의 인터페이스를 구축하기 위해 LLM을 신뢰하고 싶다면. AI-Generated UI의 미래 A2는 컴퓨터 집 JSON Pointer 정규 표현 또한 접근 방식이 업계 표준을 기반으로하는 경우 도움이됩니다. cCOM 및 JPRX 훈련 중에 LLM이 아마도 소비 한 많은 문서와 예가 있습니다. cCOM 및 JPRX는 JSON Pointers, JSON Schema 및 XPath의 개념과 구문을 통합합니다. 이전 기사에서 cDOM과 JPRX가 어떻게 작동하는지 보여주기 위해, 나는 반응 계산기를 사용했지만, 현실이 될 수 있습니다 : 반응 계산기와 할 일 목록은 쉽습니다. 논리가 소파에 맞을 때 모든 프레임 워크가 우아해 보입니다. JSON 기반 접근 방식을 실제로 유지하려면 혼란스러운 상태, 가장자리 케이스 및 구별 된 작동 모드에 문제가 필요합니다. 계산기는 본질적으로 어렵습니다 : 입력 모드: 우리는 신선한 숫자를 입력하거나 기존 숫자를 첨부합니까? 체인: 당신이 '+' 다음 '-' 다음 '*'을 칠 때 동등한 칠하지 않고 어떻게니까? DRY 논리: 버튼 0-9에 대한 10 개의 처리기 사이의 코드 차이를 최소화하는 방법은 무엇입니까? 그래서 나는 Claude Opus에게 완전히 기능적인 iOS 스타일의 계산기를 만들라고 요청했다. - 단지 선언적 cDOM 및 JPRX 표현. zero custom JavaScript functions 인공지능이 문서에서만 약간의 촉구로 선언적 계산기를 생산할 수 있다는 사실은 내가 이전 기사에서 한 또 다른 점을 보여줍니다 : cDOM과 JPRX는 단순히 새로운 구문이 아닙니다. 코드 인라인 설명을 허용하면서 문자와 인용 소음을 줄이려면 cDOM의 압축 버전인 cDOMC를 사용합니다.일반적인 cDOM은 코멘트를 지원하지 않으며 특성과 JPRX 표현 주위에 인용이 필요합니다. { div: { class: "calculator", // A calculator feels stateless, but it's actually a strict state machine. // You're never just "typing a number"; you're either entering the first operand, // waiting for an operator, or entering the next operand. onmount: =state({ display: "0", // What you see on the screen expr: "", // History string, (e.g. "8 + 5 =") prev: "", // value stored before an operation op: "", // the active operator waiting: false // true when expecting a new number vs operator },{ name: "c", // the root name of our state, so we can express things like: /c/display schema: "polymorphic", // allow type changes, e.g. "0" or 0 scope: $this // scope the path to the current element }), children: [ // Display area { div: { class: "display", children: [ { div: { class: "expression",children[=/c/expr] }}, { div: { class: "result",children[=/c/display] }} ] } }, // Button grid { div: { class: "buttons", children: [ // Row 1: AC, ±, %, ÷ { button: { class: "btn btn-clear", onclick: =/c = { display: "0", expr: "", prev: "", op: "", waiting: false }, children: ["AC"] } }, { button: { class: "btn btn-function", onclick: =/c = { display: negate(/c/display), waiting: true, expr: "" }, children: ["±"] } }, { button: { class: "btn btn-function", onclick: =/c = { display: toPercent(/c/display), waiting: true, expr: "" }, children: ["%"] } }, // Divison is our first operator. This is where it gets tricky. // When you click `+`, you can't just link `prev` to `display`. // If you did, `prev` would update every time you selected a new digit for the**second**number, // breaking the math. We need a snapshot of the value at that exact moment. // Excel solves this with INDIRECT, effectively dereferencing a cell. JPRX borrows the same concept: { button: { class: "btn btn-operator", onclick: =/c = { prev: indirect(/c/display), // Capture the value right now expr: concat(/c/display, " ÷"), op: "/", waiting: true }, children: ["÷"] } }, // Row 2: 7, 8, 9, × // I have 10 number buttons. Do I write 10 handlers? Do I write a loop? In React or Vue, // you'd probably map over an array. With JPRX, the DOM is the data key and although map is available, // I represent the calculator using literals in this example. In a future article I will cover map. // By giving each button an `id` (e.g., `id: "7"`), we write a uniform logic expression that adapts // to whichever element triggered it. We just reference $this.id in JPRX and use an xpath to get the text // content for the child node, #../@id. In cDOM (not JPRX) '#' delimits the start of an xpath expression { button: { id: "7", class: "btn btn-number", onclick: =/c = { display: if(/c/waiting, $this.id, if(/c/display==0, $this.id, concat(/c/display, $this.id))), waiting: false }, children: [#../@id] // use xpath (starting char #) to get the text for the button from parent id } }, // Here's what is happening: // Waiting for input? (e.g., just hit `+`) → Replace the display with the button's ID. // Displaying "0"? → Replace it (avoids "07"). // Otherwise: → Append the button's ID. // This is replicated identically for every number button. No loops, no external helper functions. { button: { id: "8", class: "btn btn-number", onclick: =/c = { display: if(/c/waiting, $this.id, if(/c/display==0), $this.id, concat(/c/display, $this.id))), waiting: false }, children: [#../@id] } }, { button: { id: "9", class: "btn btn-number", onclick: =/c = { display: if(/c/waiting, $this.id, if(/c/display==0, $this.id, concat(/c/display, $this.id))), waiting: false }, children: [#../@id] } }, { button: { class: "btn btn-operator", onclick: =/c = { prev: indirect(/c/display), expr: concat(/c/display, " ×"), op: "*", waiting: true }, children: ["×"] } }, // Row 3: 4, 5, 6, − { button: { id: "4", class: "btn btn-number", onclick: =/c = { display: if(/c/waiting, $this.id, if(/c/display==0, $this.id, concat(/c/display, $this.id))), waiting: false }, children: [#../@id] } }, { button: { id: "5", class: "btn btn-number", onclick: =/c = { display: if(/c/waiting, $this.id, if(/c/display==0, $this.id, concat(/c/display, $this.id))), waiting: false }, children: [#../@id] } }, { button: { id: "6", class: "btn btn-number", onclick: =/c = { display: if(/c/waiting, $this.id, if(/c/display==0, $this.id, concat(/c/display, $this.id))), waiting: false }, children: [#../@id] } }, { button: { class: "btn btn-operator", onclick: =/c = { prev: indirect(/c/display), expr: concat(/c/display, " −"), op: "-", waiting: true }, children: ["−"] } }, // Row 4: 1, 2, 3, +, use set and eq just to demonstrate equivalence with = and == // the buttons below use 'set' in place of the infix operator '=', just to show a different way of doing things { button: { id: "1", class: "btn btn-number", onclick: =set(/c, { display: if(/c/waiting, $this.id, if(eq(/c/display, "0"), $this.id, concat(/c/display, $this.id))), waiting: false }), children: [#../@id] } }, { button: { id: "2", class: "btn btn-number", onclick: =set(/c, { display: if(/c/waiting, $this.id, if(eq(/c/display, "0"), $this.id, concat(/c/display, $this.id))), waiting: false }), children: [#../@id] } }, { button: { id: "3", class: "btn btn-number", onclick: =set(/c, { display: if(/c/waiting, $this.id, if(eq(/c/display, "0"), $this.id, concat(/c/display, $this.id))), waiting: false }), children: [#../@id] } }, { button: { class: "btn btn-operator", onclick: =set(/c, { prev: indirect(/c/display), expr: concat(/c/display, " +"), op: "+", waiting: true }), children: ["+"] } }, // Row 5: 0, ., = { button: { id: "0", class: "btn btn-number btn-wide", onclick: =set(/c, { display: if(/c/waiting, $this.id, if(eq(/c/display, "0"), "0", concat(/c/display, $this.id))), waiting: false }), children: [#../@id] } }, { button: { class: "btn btn-number", onclick: =set(/c, { display: if(/c/waiting, "0.", if(contains(/c/display, "."), /c/display, concat(/c/display, "."))), waiting: false }), children: ["."] } }, // Finally, the math. We need to say: // 1. Take the snapshot we stored // 2. Apply the current operator // 3. combine it with what's on screen now // This is the job of calc(). If prev == 8 and op == * and display = 5, then calc would be evaluated as calc("8 * 5") // To keep the syntax a little cleaner we also use $(<path>) as a shorthand for indirect. { button: { class: "btn btn-equals", onclick: =set(/c, { display: if(eq(/c/op, ""), /c/display, calc(concat("$('/c/prev') ", /c/op, " $('/c/display')"))), expr: concat(/c/expr, " ", /c/display, " ="), prev: "", op: "", waiting: true }), children: ["="] } } ] } }, // Branding { div: { class: "branding", children: [ { span: { children: [ "Built with ", { a: { href: "https://github.com/anywhichway/lightview", target: "_blank", children: ["Lightview"] } }, " cDOM • No custom JS!" ] } } ] } } ] } } Lightview Hypermedia를 사용하여 cDOM 로딩 Lightview는 hypermedia 기능과 비슷한 기능을 지원합니다. 사용을 허용함으로써, the use of the 거의 모든 요소에 해당되는 글 1건 HTMX src 단순한 참조 A 파일 사용 : cDOM src <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content="A beautiful calculator built with Lightview cDOM and JPRX reactive expressions - no custom JavaScript!"> <title>Calculator | Lightview cDOM</title> <link rel="stylesheet" href="calculator.css"> <!-- Load Lightview scripts --> <script src="/lightview.js"></script> <!-- DOM as JSON and reactivity support --> <script src="/lightview-x.js"></script> <!-- hypermedia support --> <script src="/lightview-cdom.js"></script> <-- cDOM/JPRX support --> </head> <body> <!-- The calculator cDOM is loaded via Lightview's hypermedia src attribute --> <div id="app" src="./calculator.cdomc"></div> </body> </html> 이 특성은 HTML처럼 작동합니다. 또는 태그 - Lightview는 자동으로 파일을 분석하고, 반응적인 내용을 대상 요소로 변환합니다.This approach: src <img> <script> .cdomc 왜 이런 식으로 건설하는가? 당신은 볼 수 있습니다 그리고 물어보라 : concat("$('/c/prev') ...") 왜 세상에 너는 단지 글을 쓰지 않을까 parseFloat(prev) + parseFloat(curr) ? 만약 당신이 자신을 위한 인간 코드 작성자라면?당신은 아마도.Lightview는 그 이유로 표준 JS 핸들러를 지원합니다. 그러나 당신이 인프라를 구축하는 경우 선언적이고 JSON 기반의 경로에 붙어있는 것은 원본 코드가 할 수없는 것을 제공합니다. AI Agents 샌드박싱: 그것은 제어 된 환경에서 실행합니다. 논리는 '윈도우'에 액세스 할 수 없으며, 글로벌 픽치 요청을하거나 임의의 초기 코드를 실행할 수 없습니다.이로 인해 LLM이 생성한 UI 논리를 실시간으로 "뜨거운 교환"할 수 있습니다. 이 전체 UI - 논리 및 모든 -는 단지 데이터입니다.그것은 서버에서 보낼 수 있습니다, 데이터베이스에 저장, 또는 AI 모델에서 스트리밍. 정신 모델 : 그것은 상태 변환과 견해 구조 사이의 명확한 분리를 강요합니다.이것은 LLM이 가장 잘 이유하는 방법입니다. 이 계산기는 "선언"은 "불명"을 의미 할 필요가 없다는 것을 증명합니다.With the right primitives - state, conditionals, and path-based referencing - you can build rich, complex interactions without ever leaving the data structure. 더 큰 그림 이 시리즈는 새로운 도서관에 관한 것이 아니라 AI 시대에 적합한 추상 계층을 찾는 것입니다. 에서 , 우리는 LLM이 원시 스크립트를 작성하도록 허용하는 위험을 살펴보고 "Data as UI"철학을 도입했습니다. AI-Generated UI의 미래 이 문서에서 우리는 "Data as UI"는 "dumb UI"를 의미하지 않는다는 것을 보여주었습니다.우리는 상태, 컨텍스트, 데이터 스냅샷, 수학 및 DOM 탐색을 "xpath"로 처리했으며 사용자 지정 JavaScript의 단일 줄을 실행하지 않았습니다. cDOM은 구조를 정의합니다. JPRX는 행동을 정의합니다. 이것은 컴파일 없이의 반응성과 보안 위험 없이의 UI입니다. 직접 시도해 보세요 전체 계산기는 다음에서 사용할 수 있습니다: The complete calculator is available at: 라이브 데모: https://lightview.dev/docs/calculator.html 원본 코드: https://github.com/anywhichway/lightview/blob/main/docs/calculator.html https://lightview.dev/docs/calculator.html https://github.com/anywhichway/lightview/blob/main/docs/calculator.html