index.html 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025
  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-resize {
  467. display: flex;
  468. align-items: center;
  469. gap: 4px;
  470. }
  471. .map-resize input {
  472. width: 52px;
  473. padding: 4px 6px;
  474. font: inherit;
  475. font-size: 12px;
  476. background: #101418;
  477. color: #e6e9ec;
  478. border: 1px solid #2d343d;
  479. border-radius: 4px;
  480. }
  481. .map-body {
  482. display: flex;
  483. gap: 12px;
  484. flex: 1;
  485. min-height: 0;
  486. }
  487. .map-palette {
  488. width: 180px;
  489. flex: none;
  490. overflow: auto;
  491. display: flex;
  492. flex-direction: column;
  493. gap: 8px;
  494. padding: 10px;
  495. background: #1c2127;
  496. border: 1px solid #2d343d;
  497. border-radius: 6px;
  498. }
  499. .palette-list {
  500. display: flex;
  501. flex-direction: column;
  502. gap: 4px;
  503. }
  504. .palette-swatch {
  505. display: flex;
  506. align-items: center;
  507. gap: 8px;
  508. padding: 5px 8px;
  509. text-align: left;
  510. }
  511. .palette-swatch.selected {
  512. border-color: #66bb6a;
  513. background: #2e7d32;
  514. }
  515. .swatch-chip {
  516. width: 22px;
  517. height: 22px;
  518. border-radius: 3px;
  519. border: 1px solid rgba(0, 0, 0, 0.4);
  520. flex: none;
  521. overflow: hidden;
  522. }
  523. .swatch-chip img {
  524. width: 100%;
  525. height: 100%;
  526. display: block;
  527. object-fit: cover;
  528. image-rendering: pixelated;
  529. }
  530. .swatch-label {
  531. font-size: 12px;
  532. overflow: hidden;
  533. text-overflow: ellipsis;
  534. }
  535. .add-tile-form {
  536. display: flex;
  537. gap: 6px;
  538. }
  539. .add-tile-form input {
  540. flex: 1;
  541. min-width: 0;
  542. font: inherit;
  543. padding: 5px 8px;
  544. border: 1px solid #4a5560;
  545. border-radius: 4px;
  546. background: #262c34;
  547. color: #e6e9ec;
  548. }
  549. .map-canvas-wrap {
  550. flex: 1;
  551. min-width: 0;
  552. overflow: auto;
  553. background: #111417;
  554. border: 1px solid #2d343d;
  555. border-radius: 6px;
  556. padding: 8px;
  557. }
  558. #map-canvas,
  559. .map-canvas-wrap canvas {
  560. display: block;
  561. image-rendering: pixelated;
  562. cursor: crosshair;
  563. }
  564. .scene-editor {
  565. display: flex;
  566. flex-direction: column;
  567. gap: 10px;
  568. flex: 1;
  569. min-height: 0;
  570. }
  571. .scene-toolbar {
  572. display: flex;
  573. align-items: center;
  574. gap: 10px;
  575. flex-wrap: wrap;
  576. }
  577. .scene-toolbar h3 {
  578. margin: 0;
  579. }
  580. .scene-path {
  581. color: #7f8c99;
  582. font-size: 12px;
  583. flex: 1;
  584. min-width: 0;
  585. overflow: hidden;
  586. text-overflow: ellipsis;
  587. white-space: nowrap;
  588. }
  589. .scene-mode-toggle {
  590. display: flex;
  591. gap: 6px;
  592. }
  593. .scene-mode-button {
  594. padding: 5px 12px;
  595. font-size: 12px;
  596. }
  597. .scene-mode-button.selected {
  598. background: #2e7d32;
  599. border-color: #66bb6a;
  600. }
  601. .scene-body {
  602. display: flex;
  603. gap: 12px;
  604. flex: 1;
  605. min-height: 0;
  606. }
  607. .scene-palette {
  608. width: 180px;
  609. flex: none;
  610. overflow: auto;
  611. display: flex;
  612. flex-direction: column;
  613. gap: 8px;
  614. padding: 10px;
  615. background: #1c2127;
  616. border: 1px solid #2d343d;
  617. border-radius: 6px;
  618. }
  619. .scene-palette-section {
  620. margin: 6px 0 0;
  621. font-size: 11px;
  622. color: #90a4ae;
  623. text-transform: uppercase;
  624. letter-spacing: 0.06em;
  625. }
  626. .scene-palette-list {
  627. display: flex;
  628. flex-direction: column;
  629. gap: 4px;
  630. }
  631. .scene-palette-item {
  632. display: flex;
  633. align-items: center;
  634. justify-content: space-between;
  635. gap: 8px;
  636. padding: 5px 8px;
  637. text-align: left;
  638. }
  639. .scene-palette-item.selected {
  640. border-color: #66bb6a;
  641. background: #2e7d32;
  642. }
  643. .scene-palette-badge {
  644. color: #90a4ae;
  645. font-size: 11px;
  646. font-variant-numeric: tabular-nums;
  647. flex: none;
  648. }
  649. .scene-npc-form {
  650. display: flex;
  651. flex-direction: column;
  652. gap: 6px;
  653. }
  654. .scene-npc-form input {
  655. font: inherit;
  656. padding: 5px 8px;
  657. border: 1px solid #4a5560;
  658. border-radius: 4px;
  659. background: #262c34;
  660. color: #e6e9ec;
  661. }
  662. .scene-main {
  663. flex: 1;
  664. min-width: 0;
  665. display: flex;
  666. flex-direction: column;
  667. gap: 8px;
  668. }
  669. .scene-entity-list {
  670. border: 1px solid #2d343d;
  671. border-radius: 6px;
  672. background: #1c2127;
  673. padding: 8px 10px;
  674. max-height: 180px;
  675. overflow: auto;
  676. display: flex;
  677. flex-direction: column;
  678. gap: 6px;
  679. }
  680. .scene-entity-list h4 {
  681. margin: 0;
  682. font-size: 11px;
  683. color: #90a4ae;
  684. text-transform: uppercase;
  685. letter-spacing: 0.06em;
  686. }
  687. .scene-entity-rows {
  688. display: flex;
  689. flex-direction: column;
  690. gap: 3px;
  691. }
  692. .scene-entity-row {
  693. display: flex;
  694. align-items: center;
  695. gap: 8px;
  696. padding: 4px 8px;
  697. border-radius: 4px;
  698. cursor: pointer;
  699. font-size: 12px;
  700. }
  701. .scene-entity-row:hover {
  702. background: #262c34;
  703. }
  704. .scene-entity-row.selected {
  705. background: #2e7d32;
  706. }
  707. .scene-entity-dot {
  708. width: 10px;
  709. height: 10px;
  710. border-radius: 50%;
  711. border: 1px solid rgba(0, 0, 0, 0.4);
  712. flex: none;
  713. }
  714. .scene-entity-coords {
  715. color: #7f8c99;
  716. font-variant-numeric: tabular-nums;
  717. margin-left: auto;
  718. }
  719. .scene-entity-delete {
  720. padding: 2px 7px;
  721. font-size: 12px;
  722. line-height: 1.1;
  723. flex: none;
  724. }
  725. .mods-body {
  726. flex: 1;
  727. min-height: 0;
  728. overflow: auto;
  729. }
  730. .mods-list {
  731. list-style: none;
  732. margin: 0;
  733. padding: 0;
  734. display: flex;
  735. flex-direction: column;
  736. gap: 6px;
  737. }
  738. .mod-row {
  739. display: flex;
  740. align-items: center;
  741. gap: 10px;
  742. padding: 8px 10px;
  743. border: 1px solid #2d343d;
  744. border-radius: 6px;
  745. background: #1c2127;
  746. }
  747. .mod-info {
  748. flex: 1;
  749. min-width: 0;
  750. display: flex;
  751. flex-direction: column;
  752. gap: 2px;
  753. }
  754. .mod-id {
  755. font-weight: 600;
  756. color: #cfd8dc;
  757. }
  758. .mod-meta {
  759. color: #7f8c99;
  760. font-size: 12px;
  761. overflow: hidden;
  762. text-overflow: ellipsis;
  763. white-space: nowrap;
  764. }
  765. .mod-toggle {
  766. flex: none;
  767. font-size: 12px;
  768. padding: 5px 12px;
  769. }
  770. .assets-toolbar {
  771. display: flex;
  772. gap: 8px;
  773. }
  774. .assets-body {
  775. flex: 1;
  776. min-height: 0;
  777. overflow: auto;
  778. display: flex;
  779. flex-direction: column;
  780. gap: 10px;
  781. }
  782. .assets-grid {
  783. display: grid;
  784. grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  785. gap: 8px;
  786. }
  787. .asset-tile {
  788. display: flex;
  789. flex-direction: column;
  790. align-items: center;
  791. gap: 4px;
  792. padding: 8px;
  793. border: 1px solid #2d343d;
  794. border-radius: 6px;
  795. background: #1c2127;
  796. }
  797. .asset-tile img {
  798. width: 64px;
  799. height: 64px;
  800. object-fit: contain;
  801. image-rendering: pixelated;
  802. }
  803. .asset-label {
  804. font-size: 11px;
  805. color: #cfd8dc;
  806. max-width: 100%;
  807. overflow: hidden;
  808. text-overflow: ellipsis;
  809. white-space: nowrap;
  810. }
  811. .asset-delete {
  812. padding: 3px 10px;
  813. font-size: 11px;
  814. }
  815. .assets-audio {
  816. display: flex;
  817. flex-direction: column;
  818. gap: 6px;
  819. }
  820. .asset-row {
  821. display: flex;
  822. align-items: center;
  823. gap: 10px;
  824. padding: 8px 10px;
  825. border: 1px solid #2d343d;
  826. border-radius: 6px;
  827. background: #1c2127;
  828. }
  829. .asset-info {
  830. flex: 1;
  831. min-width: 0;
  832. display: flex;
  833. flex-direction: column;
  834. gap: 2px;
  835. }
  836. .asset-id {
  837. font-weight: 600;
  838. color: #cfd8dc;
  839. }
  840. .asset-meta {
  841. color: #7f8c99;
  842. font-size: 12px;
  843. }
  844. #playtest {
  845. flex: 1;
  846. display: flex;
  847. flex-direction: column;
  848. min-height: 0;
  849. background: #0d1013;
  850. }
  851. #playtest-topbar {
  852. display: flex;
  853. align-items: center;
  854. gap: 14px;
  855. padding: 8px 16px;
  856. background: #1f242b;
  857. border-bottom: 1px solid #2d343d;
  858. }
  859. #playtest-title {
  860. font-weight: 600;
  861. color: #a5d6a7;
  862. }
  863. #playtest-status {
  864. color: #7f8c99;
  865. font-size: 12px;
  866. flex: 1;
  867. }
  868. #playtest-game {
  869. flex: 1;
  870. min-height: 0;
  871. position: relative;
  872. }
  873. #playtest-hud {
  874. display: flex;
  875. align-items: center;
  876. gap: 16px;
  877. padding: 6px 16px;
  878. background: #1f242b;
  879. border-top: 1px solid #2d343d;
  880. color: #e6e9ec;
  881. font-size: 12px;
  882. }
  883. #playtest-time {
  884. color: #a5d6a7;
  885. font-variant-numeric: tabular-nums;
  886. }
  887. #playtest-stats {
  888. color: #e6e9ec;
  889. flex: 1;
  890. overflow: hidden;
  891. text-overflow: ellipsis;
  892. white-space: nowrap;
  893. }
  894. #playtest-feedback {
  895. color: #8bc34a;
  896. min-height: 14px;
  897. }
  898. .dialog-backdrop {
  899. position: fixed;
  900. inset: 0;
  901. background: rgba(0, 0, 0, 0.5);
  902. display: flex;
  903. align-items: center;
  904. justify-content: center;
  905. z-index: 40;
  906. }
  907. .dialog {
  908. width: 520px;
  909. max-width: 92vw;
  910. max-height: 82vh;
  911. overflow: auto;
  912. background: #1c2127;
  913. border: 1px solid #2d343d;
  914. border-radius: 8px;
  915. padding: 18px;
  916. display: flex;
  917. flex-direction: column;
  918. gap: 12px;
  919. }
  920. .dialog h3 {
  921. margin: 0;
  922. }
  923. .pack-checks {
  924. display: flex;
  925. flex-direction: column;
  926. gap: 6px;
  927. }
  928. .dialog-actions {
  929. display: flex;
  930. gap: 10px;
  931. }
  932. #pack-result {
  933. white-space: pre-wrap;
  934. font-family: ui-monospace, monospace;
  935. font-size: 12px;
  936. color: #8bc34a;
  937. max-height: 180px;
  938. overflow: auto;
  939. border: 1px solid #2d343d;
  940. border-radius: 4px;
  941. padding: 8px;
  942. background: #171b20;
  943. margin: 0;
  944. }
  945. #pack-result.status-error {
  946. color: #ef5350;
  947. }
  948. </style>
  949. </head>
  950. <body>
  951. <div id="app">
  952. <section id="home" class="screen">
  953. <h1>nEngine Editor</h1>
  954. <p class="muted">Create or open an nEngine project — a folder with <code>content/</code> (and optional <code>mods/</code>).</p>
  955. <div id="home-actions">
  956. <button id="btn-open" type="button" class="primary">Open project…</button>
  957. <button id="btn-new" type="button">New project…</button>
  958. </div>
  959. <h2>Recent projects</h2>
  960. <ul id="recent-list"></ul>
  961. <p id="home-error"></p>
  962. </section>
  963. <section id="editor" class="screen hidden">
  964. <header id="topbar">
  965. <span id="project-name"></span>
  966. <span id="project-path"></span>
  967. <span id="mcp-url" title="Embedded MCP endpoint — paste into your AI tool"></span>
  968. <button id="btn-mcp-copy" type="button" disabled>Copy MCP URL</button>
  969. <button id="btn-package" type="button">Package</button>
  970. <button id="btn-play" type="button" class="primary">Play</button>
  971. <button id="btn-close" type="button">Close project</button>
  972. </header>
  973. <div id="workspace">
  974. <aside id="content-browser">
  975. <h3>Content</h3>
  976. <ul id="content-list"></ul>
  977. </aside>
  978. <main id="editor-panel">
  979. <h3>Editor</h3>
  980. <p class="placeholder">Select a content kind from the sidebar. Maps open the visual tile editor.</p>
  981. </main>
  982. </div>
  983. <div id="statusbar">
  984. <span id="status"></span>
  985. </div>
  986. </section>
  987. <section id="playtest" class="screen hidden">
  988. <header id="playtest-topbar">
  989. <span id="playtest-title">Playtest</span>
  990. <span id="playtest-status"></span>
  991. <button id="btn-playtest-exit" type="button">Exit</button>
  992. </header>
  993. <div id="playtest-game"></div>
  994. <div id="playtest-hud">
  995. <span id="playtest-time"></span>
  996. <span id="playtest-stats"></span>
  997. <span id="playtest-feedback"></span>
  998. </div>
  999. </section>
  1000. <section id="pack-dialog" class="hidden">
  1001. <div class="dialog-backdrop">
  1002. <div class="dialog">
  1003. <h3>Package</h3>
  1004. <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>
  1005. <label class="form-field">Product name<input id="pack-name" type="text"></label>
  1006. <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>
  1007. <div class="pack-checks">
  1008. <label class="form-boolean"><input id="pack-desktop" type="checkbox">Desktop app (Electron shell)</label>
  1009. <label class="form-boolean"><input id="pack-installer" type="checkbox">Installer config (electron-builder)</label>
  1010. <label class="form-boolean"><input id="pack-mods" type="checkbox">Include Mods (<code>mods/</code>)</label>
  1011. <label class="form-boolean"><input id="pack-split" type="checkbox">Split assets into <code>nengine-assets/</code></label>
  1012. </div>
  1013. <label class="form-field">Output directory<input id="pack-outdir" type="text"></label>
  1014. <pre id="pack-result"></pre>
  1015. <div class="dialog-actions">
  1016. <button id="btn-pack-run" type="button" class="primary">Run package</button>
  1017. <button id="btn-pack-close" type="button">Close</button>
  1018. </div>
  1019. </div>
  1020. </div>
  1021. </section>
  1022. </div>
  1023. <script type="module" src="/src/renderer/main.ts"></script>
  1024. </body>
  1025. </html>