index.html 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002
  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>nEngine Editor</title>
  7. <style>
  8. html,
  9. body {
  10. margin: 0;
  11. height: 100%;
  12. background: #171b20;
  13. color: #e6e9ec;
  14. font-family: system-ui, sans-serif;
  15. font-size: 14px;
  16. }
  17. #app {
  18. display: flex;
  19. flex-direction: column;
  20. height: 100%;
  21. }
  22. .screen {
  23. flex: 1;
  24. display: flex;
  25. flex-direction: column;
  26. padding: 24px;
  27. gap: 12px;
  28. overflow: auto;
  29. }
  30. .hidden {
  31. display: none;
  32. }
  33. h1 {
  34. margin: 0;
  35. font-size: 28px;
  36. color: #a5d6a7;
  37. }
  38. h2 {
  39. margin: 24px 0 8px;
  40. font-size: 15px;
  41. color: #90a4ae;
  42. text-transform: uppercase;
  43. letter-spacing: 0.08em;
  44. }
  45. h3 {
  46. margin: 0 0 8px;
  47. font-size: 13px;
  48. color: #90a4ae;
  49. text-transform: uppercase;
  50. letter-spacing: 0.08em;
  51. }
  52. button {
  53. font: inherit;
  54. padding: 7px 16px;
  55. border: 1px solid #4a5560;
  56. border-radius: 5px;
  57. background: #262c34;
  58. color: #e6e9ec;
  59. cursor: pointer;
  60. }
  61. button:hover {
  62. background: #313a45;
  63. }
  64. button.primary {
  65. background: #2e7d32;
  66. border-color: #66bb6a;
  67. }
  68. button.primary:hover {
  69. background: #388e3c;
  70. }
  71. #home-actions {
  72. display: flex;
  73. gap: 10px;
  74. }
  75. #recent-list {
  76. list-style: none;
  77. margin: 0;
  78. padding: 0;
  79. display: flex;
  80. flex-direction: column;
  81. gap: 6px;
  82. max-width: 560px;
  83. }
  84. #recent-list button {
  85. width: 100%;
  86. text-align: left;
  87. display: flex;
  88. justify-content: space-between;
  89. gap: 12px;
  90. }
  91. #recent-list .recent-path {
  92. color: #7f8c99;
  93. font-size: 12px;
  94. }
  95. .muted {
  96. color: #7f8c99;
  97. }
  98. #home-error {
  99. color: #ef5350;
  100. min-height: 18px;
  101. }
  102. #topbar {
  103. display: flex;
  104. align-items: center;
  105. gap: 14px;
  106. padding: 10px 16px;
  107. background: #1f242b;
  108. border-bottom: 1px solid #2d343d;
  109. }
  110. #project-name {
  111. font-weight: 600;
  112. color: #a5d6a7;
  113. }
  114. #project-path {
  115. color: #7f8c99;
  116. font-size: 12px;
  117. flex: 1;
  118. min-width: 0;
  119. overflow: hidden;
  120. text-overflow: ellipsis;
  121. white-space: nowrap;
  122. }
  123. #mcp-url {
  124. color: #7f8c99;
  125. font-size: 12px;
  126. font-family: ui-monospace, monospace;
  127. max-width: 280px;
  128. overflow: hidden;
  129. text-overflow: ellipsis;
  130. white-space: nowrap;
  131. }
  132. #mcp-url.running {
  133. color: #a5d6a7;
  134. }
  135. #workspace {
  136. flex: 1;
  137. display: flex;
  138. min-height: 0;
  139. }
  140. #content-browser {
  141. width: 240px;
  142. border-right: 1px solid #2d343d;
  143. padding: 14px;
  144. overflow: auto;
  145. background: #1c2127;
  146. }
  147. #content-list {
  148. list-style: none;
  149. margin: 0;
  150. padding: 0;
  151. display: flex;
  152. flex-direction: column;
  153. gap: 3px;
  154. }
  155. #content-list li {
  156. display: flex;
  157. justify-content: space-between;
  158. align-items: center;
  159. padding: 4px 8px;
  160. border-radius: 4px;
  161. font-size: 13px;
  162. }
  163. #content-list li:hover {
  164. background: #262c34;
  165. }
  166. #content-list li.clickable {
  167. cursor: pointer;
  168. }
  169. .kind-name {
  170. color: #cfd8dc;
  171. }
  172. .kind-count {
  173. color: #7f8c99;
  174. font-variant-numeric: tabular-nums;
  175. }
  176. .kind-files {
  177. list-style: none;
  178. margin: 2px 0 0 10px;
  179. padding: 0;
  180. display: flex;
  181. flex-direction: column;
  182. gap: 2px;
  183. }
  184. .kind-file {
  185. font-size: 12px;
  186. padding: 3px 8px;
  187. text-align: left;
  188. width: 100%;
  189. background: #1f242b;
  190. }
  191. .kind-file:hover {
  192. background: #313a45;
  193. }
  194. .kind-new {
  195. padding: 2px 9px;
  196. font-size: 13px;
  197. line-height: 1.2;
  198. flex: none;
  199. }
  200. #editor-panel {
  201. flex: 1;
  202. padding: 16px;
  203. display: flex;
  204. flex-direction: column;
  205. gap: 8px;
  206. min-width: 0;
  207. }
  208. #editor-panel .placeholder {
  209. color: #7f8c99;
  210. border: 1px dashed #2d343d;
  211. border-radius: 6px;
  212. padding: 32px;
  213. text-align: center;
  214. }
  215. .form-panel {
  216. display: flex;
  217. flex-direction: column;
  218. gap: 10px;
  219. flex: 1;
  220. min-height: 0;
  221. }
  222. .form-header {
  223. display: flex;
  224. align-items: center;
  225. gap: 10px;
  226. flex-wrap: wrap;
  227. }
  228. .form-header h3 {
  229. margin: 0;
  230. }
  231. .form-path {
  232. color: #7f8c99;
  233. font-size: 12px;
  234. flex: 1;
  235. min-width: 0;
  236. overflow: hidden;
  237. text-overflow: ellipsis;
  238. white-space: nowrap;
  239. }
  240. .form-errors {
  241. color: #ef5350;
  242. font-size: 12px;
  243. max-height: 140px;
  244. overflow: auto;
  245. }
  246. .form-errors ul {
  247. margin: 0;
  248. padding-left: 18px;
  249. }
  250. .form-body {
  251. flex: 1;
  252. min-height: 0;
  253. overflow: auto;
  254. display: flex;
  255. flex-direction: column;
  256. gap: 10px;
  257. padding: 10px;
  258. background: #1c2127;
  259. border: 1px solid #2d343d;
  260. border-radius: 6px;
  261. }
  262. .form-field {
  263. display: flex;
  264. flex-direction: column;
  265. gap: 4px;
  266. max-width: 560px;
  267. }
  268. .form-field > label {
  269. font-size: 12px;
  270. color: #90a4ae;
  271. }
  272. .form-field input[type="text"],
  273. .form-field input[type="number"],
  274. .form-field select {
  275. font: inherit;
  276. padding: 5px 8px;
  277. border: 1px solid #4a5560;
  278. border-radius: 4px;
  279. background: #262c34;
  280. color: #e6e9ec;
  281. }
  282. .form-field input[type="text"].readonly,
  283. .form-field select.readonly {
  284. opacity: 0.7;
  285. }
  286. .form-boolean {
  287. display: flex;
  288. align-items: center;
  289. gap: 8px;
  290. font-size: 13px;
  291. cursor: pointer;
  292. }
  293. .form-section {
  294. border: 1px solid #2d343d;
  295. border-radius: 6px;
  296. padding: 8px 10px;
  297. display: flex;
  298. flex-direction: column;
  299. gap: 8px;
  300. margin: 0;
  301. background: #171b20;
  302. }
  303. .form-section legend {
  304. font-size: 12px;
  305. color: #90a4ae;
  306. text-transform: uppercase;
  307. letter-spacing: 0.06em;
  308. padding: 0 6px;
  309. }
  310. .form-section-body {
  311. display: flex;
  312. flex-direction: column;
  313. gap: 8px;
  314. }
  315. .form-array,
  316. .form-record {
  317. display: flex;
  318. flex-direction: column;
  319. gap: 6px;
  320. border: 1px solid #2d343d;
  321. border-radius: 6px;
  322. padding: 8px 10px;
  323. background: #171b20;
  324. }
  325. .form-array-header,
  326. .form-record-header,
  327. .form-union-header {
  328. display: flex;
  329. align-items: center;
  330. gap: 8px;
  331. }
  332. .form-array-label,
  333. .form-record-label,
  334. .form-union-label {
  335. font-size: 12px;
  336. color: #90a4ae;
  337. text-transform: uppercase;
  338. letter-spacing: 0.06em;
  339. flex: 1;
  340. }
  341. .form-array-list,
  342. .form-record-rows {
  343. display: flex;
  344. flex-direction: column;
  345. gap: 6px;
  346. }
  347. .form-array-item {
  348. display: flex;
  349. align-items: flex-start;
  350. gap: 8px;
  351. padding: 6px;
  352. border: 1px solid #2d343d;
  353. border-radius: 4px;
  354. background: #1c2127;
  355. }
  356. .form-array-item > input,
  357. .form-array-item > select {
  358. flex: 1;
  359. min-width: 0;
  360. }
  361. .form-array-item-body {
  362. flex: 1;
  363. min-width: 0;
  364. }
  365. .form-array-controls {
  366. display: flex;
  367. gap: 4px;
  368. flex: none;
  369. }
  370. .form-array-controls button {
  371. padding: 4px 8px;
  372. font-size: 12px;
  373. }
  374. .form-union {
  375. display: flex;
  376. flex-direction: column;
  377. gap: 6px;
  378. border: 1px solid #2d343d;
  379. border-radius: 6px;
  380. padding: 8px 10px;
  381. background: #171b20;
  382. }
  383. .form-union-body {
  384. display: flex;
  385. flex-direction: column;
  386. gap: 8px;
  387. }
  388. .form-branch-select {
  389. font: inherit;
  390. padding: 5px 8px;
  391. border: 1px solid #4a5560;
  392. border-radius: 4px;
  393. background: #262c34;
  394. color: #e6e9ec;
  395. }
  396. .form-record-row {
  397. display: flex;
  398. align-items: flex-start;
  399. gap: 8px;
  400. }
  401. .form-record-row > input {
  402. flex: 1;
  403. min-width: 0;
  404. font: inherit;
  405. padding: 5px 8px;
  406. border: 1px solid #4a5560;
  407. border-radius: 4px;
  408. background: #262c34;
  409. color: #e6e9ec;
  410. }
  411. .form-record-row > .form-record-value {
  412. flex: 2;
  413. min-width: 0;
  414. }
  415. #statusbar {
  416. padding: 6px 16px;
  417. background: #1f242b;
  418. border-top: 1px solid #2d343d;
  419. color: #7f8c99;
  420. font-size: 12px;
  421. }
  422. .status-error {
  423. color: #ef5350;
  424. }
  425. button:disabled {
  426. opacity: 0.5;
  427. cursor: default;
  428. }
  429. .map-editor {
  430. display: flex;
  431. flex-direction: column;
  432. gap: 10px;
  433. flex: 1;
  434. min-height: 0;
  435. }
  436. .map-toolbar {
  437. display: flex;
  438. align-items: center;
  439. gap: 10px;
  440. flex-wrap: wrap;
  441. }
  442. .map-toolbar h3 {
  443. margin: 0;
  444. }
  445. .map-path {
  446. color: #7f8c99;
  447. font-size: 12px;
  448. flex: 1;
  449. min-width: 0;
  450. overflow: hidden;
  451. text-overflow: ellipsis;
  452. white-space: nowrap;
  453. }
  454. .map-tools {
  455. display: flex;
  456. gap: 6px;
  457. }
  458. .map-tool-button {
  459. padding: 5px 12px;
  460. font-size: 12px;
  461. }
  462. .map-tool-button.selected {
  463. background: #2e7d32;
  464. border-color: #66bb6a;
  465. }
  466. .map-body {
  467. display: flex;
  468. gap: 12px;
  469. flex: 1;
  470. min-height: 0;
  471. }
  472. .map-palette {
  473. width: 180px;
  474. flex: none;
  475. overflow: auto;
  476. display: flex;
  477. flex-direction: column;
  478. gap: 8px;
  479. padding: 10px;
  480. background: #1c2127;
  481. border: 1px solid #2d343d;
  482. border-radius: 6px;
  483. }
  484. .palette-list {
  485. display: flex;
  486. flex-direction: column;
  487. gap: 4px;
  488. }
  489. .palette-swatch {
  490. display: flex;
  491. align-items: center;
  492. gap: 8px;
  493. padding: 5px 8px;
  494. text-align: left;
  495. }
  496. .palette-swatch.selected {
  497. border-color: #66bb6a;
  498. background: #2e7d32;
  499. }
  500. .swatch-chip {
  501. width: 22px;
  502. height: 22px;
  503. border-radius: 3px;
  504. border: 1px solid rgba(0, 0, 0, 0.4);
  505. flex: none;
  506. }
  507. .swatch-label {
  508. font-size: 12px;
  509. overflow: hidden;
  510. text-overflow: ellipsis;
  511. }
  512. .add-tile-form {
  513. display: flex;
  514. gap: 6px;
  515. }
  516. .add-tile-form input {
  517. flex: 1;
  518. min-width: 0;
  519. font: inherit;
  520. padding: 5px 8px;
  521. border: 1px solid #4a5560;
  522. border-radius: 4px;
  523. background: #262c34;
  524. color: #e6e9ec;
  525. }
  526. .map-canvas-wrap {
  527. flex: 1;
  528. min-width: 0;
  529. overflow: auto;
  530. background: #111417;
  531. border: 1px solid #2d343d;
  532. border-radius: 6px;
  533. padding: 8px;
  534. }
  535. #map-canvas,
  536. .map-canvas-wrap canvas {
  537. display: block;
  538. image-rendering: pixelated;
  539. cursor: crosshair;
  540. }
  541. .scene-editor {
  542. display: flex;
  543. flex-direction: column;
  544. gap: 10px;
  545. flex: 1;
  546. min-height: 0;
  547. }
  548. .scene-toolbar {
  549. display: flex;
  550. align-items: center;
  551. gap: 10px;
  552. flex-wrap: wrap;
  553. }
  554. .scene-toolbar h3 {
  555. margin: 0;
  556. }
  557. .scene-path {
  558. color: #7f8c99;
  559. font-size: 12px;
  560. flex: 1;
  561. min-width: 0;
  562. overflow: hidden;
  563. text-overflow: ellipsis;
  564. white-space: nowrap;
  565. }
  566. .scene-mode-toggle {
  567. display: flex;
  568. gap: 6px;
  569. }
  570. .scene-mode-button {
  571. padding: 5px 12px;
  572. font-size: 12px;
  573. }
  574. .scene-mode-button.selected {
  575. background: #2e7d32;
  576. border-color: #66bb6a;
  577. }
  578. .scene-body {
  579. display: flex;
  580. gap: 12px;
  581. flex: 1;
  582. min-height: 0;
  583. }
  584. .scene-palette {
  585. width: 180px;
  586. flex: none;
  587. overflow: auto;
  588. display: flex;
  589. flex-direction: column;
  590. gap: 8px;
  591. padding: 10px;
  592. background: #1c2127;
  593. border: 1px solid #2d343d;
  594. border-radius: 6px;
  595. }
  596. .scene-palette-section {
  597. margin: 6px 0 0;
  598. font-size: 11px;
  599. color: #90a4ae;
  600. text-transform: uppercase;
  601. letter-spacing: 0.06em;
  602. }
  603. .scene-palette-list {
  604. display: flex;
  605. flex-direction: column;
  606. gap: 4px;
  607. }
  608. .scene-palette-item {
  609. display: flex;
  610. align-items: center;
  611. justify-content: space-between;
  612. gap: 8px;
  613. padding: 5px 8px;
  614. text-align: left;
  615. }
  616. .scene-palette-item.selected {
  617. border-color: #66bb6a;
  618. background: #2e7d32;
  619. }
  620. .scene-palette-badge {
  621. color: #90a4ae;
  622. font-size: 11px;
  623. font-variant-numeric: tabular-nums;
  624. flex: none;
  625. }
  626. .scene-npc-form {
  627. display: flex;
  628. flex-direction: column;
  629. gap: 6px;
  630. }
  631. .scene-npc-form input {
  632. font: inherit;
  633. padding: 5px 8px;
  634. border: 1px solid #4a5560;
  635. border-radius: 4px;
  636. background: #262c34;
  637. color: #e6e9ec;
  638. }
  639. .scene-main {
  640. flex: 1;
  641. min-width: 0;
  642. display: flex;
  643. flex-direction: column;
  644. gap: 8px;
  645. }
  646. .scene-entity-list {
  647. border: 1px solid #2d343d;
  648. border-radius: 6px;
  649. background: #1c2127;
  650. padding: 8px 10px;
  651. max-height: 180px;
  652. overflow: auto;
  653. display: flex;
  654. flex-direction: column;
  655. gap: 6px;
  656. }
  657. .scene-entity-list h4 {
  658. margin: 0;
  659. font-size: 11px;
  660. color: #90a4ae;
  661. text-transform: uppercase;
  662. letter-spacing: 0.06em;
  663. }
  664. .scene-entity-rows {
  665. display: flex;
  666. flex-direction: column;
  667. gap: 3px;
  668. }
  669. .scene-entity-row {
  670. display: flex;
  671. align-items: center;
  672. gap: 8px;
  673. padding: 4px 8px;
  674. border-radius: 4px;
  675. cursor: pointer;
  676. font-size: 12px;
  677. }
  678. .scene-entity-row:hover {
  679. background: #262c34;
  680. }
  681. .scene-entity-row.selected {
  682. background: #2e7d32;
  683. }
  684. .scene-entity-dot {
  685. width: 10px;
  686. height: 10px;
  687. border-radius: 50%;
  688. border: 1px solid rgba(0, 0, 0, 0.4);
  689. flex: none;
  690. }
  691. .scene-entity-coords {
  692. color: #7f8c99;
  693. font-variant-numeric: tabular-nums;
  694. margin-left: auto;
  695. }
  696. .scene-entity-delete {
  697. padding: 2px 7px;
  698. font-size: 12px;
  699. line-height: 1.1;
  700. flex: none;
  701. }
  702. .mods-body {
  703. flex: 1;
  704. min-height: 0;
  705. overflow: auto;
  706. }
  707. .mods-list {
  708. list-style: none;
  709. margin: 0;
  710. padding: 0;
  711. display: flex;
  712. flex-direction: column;
  713. gap: 6px;
  714. }
  715. .mod-row {
  716. display: flex;
  717. align-items: center;
  718. gap: 10px;
  719. padding: 8px 10px;
  720. border: 1px solid #2d343d;
  721. border-radius: 6px;
  722. background: #1c2127;
  723. }
  724. .mod-info {
  725. flex: 1;
  726. min-width: 0;
  727. display: flex;
  728. flex-direction: column;
  729. gap: 2px;
  730. }
  731. .mod-id {
  732. font-weight: 600;
  733. color: #cfd8dc;
  734. }
  735. .mod-meta {
  736. color: #7f8c99;
  737. font-size: 12px;
  738. overflow: hidden;
  739. text-overflow: ellipsis;
  740. white-space: nowrap;
  741. }
  742. .mod-toggle {
  743. flex: none;
  744. font-size: 12px;
  745. padding: 5px 12px;
  746. }
  747. .assets-toolbar {
  748. display: flex;
  749. gap: 8px;
  750. }
  751. .assets-body {
  752. flex: 1;
  753. min-height: 0;
  754. overflow: auto;
  755. display: flex;
  756. flex-direction: column;
  757. gap: 10px;
  758. }
  759. .assets-grid {
  760. display: grid;
  761. grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  762. gap: 8px;
  763. }
  764. .asset-tile {
  765. display: flex;
  766. flex-direction: column;
  767. align-items: center;
  768. gap: 4px;
  769. padding: 8px;
  770. border: 1px solid #2d343d;
  771. border-radius: 6px;
  772. background: #1c2127;
  773. }
  774. .asset-tile img {
  775. width: 64px;
  776. height: 64px;
  777. object-fit: contain;
  778. image-rendering: pixelated;
  779. }
  780. .asset-label {
  781. font-size: 11px;
  782. color: #cfd8dc;
  783. max-width: 100%;
  784. overflow: hidden;
  785. text-overflow: ellipsis;
  786. white-space: nowrap;
  787. }
  788. .asset-delete {
  789. padding: 3px 10px;
  790. font-size: 11px;
  791. }
  792. .assets-audio {
  793. display: flex;
  794. flex-direction: column;
  795. gap: 6px;
  796. }
  797. .asset-row {
  798. display: flex;
  799. align-items: center;
  800. gap: 10px;
  801. padding: 8px 10px;
  802. border: 1px solid #2d343d;
  803. border-radius: 6px;
  804. background: #1c2127;
  805. }
  806. .asset-info {
  807. flex: 1;
  808. min-width: 0;
  809. display: flex;
  810. flex-direction: column;
  811. gap: 2px;
  812. }
  813. .asset-id {
  814. font-weight: 600;
  815. color: #cfd8dc;
  816. }
  817. .asset-meta {
  818. color: #7f8c99;
  819. font-size: 12px;
  820. }
  821. #playtest {
  822. flex: 1;
  823. display: flex;
  824. flex-direction: column;
  825. min-height: 0;
  826. background: #0d1013;
  827. }
  828. #playtest-topbar {
  829. display: flex;
  830. align-items: center;
  831. gap: 14px;
  832. padding: 8px 16px;
  833. background: #1f242b;
  834. border-bottom: 1px solid #2d343d;
  835. }
  836. #playtest-title {
  837. font-weight: 600;
  838. color: #a5d6a7;
  839. }
  840. #playtest-status {
  841. color: #7f8c99;
  842. font-size: 12px;
  843. flex: 1;
  844. }
  845. #playtest-game {
  846. flex: 1;
  847. min-height: 0;
  848. position: relative;
  849. }
  850. #playtest-hud {
  851. display: flex;
  852. align-items: center;
  853. gap: 16px;
  854. padding: 6px 16px;
  855. background: #1f242b;
  856. border-top: 1px solid #2d343d;
  857. color: #e6e9ec;
  858. font-size: 12px;
  859. }
  860. #playtest-time {
  861. color: #a5d6a7;
  862. font-variant-numeric: tabular-nums;
  863. }
  864. #playtest-stats {
  865. color: #e6e9ec;
  866. flex: 1;
  867. overflow: hidden;
  868. text-overflow: ellipsis;
  869. white-space: nowrap;
  870. }
  871. #playtest-feedback {
  872. color: #8bc34a;
  873. min-height: 14px;
  874. }
  875. .dialog-backdrop {
  876. position: fixed;
  877. inset: 0;
  878. background: rgba(0, 0, 0, 0.5);
  879. display: flex;
  880. align-items: center;
  881. justify-content: center;
  882. z-index: 40;
  883. }
  884. .dialog {
  885. width: 520px;
  886. max-width: 92vw;
  887. max-height: 82vh;
  888. overflow: auto;
  889. background: #1c2127;
  890. border: 1px solid #2d343d;
  891. border-radius: 8px;
  892. padding: 18px;
  893. display: flex;
  894. flex-direction: column;
  895. gap: 12px;
  896. }
  897. .dialog h3 {
  898. margin: 0;
  899. }
  900. .pack-checks {
  901. display: flex;
  902. flex-direction: column;
  903. gap: 6px;
  904. }
  905. .dialog-actions {
  906. display: flex;
  907. gap: 10px;
  908. }
  909. #pack-result {
  910. white-space: pre-wrap;
  911. font-family: ui-monospace, monospace;
  912. font-size: 12px;
  913. color: #8bc34a;
  914. max-height: 180px;
  915. overflow: auto;
  916. border: 1px solid #2d343d;
  917. border-radius: 4px;
  918. padding: 8px;
  919. background: #171b20;
  920. margin: 0;
  921. }
  922. #pack-result.status-error {
  923. color: #ef5350;
  924. }
  925. </style>
  926. </head>
  927. <body>
  928. <div id="app">
  929. <section id="home" class="screen">
  930. <h1>nEngine Editor</h1>
  931. <p class="muted">Create or open an nEngine project — a folder with <code>content/</code> (and optional <code>mods/</code>).</p>
  932. <div id="home-actions">
  933. <button id="btn-open" type="button" class="primary">Open project…</button>
  934. <button id="btn-new" type="button">New project…</button>
  935. </div>
  936. <h2>Recent projects</h2>
  937. <ul id="recent-list"></ul>
  938. <p id="home-error"></p>
  939. </section>
  940. <section id="editor" class="screen hidden">
  941. <header id="topbar">
  942. <span id="project-name"></span>
  943. <span id="project-path"></span>
  944. <span id="mcp-url" title="Embedded MCP endpoint — paste into your AI tool"></span>
  945. <button id="btn-mcp-copy" type="button" disabled>Copy MCP URL</button>
  946. <button id="btn-package" type="button">Package</button>
  947. <button id="btn-play" type="button" class="primary">Play</button>
  948. <button id="btn-close" type="button">Close project</button>
  949. </header>
  950. <div id="workspace">
  951. <aside id="content-browser">
  952. <h3>Content</h3>
  953. <ul id="content-list"></ul>
  954. </aside>
  955. <main id="editor-panel">
  956. <h3>Editor</h3>
  957. <p class="placeholder">Select a content kind from the sidebar. Maps open the visual tile editor.</p>
  958. </main>
  959. </div>
  960. <div id="statusbar">
  961. <span id="status"></span>
  962. </div>
  963. </section>
  964. <section id="playtest" class="screen hidden">
  965. <header id="playtest-topbar">
  966. <span id="playtest-title">Playtest</span>
  967. <span id="playtest-status"></span>
  968. <button id="btn-playtest-exit" type="button">Exit</button>
  969. </header>
  970. <div id="playtest-game"></div>
  971. <div id="playtest-hud">
  972. <span id="playtest-time"></span>
  973. <span id="playtest-stats"></span>
  974. <span id="playtest-feedback"></span>
  975. </div>
  976. </section>
  977. <section id="pack-dialog" class="hidden">
  978. <div class="dialog-backdrop">
  979. <div class="dialog">
  980. <h3>Package</h3>
  981. <p class="muted">Run the nengine-pack pipeline on this project: content → bundle.json + static site, plus optional desktop app and installer config. Fields pre-fill from project.yaml and are saved back on run.</p>
  982. <label class="form-field">Product name<input id="pack-name" type="text"></label>
  983. <label class="form-field">Icon path<input id="pack-icon" type="text" placeholder="icon.png (stored for future use — v1 ships the default Electron icon)"></label>
  984. <div class="pack-checks">
  985. <label class="form-boolean"><input id="pack-desktop" type="checkbox">Desktop app (Electron shell)</label>
  986. <label class="form-boolean"><input id="pack-installer" type="checkbox">Installer config (electron-builder)</label>
  987. <label class="form-boolean"><input id="pack-mods" type="checkbox">Include Mods (<code>mods/</code>)</label>
  988. <label class="form-boolean"><input id="pack-split" type="checkbox">Split assets into <code>nengine-assets/</code></label>
  989. </div>
  990. <label class="form-field">Output directory<input id="pack-outdir" type="text"></label>
  991. <pre id="pack-result"></pre>
  992. <div class="dialog-actions">
  993. <button id="btn-pack-run" type="button" class="primary">Run package</button>
  994. <button id="btn-pack-close" type="button">Close</button>
  995. </div>
  996. </div>
  997. </div>
  998. </section>
  999. </div>
  1000. <script type="module" src="/src/renderer/main.ts"></script>
  1001. </body>
  1002. </html>