claude-ai-office 0.34.2 โ 0.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +11 -1
- package/README.md +28 -2
- package/docs/ARCHITECTURE.md +7 -1
- package/lib/floorStyle.js +8 -1
- package/lib/state.js +4 -4
- package/package.json +1 -1
- package/public/assets/characters/ghost.webp +0 -0
- package/public/assets/characters/reindeer.webp +0 -0
- package/public/assets/characters/skeleton.webp +0 -0
- package/public/assets/characters/snowman.webp +0 -0
- package/public/assets/characters/vampire.webp +0 -0
- package/public/assets/characters/zombie.webp +0 -0
- package/public/index.html +3 -0
- package/public/js/customize.js +21 -5
- package/public/js/floorStylePicker.js +4 -2
- package/public/js/i18n.js +14 -0
- package/public/js/main.js +54 -2
- package/public/js/scene/characters.js +98 -27
- package/public/js/scene/decor.js +368 -0
- package/public/js/scene/decorPlan.js +153 -0
- package/public/js/scene/floorStyle.js +67 -9
- package/public/js/scene/index.js +5 -1
- package/public/js/scene/map.js +59 -7
- package/public/js/scene/render.js +115 -11
- package/public/js/scene/sprites.js +23 -8
- package/public/js/store.js +9 -0
- package/public/js/weather.js +53 -0
- package/public/style.css +3 -0
- package/scripts/bake-characters/bake.js +19 -4
- package/scripts/bake-characters/render.html +31 -1
package/README.en.md
CHANGED
|
@@ -96,6 +96,12 @@ Each floor (= project) is assigned an **interior preset** automatically (hash of
|
|
|
96
96
|
|
|
97
97
|
A session's rest room (cafรฉ or terrace) becomes that floor's facility: on a Mint floor the character goes to the Gym (`๐ working out`) and the log reads `โ
โฆ โ ๐ Gym`. Press **๐จ** on the selected floor to pick a preset by hand; the choice is stored on the server (`~/.ai-office/floor-styles.json`) so every browser sees the same floor (`Auto` restores the automatic choice). Building view shows the different layouts too.
|
|
98
98
|
|
|
99
|
+
**Theme floors**: the bottom of the ๐จ menu has **๐ Halloween ยท ๐ Christmas ยท ๐๏ธ Summer vacation**. They keep the floor's layout and facilities and change the floor, walls, colors and decorations โ pumpkins, a bubbling cauldron, gravestones, floating ghosts and bats; a tree with twinkling lights, a fireplace with stockings, presents, snowmen and wreaths; parasols, loungers, a pool, beach balls and surfboards. Large pieces block the way only when every seat stays reachable. โ๏ธ Settings โ **Theme floors** โ `๐
Auto by date` puts the season's theme on every floor you did not pick by hand (Halloween 10/15โ11/1, Christmas 12/1โ12/26, Summer 7/15โ8/31).
|
|
100
|
+
|
|
101
|
+

|
|
102
|
+
|
|
103
|
+
The whole floor changes: the sky outside the office (a purple night with a big moon and bats / a snowy night / a blue sky with sun and clouds), garlands on every wall including partitions, a rug in every room plus low fog for Halloween, and pumpkins, lanterns or tiki torches along the corridors.
|
|
104
|
+
|
|
99
105
|

|
|
100
106
|
|
|
101
107
|
Subagents wear blue hard hats and are named after the `description` given to the Agent tool. You (`me`) sit at My Desk, and Claude comes over when it has a question or needs permission.
|
|
@@ -115,7 +121,9 @@ Open **โ๏ธ Settings** in the header. Everything here is stored in this browse
|
|
|
115
121
|
| Demo | Four fake sessions on separate `(demo)` floors | `D` |
|
|
116
122
|
| Character looks | Opens the customizer (below) | |
|
|
117
123
|
| Daylight | Window, terrace and room lighting follow the real clock: morning (06โ10), day (10โ17), evening (17โ20), night (20โ06). Can be fixed to one period or turned off | |
|
|
118
|
-
|
|
|
124
|
+
| Outdoor weather | Rain or snow over outdoor rooms (terrace, rooftop garden): `๐ Match theme` (default; snow on Christmas floors) ยท `๐ค๏ธ Real weather` (type a city โ current weather from [Open-Meteo](https://open-meteo.com) every 30 minutes; only the city name and coordinates are sent) ยท `โ๏ธ Clear` ยท `๐ง๏ธ Rain` ยท `โ๏ธ Snow` | |
|
|
125
|
+
| Theme floors | `๐จ Pick manually` (default) or `๐
Auto by date` | |
|
|
126
|
+
| Floor interior | Interior preset or theme of the floor you are viewing | |
|
|
119
127
|
| Language | ํ๊ตญ์ด / English | |
|
|
120
128
|
| Shortcuts | Keyboard shortcut panel | `?` |
|
|
121
129
|
|
|
@@ -148,6 +156,8 @@ Characters are the 12 [Kenney Mini Characters](https://kenney.nl/assets/mini-cha
|
|
|
148
156
|
|
|
149
157
|

|
|
150
158
|
|
|
159
|
+
**Theme characters**: a row of ๐ง Vampire ยท ๐ง Zombie ยท ๐ Skeleton ยท ๐ป Ghost ([Kenney Graveyard Kit](https://kenney.nl/assets/graveyard-kit), CC0), โ Snowman ยท ๐ฆ Rudolph ([Kenney Holiday Kit](https://kenney.nl/assets/holiday-kit), CC0) and ready-made ๐ Pumpkin head ยท ๐
Santa. Vampire, zombie and skeleton walk, sit, type and nod like the others and mix with other heads and bodies; snowman and Rudolph are still models that move as a whole. The accessory row adds hand-drawn **Santa hat ยท reindeer antlers ยท pumpkin mask** for anyone. Auto still uses only the 12 default characters.
|
|
160
|
+
|
|
151
161
|
**My characters (your own pictures)**: click **๏ผ Upload PNG** or drop a file anywhere on the customizer to use a picture with a transparent background.
|
|
152
162
|
|
|
153
163
|
- The transparent margin is trimmed and the long side scaled to 256 px, then stored in this browser's localStorage (several pictures, up to 24 / about 2.5 MB). Hover a tile and press **ร** twice to delete it; targets using it go back to their default.
|
package/README.md
CHANGED
|
@@ -100,6 +100,26 @@ node bin/ai-office.js # ๋๋ npm run install-hooks && npm start
|
|
|
100
100
|
|
|
101
101
|

|
|
102
102
|
|
|
103
|
+
**ํ
๋ง ์ธต**: ๐จ ๋ฉ๋ด ์๋์ชฝ์ **๐ ํ ๋ก์ ยท ๐ ํฌ๋ฆฌ์ค๋ง์ค ยท ๐๏ธ ์ฌ๋ฆํด๊ฐ** ๋ ์ธต ๊ตฌ์กฐ(ํธ์์์ค)๋ ๊ทธ๋๋ก ๋๊ณ ๋ฐ๋ฅยท๋ฒฝยท์๊ณผ ์ํ๋ง ๋ฐ๊ฟ๋๋ค.
|
|
104
|
+
|
|
105
|
+
| ํ
๋ง | ๋ฐ๋ฅ ยท ๋ฒฝ | ์ํ |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| ๐ ํ ๋ก์ | ๋ณด๋ผ ์ฒด์ปค ยท ์ค๋ฌด๋ฌ ๋ฒฝ์ง, ๋ณด๋๋น ํดํธ | ํธ๋ฐ ๋ฑ๋ถ, ๋๋ ์ฅ, ๋ฌ๋น, ์ด๋ถ, ๋ฅ์ค ์ ๋ น, ๋ ์๋ค๋๋ ๋ฐ์ฅ, ๊ฑฐ๋ฏธ์ค, ์ฃผํฉยท๋ณด๋ผ ๊น๋ฐ |
|
|
108
|
+
| ๐ ํฌ๋ฆฌ์ค๋ง์ค | ๋๋ฌด ๋ฐ๋ฅ ยท ํต๋๋ฌด ๋ฒฝ, ๋ฐ๋ปํ ๋น | ํธ๋ฆฌ(๋ฐ์ง์ด๋ ์ ๊ตฌ), ๋ฒฝ๋๋ก(๋ถ๊ฝยท์๋ง), ์ ๋ฌผ, ๋์ฌ๋, ๋ฆฌ์ค, ์ ๊ตฌ ์ค |
|
|
109
|
+
| ๐๏ธ ์ฌ๋ฆํด๊ฐ | ๋ฐํฌ ยท ํฐ ํ์ ๋ฒฝ(๋ฏผํธ ๋ ) | ํ๋ผ์, ์ ๋ฒ ๋, ํ์ฅ, ํ๋ธ, ๋น์น๋ณผ, ์์ด์ค๋ฐ์ค, ์ํ๋ณด๋, ๊ตฌ๋ช
ํ๋ธ, ๊น๋ฐ ์ค |
|
|
110
|
+
|
|
111
|
+

|
|
112
|
+
|
|
113
|
+
ํ
๋ง ์ธต์ ์ธต ์ ์ฒด๊ฐ ๋ฐ๋๋๋ค โ ์ฌ๋ฌด์ค ๋ฐ ํ๋(ํ ๋ก์ ๋ณด๋ผ ๋ฐคํ๋ยทํฐ ๋ฌยท๋ฐ์ฅ ๋ผ / ํฌ๋ฆฌ์ค๋ง์ค ๋ ๋ด๋ฆฌ๋ ๋ฐค / ์ฌ๋ฆ ํ๋ ํ๋ยทํดยท๊ตฌ๋ฆ), ์นธ๋ง์ด๊น์ง ๋ชจ๋ ๋ฒฝ์ ๊น๋ฐยท์ ๊ตฌ ๊ฐ๋๋, ๋ฐฉ๋ง๋ค ๋ฌ๊ทธ(๊ฑฐ๋ฏธ์ค ๋ฌ๊ทธยท์ฒดํฌ ๋ฌ๊ทธยท๋น์น ํ์)์ ํ ๋ก์ ๋ฐ๋ฅ ์๊ฐ, ๋ณต๋์ ํธ๋ฐยท์ด๋ถ / ๋ํด / ํ๋ถ.
|
|
114
|
+
|
|
115
|
+

|
|
116
|
+
|
|
117
|
+
**์ผ์ธ ๋ ์จ**: ์ผ์ธ ๋ฐฉ์ด ์๋ ๊ตฌ์กฐ(ํ
๋ผ์คยท์ฅ์์ ์)์์๋ ๋น๊ฐ ์ค๋ฉด ๋ฐ๋ฅ์ด ์ ์ ํค์ด ๋๊ณ ๋น์ค๊ธฐ๊ฐ, ๋์ด ์ค๋ฉด ๋ฐ๋ฅ์ด ํ์๊ฒ ๋ฎ์ด๊ณ ๋์ก์ด๊ฐ ๋ด๋ฆฝ๋๋ค. โ๏ธ ์ค์ **์ผ์ธ ๋ ์จ** ๋ฅผ `๐ค๏ธ ์ค์ ๋ ์จ` ๋ก ๋๊ณ ๋์ ์ด๋ฆ(`์์ธ`, `Tokyo` โฆ)์ ์ ์ผ๋ฉด [Open-Meteo](https://open-meteo.com)(ํค ์์, CC BY 4.0)์์ ๊ทธ ๋์์ ํ์ฌ ๋ ์จ๋ฅผ 30๋ถ๋ง๋ค ๋ฐ์์ต๋๋ค โ ๋์ ์ด๋ฆ๊ณผ ์ขํ๋ง ๋ณด๋
๋๋ค.
|
|
118
|
+
|
|
119
|
+

|
|
120
|
+
|
|
121
|
+
ํธ๋ฆฌยท๋ฒฝ๋๋กยท์ฅยทํ์ฅยท์ ๋ฒ ๋์ฒ๋ผ ํฐ ์ํ์ ๊ธธ์ ๋ง์ง ์์ ๋๋ง ์ง๋๊ฐ ์ ์๊ฒ ๋ก๋๋ค. โ๏ธ ์ค์ ์ **ํ
๋ง ์ธต** ์ `๐
๋ ์ง์ ๋ง์ถฐ ์๋` ์ผ๋ก ๋ฐ๊พธ๋ฉด ์ง์ ๊ณ ๋ฅด์ง ์์ ์ธต์ ๊ทธ ์ฒ ์ ํ
๋ง๊ฐ ๋ฎ์
๋๋ค (10/15~11/1 ํ ๋ก์ ยท 12/1~12/26 ํฌ๋ฆฌ์ค๋ง์ค ยท 7/15~8/31 ์ฌ๋ฆํด๊ฐ). ๊ธฐ๋ณธ์ `๐จ ์ง์ ๊ณ ๋ฅด๊ธฐ`.
|
|
122
|
+
|
|
103
123
|
์๋ธ์์ด์ ํธ๋ ํ๋ ์์ ๋ชจ๋ฅผ ์ฐ๊ณ , Agent ๋๊ตฌ์ ์ ์ description ์ด ์ด๋ฆ์ด ๋ฉ๋๋ค. ์ฌ์ฉ์ ๋ณธ์ธ(`๋`)์ ๋ด ์๋ฆฌ์ ์์ ์๊ณ , Claude ๊ฐ ์ง๋ฌธํ๊ฑฐ๋ ํ๋ฝ์ ๊ธฐ๋ค๋ฆฌ๋ฉด ๊ทธ ์์ผ๋ก ์ฐพ์์ต๋๋ค.
|
|
104
124
|
|
|
105
125
|
## ์ค์ ๋ฉ๋ด์ ๋จ์ถํค
|
|
@@ -116,7 +136,9 @@ node bin/ai-office.js # ๋๋ npm run install-hooks && npm start
|
|
|
116
136
|
| ํ
๋ง | ๋ผ์ดํธ / ๋คํฌ | `T` |
|
|
117
137
|
| ๋ฐ๋ชจ | ๊ฐ์ง ์ธ์
4๊ฐ๋ฅผ ๋์ ์ฌ๋ฌด์ค์ ๋ถ์ ์ด๊ฒ. ์ค์ ์ธ์
๊ณผ ๋ณ๋ ์ธต์ด๊ณ `(๋ฐ๋ชจ)` ๊ฐ ๋ถ๋๋ค | `D` |
|
|
118
138
|
| ์บ๋ฆญํฐ ์ธํ | ์ธํ ๋ชจ๋ฌ ์ด๊ธฐ (์๋ ์ฐธ๊ณ ) | |
|
|
119
|
-
|
|
|
139
|
+
| ์ผ์ธ ๋ ์จ | ํ
๋ผ์คยท์ฅ์์ ์ ๊ฐ์ ์ผ์ธ ๋ฐฉ์ ๋นยท๋. `๐ ํ
๋ง์ ๋ง์ถฐ`(๊ธฐ๋ณธ, ํฌ๋ฆฌ์ค๋ง์ค ์ธต์ ๋) ยท `๐ค๏ธ ์ค์ ๋ ์จ`(์๋ ์นธ์ ๋์ ์ด๋ฆ โ [Open-Meteo](https://open-meteo.com) ํ์ฌ ๋ ์จ, 30๋ถ๋ง๋ค) ยท `โ๏ธ ๋ง์` ยท `๐ง๏ธ ๋น` ยท `โ๏ธ ๋` | |
|
|
140
|
+
| ํ
๋ง ์ธต | `๐จ ์ง์ ๊ณ ๋ฅด๊ธฐ`(๊ธฐ๋ณธ) ๋๋ `๐
๋ ์ง์ ๋ง์ถฐ ์๋` โ ํ ๋ก์ยทํฌ๋ฆฌ์ค๋ง์คยท์ฌ๋ฆํด๊ฐ ์ฒ ์ ์ง์ ๊ณ ๋ฅด์ง ์์ ์ธต์ ๊ทธ ํ
๋ง๋ก | |
|
|
141
|
+
| ์ธต ์ธํ
๋ฆฌ์ด | ์ง๊ธ ๋ณด๋ ์ธต์ ์ธํ
๋ฆฌ์ด ํ๋ฆฌ์
ยทํ
๋ง (์ธต ํ์ ๐จ ์ ๊ฐ์) | |
|
|
120
142
|
| ์ธ์ด | ํ๊ตญ์ด / English. ํ๋ฉด ๋ฌธ๊ตฌยท์บ๋ฒ์ค ๊ธ์ยท์๋ฆผ์ด ๋ฐ๋๋ค. ์ธ์
์ ๋ชฉยทํ๋กฌํํธยทClaude ๋ฉ์์ง๋ ์ด ๊ทธ๋๋ก | |
|
|
121
143
|
| ์๊ฐ๋ ์กฐ๋ช
| ์ค์ ์๊ฐ์ ๋ฐ๋ผ ์ฐฝ๋ฌธยทํ
๋ผ์คยท์ค๋ด ๋น์ด ์์นจ(06~10)ยท๋ฎ(10~17)ยท์ ๋
(17~20)ยท๋ฐค(20~06)์ผ๋ก ๋ฐ๋๋ค. `์๋` ์ธ์ `๋` ์ด๋ ์๊ฐ๋ ํ๋๋ก ๊ณ ์ ๊ฐ๋ฅ | |
|
|
122
144
|
| ๋จ์ถํค | ํค๋ณด๋ ๋จ์ถํค ํ ํจ๋ | `?` |
|
|
@@ -162,6 +184,10 @@ node bin/ai-office.js # ๋๋ npm run install-hooks && npm start
|
|
|
162
184
|
|
|
163
185
|

|
|
164
186
|
|
|
187
|
+
**ํ
๋ง ์บ๋ฆญํฐ**: ๊ฐ์ ์ฐฝ์ ํ
๋ง ์บ๋ฆญํฐ ์ค์์ ๐ง ๋ฑํ์ด์ด ยท ๐ง ์ข๋น ยท ๐ ํด๊ณจ ยท ๐ป ์ ๋ น([Kenney Graveyard Kit](https://kenney.nl/assets/graveyard-kit), CC0)๊ณผ โ ๋์ฌ๋ ยท ๐ฆ ๋ฃจ๋ํ([Kenney Holiday Kit](https://kenney.nl/assets/holiday-kit), CC0), ์ถ์ฒ ์กฐํฉ ๐ ํธ๋ฐ ๋จธ๋ฆฌ ยท ๐
์ฐํ๋ฅผ ๊ณ ๋ฆ
๋๋ค. ๋ฑํ์ด์ดยท์ข๋นยทํด๊ณจ์ ๊ฑท๊ธฐยท์๊ธฐยทํ์ดํยท๋๋์์ด ๊ทธ๋๋ก ๋๊ณ ๋จธ๋ฆฌยท๋ชธ์ ๋ค๋ฅธ ์บ๋ฆญํฐ์ ์์ ์ ์์ต๋๋ค. ๋์ฌ๋ยท๋ฃจ๋ํ๋ ์ ์ง ๋ชจ๋ธ์ด๋ผ ๋ชธ ์ ์ฒด๋ฅผ ์์ง์ฌ ๋ณด์
๋๋ค. ์ํ ์ค์๋ ์ง์ ๊ทธ๋ฆฐ **์ฐํ ๋ชจ์ ยท ๋ฃจ๋ํ ๋ฟ ยท ํธ๋ฐ ํ** ์ด ์์ด ๋๊ตฌ์๊ฒ๋ ์์ธ ์ ์์ต๋๋ค. ์๋ ๋ฐฐ์ ์ ์ง๊ธ์ฒ๋ผ ๊ธฐ๋ณธ 12๋ช
๋ง ์๋๋ค.
|
|
188
|
+
|
|
189
|
+

|
|
190
|
+
|
|
165
191
|
**๋ด ์บ๋ฆญํฐ (์ง์ ์ฌ๋ฆฐ ๊ทธ๋ฆผ)**: ๊ฐ์ ์ฐฝ์ **๏ผ PNG ์ฌ๋ฆฌ๊ธฐ** ํ์ผ์ ๋๋ฅด๊ฑฐ๋ ๊ทธ ์(์ฐฝ ์ด๋๋ )์ ํ์ผ์ ๋์ด๋ค ๋์ผ๋ฉด, ๋ฐฐ๊ฒฝ์ด ํฌ๋ช
ํ ๊ทธ๋ฆผ์ ์บ๋ฆญํฐ๋ก ์๋๋ค.
|
|
166
192
|
|
|
167
193
|

|
|
@@ -229,6 +255,6 @@ npm run bump:minor # 0.19.0 โ 0.20.0
|
|
|
229
255
|
|
|
230
256
|
๋ฆด๋ฆฌ์ค: `git tag vX.Y.Z && git push origin vX.Y.Z` ๋ฅผ ์ฌ๋ฆฌ๋ฉด GitHub Actions ๊ฐ ๋งฅยทWindows ์ฑ์ ๋น๋ํด Release ์ ์ฌ๋ฆฌ๊ณ (updater ์ฉ `latest.json` ํฌํจ), Windows ๋ฌ๋์์ ์ค์น โ ์คํ โ ํ
๊น์ง ํ์ธํฉ๋๋ค. ํ
์คํธ๋ pushยทPR ๋ง๋ค UbuntuยทWindowsยทmacOS ์์ ๋๋๋ค.
|
|
231
257
|
|
|
232
|
-
์บ๋ฆญํฐ ์คํ๋ผ์ดํธ ๋ค์ ๊ตฝ๊ธฐ: [Kenney Mini Characters](https://kenney.nl/assets/mini-characters) ํฉ์ ๋ฐ์ ํ๊ณ `node scripts/bake-characters/bake.js <ํฉ ํด๋>` (ํค๋๋ฆฌ์ค ํฌ๋กฌ + three.js CDN, ๊ฒฐ๊ณผ๋ `public/assets/characters/*.webp`). ์บ๋ฆญํฐ๋ง๋ค ์ ์ํธ(๋จธ๋ฆฌ ๋ ์ด์ด + ๋ชธํต ๋ ์ด์ด)์ ๋ง์คํฌ ์ํธ(๋จธ๋ฆฌ์นด๋ฝยท์์ยทํ์ ์์), ์ํ๋ง๋ค ์ํธ ํ๋๋ฅผ ๊ตฝ๋๋ค. ์ํธ ๊ตฌ์ฑ(ํ๋ ์ ํฌ๊ธฐยท๋์ ํยท๋ ์ด์ด)๊ณผ ์ํ ์์น๋ `characters.js` ์ `SHEET`ยท`ACCESSORIES` ์์ ์ ํ๋ค.
|
|
258
|
+
์บ๋ฆญํฐ ์คํ๋ผ์ดํธ ๋ค์ ๊ตฝ๊ธฐ: [Kenney Mini Characters](https://kenney.nl/assets/mini-characters) ํฉ์ ๋ฐ์ ํ๊ณ `node scripts/bake-characters/bake.js <ํฉ ํด๋>` (ํค๋๋ฆฌ์ค ํฌ๋กฌ + three.js CDN, ๊ฒฐ๊ณผ๋ `public/assets/characters/*.webp`). ์บ๋ฆญํฐ๋ง๋ค ์ ์ํธ(๋จธ๋ฆฌ ๋ ์ด์ด + ๋ชธํต ๋ ์ด์ด)์ ๋ง์คํฌ ์ํธ(๋จธ๋ฆฌ์นด๋ฝยท์์ยทํ์ ์์), ์ํ๋ง๋ค ์ํธ ํ๋๋ฅผ ๊ตฝ๋๋ค. ์ํธ ๊ตฌ์ฑ(ํ๋ ์ ํฌ๊ธฐยท๋์ ํยท๋ ์ด์ด)๊ณผ ์ํ ์์น๋ `characters.js` ์ `SHEET`ยท`ACCESSORIES` ์์ ์ ํ๋ค. ํ
๋ง ์บ๋ฆญํฐ๋ `--graveyard <Graveyard Kit ํด๋> --holiday <Holiday Kit ํด๋>` ๋ฅผ ๋ํ๋ค.
|
|
233
259
|
|
|
234
260
|
์์
ํ๋ฆ: GitHub ์ด์๋ฅผ `[FE-30]`, `[BE-13]`, `[APP-03]` ์ฒ๋ผ FE/BE/APP ์ผ๋ก ๋๋ ๋ง๋ค๊ณ , `main` ์์ `feature/FE-30` ์ ๋ธ๋์น๋ฅผ ๋ถ๋ฆฌํด ์์
ํ ๋ค ๊ฒฐ๊ณผยทํ
์คํธยท์บก์ฒ๋ฅผ ์ด์ ๋๊ธ๋ก ๋จ๊ธด๋ค.
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -168,8 +168,9 @@ public/
|
|
|
168
168
|
index.html 3๋จ ๋ ์ด์์ (์ข ์ธต๋ณ ์๋ด / ์ค์ canvas / ์ฐ ์ฌ๋๋คยท๋ก๊ทธ), ์๋จ ํค๋(์ธตยท์ธ์
์ ๋ชฉยท๋ธ๋์นยทLIVE)
|
|
169
169
|
style.css ๋ผ์ดํธ/๋คํฌ ํ
๋ง ํ ํฐ, ํจ๋ ์นด๋
|
|
170
170
|
fonts/ ์๋ธ์
์นํฐํธ (scripts/subset-fonts.sh)
|
|
171
|
-
assets/characters/ ์บ๋ฆญํฐ ์คํ๋ผ์ดํธ webp: 12๋ช
์ ์ํธ(๋จธ๋ฆฌยท๋ชธํต ๋ ์ด์ด) + ๋ง์คํฌ ์ํธ(๋จธ๋ฆฌ์นด๋ฝยท์์ยทํ์ ์์) + ์ํ 4์ฅ (scripts/bake-characters ๋ก ๊ตฝ๋๋ค)
|
|
171
|
+
assets/characters/ ์บ๋ฆญํฐ ์คํ๋ผ์ดํธ webp: 12๋ช
์ ์ํธ(๋จธ๋ฆฌยท๋ชธํต ๋ ์ด์ด) + ๋ง์คํฌ ์ํธ(๋จธ๋ฆฌ์นด๋ฝยท์์ยทํ์ ์์) + ์ํ 4์ฅ + ํ
๋ง ์บ๋ฆญํฐ 6์ฅ (scripts/bake-characters ๋ก ๊ตฝ๋๋ค)
|
|
172
172
|
js/
|
|
173
|
+
weather.js ์ผ์ธ ๋ ์จ (FE-61): WMO ์ฝ๋ โ ๋นยท๋, Open-Meteo ์ง์ค์ฝ๋ฉยทํ์ฌ ๋ ์จ
|
|
173
174
|
main.js SSE ์ฐ๊ฒฐยท์ฌ์ ์, store ๊ฐฑ์ , ์๋ฆผ(์๋ฆฌยท๋ธ๋ผ์ฐ์ ์๋ฆผ), ์ค์ , ํค๋ณด๋(1~9 ์ธต ์ด๋)
|
|
174
175
|
store.js floors/sessions/log ๋ฏธ๋ฌ, ์ ํ๋ ์ธต/์ธ์
, ์ธต ์ ๊ธฐ(auto/all/none), subscribe()
|
|
175
176
|
panels.js ์ขยท์ฐ ํจ๋ DOM (์ธต ๋ชฉ๋ก + ๋ชจ๋ ํผ์น๊ธฐ/์ ๊ธฐ, ์ธ์
๋ชฉ๋ก, ์ฌ๋๋ค, ํ๋ ๋ก๊ทธ, ๋ฒ๋ก)
|
|
@@ -187,6 +188,8 @@ public/
|
|
|
187
188
|
pathfind.js A* (4๋ฐฉํฅ), ๋ฒฝ edge ์ solid ๊ฐ๊ตฌ ํํผ
|
|
188
189
|
actors.js Agent โ Actor(์์น, ๊ฒฝ๋ก, ๊ฑท๊ธฐ ์ ๋, ๋งํ์ , ์ฌ๋กฏ ์ ์ /ํด์ , ํด์ ๋ฐฉ ์ ๋ฐฐํ)
|
|
189
190
|
spots.js ํด์ ๋ฐฉ ๋ช
์ (๋ ์ด์์์ด ์ฑ์ฐ๋ live ๊ฐ์ฒด)
|
|
191
|
+
decorPlan.js ํ
๋ง ์ธต ์ํ ๋ฐฐ์น (์์ ํจ์, FE-60)
|
|
192
|
+
decor.js ํ
๋ง ์ธต ์ํ ๊ทธ๋ฆฌ๊ธฐ (ํธ๋ฐยท์ฅยท์ ๋ นยท๋ฐ์ฅยทํธ๋ฆฌยท๋ฒฝ๋๋กยท๋์ฌ๋ยทํ๋ผ์ยท์ ๋ฒ ๋ยทํ์ฅ โฆ)
|
|
190
193
|
characters.js ์บ๋ฆญํฐ ์คํ๋ผ์ดํธ (Kenney Mini Characters, CC0) ๋์ ํ๋ ์ยท์์ ๋ชจ, ํํธ ์กฐํฉ('mix:...') ๋ ์ด์ดยท์ ์
ํ๊ธฐยท์ํ, ์ปค์คํ
๊ทธ๋ฆผ('custom:<id>') ๊ทธ๋ฆฌ๊ธฐ
|
|
191
194
|
render.js ๋ฐ๋ฅ โ 3D AABB ๊น์ด ์ ๋ ฌ๋ ๊ฐ๊ตฌ/๋ฒฝ/์บ๋ฆญํฐ โ ๋ฐฉ ์ด๋ฆํยท๋งํ์
|
|
192
195
|
sprites.js ๊ฐ๊ตฌยท๋ฒฝ์ ์ฝ๋๋ก ๊ทธ๋ฆฌ๋ ํจ์๋ค (์ธ๋ถ ์ด๋ฏธ์ง ์์)
|
|
@@ -238,6 +241,9 @@ public/
|
|
|
238
241
|
- **๋ทฐ์ด ์ค์ ์ ๋ธ๋ผ์ฐ์ ์**: ๋ณ๋ช
ยท์ธํยท์ธ์ดยทํ
๋ง๋ localStorage. ์ธต ์ธํ
๋ฆฌ์ด์ฒ๋ผ ๋ชจ๋๊ฐ ๊ฐ์ด ๋ด์ผ ํ๋ ๊ฒ๋ง ์๋ฒ์ ๋๋ค.
|
|
239
242
|
- **์ปค์คํ
์บ๋ฆญํฐ๋ ๋ธ๋ผ์ฐ์ ์ (FE-56)**: ์ฌ๋ฆฐ PNG ๋ ์๋ฒ๋ก ๋ณด๋ด์ง ์๋๋ค. ํฌ๋ช
์ฌ๋ฐฑ์ ์๋ฅด๊ณ ๊ธด ๋ณ 256px ๋ก ์ค์ธ dataURL ์ `aiOffice.customCharacters.v1` ์ ๋๊ณ (์ต๋ 24์ฅยท2.5MB, ์คํจํ๋ฉด ๋๋๋ฆผ), ๊ณ ๋ฅธ ๊ฐ์ `custom:<id>` ๋ก `aiOffice.characters.v1` ์ ์์ธ๋ค. ํค๋ฅผ ๋๋ ๊ทธ๋ฆผ ์ ์ฅ์ด ์คํจํด๋ ๊ณ ๋ฅด๊ธฐ ์ค์ ์ ๋จ๋๋ค. '์๋' ํด์๋ Kenney 12๋ช
๋ง ์ด๋ค. ์ ์ง ๊ทธ๋ฆผ ํ ์ฅ์ด๋ผ ๋์์ ๋ชธ ์ ์ฒด์ ์คํ์
ยท๊ธฐ์ธ๊ธฐยท๋๋ฆผ์ผ๋ก ๋ณด์ด๊ณ , ์์ ๋ชจ ์์น๋ ์คํ๋ผ์ดํธ์ ๊ฐ์ ์ํ ์ค์บ(`scanHead`)์ผ๋ก ์ฐพ๋๋ค.
|
|
240
243
|
- **ํํธ ์กฐํฉ์ ๊ตฝ๊ณ ๊ฒน์น๋ค (FE-57)**: Kenney 12๋ช
์ ๋ผ๋๊ฐ ๊ฐ๊ณ ๋ชจ๋ธ์ด ๋จธ๋ฆฌยท๋ชธํต ๋ ๋ฉ์ด๋ฆฌ๋ผ, ๊ตฝ๊ธฐ์์ ๋จธ๋ฆฌ ๋ ์ด์ด(๋ชธํต์ ๊น์ด๋ง ์จ์ ํ์ด ์์ ์ค๋ฉด ๋จธ๋ฆฌ๊ฐ ์๋ฆฐ๋ค)์ ๋ชธํต ๋ ์ด์ด๋ฅผ ๋ฐ๋ก ๊ตฝ๋๋ค. ๋ชจ๋ ๊ฐ์ ๋ฐฐ์จ(๊ฐ์ฅ ํฐ ์บ๋ฆญํฐ ๊ธฐ์ค)ยท๊ฐ์ ์๊ธฐ ๋์ด๋ผ ์๋ฌด ๋จธ๋ฆฌ๋ ์๋ฌด ๋ชธ ์์ ๊ฒน์น๋ฉด ๋ง๋๋ค. ์ ๋ฐ๊พธ๊ธฐ๋ ํ๋ ํธ(8์ดร4๋ ) ์นธ์ผ๋ก ๋ถ์๋ฅผ ๊ณ ๋ฅธ๋ค: ํผ๋ถ = ๋จธ๋ฆฌ์ ํ์ ํจ๊ป ์ฐ์ธ ์นธ, ๋จธ๋ฆฌ์นด๋ฝ = ๋จธ๋ฆฌ์ ๊ทธ๋ค์ ๋์ ์นธ(์ผ๊ตด์ ๋ถ์ ๋ฉ์ํ ๋ยท์
์ ๋บ), ์์ยทํ์ = ๋ชธํตยทํ / ๋ค๋ฆฌ(์ ๋ฐ ์)์์ ๊ฐ์ฅ ๋์ ์นธ. ๊ทธ ๋ถ์๋ฅผ ํฐ ์๋ฒ ๋ ร ์๋ ๋ฐ๊ธฐ๋ก ๋ค์ ๊ทธ๋ฆฐ ์์์ ๋ง์คํฌ ์ํธ์ ๋๊ณ , ๊ทธ๋ฆด ๋ ์ ํ ์๊ณต๊ฐ์์ ์์ ร ์ ์์ผ๋ก ์น ํ ๋ ์ด์ด๋ฅผ ์บ์ํ๋ค(์ต๊ทผ 16๊ฐ). ์ํ์ ์๋ ์๊ฒฝ์ด ์๋ male-f ์ ๋จธ๋ฆฌ ๋ผ์ ๋ถ์ฌ ๊ตฌ์ด ์ํธ ํ๋๋ฅผ ๋ชจ๋ ๋จธ๋ฆฌ์ ์น๋๋ค. ๊ฐ์ ๋ฌธ์์ด `mix:<๋จธ๋ฆฌ>.<๋ชธ>.<๋จธ๋ฆฌ์นด๋ฝ>.<์์>.<ํ์>.<์ํ>` ์ด๋ผ ๊ธฐ์กด ์ ์ฅยท์ ๊ทํยท๋์๋ณ ์ค์ ์ ๊ทธ๋๋ก ํ๋ค.
|
|
244
|
+
- **ํ
๋ง ์ธต์ ๊ตฌ์กฐ ์์ ๋ฎ๋ ์คํจ (FE-60)**: ํ ๋ก์ยทํฌ๋ฆฌ์ค๋ง์คยท์ฌ๋ฆํด๊ฐ๋ ํ๋ฆฌ์
๊ณผ ๋ฌ๋ฆฌ ๋ ์ด์์์ ๋ฐ๊พธ์ง ์๋๋ค. ๊ทธ๋ฆฌ๋ ํค๊ฐ `halloween/mint` ์ฒ๋ผ ํ
๋ง/๊ตฌ์กฐ๋ผ, ์๋ฒ๋ ์ง์ ๊ณ ๋ฅธ ๊ฐ(`floor.style`)์ด ํ
๋ง๋ฉด ํด์ ๋ฐฉ ์ด๋ฆ์ ์๋ ํ๋ฆฌ์
(`styleAuto`) ๊ตฌ์กฐ๋ก ๋งํ๋ค(`FS.baseOf`). ๋ ์ง ์๋(์ค์ 'ํ
๋ง ์ธต')์ ๋ธ๋ผ์ฐ์ ์ค์ ์ด๊ณ ์ง์ ๊ณ ๋ฅด์ง ์์ ์ธต์๋ง ๋ฎ์ด๋ฉฐ, ๊ตฌ์กฐ๊ฐ ๊ทธ๋๋ก๋ผ ์๋ฒ์ ํด์ ๋ฐฉ ๋ง๊ณผ ์ด๊ธ๋์ง ์๋๋ค. ์ํ ์๋ฆฌ๋ `decorPlan.js` ๊ฐ ๋ ์ด์์๋ง ๋ณด๊ณ ์ ํ๊ณ (์๋ฆฌยท๋ช
์ยท๋ฌธ ์ ๋น์ฐ๊ธฐ, ๊ตฌ์ยท๋ท๋ฒฝ ์ฐ์ , ๋ฒฝ ์ํ์ ํค ํฐ ๋ฐ๊นฅ๋ฒฝ์๋ง), `map.js` ๊ฐ ํฐ ์ํ์ ๋ง์๋ ์๋ฆฌ๋ฒ ์ดํฐ์์ ๋ชจ๋ ์๋ฆฌยท๋ช
์์ ๊ธธ์ด ๋จ์ ๋๋ง ๋ง๋๋ค. ๊ทธ๋ฆฌ๊ธฐ๋ `decor.js`(ํ๋ฉด ๋ฒกํฐ, ๋ฐ์ง์ดยทํ๋ค๋ฆฌ๋ ๊ฒ๋ง ๋งค ํ๋ ์), ๋ฐ๋ฅ ๋ฌด๋ฌยท๋ฒฝ ์ยทํดํธ๋ ํ๋ ํธ(`palette()`)๊ฐ ํ
๋ง์์ ์ค๋ค. ์ ์ ์ํฐํฐ ์บ์๋ `๋ ์ด์์|ํ
๋ง` ํค. ์ธต ์ ์ฒด ๋ถ์๊ธฐ(FE-63): ํ๋ฉด ๊ณต๊ฐ ๋ฐฐ๊ฒฝ ํ๋(`drawThemeBackdrop`, ๋ณยท๋ฌยท๋ฐ์ฅ ๋ผยท๋์ก์ดยท๊ตฌ๋ฆ), ๋ฌธ ๊ตฌ๋ฉ ์ฌ์ด ๋ฒฝ ํ ๋ง๋ง๋ค ๊ฐ๋๋(๊ทธ ๋ฒฝ ๋์ด์ ๋ง์ถฐ), ๋ฐฉ๋ง๋ค ๋ฌ๊ทธยท์๊ฐ(`kind: 'flat'` ์ ๋ฐ๋ฅ ์บ์์ ๊ทธ๋ฆฐ๋ค), ๋ณต๋ ๋น ํ์ผ์ ๋์๋์ ์ํ.
|
|
245
|
+
- **ํ
๋ง ์บ๋ฆญํฐ (FE-62)**: Graveyard Kit ์บ๋ฆญํฐ๋ ๋ผ๋ ๋์ ๋ถ์๋ณ ๋ฉ์(๋จธ๋ฆฌยท๋ชธํตยทํยท๋ค๋ฆฌ)๋ก ์์ง์ด์ง๋ง ๋์ ์ด๋ฆ์ด ๋ฏธ๋ ์บ๋ฆญํฐ์ ๊ฐ์, ๊ตฝ๊ธฐ์์ `head` ๋ฉ์๋ฅผ ๋จธ๋ฆฌ ๋ ์ด์ด๋ก ๋๋ ๊ฐ์ ์ํธ ๋ชจ์(๋จธ๋ฆฌ ํ + ๋ชธํต ํ)์ผ๋ก ๊ตฝ๋๋ค(์ ๋ง์คํฌ ์์). ๊ทธ๋์ ๋ฑํ์ด์ดยท์ข๋นยทํด๊ณจ์ `MIX_PARTS` ๋ก ๋จธ๋ฆฌยท๋ชธ ์๊ธฐ์ ๋ค์ด๊ฐ๋ค. Holiday Kit ์ ๋์ฌ๋ยท์๋ก์ ๋์์ด ์์ด ๋ฏธ๋ ์บ๋ฆญํฐ ํค๋ก ์ค์ฌ ํ ์ฅ์ ๋ชจ๋ ์นธ์ ๋ณต์ฌํ๊ณ , ๊ทธ๋ฆด ๋ ์ฌ๋ฆฐ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ ๋ชธ ์ ์ฒด ์์ง์(`motionOf`)์ ์ค๋ค. ์ฐํ ๋ชจ์ยท๋ฃจ๋ํ ๋ฟยทํธ๋ฐ ํ์ ๊ตฝ์ง ์๋ ์ฝ์คํฌ(`ACCESSORIES` ์ `draw`)์ผ๋ก ์์ ๋ชจ์ฒ๋ผ ํ๋ ์๋ง๋ค ์ฐพ์ ๋จธ๋ฆฌ ์์น์ ๊ทธ๋ฆฐ๋ค(๋จธ๋ฆฌ ๋ ์ด์ด๊ฐ ๋น๋ฉด ๋ชธํต ๋ ์ด์ด์์ ์ฐพ๋๋ค).
|
|
246
|
+
- **์ผ์ธ ๋ ์จ๋ ๋ธ๋ผ์ฐ์ ์์ (FE-61)**: ๋นยท๋์ ๋ทฐ์ด ์ค์ (`aiOffice.weather`)์ด๋ผ ์๋ฒ๋ฅผ ๊ฑฐ์น์ง ์๋๋ค. ์ค์ ๋ ์จ๋ ๋ธ๋ผ์ฐ์ ๊ฐ Open-Meteo ์ ์ง์ ๋ฌป๋๋ค(ํคยทCORS ๋ฌธ์ ์์, ๋์ ์ด๋ฆ๊ณผ ์ขํ๋ง): ์ง์ค์ฝ๋ฉ์ ์ด๋ฆ์ด ๊ฐ์ ์์ ๋ง์์ด ๋จผ์ ๋์ค๊ณ ํ๊ธ ์งง์ ์ด๋ฆ('์์ธ')์ด ์ ์กํ, ํ์ ๊ตฌ์ญ ์ ๋ฏธ์ฌ๋ฅผ ๋ถ์ธ ํ๋ณด๊น์ง ๋ชจ์ ์ธ๊ตฌ๊ฐ ๊ฐ์ฅ ๋ง์ ๊ณณ์ ๊ณ ๋ฅธ๋ค. ํ์ฌ ๋ ์จ ์ฝ๋(WMO)๋ฅผ 30๋ถ๋ง๋ค ๋ฐ์ ๋นยท๋ยท๋ง์์ผ๋ก ์ค์ด๊ณ , ์ง๊ธ ๋ด๋ฆด ๊ฒ์ ์ค์ ยท์ค์ ๋ ์จยท์ธต ํ
๋ง(ํฌ๋ฆฌ์ค๋ง์ค๋ ๊ธฐ๋ณธ ๋)๋ฅผ ํฉ์ณ ๋งค ํ๋ ์ ์ ํ๋ค(`effectiveWeather`). ๊ทธ๋ฆฌ๊ธฐ๋ ์ผ์ธ ๋ฐฉ ์ ์์ ์์์ ๋จ์ด์ง๋ ์
์(์๋ ๊ณต๊ฐ, ์ํฐํฐ ๋ค์ ๊ทธ๋ ค ์์ ๋ฎ์)์ ๋ฐ๋ฅ ํค, ๋น ์ค๋ ๋ ์ฅ๋ฉด ํดํธ.
|
|
241
247
|
- **๋ฒ์**: Claude Code ํ
๋ง ๋ฐ๋๋ค. claude.ai ์ฑํ
ยทํด๋ผ์ฐ๋ ์ธ์
์ ํ
์ด ์์ด ๋ค๋ฃจ์ง ์๋๋ค.
|
|
242
248
|
|
|
243
249
|
## 9. ๋ณด์ (BE-21)
|
package/lib/floorStyle.js
CHANGED
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
// ์๋ฒ๋ ์ธต๋ง๋ค ํ๋ฆฌ์
์ ์ ํด(์ง์ ์ ํ > ์ธต id ํด์) ํด์ ๋ฐฉ cafe/terrace ๋ฅผ ๊ทธ ์ธต์ ํธ์์์ค ์ด๋ฆยท์์ด์ฝยท๋งํ์ ์ผ๋ก ๋ฐ๊ฟ ๋งํ๋ค.
|
|
5
5
|
|
|
6
6
|
const PRESET_IDS = ['warm', 'cool', 'mint', 'lavender', 'sand', 'pastel'];
|
|
7
|
+
// ํ
๋ง ์ธต (FE-60): ์ง์ ๊ณ ๋ฅผ ์๋ง ์๊ณ , ๊ตฌ์กฐ(ํด์ ๋ฐฉ)๋ ๊ทธ ์ธต์ ์๋ ํ๋ฆฌ์
์ ๋ฐ๋ฅธ๋ค
|
|
8
|
+
const THEME_IDS = ['halloween', 'christmas', 'summer'];
|
|
9
|
+
const isTheme = (id) => THEME_IDS.includes(id);
|
|
10
|
+
/** ์ง์ ๊ณ ๋ฅผ ์ ์๋ ๊ฐ์ธ์ง (ํ๋ฆฌ์
๋๋ ํ
๋ง) */
|
|
11
|
+
const isChoice = (id) => PRESET_IDS.includes(id) || isTheme(id);
|
|
12
|
+
/** ์ธต์ ๊ตฌ์กฐ๋ฅผ ์ ํ๋ ํ๋ฆฌ์
: ํ
๋ง๋ฅผ ๊ณจ๋์ผ๋ฉด ์๋ ํ๋ฆฌ์
*/
|
|
13
|
+
const baseOf = (floor) => (floor ? (isTheme(floor.style) ? floor.styleAuto : floor.style) : null);
|
|
7
14
|
const DEMO_PRESET = 'pastel';
|
|
8
15
|
const AUTO_PRESETS = PRESET_IDS.filter((id) => id !== DEMO_PRESET); // ์ค์ ์ธต์ ์๋ ๋ฐฐ์ ํ๋ณด (๋ฐ๋ชจ ์ธต์ ์ ์ฒด)
|
|
9
16
|
const PRESET_LAYOUT = { warm: 'classic', cool: 'studio', mint: 'fitness', lavender: 'play', sand: 'canteen', pastel: 'classic' };
|
|
@@ -68,4 +75,4 @@ function restInfo(presetId, room) {
|
|
|
68
75
|
return ROOM_REST[id] || { label: id, icon: 'โ', bubble: 'ํด์ ์ค' };
|
|
69
76
|
}
|
|
70
77
|
|
|
71
|
-
module.exports = { PRESET_IDS, DEMO_PRESET, AUTO_PRESETS, PRESET_LAYOUT, LAYOUT_REST, ROOM_REST, hashStr, isDemoFloor, assignPresets, restRoomIn, restInfo };
|
|
78
|
+
module.exports = { PRESET_IDS, THEME_IDS, isTheme, isChoice, baseOf, DEMO_PRESET, AUTO_PRESETS, PRESET_LAYOUT, LAYOUT_REST, ROOM_REST, hashStr, isDemoFloor, assignPresets, restRoomIn, restInfo };
|
package/lib/state.js
CHANGED
|
@@ -33,7 +33,7 @@ function restRoomFor(sessionId) {
|
|
|
33
33
|
/** ํด์ ์ ์ด: ๋ฐฉยท๋งํ์ ยท์์ด์ฝ์ ์ธ์
์ ํด์ ์ฅ์ + ์ธต์ ํธ์์์ค(ํ๋ฆฌ์
)์ ๋ง์ถ๋ค (FE-36) */
|
|
34
34
|
function rest(bubble, sessionId, floor) {
|
|
35
35
|
const room = restRoomFor(sessionId);
|
|
36
|
-
const info = FS.restInfo(floor
|
|
36
|
+
const info = FS.restInfo(FS.baseOf(floor), room);
|
|
37
37
|
return { room, activity: 'rest', bubble: bubble || info.bubble, icon: info.icon };
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -111,7 +111,7 @@ function createState({ me = 'me', statFile = defaultStat, titleOf = defaultTitle
|
|
|
111
111
|
// ---------- ์ธต ์ธํ
๋ฆฌ์ด ํ๋ฆฌ์
(FE-36): ์ง์ ์ ํ(styleFile ์ ์ ์ฅ) > ์ธต id ํด์ ์๋ ๋ฐฐ์ ----------
|
|
112
112
|
const styleOverrides = new Map(); // floorId โ presetId
|
|
113
113
|
if (styleFile) {
|
|
114
|
-
try { for (const [k, v] of Object.entries(JSON.parse(fs.readFileSync(styleFile, 'utf8')))) if (FS.
|
|
114
|
+
try { for (const [k, v] of Object.entries(JSON.parse(fs.readFileSync(styleFile, 'utf8')))) if (FS.isChoice(v)) styleOverrides.set(k, v); } catch {}
|
|
115
115
|
}
|
|
116
116
|
function saveStyles() {
|
|
117
117
|
if (!styleFile) return;
|
|
@@ -129,7 +129,7 @@ function createState({ me = 'me', statFile = defaultStat, titleOf = defaultTitle
|
|
|
129
129
|
}
|
|
130
130
|
/** ์ธต ํ๋ฆฌ์
์ง์ ์ ํ (null ์ด๋ฉด ์๋์ผ๋ก). ์ ์ฅํ๊ณ ๋ฐ๋ ์ธต์ 'floor' ๋ก ์๋ฆฐ๋ค */
|
|
131
131
|
function setFloorStyle(id, preset) {
|
|
132
|
-
const p = FS.
|
|
132
|
+
const p = FS.isChoice(preset) ? preset : null;
|
|
133
133
|
if (p) styleOverrides.set(id, p); else styleOverrides.delete(id);
|
|
134
134
|
saveStyles();
|
|
135
135
|
for (const f of refreshStyles()) emitter.emit('floor', publicFloor(f));
|
|
@@ -395,7 +395,7 @@ function createState({ me = 'me', statFile = defaultStat, titleOf = defaultTitle
|
|
|
395
395
|
const a = mainAgent(s, now);
|
|
396
396
|
set(a, rest(null, s.id, floor), now);
|
|
397
397
|
s.status = 'idle';
|
|
398
|
-
entry = pushLog(now, floor, s, a, 'โ
', `${ev.lastMessage ? cut(ev.lastMessage, 60) : '์์
์๋ฃ'} โ ${a.icon} ${FS.restInfo(floor
|
|
398
|
+
entry = pushLog(now, floor, s, a, 'โ
', `${ev.lastMessage ? cut(ev.lastMessage, 60) : '์์
์๋ฃ'} โ ${a.icon} ${FS.restInfo(FS.baseOf(floor), a.room).label}`);
|
|
399
399
|
break;
|
|
400
400
|
}
|
|
401
401
|
case 'PreCompact': {
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/public/index.html
CHANGED
|
@@ -42,6 +42,9 @@
|
|
|
42
42
|
<div class="settings-row"><span class="settings-label">๋ฐ๋ชจ <kbd>D</kbd></span><button id="demo-btn" class="tool" title="๊ฐ์ง ์ธ์
4๊ฐ๋ก ์ฌ๋ฌด์ค์ ๋ถ์ ์ด๊ฒ (์ค์ ์ธ์
๊ณผ ๋ณ๋ ์ธต, '(๋ฐ๋ชจ)' ํ์)">๐ฌ ๊บผ์ง</button></div>
|
|
43
43
|
<div class="settings-row"><span class="settings-label">์บ๋ฆญํฐ ์ธํ</span><button id="customize-btn" class="tool" title="๋ / Claude / ์๋ธ์์ด์ ํธ ์บ๋ฆญํฐ ๊ณ ๋ฅด๊ธฐ โ 12๋ช
์ค ํ๋ ๋๋ ๋จธ๋ฆฌยท๋ชธยท์ยท์ํ ์กฐํฉ, ์ธ์
๋ง๋ค ์๋ ๋๋ ์ง์ ์ฌ๋ฆฐ PNG, ์ง๊ธ ๋ณด๋ ์ธ์
์ ์บ๋ฆญํฐ ์ด๋ฆ">๐ค ๊ณ ๋ฅด๊ธฐโฆ</button></div>
|
|
44
44
|
<div class="settings-row"><span class="settings-label">์๊ฐ๋ ์กฐ๋ช
</span><select id="daylight-select" class="tool" title="์ค์ ์๊ฐ์ ๋ฐ๋ผ ์ฐฝ๋ฌธยทํ
๋ผ์คยท์ค๋ด ๋น์ด ๋ฐ๋๋๋ค (์์นจ 06~10 ยท ๋ฎ 10~17 ยท ์ ๋
17~20 ยท ๋ฐค 20~06). ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ก ์๊ฐ๋๋ฅผ ๊ณ ์ ํ ์๋ ์์ด์"><option value="auto">๐ ์๋ (์ค์ ์๊ฐ)</option><option value="off">๋</option><option value="morning">๐
์์นจ</option><option value="day">โ๏ธ ๋ฎ</option><option value="evening">๐ ์ ๋
</option><option value="night">๐ ๋ฐค</option></select></div>
|
|
45
|
+
<div class="settings-row"><span class="settings-label">์ผ์ธ ๋ ์จ</span><select id="weather-select" class="tool" title="ํ
๋ผ์คยท์ฅ์์ ์ ๊ฐ์ ์ผ์ธ ๋ฐฉ์ ๋นยท๋ โ ํ
๋ง์ ๋ง์ถฐ๋ ํฌ๋ฆฌ์ค๋ง์ค ์ธต์ ๋, ์ค์ ๋ ์จ๋ ์๋ ๋์์ ํ์ฌ ๋ ์จ(Open-Meteo, 30๋ถ๋ง๋ค)"><option value="theme">๐ ํ
๋ง์ ๋ง์ถฐ</option><option value="real">๐ค๏ธ ์ค์ ๋ ์จ</option><option value="clear">โ๏ธ ๋ง์</option><option value="rain">๐ง๏ธ ๋น</option><option value="snow">โ๏ธ ๋</option></select></div>
|
|
46
|
+
<div class="settings-row hidden" id="weather-place-row"><span class="settings-label">๋ ์จ ๋์</span><input id="weather-place" class="tool weather-place" placeholder="์: ์์ธ, Tokyo" autocomplete="off" spellcheck="false" title="๋์ ์ด๋ฆ์ ์ ๊ณ Enter โ ๋์ ์ด๋ฆ๊ณผ ์ขํ๋ง Open-Meteo ๋ก ๋ณด๋
๋๋ค"><span id="weather-status" class="muted weather-status"></span></div>
|
|
47
|
+
<div class="settings-row"><span class="settings-label">ํ
๋ง ์ธต</span><select id="theme-mode-select" class="tool" title="ํ ๋ก์ยทํฌ๋ฆฌ์ค๋ง์คยท์ฌ๋ฆํด๊ฐ ํ
๋ง ์ธต โ ์ง์ ๊ณ ๋ฅด๊ธฐ๋ ๐จ ์์ ์ธต๋ง๋ค, ๋ ์ง์ ๋ง์ถฐ ์๋์ 10/15~11/1 ํ ๋ก์ ยท 12/1~12/26 ํฌ๋ฆฌ์ค๋ง์ค ยท 7/15~8/31 ์ฌ๋ฆํด๊ฐ (์ง์ ๊ณ ๋ฅธ ์ธต์ ๊ทธ๋๋ก)"><option value="manual">๐จ ์ง์ ๊ณ ๋ฅด๊ธฐ</option><option value="season">๐
๋ ์ง์ ๋ง์ถฐ ์๋</option></select></div>
|
|
45
48
|
<div class="settings-row"><span class="settings-label">์ธต ์ธํ
๋ฆฌ์ด</span><button id="floor-style-btn" class="tool" title="์ง๊ธ ๋ณด๋ ์ธต์ ์ธํ
๋ฆฌ์ด ํ๋ฆฌ์
(์ธต๋ณ ์๋ด์ ๐จ ์ ๊ฐ์) โ ์๊ณผ ์ฌ๋ฌด์ค ๊ตฌ์กฐ๊ฐ ๋ฐ๋๋๋ค">๐จ ๊ณ ๋ฅด๊ธฐโฆ</button></div>
|
|
46
49
|
<div class="settings-row"><span class="settings-label">์ธ์ด</span><select id="lang-select" class="tool" title="ํ๋ฉด ์ธ์ด"><option value="ko">ํ๊ตญ์ด</option><option value="en">English</option></select></div>
|
|
47
50
|
<div class="settings-row"><span class="settings-label">๋จ์ถํค <kbd>?</kbd></span><button id="keys-btn" class="tool" title="ํค๋ณด๋ ๋จ์ถํค ํ">โจ๏ธ ๋ณด๊ธฐโฆ</button></div>
|
package/public/js/customize.js
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
// ์บ๋ฆญํฐ ๊ณ ๋ฅด๊ธฐ ๋ชจ๋ฌ: ๋์ ์ ํ โ 12๋ช
์ค ํ๋(Claude ๊ณ์ด์ '์๋: ์ธ์
๋ง๋ค ๋ค๋ฅด๊ฒ'๋) ๋๋ ์ฌ๋ฆฐ ๊ทธ๋ฆผ โ ๊ฑท๋ ๋ฏธ๋ฆฌ๋ณด๊ธฐ โ ์ ์ฅ(localStorage)
|
|
2
2
|
// ์ปค์คํ
์บ๋ฆญํฐ(FE-56): '๏ผ PNG ์ฌ๋ฆฌ๊ธฐ' ํ์ผ์ ํ์ผ์ ๊ณ ๋ฅด๊ฑฐ๋ ๋์ด๋ค ๋์ผ๋ฉด ํฌ๋ช
์ฌ๋ฐฑ์ ์๋ผ ์ ์ฅํ๊ณ ๊ทธ ๊ทธ๋ฆผ์ ๊ณ ๋ฅธ๋ค.
|
|
3
3
|
// ํํธ ๋ฐ๊พธ๊ธฐ(FE-57): ๋จธ๋ฆฌยท๋ชธํต์ ๋ค๋ฅธ ์บ๋ฆญํฐ ๊ฒ์ผ๋ก, ๋จธ๋ฆฌ์นด๋ฝยท์์ยทํ์ ์, ์ผ๊ตด ์ํ. ๊ฐ์ 'mix:...' (characters.js)
|
|
4
|
-
import { ACCESSORIES, CHARACTERS, DEFAULTS, drawCharacter, isCustomCharacter, lookValue, parseLook, preloadCharacters } from './scene/characters.js';
|
|
4
|
+
import { ACCESSORIES, CHARACTERS, DEFAULTS, MIX_PARTS, SPECIALS, drawCharacter, isCustomCharacter, lookValue, parseLook, preloadCharacters } from './scene/characters.js';
|
|
5
5
|
import { MESSAGES, prepareImage } from './customCharacters.js';
|
|
6
6
|
|
|
7
7
|
// ์ ๊ณ ๋ฅด๊ธฐ ์นธ (6์๋ฆฌ hex). ๊ทธ ๋ฐ์ ์์ ์ ๊ณ ๋ฅด๊ฐ๋ก
|
|
8
8
|
export const HAIR_COLORS = ['2b2b30', '4a3426', '7b4a2c', 'b5793f', 'e3c16f', 'c4532f', '9aa0ad', 'f1efe8', 'e48bb4', '5b86d6'];
|
|
9
9
|
export const CLOTH_COLORS = ['e8683a', 'f2b51d', '5ac487', '1f8a70', '4f7fd0', '2f3b66', '8a5cc7', 'd0507a', '2b2f3a', 'eceef3', '9aa0ad', '8b5a3c'];
|
|
10
|
+
// ํ
๋ง ์บ๋ฆญํฐ ์ค (FE-62): ํคํธ ์บ๋ฆญํฐ + ์ฝ์คํฌ์ ์์ด ์ถ์ฒ ์กฐํฉ
|
|
11
|
+
export const THEME_LOOKS = [
|
|
12
|
+
...SPECIALS.filter((c) => !c.still).map((c) => [c.id, c.label]),
|
|
13
|
+
['mix:male-f.male-f.-.2b2f3a.4a3426.pumpkin', 'ํธ๋ฐ ๋จธ๋ฆฌ'],
|
|
14
|
+
['mix:male-b.male-b.f4f4f4.d42a32.b0262e.santa', '์ฐํ'],
|
|
15
|
+
...SPECIALS.filter((c) => c.still).map((c) => [c.id, c.label]),
|
|
16
|
+
];
|
|
17
|
+
/** ์์ ์ ์๋ ๋จธ๋ฆฌยท๋ชธ(์ ๋ นยท๋์ฌ๋ยท๋ฃจ๋ํ)์ด ๋ผ๋ฉด ์์ ์ ์๊ฒ ๋ง์ถ๋ค */
|
|
18
|
+
function fixLook(l) {
|
|
19
|
+
if (l.head === l.body) return l;
|
|
20
|
+
if (!MIX_PARTS.includes(l.body)) l.body = MIX_PARTS.includes(l.head) ? l.head : DEFAULTS.me;
|
|
21
|
+
if (!MIX_PARTS.includes(l.head)) l.head = l.body;
|
|
22
|
+
return l;
|
|
23
|
+
}
|
|
10
24
|
const COLOR_ROWS = [['hair', '๋จธ๋ฆฌ์นด๋ฝ', HAIR_COLORS], ['top', '์์', CLOTH_COLORS], ['bottom', 'ํ์', CLOTH_COLORS]];
|
|
11
25
|
|
|
12
26
|
const esc = (s) => String(s ?? '').replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
|
|
@@ -69,6 +83,8 @@ export function createCustomizer(store, avatars) {
|
|
|
69
83
|
${nameRow()}
|
|
70
84
|
<div class="cz-group"><div class="cz-label">์บ๋ฆญํฐ${allowAuto() ? ' <span class="muted">โ ์๋์ด๋ฉด ์ธ์
๋ง๋ค ๋ค๋ฅด๊ฒ</span>' : ''}</div>
|
|
71
85
|
<div class="cz-presets">${auto}${CHARACTERS.map((c) => `<button class="cz-preset ${c === draft ? 'on' : ''}" data-char="${c}" title="${c}"><canvas class="cz-thumb" data-char="${c}"></canvas></button>`).join('')}</div></div>
|
|
86
|
+
<div class="cz-group"><div class="cz-label">ํ
๋ง ์บ๋ฆญํฐ <span class="muted">โ ํ ๋ก์ยทํฌ๋ฆฌ์ค๋ง์ค (์๋์๋ ์์ด์ง ์์)</span></div>
|
|
87
|
+
<div class="cz-presets">${THEME_LOOKS.map(([v, label]) => `<button class="cz-preset cz-theme ${v === draft ? 'on' : ''}" data-char="${esc(v)}" title="${esc(label)}"><canvas class="cz-thumb" data-char="${esc(v)}"></canvas><span>${esc(label)}</span></button>`).join('')}</div></div>
|
|
72
88
|
${partsGroup()}
|
|
73
89
|
<div class="cz-group"><div class="cz-label">๋ด ์บ๋ฆญํฐ <span class="muted">โ ๋ฐฐ๊ฒฝ ์๋ PNG ๋ฅผ ์ฌ๋ฆฌ๊ฑฐ๋ ๋์ด๋ค ๋๊ธฐ ยท ์๋์๋ ์์ด์ง ์์</span></div>
|
|
74
90
|
<div class="cz-presets">${customs}${upload}</div>
|
|
@@ -84,7 +100,7 @@ export function createCustomizer(store, avatars) {
|
|
|
84
100
|
function setPart(part, value) {
|
|
85
101
|
const l = baseLook();
|
|
86
102
|
l[part] = value || null;
|
|
87
|
-
draft = lookValue(l); armed = null;
|
|
103
|
+
draft = lookValue(fixLook(l)); armed = null;
|
|
88
104
|
render();
|
|
89
105
|
}
|
|
90
106
|
/** ํํธ ๋ฐ๊พธ๊ธฐ: ๋จธ๋ฆฌยท๋ชธํต ์ค(์ธ๋ค์ผ), ์ ์ค(์๋ + ํ๋ ํธ + ์ ๊ณ ๋ฅด๊ฐ), ์ํ ์ค */
|
|
@@ -93,13 +109,13 @@ export function createCustomizer(store, avatars) {
|
|
|
93
109
|
const thumb = (look, zoom) => `<canvas class="cz-thumb" data-char="${esc(lookValue(look))}"${zoom ? ` data-zoom="${zoom}"` : ''}></canvas>`;
|
|
94
110
|
const tile = (part, value, on, title, inner) => `<button class="cz-preset cz-mini ${on ? 'on' : ''}" data-part="${part}" data-value="${esc(value)}" title="${esc(title)}">${inner}</button>`;
|
|
95
111
|
const row = (label, inner) => `<div class="cz-partrow"><span class="cz-partlabel">${label}</span><div class="cz-presets">${inner}</div></div>`;
|
|
96
|
-
const heads =
|
|
97
|
-
const bodies =
|
|
112
|
+
const heads = MIX_PARTS.map((c) => tile('head', c, cur && cur.head === c, `${c} ์ ๋จธ๋ฆฌ`, thumb(fixLook({ ...l, head: c, hair: null }), 'head'))).join('');
|
|
113
|
+
const bodies = MIX_PARTS.map((c) => tile('body', c, cur && cur.body === c, `${c} ์ ๋ชธ`, thumb(fixLook({ ...l, body: c, top: null, bottom: null })))).join('');
|
|
98
114
|
const colors = COLOR_ROWS.map(([part, label, list]) => row(label,
|
|
99
115
|
`<button class="cz-swatch cz-orig ${cur && !cur[part] ? 'on' : ''}" data-part="${part}" data-value="" title="์๋ ์">์๋</button>`
|
|
100
116
|
+ list.map((c) => `<button class="cz-swatch ${cur && cur[part] === c ? 'on' : ''}" data-part="${part}" data-value="${c}" title="#${c}" style="--sw:#${c}"></button>`).join('')
|
|
101
117
|
+ `<label class="cz-swatch cz-pick ${cur && cur[part] && !list.includes(cur[part]) ? 'on' : ''}" title="๋ค๋ฅธ ์ ๊ณ ๋ฅด๊ธฐ" style="${cur && cur[part] && !list.includes(cur[part]) ? `--sw:#${cur[part]}` : ''}"><input type="color" class="cz-color" data-part="${part}" value="#${(cur && cur[part]) || list[0]}"></label>`)).join('');
|
|
102
|
-
const accs = [{ id: '', label: '์์' }, ...ACCESSORIES].map((a) => tile('acc', a.id, cur && (cur.acc || '') === a.id, a.label, `${thumb({ ...l, acc: a.id || null }, 'head')}<span>${esc(a.label)}</span>`)).join('');
|
|
118
|
+
const accs = [{ id: '', label: '์์' }, ...ACCESSORIES].map((a) => tile('acc', a.id, cur && (cur.acc || '') === a.id, a.label, `${thumb({ ...fixLook({ ...l }), acc: a.id || null }, 'head')}<span>${esc(a.label)}</span>`)).join('');
|
|
103
119
|
return `<div class="cz-group"><div class="cz-label">ํํธ ๋ฐ๊พธ๊ธฐ <span class="muted">โ ${cur ? '๋จธ๋ฆฌยท๋ชธ์ ์๊ณ ์ยท์ํ ๊ณ ๋ฅด๊ธฐ' : '์ง๊ธ ์บ๋ฆญํฐ์์ ์์ํฉ๋๋ค'}</span></div>
|
|
104
120
|
${row('๋จธ๋ฆฌ', heads)}${row('๋ชธ', bodies)}${colors}${row('์ํ', accs)}</div>`;
|
|
105
121
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ์ธต ํ์ ๐จ ๋ฒํผ โ ์ธํ
๋ฆฌ์ด ํ๋ฆฌ์
ํ์ค๋ฒ (FE-34). ์ ํ์ localStorage(aiOffice.floorStyle.<floorId>) ์ ๋จ๋๋ค.
|
|
2
|
-
import { PRESETS, presetById } from './scene/floorStyle.js';
|
|
2
|
+
import { PRESETS, THEMES, presetById } from './scene/floorStyle.js';
|
|
3
3
|
import { LAYOUTS } from './scene/layouts.js';
|
|
4
4
|
|
|
5
5
|
export function createFloorStylePicker(store, book, { onChange } = {}) {
|
|
@@ -19,7 +19,9 @@ export function createFloorStylePicker(store, book, { onChange } = {}) {
|
|
|
19
19
|
pop.innerHTML = `
|
|
20
20
|
<div class="style-pop-head">๐จ ${esc(floor.no)}F ์ธํ
๋ฆฌ์ด</div>
|
|
21
21
|
<button class="style-opt ${chosen ? '' : 'on'}" data-preset=""><span class="style-chips">${chips(autoP)}</span><span class="style-name">์๋ <span class="muted">(${esc(autoP.label)})</span></span></button>
|
|
22
|
-
${PRESETS.map((p) => `<button class="style-opt ${chosen === p.id ? 'on' : ''}" data-preset="${p.id}"><span class="style-chips">${chips(p)}</span><span class="style-name">${p.emoji} ${esc(p.label)}<span class="style-layout">${esc(LAYOUTS[p.layout] ? LAYOUTS[p.layout].label : '')}</span></span></button>`).join('')}
|
|
22
|
+
${PRESETS.map((p) => `<button class="style-opt ${chosen === p.id ? 'on' : ''}" data-preset="${p.id}"><span class="style-chips">${chips(p)}</span><span class="style-name">${p.emoji} ${esc(p.label)}<span class="style-layout">${esc(LAYOUTS[p.layout] ? LAYOUTS[p.layout].label : '')}</span></span></button>`).join('')}
|
|
23
|
+
<div class="style-pop-sub">ํ
๋ง <span class="muted">โ ๊ตฌ์กฐ๋ ๊ทธ๋๋ก, ์๊ณผ ์ํ๋ง</span></div>
|
|
24
|
+
${THEMES.map((t) => `<button class="style-opt ${chosen === t.id ? 'on' : ''}" data-preset="${t.id}"><span class="style-chips">${chips(t)}</span><span class="style-name">${t.emoji} ${esc(t.label)}</span></button>`).join('')}`;
|
|
23
25
|
}
|
|
24
26
|
function open(id, anchor) {
|
|
25
27
|
floorId = id;
|
package/public/js/i18n.js
CHANGED
|
@@ -70,6 +70,18 @@ const DICT = {
|
|
|
70
70
|
'๊ทธ๋ฆผ์ด ๋๋ฌด ํฝ๋๋ค โ ํ ๋ณ 8192px ์ดํ๋ก ์ฌ๋ ค ์ฃผ์ธ์': 'Picture is too large โ each side must be 8192 px or less', '์ด๋ฏธ์ง๋ฅผ ์ฝ์ง ๋ชปํ์ต๋๋ค โ ๋ค๋ฅธ ํ์ผ๋ก ํด ๋ณด์ธ์': 'Could not read the image โ try another file', '๊ทธ๋ฆผ์ด ์ ๋ถ ํฌ๋ช
ํฉ๋๋ค': 'The picture is fully transparent',
|
|
71
71
|
'์ปค์คํ
์บ๋ฆญํฐ๊ฐ ๋๋ฌด ๋ง์ต๋๋ค โ ์ ์ฐ๋ ๊ฒ์ ์ง์ ์ฃผ์ธ์': 'Too many custom characters โ delete ones you no longer use', '์ ์ฅ ๊ณต๊ฐ์ด ๋ถ์กฑํฉ๋๋ค โ ์ ์ฐ๋ ์ปค์คํ
์บ๋ฆญํฐ๋ฅผ ์ง์ ์ฃผ์ธ์': 'Out of storage space โ delete custom characters you no longer use',
|
|
72
72
|
'๋ฐฐ๊ฒฝ์ด ํฌ๋ช
ํ์ง ์์ ๊ทธ๋ฆผ์ด๋ผ ์ฌ๊ฐํ ๊ทธ๋๋ก ๋ณด์
๋๋ค': 'The background is not transparent, so it shows as a rectangle', '์ถ๊ฐํ์ต๋๋ค โ ์ ์ฅ์ ๋๋ฅด๋ฉด ์ด ๋์์ ์ ์ฉ๋ฉ๋๋ค': 'Added โ press Save to use it for this target',
|
|
73
|
+
// ์ผ์ธ ๋ ์จ (FE-61)
|
|
74
|
+
'์ผ์ธ ๋ ์จ': 'Outdoor weather', '๐ ํ
๋ง์ ๋ง์ถฐ': '๐ Match theme', '๐ค๏ธ ์ค์ ๋ ์จ': '๐ค๏ธ Real weather', 'โ๏ธ ๋ง์': 'โ๏ธ Clear', '๐ง๏ธ ๋น': '๐ง๏ธ Rain', 'โ๏ธ ๋': 'โ๏ธ Snow', '๋ ์จ ๋์': 'Weather city',
|
|
75
|
+
'์: ์์ธ, Tokyo': 'e.g. Seoul, Tokyo', '๋์๋ฅผ ์ ๊ณ Enter': 'Type a city and press Enter', '๋ฐ๋ ์คโฆ': 'Fetchingโฆ', '์ฐพ๋ ์คโฆ': 'Searchingโฆ', '๋ ์จ๋ฅผ ๋ฐ์ง ๋ชปํ์ต๋๋ค': 'Could not get the weather', '๋์๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค (์ธํฐ๋ท ์ฐ๊ฒฐ)': 'Could not look up the city (internet connection)',
|
|
76
|
+
'ํ
๋ผ์คยท์ฅ์์ ์ ๊ฐ์ ์ผ์ธ ๋ฐฉ์ ๋นยท๋ โ ํ
๋ง์ ๋ง์ถฐ๋ ํฌ๋ฆฌ์ค๋ง์ค ์ธต์ ๋, ์ค์ ๋ ์จ๋ ์๋ ๋์์ ํ์ฌ ๋ ์จ(Open-Meteo, 30๋ถ๋ง๋ค)': 'Rain or snow over outdoor rooms such as the terrace or rooftop garden โ Match theme snows on Christmas floors, Real weather uses the current weather of the city below (Open-Meteo, every 30 minutes)',
|
|
77
|
+
'๋์ ์ด๋ฆ์ ์ ๊ณ Enter โ ๋์ ์ด๋ฆ๊ณผ ์ขํ๋ง Open-Meteo ๋ก ๋ณด๋
๋๋ค': 'Type a city name and press Enter โ only the city name and coordinates are sent to Open-Meteo',
|
|
78
|
+
// ํ
๋ง ์บ๋ฆญํฐ (FE-62)
|
|
79
|
+
'ํ
๋ง ์บ๋ฆญํฐ': 'Theme characters', 'โ ํ ๋ก์ยทํฌ๋ฆฌ์ค๋ง์ค (์๋์๋ ์์ด์ง ์์)': 'โ Halloween ยท Christmas (never picked by auto)', '๋ฑํ์ด์ด': 'Vampire', '์ข๋น': 'Zombie', 'ํด๊ณจ': 'Skeleton', '์ ๋ น': 'Ghost', 'ํธ๋ฐ ๋จธ๋ฆฌ': 'Pumpkin head', '์ฐํ': 'Santa', '๋ฃจ๋ํ': 'Rudolph', '๋์ฌ๋': 'Snowman',
|
|
80
|
+
'์ฐํ ๋ชจ์': 'Santa hat', '๋ฃจ๋ํ ๋ฟ': 'Reindeer antlers', 'ํธ๋ฐ ํ': 'Pumpkin mask',
|
|
81
|
+
// ํ
๋ง ์ธต (FE-60)
|
|
82
|
+
'ํ
๋ง ์ธต': 'Theme floors', '๐จ ์ง์ ๊ณ ๋ฅด๊ธฐ': '๐จ Pick manually', '๐
๋ ์ง์ ๋ง์ถฐ ์๋': '๐
Auto by date', 'ํ
๋ง': 'Themes', 'ํ ๋ก์': 'Halloween', 'ํฌ๋ฆฌ์ค๋ง์ค': 'Christmas', '์ฌ๋ฆํด๊ฐ': 'Summer vacation',
|
|
83
|
+
'ํ ๋ก์ยทํฌ๋ฆฌ์ค๋ง์คยท์ฌ๋ฆํด๊ฐ ํ
๋ง ์ธต โ ์ง์ ๊ณ ๋ฅด๊ธฐ๋ ๐จ ์์ ์ธต๋ง๋ค, ๋ ์ง์ ๋ง์ถฐ ์๋์ 10/15~11/1 ํ ๋ก์ ยท 12/1~12/26 ํฌ๋ฆฌ์ค๋ง์ค ยท 7/15~8/31 ์ฌ๋ฆํด๊ฐ (์ง์ ๊ณ ๋ฅธ ์ธต์ ๊ทธ๋๋ก)': 'Halloween ยท Christmas ยท Summer vacation theme floors โ pick per floor from ๐จ, or auto by date: Halloween 10/15โ11/1 ยท Christmas 12/1โ12/26 ยท Summer 7/15โ8/31 (floors you picked stay as they are)',
|
|
84
|
+
'โ ๊ตฌ์กฐ๋ ๊ทธ๋๋ก, ์๊ณผ ์ํ๋ง': 'โ same layout, new colors and decorations',
|
|
73
85
|
// ํํธ ๋ฐ๊พธ๊ธฐ (FE-57)
|
|
74
86
|
'ํํธ ๋ฐ๊พธ๊ธฐ': 'Mix parts', 'โ ๋จธ๋ฆฌยท๋ชธ์ ์๊ณ ์ยท์ํ ๊ณ ๋ฅด๊ธฐ': 'โ mix heads and bodies, pick colors and accessories', 'โ ์ง๊ธ ์บ๋ฆญํฐ์์ ์์ํฉ๋๋ค': 'โ starts from the current character',
|
|
75
87
|
'๋จธ๋ฆฌ': 'Head', '๋ชธ': 'Body', '๋จธ๋ฆฌ์นด๋ฝ': 'Hair', '์์': 'Top', 'ํ์': 'Bottom', '์ํ': 'Accessory', '์๋': 'Original', '์๋ ์': 'Original color', '๋ค๋ฅธ ์ ๊ณ ๋ฅด๊ธฐ': 'Pick another color',
|
|
@@ -119,6 +131,8 @@ const RULES = [
|
|
|
119
131
|
[/^(.+) ์ธ์
์ ๋ชฉ๋ก์์ ์น์ฐ๊ธฐ$/, (m) => `Dismiss this ${tr(m[1])} session`],
|
|
120
132
|
[/^(\d+)F (.+) ์ Claude$/, (m) => `Claude of ${m[1]}F ${m[2]}`],
|
|
121
133
|
[/^(.+) \(๋\)$/, (m) => `${m[1]} (me)`],
|
|
134
|
+
[/^"(.+)" ๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค$/, (m) => `Could not find "${m[1]}"`],
|
|
135
|
+
[/^(๐ง๏ธ ๋น|โ๏ธ ๋|โ๏ธ ๋ง์) ยท (\d\d:\d\d)$/, (m) => `${tr(m[1])} ยท ${m[2]}`],
|
|
122
136
|
[/^(.+) ์ ๋จธ๋ฆฌ$/, (m) => `head of ${m[1]}`],
|
|
123
137
|
[/^(.+) ์ ๋ชธ$/, (m) => `body of ${m[1]}`],
|
|
124
138
|
[/^๐จ (\d+)F ์ธํ
๋ฆฌ์ด$/, (m) => `๐จ ${m[1]}F interior`],
|
package/public/js/main.js
CHANGED
|
@@ -10,7 +10,8 @@ import { initTextTips } from './texttip.js';
|
|
|
10
10
|
import { createAvatarStore } from './avatarStore.js';
|
|
11
11
|
import { createCustomizer } from './customize.js';
|
|
12
12
|
import { createOverview } from './overview.js';
|
|
13
|
-
import { createFloorStyleBook } from './scene/floorStyle.js';
|
|
13
|
+
import { createFloorStyleBook, seasonTheme } from './scene/floorStyle.js';
|
|
14
|
+
import { geocode, currentWeather, REFRESH_MS } from './weather.js';
|
|
14
15
|
import { createFloorStylePicker } from './floorStylePicker.js';
|
|
15
16
|
import { layoutOfPreset } from './scene/map.js';
|
|
16
17
|
import { buildLayout, roomLabelIn } from './scene/layouts.js';
|
|
@@ -23,7 +24,8 @@ const tooltip = createTooltip();
|
|
|
23
24
|
|
|
24
25
|
// ---------- ๐จ ์ธต ์ธํ
๋ฆฌ์ด (ํ๋ฆฌ์
= ์ + ์ฌ๋ฌด์ค ๊ตฌ์กฐ) ----------
|
|
25
26
|
const floorStyles = createFloorStyleBook(); // ์ธต๋ณ ์ธํ
๋ฆฌ์ด ํ๋ฆฌ์
(FE-34/35): ์ง์ ๊ณ ๋ฅธ ๊ฒ > id ํด์ ์๋ ๋ฐฐ์
|
|
26
|
-
|
|
27
|
+
// ์ค์ 'ํ
๋ง ์ธต: ๋ ์ง์ ๋ง์ถฐ ์๋' ์ด๋ฉด ์ง์ ๊ณ ๋ฅด์ง ์์ ์ธต์ ๊ทธ ์ฒ ์ ํ
๋ง(ํ ๋ก์ยทํฌ๋ฆฌ์ค๋ง์คยท์ฌ๋ฆํด๊ฐ)๋ฅผ ๋ฎ๋๋ค (FE-60)
|
|
28
|
+
const styleOfFloor = (f) => floorStyles.resolve(f, store.floorsByNo(), store.themeMode === 'season' ? seasonTheme() : null);
|
|
27
29
|
function applyFloorStyle() { scene.setFloorStyle(styleOfFloor(store.selectedFloor())); }
|
|
28
30
|
// ์บ๋ฆญํฐ(createActors)๋ณด๋ค ๋จผ์ ๊ตฌ๋
ํ๋ค: ์ธต์ด ๋ฐ๋๋ฉด ๋ ์ด์์๋ถํฐ ๋ฐ๊พผ ๋ค ์บ๋ฆญํฐ๋ฅผ ๊ทธ ๋ ์ด์์์ ์๋ฆฌ์ ๋ฐฐ์นํด์ผ ํ๋ค
|
|
29
31
|
store.subscribe((kind) => { if (kind === 'select' || kind === 'snapshot' || kind === 'update' || kind === 'remove') applyFloorStyle(); });
|
|
@@ -412,6 +414,56 @@ applyTheme();
|
|
|
412
414
|
|
|
413
415
|
// ---------- ์๊ฐ๋ ์กฐ๋ช
(FE-33): ์ค์ ์๊ฐ์ผ๋ก ์์นจยท๋ฎยท์ ๋
ยท๋ฐค, ์ค์ ํ์ผ๋ก ๊ณ ์ ๊ฐ๋ฅ ----------
|
|
414
416
|
const daylightSel = document.getElementById('daylight-select');
|
|
417
|
+
// ---------- ์ผ์ธ ๋ ์จ (FE-61) ----------
|
|
418
|
+
const weatherSel = document.getElementById('weather-select');
|
|
419
|
+
const weatherRow = document.getElementById('weather-place-row');
|
|
420
|
+
const weatherInput = document.getElementById('weather-place');
|
|
421
|
+
const weatherStatus = document.getElementById('weather-status');
|
|
422
|
+
const WEATHER_LABEL = { rain: '๐ง๏ธ ๋น', snow: 'โ๏ธ ๋', clear: 'โ๏ธ ๋ง์' };
|
|
423
|
+
let weatherBusy = false, weatherErr = '';
|
|
424
|
+
async function refreshWeather() {
|
|
425
|
+
if (store.weatherMode !== 'real' || !store.weatherPlace || weatherBusy) return;
|
|
426
|
+
weatherBusy = true;
|
|
427
|
+
try { store.setWeatherNow(await currentWeather(store.weatherPlace)); weatherErr = ''; } catch { weatherErr = '๋ ์จ๋ฅผ ๋ฐ์ง ๋ชปํ์ต๋๋ค'; renderWeather(); }
|
|
428
|
+
weatherBusy = false;
|
|
429
|
+
}
|
|
430
|
+
function renderWeather() {
|
|
431
|
+
if (weatherSel.value !== store.weatherMode) weatherSel.value = store.weatherMode;
|
|
432
|
+
weatherRow.classList.toggle('hidden', store.weatherMode !== 'real');
|
|
433
|
+
const p = store.weatherPlace, w = store.weatherNow;
|
|
434
|
+
if (document.activeElement !== weatherInput) weatherInput.value = p ? p.name : '';
|
|
435
|
+
weatherStatus.textContent = weatherErr || (!p ? '๋์๋ฅผ ์ ๊ณ Enter' : w ? `${WEATHER_LABEL[w.kind]} ยท ${new Date(w.at).toTimeString().slice(0, 5)}` : '๋ฐ๋ ์คโฆ');
|
|
436
|
+
scene.setWeather({ mode: store.weatherMode, real: w });
|
|
437
|
+
}
|
|
438
|
+
weatherSel.onchange = () => { store.setWeatherMode(weatherSel.value); refreshWeather(); };
|
|
439
|
+
weatherInput.addEventListener('keydown', (e) => {
|
|
440
|
+
e.stopPropagation(); // ๊ธ์ ๋จ์ถํค๊ฐ ์์ง ์๊ฒ
|
|
441
|
+
if (e.key !== 'Enter') return;
|
|
442
|
+
e.preventDefault();
|
|
443
|
+
const q = weatherInput.value.trim();
|
|
444
|
+
if (!q) { store.setWeatherPlace(null); return; }
|
|
445
|
+
weatherStatus.textContent = '์ฐพ๋ ์คโฆ';
|
|
446
|
+
geocode(q, getLang()).then((p) => {
|
|
447
|
+
if (!p) { weatherErr = `"${q}" ๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค`; renderWeather(); weatherErr = ''; return; }
|
|
448
|
+
weatherErr = ''; store.setWeatherPlace(p); weatherInput.blur(); refreshWeather();
|
|
449
|
+
}).catch(() => { weatherErr = '๋์๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค (์ธํฐ๋ท ์ฐ๊ฒฐ)'; renderWeather(); weatherErr = ''; });
|
|
450
|
+
});
|
|
451
|
+
store.subscribe((kind) => { if (kind === 'option') renderWeather(); });
|
|
452
|
+
renderWeather();
|
|
453
|
+
refreshWeather();
|
|
454
|
+
setInterval(refreshWeather, REFRESH_MS);
|
|
455
|
+
|
|
456
|
+
const themeModeSel = document.getElementById('theme-mode-select');
|
|
457
|
+
let appliedThemeMode = store.themeMode;
|
|
458
|
+
function applyThemeMode() {
|
|
459
|
+
if (themeModeSel.value !== store.themeMode) themeModeSel.value = store.themeMode;
|
|
460
|
+
if (appliedThemeMode === store.themeMode) return;
|
|
461
|
+
appliedThemeMode = store.themeMode;
|
|
462
|
+
applyFloorStyle(); actors.sync(true); overview.invalidate(); dirty = true;
|
|
463
|
+
}
|
|
464
|
+
themeModeSel.onchange = () => store.setThemeMode(themeModeSel.value);
|
|
465
|
+
store.subscribe((kind) => { if (kind === 'option') applyThemeMode(); });
|
|
466
|
+
themeModeSel.value = store.themeMode;
|
|
415
467
|
function applyDaylight() { scene.setDaylight(store.daylight); if (daylightSel.value !== store.daylight) daylightSel.value = store.daylight; }
|
|
416
468
|
daylightSel.onchange = () => store.setDaylight(daylightSel.value);
|
|
417
469
|
store.subscribe((kind) => { if (kind === 'option') applyDaylight(); });
|