feat(sw): add SW Footer component and Storybook stories

This commit is contained in:
shkim4 committed 2026-10-20 09:41:12 +09:00
1 parent 77a809baa3
commit 6cf885412e
105 files changed
+3288 -3

No files matched your search

@@ -0,0 +1,82 @@
# SW Footer — Figma 35개 노드 매핑·구현 기록 (2026-10-20)
- Figma: 이지빔-SW `rj6wpsfk24lOWvWiVRAANP`. 35개 노드 모두 Figma MCP(`get_metadata`, `get_design_context`, `get_screenshot`)로 읽었고, 글자 stroke·변형 레이어·opacity는 Plugin API(`use_figma` 읽기 전용)로 확인했습니다. Figma 파일은 수정하지 않았습니다.
- 추가 기준(사용자 제공, 2026-10-20): 실제 SW 화면 `src/sw/assets/footer-reference/composition-2026-10-20.png`(1915 × 1030), 좁은 화면 `narrow-2026-10-20.png`(952 × 183). 바 안 명령창·좌표·보조기능 켜짐·좁은 폭 동작은 이 화면을 따르고, Figma와 다르면 Figma가 우선입니다.
- 구현: `packages/sw/src/components/sw-footer/`, `packages/sw/src/theme/sw-footer.css`, 자산 `packages/sw/src/components/sw-footer/assets/`(Figma SVG 48개).
- Storybook: `SW/Footer` — Figma 렌더 비교 이미지는 `src/sw/assets/figma-reference/footer/<node>.png`(35개).
## 1. 노드 → 구현 → Story
35개 노드는 34개 Component Set/부품과 1개 전체 화면입니다. 같은 UI의 상태 변화는 별도 컴포넌트가 아니라 Props·CSS 상태로 구현했습니다.
| # | Node | 이름 | 분류 | 구현 (컴포넌트 · Props) | Figma 상태 → 구현 | Story |
|---|---|---|---|---|---|---|
| 1 | 26:9776 | btn_add_layout | Layout 탭 | `SWFooterLayoutTabs onAdd` | Default / Variant2(Hover) → CSS hover | Figma Variant · Layout 탭 |
| 2 | 26:9785 | btn_delete | Layout 탭 | 둥근 탭 ×(`onRemove`) | Default / Variant2(Hover, 노랑 ×) | 〃 |
| 3 | 26:9931 | btn_close2 | Layout 탭 | 사선 탭 ×(`onRemove`) | Default / Variant2(Hover) | 〃 |
| 4 | 26:9794 | layout01 | Layout 탭 | 마지막 탭 `shape="slant"` | default · hover · on · on hover · Variant5(이름 편집) · Variant6(편집 + hover) → `value`, `editingId`, hover | 〃 |
| 5 | 26:9837 | layout02 | Layout 탭 | `shape="rounded"` | 같은 6상태 | 〃 |
| 6 | 26:9885 | layout03 | Layout 탭 | `shape="rounded"` | default · hover · on · on hover | 〃 |
| 7 | 26:9914 | model | Layout 탭 | `modelLabel`, `value === SW_FOOTER_MODEL` | Default · hover · on | 〃 |
| 8 | 26:9942 | 예시) 탭 상태변화 | Layout 탭 | `SWFooterLayoutTabs layouts value onAdd` | Model / Layout 01 / 02 / 03 선택 | 〃, 전체 Footer |
| 9–18 | 26:9459, 26:9466, 26:9473, 26:9480, 26:9487, 26:9494, 26:9501, 26:9508, 26:9515, 26:9522 | text_도형 … text_구조배근 | 분류 탭 | `SWFooterMenu` 항목(`SWFooter categories`) | Default(#D4CBBD) · Variant2(Hover) · Variant3(현재, Pocket) | Figma Variant · 분류 탭 |
| 19–27 | 26:9529, 26:9551, 26:9576, 26:9601, 26:9629, 26:9651, 26:9673, 26:9689, 26:9702 | line · Pline · Arc · circle · ellipse · rect · poly · point · hatch | 명령 | `SWFooterToolButton`(`SWFooterTool` + `swFooterToolIcons.*`) | Default · Variant2(Hover) · Variant3(선택) → hover, `activeToolId`/`pressed` | Figma Variant · 명령 |
| 28 | 26:9739 | Ncopy | 명령 | 〃 (`swFooterToolIcons.ncopy`, Story 수정 분류) | 같은 3상태 | 〃 |
| 29 | 26:9971 | btn_add_menu | 빈 칸 | `SWFooterAddSlot` / `SWFooter onSlotAdd` | Default · hover(주황 점선 링) | Figma Variant · 빈 칸 |
| 30 | 26:9242 | 가이드 영상 보기 | 빈 칸 가이드 | `SWFooterAddSlot guide` / `SWFooter slotGuide` | Default · Variant2(가이드 표시) → hover·focus·`guideOpen` | 〃, 전체 Footer |
| 31 | 26:9096 | guide_바로가기 등록 | 빈 칸 가이드 | `SWFooterGuide media` | Variant5·6·4·9·8·9 = 가이드 영상 프레임 → `media`(프로그램 제공), Story는 6프레임 순환 | 〃 |
| 32 | 26:10023 | ico_arrow | 명령창·▲ | `SWFooterCommandLine` 펼치기, `SWFooter` ▲ 칸 | Default ↑ · hover · on ↓ · Variant4(on hover) → `expanded` / ▲ 패널 열림 | Figma Variant · 명령창, 화면 폭 |
| 33 | 26:10048 | command (접힘) | 명령창 | `SWFooterCommandLine` | 71px, 마지막 3줄 | Figma Variant · 명령창 |
| 34 | 26:10056 | command (펼침) | 명령창 | `SWFooterCommandLine expanded` | 270px | 〃 |
| 35 | 26:10097 | 레이아웃 분리 | 전체 | `SWFooter`(+ `SWFooterLayoutTabs`) | 하단 72px 전체 | 기본 Footer, Figma Variant · Footer 전체 |
26:10097 안의 보조기능 버튼(btn_객체스냅 …, 26:10104)·측면 버튼(26:10189 / 26:10192)·구분선·bg_vacant·menu_bg도 같은 노드에서 읽어 `SWFooterAuxGroup`·`SWFooterSideButton`·`SWFooterMenu` 면으로 구현했습니다.
## 2. Storybook `SW/Footer`
| Story | 내용 |
|---|---|
| 기본 Footer | Figma 데이터만 넣은 `<SWFooter />` 1920 + Figma 26:10097 하단 렌더 |
| 전체 Footer · 실제 조합 | 실제 화면 구성(SWHeader + 도면 + 좌표 + 바 안 명령창 + 명령 10개 + 분류 12개 + Layout 탭), 모든 Callback 동작, 실제 화면 이미지 |
| 도구 그룹별 표시 | 도형(9) · 수정(NCopy) · 빈 분류, 보조기능·측면 버튼 없는 구성 |
| Figma 디자인별 Variant (35 노드) | 노드마다 Figma 렌더 ↔ 구현 상태 |
| Hover / Active / Selected / Disabled | 명령·빈 칸·분류·보조기능·측면 버튼 상태 |
| 도구 및 메뉴 변경 예제 | 사용자 분류·lucide 아이콘·slotCount 10·빈 칸으로 명령 추가·분류 추가·문구 변경·trailing·잠긴 Layout |
| 화면 폭 변화 | 실제 조합 1920 / 1440 / 1280 / 950, 좁은 실제 화면 이미지, Figma 구성 1920 / 1280 / 1024 / 820 |
| 개별 부품 (조합용) | `SWFooterAuxGroup`(3·2열), `SWFooterMenu` + `SWFooterToolButton`, `SWFooterCommandLine` |
## 3. Figma·실제 화면과 구현의 차이
| 항목 | Figma / 실제 화면 | 구현 | 이유 |
|---|---|---|---|
| 글꼴 | Dotum | Noto Sans KR(SW 공통) | SW Header와 같은 확정 대체. 글자 폭·굵기 차이로 분류 탭 위치가 끝에서 최대 12px 차이 |
| 글자 stroke | 검정 1.3 / 1px + 자기 색 0.3 / 0.4px | 검정 외곽선만(Header 토큰) | Header 2026-10-16 수정과 동일 |
| 분류 탭 자간 | 앞 4개 4%, 나머지 10% | 모두 10% | 데이터 기반 탭 하나의 규칙. 10%일 때 Figma 상자 폭(45/61/43/84)에 가장 가까움 |
| Layout 01 vs 02 Hover | layout01 Hover = 주황 면 + 검정 글자, layout02 Hover = 어두운 베이지 + 주황 글자 | 모양별로 Figma 그대로 | Figma 두 컴포넌트가 다르게 그려짐 → 검토 필요 |
| Model 선택 | Figma On = 갈색 / 실제 화면 = 주황 | Figma(갈색) | Figma 우선 |
| Layout 01 단독 | 26:10097에는 Model 없이 Layout 01만 | `modelLabel={false}` 지원, 마지막 탭은 항상 사선 | 두 구성 모두 표현 |
| Variant5 / 6 의미 | 이름 없음(검정 면 + 빨강 글자) | 이름 편집 상태로 해석(Header 입력 편집 `#0F0D0A`·`#E60403`과 같은 값) | 해석 — 검토 필요 |
| 보조기능 켜짐 | Figma 없음 / 실제 화면 주황 테두리·글자 | 실제 화면 적용(아이콘은 자산 색 유지) | 주황 아이콘 자산 없음 |
| Hover(보조기능·측면 버튼), Disabled | Figma 없음 | 글자 `#FF5C00`, 40% | AI 구성 값 — 디자인 확인 필요 |
| 좁은 폭 | Figma 없음 / 실제 화면 | 빈 칸 축소 → ▲ 패널, 분류 ◀ ▶, 보조기능 아이콘만(1279px 이하), 명령창 최소 280 | 전환 폭·최소 폭은 AI 구성 값 |
| 명령창 줄 수 | Figma 3줄(줄 14px) / 실제 화면 접힘 5줄 | Figma 3줄 | Figma 우선 |
| 가이드 영상 | Figma는 6프레임 스토리보드(래스터) | `media`로 프로그램이 제공, Story만 프레임 순환 | 영상 자산은 프로그램 소유 |
| WIPEOUT · 객체스냅추적 아이콘 | 실제 화면에만 있음 | Story에서 lucide 아이콘(프로그램 제공 방식) | Figma 자산 없음 |
## 4. 검증 (2026-10-20, 이 작업에서 실행한 것만)
| 항목 | 결과 |
|---|---|
| 35개 노드 읽기·매핑 | PASS (§1) |
| `npx tsc -p packages/sw/tsconfig.json --noEmit` | PASS |
| `npm run build:sw` (dist·d.ts, CSS 자산 data URI 인라인) | PASS |
| 루트 `npx tsc -b` (Storybook·Story 코드, `@hm-design/sw` 패키지 Import) | PASS |
| `npm run check:ai-docs` | PASS |
| Storybook 렌더링(8개 Story, 실행 중인 6024) | PASS — Story별 Footer 수·콘솔 오류 확인 |
| Figma ↔ 구현 1:1 비교(기본 Footer, 35 노드 Story) | PASS — 위치 측정: 보조기능 (76, 2), 명령 칸 396·y 1, 명령어 전체보기 1552, 분류 탭 Figma 대비 ≤ 12px |
| 동작(실제 조합 Story) | PASS — 명령 선택, 분류 ←/→, 빈 칸 가이드 위치(가운데·4px 위)·클릭, 보조기능 토글, Layout 선택·F2 이름 변경·Delete·추가, 명령창 Enter·펼치기, ▲ 패널 열기·선택 후 닫힘·Escape 복귀 |
| 화면 폭 | PASS — 실제 조합 1920 → 12칸, 1440 → 10칸, 1280 → 명령 6 + ▲, 950 → 명령 4 + ▲·보조기능 아이콘만·분류 ◀ ▶ |
| Header·Popup 영향 | 수정 없음(기존 파일 변경 없음, Footer CSS는 `hm-sw-footer*`와 새 토큰만) |
| 독립 소비 프로젝트(Tarball) 설치·빌드 | 미검증 |
| `npm run build-storybook` (정적 빌드) | 미검증 |
| Firefox·Safari | 미검증 (Chromium Storybook만) |
+4
View File
@@ -96,3 +96,7 @@ npm run typecheck -w @hm-design/sw
## AI 코딩 도구용 가이드 (2026-10-19)
다른 프로젝트에서 AI 코딩 도구가 이 패키지를 재사용·조합·확장할 때는 설치된 패키지의 `ai/` 폴더를 먼저 읽습니다: [AI_GUIDE.md](./ai/AI_GUIDE.md)(HMDesign 공통 진입점: Web / SW / OrgChart / 혼합 선택, 패키지 분리, 결과 보고. 모든 HMDesign 패키지에 같은 사본, 2026-10-19 추가) → [AI_USAGE.md](./ai/AI_USAGE.md)(작업 순서·Import 규칙) → [COMPONENT_CATALOG.md](./ai/COMPONENT_CATALOG.md)(공개 API) → [DESIGN_RULES.md](./ai/DESIGN_RULES.md)(토큰·상태·Motion) → [COMPOSITION_PATTERNS.md](./ai/COMPOSITION_PATTERNS.md)(조합 방법) → [examples/](./ai/examples)(공개 API만 쓰는 예제 A~D). `ai/`는 npm 패키지(`files`)에 포함됩니다. 런타임 API는 바뀌지 않았습니다.
## SW Footer (2026-10-20)
하단 명령 막대 `SWFooter`(보조기능 버튼·3 × 2 보조기능 토글·바 안 명령창·명령 칸 68 × 47·분류 탭·명령어 전체보기), 도면 공간 탭 `SWFooterLayoutTabs`(+ · Layout · Model), 명령창 `SWFooterCommandLine`과 조합용 부품 `SWFooter*`를 추가했습니다(SW 일반 모드). 기준: Figma 이지빔-SW 26:10097과 부품 34개, 바 안 명령창·좁은 폭은 사용자 제공 실제 화면. 명령 실행·등록·Layout 생성은 호스트가 Callback으로 처리합니다. AI 사용 안내는 [ai/FOOTER.md](./ai/FOOTER.md), 노드 매핑은 [docs/sw/sw-footer-figma-mapping-2026-10-20.md](../../docs/sw/sw-footer-figma-mapping-2026-10-20.md), Storybook은 `SW/Footer`.
+4 -1
View File
@@ -11,6 +11,7 @@
| [DESIGN_RULES.md](./DESIGN_RULES.md) | SW 전용 토큰·Typography·간격·Hover/Focus/Selected·Overlay·Motion·반응형·Popup 외형·도구 버튼·로고 클릭 메뉴(일반 모드 §1~10·§12), 뷰어 토큰·상태값 요약(§11) |
| [COMPOSITION_PATTERNS.md](./COMPOSITION_PATTERNS.md) | 완성형·간소화·기능 중심·확장 Header, Popup(자유 콘텐츠), 일반 모드 Header + 도구 막대(1~6), 뷰어 모드 기본 조합(7) |
| [examples/](./examples) | 공개 API만 쓰는 예제. **일반 모드** A~F(E = 사용자 정의 Popup, F = 파일 열기 + 도구 막대. 독립 소비 프로젝트에서 Typecheck·Build·브라우저 검증. B·F 파일명의 "Viewer"는 과거 이름이며 일반 모드 `SWHeader` 구성), **뷰어 모드** H(`SWViewer*`) |
| [FOOTER.md](./FOOTER.md) | **일반 모드 하단 막대**: `SWFooter`(보조기능·바 안 명령창·명령 칸·분류 탭·명령어 전체보기), `SWFooterLayoutTabs`(Model/Layout 탭), `SWFooterCommandLine`(명령창). AI 작업 순서 1~6 |
| [VIEWER.md](./VIEWER.md) | **뷰어 모드 전용**: `SWViewerHeader`(데이터 메뉴·More·외곽선·로고 규칙), `SWViewerMenu`(Dropdown·서브메뉴·우클릭), `SWViewerPanel`(팝업 프레임 + 자유 콘텐츠). 일반 SW 모드는 `SWHeader`·`SWPopup`을 그대로 씁니다 |
## 0. SW 모드 선택 — SW 일반 모드 / SW 뷰어 모드 (코드 작성 전에 결정)
@@ -42,12 +43,14 @@ SW 패키지에는 Header·Popup 계열이 두 벌 있습니다. 화면마다 **
| Dropdown | 각 필드 Dropdown(`SWHeaderLayerMenu` …) | `type: "menu"` 항목, 단독·우클릭은 `SWViewerMenu` |
| 팝업·설정창 | `SWPopup`(`titleActions`, `footer`) + `SWPopupSection` / `SWPopupRow` + `SWHeaderInput`, 속성창 `SWHeaderPropertyPanel`, 속성Bar `SWHeaderPropertyBar` | `SWViewerPanel`(이동·크기 조절·접기·`modal`, `headerActions`, `footer`) + `SWViewerSection` / `SWViewerField` / `SWViewerSwitch` / `SWViewerSlider` / `SWViewerInput` / `SWViewerButton` / `SWViewerBadge` / `SWViewerHint` |
| 로고(홈) 버튼 메뉴 | `SWHeader`의 `homeMenu`(`SWHomeMenu` 데이터, 최근파일 `SWRecentFiles`, Figma 26:10591). 기본 숨김 → 로고 클릭 열기 → 다시 클릭 닫기 | **사용하지 않음.** `SWViewerHeader`에는 홈 메뉴 Props가 없고 로고 클릭은 `onHomeClick`만 호출합니다. 파일 열기·샘플은 Header `groups`의 `button`·`menu` 항목 |
| 하단 Footer | `SWFooter`(+ 바 안 명령창 `command={<SWFooterCommandLine />}`, 좌표 `status`, `SWFooterLayoutTabs`, 부품 `SWFooter*`), [FOOTER.md](./FOOTER.md) | **사용하지 않음.** 뷰어 화면에는 하단 명령 막대가 없습니다. 도면 공간 탭은 `SWViewerHeader`의 `tabs` 항목 |
| 버튼·탭·토글 | `SWToolButton` / `SWToolGroup` | Header 항목 `type: "button" \| "toggle" \| "tabs" \| "switch"`, 패널 안 `SWViewerButton` |
| 아이콘 | 패키지 자산(필드 안 아이콘) | 프로그램 아이콘 16px, 직접 그린 아이콘은 `SWViewerIcon`으로 감쌈 |
| 범위 | Header 밖 부품은 `<SWTheme>` 안 | `SWViewerHeader`·`SWViewerMenu`·`SWViewerPanel`은 루트에 `hm-sw-theme`이 있어 `<SWTheme>` 불필요. 패널 컨트롤은 `SWViewerPanel` 안 |
| 토큰 | `--hm-sw-header-*`, Popup 콘텐츠 `--hm-sw-property-*` | `--hm-sw-viewer-*` + 공유 `--hm-sw-header-*`(아래 0.3) |
| 읽을 문서 | [COMPONENT_CATALOG.md](./COMPONENT_CATALOG.md) §1~8, [DESIGN_RULES.md](./DESIGN_RULES.md) §1~10·§12, [COMPOSITION_PATTERNS.md](./COMPOSITION_PATTERNS.md) 1~6·8, 예제 A~G | [VIEWER.md](./VIEWER.md), [COMPONENT_CATALOG.md](./COMPONENT_CATALOG.md) §9, [DESIGN_RULES.md](./DESIGN_RULES.md) §11, [COMPOSITION_PATTERNS.md](./COMPOSITION_PATTERNS.md) 7, [예제 H](./examples/ExampleH-ViewerMode.tsx) |
| 읽을 문서 | [COMPONENT_CATALOG.md](./COMPONENT_CATALOG.md) §1~8·§10, [DESIGN_RULES.md](./DESIGN_RULES.md) §1~10·§12·§13, [COMPOSITION_PATTERNS.md](./COMPOSITION_PATTERNS.md) 1~6·8·10, [FOOTER.md](./FOOTER.md), 예제 A~G·I | [VIEWER.md](./VIEWER.md), [COMPONENT_CATALOG.md](./COMPONENT_CATALOG.md) §9, [DESIGN_RULES.md](./DESIGN_RULES.md) §11, [COMPOSITION_PATTERNS.md](./COMPOSITION_PATTERNS.md) 7, [예제 H](./examples/ExampleH-ViewerMode.tsx) |
- **하단 명령 막대(Footer)** 요청은 일반 모드입니다: [FOOTER.md](./FOOTER.md) §1 순서로 `SWFooter`가 있는지 확인하고, 명령·분류·보조기능·문구·아이콘만 Props로 바꿉니다. Footer 외형을 새로 그리지 않습니다.
- **일반 모드 Header에 도구 막대를 더한 구성**(로고 옆 파일 열기 + Model/Layout·Fit·2.5D 버튼을 `SWHeader`에 추가)은 **일반 모드**입니다: `SWHeader`의 `documentOpenPlacement`·`leading`·`tools` + `SWToolButton` / `SWToolGroup`(PATTERNS 6, 예제 F). "뷰어"·"도면 보기" 화면을 요청받았다면 이 구성이 아니라 뷰어 모드(`SWViewer*`)를 씁니다.
- 모드 진입·종료, 파일 처리, 도면 렌더링, 메뉴 명령 실행은 어느 모드든 **프로그램 담당**입니다.
+22
View File
@@ -9,6 +9,7 @@
- **검증 표시:** ✅ = 2026-10-19 독립 소비 프로젝트(Tarball 설치)에서 브라우저 검증, ◻ = 소비 검증 범위 밖(Storybook 검증 기록만 있음).
- **먼저 모드:** SW 일반 모드(편집) / SW 뷰어 모드(도면 보기)를 [AI_USAGE.md](./AI_USAGE.md) §0으로 정합니다. **뷰어 모드는 §9(`SWViewer*`)만** 쓰고, 아래 §1~8은 일반 모드용입니다. 두 모드를 한 화면에 섞지 않습니다.
- **무엇을 쓸까(일반 모드):** Header 전체 → `SWHeader`(1). Header 부품 조합 → 개별 컨트롤(2). 팝업·설정창·정보창 → `SWPopup`(3). 도구 막대·탭·토글 → `SWToolButton` / `SWToolGroup`(4). 로고 클릭 2단 메뉴·최근파일 → `SWHeader homeMenu` / `SWHomeMenu`(5). Slider·Switch·Badge·Progress·Dialog(모달)·Label·FilterBar는 SW에 없습니다 → `@hm-design/ui`를 그대로 씁니다(필요하면 `SWPopup` children 안에).
- **무엇을 쓸까(일반 모드 하단):** 하단 명령 막대(보조기능·명령 칸·분류 탭·명령어 전체보기) → `SWFooter`, 도면 공간 탭(Model/Layout) → `SWFooterLayoutTabs`, 명령창 → `SWFooterCommandLine`(10, [FOOTER.md](./FOOTER.md)).
- **무엇을 쓸까(뷰어 모드):** Header → `SWViewerHeader`(`groups` + `trailing`), Dropdown·우클릭 → `SWViewerMenu`, 팝업·설정창·모달 → `SWViewerPanel` + `SWViewerSection`/`SWViewerField`/`SWViewerSwitch`/`SWViewerSlider`/`SWViewerInput`/`SWViewerButton`, 상태 → `SWViewerStatus`(9).
## 1. 완성형 템플릿
@@ -151,3 +152,24 @@ SW **뷰어 모드** 전용 Export입니다(2026-10-19 추가, 참고 DWG/DXF
- **공개 타입:** `SWViewerHeaderProps`, `SWViewerHeaderButtonProps`, `SWViewerMenuProps`, `SWViewerIconProps`, `SWViewerPanelProps`, `SWViewerPanelPlacement`(`"top-end"` / `"top-start"` / `"center"`), `SWViewerSectionProps`, `SWViewerFieldProps`, `SWViewerSwitchProps`, `SWViewerSliderProps`, `SWViewerInputProps`, `SWViewerButtonProps`, `SWViewerBadgeProps`, `SWViewerStatusProps`, `SWViewerStatusState`, `SWViewerHeaderItem`(`button`·`toggle`·`menu`·`tabs`·`switch`·`custom`), `SWViewerHeaderGroup`, `SWViewerMenuEntry`(`item`·`checkbox`·`radio`·`submenu`·`label`·`separator`), `SWViewerCommand`(`id`, `itemId`, `value`).
- **검증 표시:** 뷰어 Export는 이 카탈로그 기준의 독립 소비 프로젝트(Tarball) 브라우저 검증 기록이 없어 ◻입니다. 예제 H는 공개 Export만 사용합니다.
- 상세 크기·상태는 [VIEWER.md](./VIEWER.md) §2~4가 기준입니다. 이 표에 없는 Props(`items`, `content`, `renderBody` 등)는 없습니다.
## 10. Footer (`SWFooter*`, SW 일반 모드)
하단 명령 막대(2026-10-20 추가, Figma 이지빔-SW 26:10097 하단 72px + 부품 34개, 바 안 명령창·좁은 폭은 사용자 제공 실제 화면). 상세 Props·상태·키보드·금지 사항은 [FOOTER.md](./FOOTER.md), 토큰은 [DESIGN_RULES.md](./DESIGN_RULES.md) §13, 조합은 [COMPOSITION_PATTERNS.md](./COMPOSITION_PATTERNS.md) 10과 [예제 I](./examples/ExampleI-Footer.tsx). 뷰어 모드 화면에서는 쓰지 않습니다. `SWFooter`·`SWFooterCommandLine`은 루트에 `hm-sw-theme`이 있어 `<SWTheme>`가 필요 없고, 나머지 부품은 `<SWTheme>` 안에 둡니다.
| 컴포넌트 | 용도 | 주요 Props | 검증 |
|---|---|---|---|
| `SWFooter` | 72px 하단 막대: 보조기능 버튼 → 3 × 2 보조기능 → (바 안 명령창) → 명령 칸(68 × 47) + 분류 행(24) → 명령어 전체보기, 위 오른쪽 끝 Layout 탭 | `categories: SWFooterCategory[]`(필수), `categoryId`/`defaultCategoryId`/`onCategoryChange`, `activeToolId`, `onToolSelect(toolId, categoryId, tool)`, `slotCount`(17), `onSlotAdd(slotIndex, categoryId)`, `slotGuide`(`SWFooterGuideContent \| false`), `onCategoryAdd`, `auxItems`, `onAuxPressedChange`, `onAuxItemClick`, `auxAll`/`commandAll`(`SWFooterSideAction \| false`), `command`(바 안 명령창), `status`(바 위 왼쪽 좌표), `layoutTabs`, `trailing`, `label`, `<footer>` 속성·`ref`. 좁은 폭: 빈 칸 축소 → ▲ 패널, 분류 ◀ ▶, 보조기능 아이콘만 | ◻ |
| `SWFooterSideButton` | 양끝 74 × 71 버튼(보조기능 / 명령어 전체보기) | `title`(필수), `subtitle`, `side`(`start`/`end`), `tooltip`, `button` 속성 | ◻ |
| `SWFooterAuxGroup` | 보조기능 토글 격자(Figma 3 × 2) | `items: SWFooterAuxItem[]`(필수), `onPressedChange`, `onItemClick`, `columns`(3), `label` | ◻ |
| `SWFooterAuxButton` | 보조기능 104 × 33 버튼 | `label`(필수), `icon`, `pressed`, `button` 속성 | ◻ |
| `SWFooterToolButton` | 명령 칸 68 × 47 | `label`(필수), `icon`, `activeIcon`, `pressed`, `button` 속성 | ◻ |
| `SWFooterAddSlot` | 빈 칸(+), Hover 가이드 | `guide`(기본 Figma / `false`), `guideOpen`, `onGuideOpenChange`, `button` 속성 | ◻ |
| `SWFooterGuide` | 빈 칸 안내 카드 208 × 149 | `title`(`명령어 등록`), `caption`(`빠른사용`), `media`, `div` 속성 | ◻ |
| `SWFooterMenu` | 분류 행(현재 탭 Pocket, 넘치면 ◀ ▶) | `items: SWFooterMenuItem[]`·`value`(필수), `onValueChange`, `onAdd`, `addLabel`, `label`, `panelId`, `tabIdPrefix` | ◻ |
| `SWFooterLayoutTabs` | 도면 공간 탭(+ · Layout · Model) | `layouts: SWFooterLayout[]`·`value`(필수), `onValueChange`, `onAdd`, `onRemove`, `onRename`, `editingId`/`onEditingChange`, `modelLabel`(`"Model"` / `false`), `label`, `addLabel`, `removeLabel` | ◻ |
| `SWFooterCommandLine` | 명령창 566 × 71(접힘) / 270(펼침) | `history: ReactNode[]`(필수), `value`/`onValueChange`, `onSubmit`, `prompt`(`Command:`), `expanded`/`defaultExpanded`/`onExpandedChange`, `inputProps`, `historyLabel`, `expandLabel`, `collapseLabel` | ◻ |
- **상수·아이콘:** `SW_FOOTER_MODEL`(`"model"`, Model 탭의 `value`), `swFooterToolIcons`(`line`·`pline`·`arc`·`circle`·`ellipse`·`rect`·`poly`·`point`·`hatch`·`ncopy` → `{ icon, activeIcon }`), `swFooterAuxIcons`(`objectSnap`·`selectionEffect`·`orthogonal`·`gridSnap`·`gridBackground`·`lineWeight`).
- **공개 타입:** `SWFooterProps`, `SWFooterSideAction`, `SWFooterSideButtonProps`, `SWFooterAuxGroupProps`, `SWFooterAuxButtonProps`, `SWFooterToolButtonProps`, `SWFooterAddSlotProps`, `SWFooterGuideProps`, `SWFooterGuideContent`, `SWFooterMenuProps`, `SWFooterMenuItem`, `SWFooterLayoutTabsProps`, `SWFooterCommandLineProps`, `SWFooterTool`(`id`, `label`, `icon`, `activeIcon`, `title`, `ariaLabel`, `disabled`), `SWFooterCategory`(`id`, `label`, `tools`, `disabled`), `SWFooterAuxItem`(`id`, `label`, `icon`, `pressed`, `title`, `disabled`), `SWFooterLayout`(`id`, `label`, `locked`, `shape`), `SWFooterToolIconName`, `SWFooterAuxIconName`.
- **검증 표시:** Footer Export는 Storybook(`SW/Footer`)과 저장소 안 Import·Build 검증만 있고 독립 소비 프로젝트(Tarball) 브라우저 검증 기록이 없어 ◻입니다.
+25 -1
View File
@@ -4,7 +4,7 @@
## 0. 어떤 패턴을 고를까
먼저 SW 모드를 정합니다([AI_USAGE.md](./AI_USAGE.md) §0). 1~6과 8은 **SW 일반 모드**, 7은 **SW 뷰어 모드**이며 한 화면에서 섞지 않습니다.
먼저 SW 모드를 정합니다([AI_USAGE.md](./AI_USAGE.md) §0). 1~6과 8·10은 **SW 일반 모드**, 7은 **SW 뷰어 모드**이며 한 화면에서 섞지 않습니다.
| 요청 | 모드 | 패턴 |
|---|---|---|
@@ -16,6 +16,7 @@
| 일반 모드 `SWHeader`에 로고 옆 파일 열기·도구 막대(Model/Layout, Fit, 2.5D) 추가 | 일반 | 6. 파일 열기 + 도구 막대 |
| 뷰어·DWG/DXF 보기·도면 뷰어·읽기 전용 화면(Header 메뉴, 패널, 모달, 불러오기 상태) | 뷰어 | 7. 뷰어 모드 기본 조합 |
| 일반 모드 `SWHeader`에서 로고를 누르면 열리는 2단 메뉴(저장·내보내기·찾아열기·최근파일 등) | 일반 | 8. 로고 클릭 메뉴 |
| 화면 하단 명령 막대(Line·Arc 등 명령, 분류 탭, 보조기능 토글, 명령창), Model/Layout 탭 | 일반 | 10. Footer |
## 1. 완성형 SW Header — [ExampleA-DefaultHeader.tsx](./examples/ExampleA-DefaultHeader.tsx)
@@ -200,6 +201,29 @@ const sections: SWHomeMenuSectionData[] = [
5. New / Open은 문서 Dropdown에 그대로 둔다(`documentOpenPlacement` 기본 `"menu"`). 로고 오른쪽에 별도 Open 버튼을 두지 않는다. 메뉴의 찾아열기·새파일은 같은 handler를 연결한다.
6. 위치·폭은 `SWHeader`가 정한다. 소비 코드에서 메뉴 위치를 계산하거나 겹치는 wrapper를 만들지 않는다.
## 10. Footer (일반 모드) — [ExampleI-Footer.tsx](./examples/ExampleI-Footer.tsx)
상세는 [FOOTER.md](./FOOTER.md). 화면 = `SWHeader` → 도면 영역 → `SWFooter`(바 안 명령창 `command`, 위 왼쪽 좌표 `status`, 위 오른쪽 `SWFooterLayoutTabs`). 실제 SW 화면(2026-10-20) 구성입니다.
```tsx
<div className="app-screen">{/* 소비 레이아웃: flex column, 높이 100% */}
<SWHeader documentTitle={title} />
<main className="app-canvas">{/* flex: 1 */}</main>
<SWFooter status={coordinates}
command={<SWFooterCommandLine history={history} onSubmit={run} expanded={listOpen} onExpandedChange={setListOpen} />}
categories={categories} categoryId={categoryId} onCategoryChange={setCategoryId}
activeToolId={running} onToolSelect={toolId => run(toolId)} onSlotAdd={openRegister}
auxItems={aux} onAuxPressedChange={toggleAux} commandAll={{ onClick: () => setListOpen(open => !open), pressed: listOpen }}
layoutTabs={<SWFooterLayoutTabs layouts={layouts} value={space} onValueChange={setSpace} onAdd={addLayout} onRemove={removeLayout} onRename={renameLayout} />} />
</div>
```
1. 명령·분류·보조기능은 데이터입니다. 프로젝트마다 바꾸고 Footer 외형은 그대로 둡니다.
2. 명령 실행·등록·Layout 생성은 Callback에서 호스트가 처리하고 Props를 갱신합니다(`activeToolId`, `categories`, `layouts`).
3. Figma 아이콘은 `swFooterToolIcons`·`swFooterAuxIcons`, 새 명령 아이콘은 프로그램 제공 18 × 18 요소.
4. 좁은 화면 처리(빈 칸 축소, ▲ 패널, 분류 ◀ ▶, 보조기능 아이콘만)는 `SWFooter`가 합니다. 소비 코드에서 명령 수를 줄이거나 media query로 다시 배치하지 않습니다.
5. 일부만 필요하면(보조기능만, 분류 탭만) `<SWTheme>` 안에서 `SWFooterAuxGroup`·`SWFooterMenu`·`SWFooterToolButton`을 씁니다. `SWFooterMenu` 단독은 부모 면을 `var(--hm-sw-footer-group-background)`로 둡니다.
## 9. 하지 말 것
- `hm-sw-*` class를 소비 코드에 쓰거나 CSS로 덮어쓰기
+28 -1
View File
@@ -5,7 +5,7 @@
- 토큰은 `.hm-sw-header`와 `.hm-sw-theme`(`<SWTheme>`)에만 정의됩니다. 새 컴포넌트는 그 안에 렌더링합니다.
- SW는 `@hm-design/ui` 토큰을 쓰지 않습니다. UI 패키지 토큰을 복제하거나 섞지 않습니다.
- Light/Dark 분기 없음. 전역 선택자(`html`, `body`, `*`) 없음.
- **모드:** §1~10과 §12는 SW **일반 모드**(`SWHeader`·`SWPopup`·`SWToolButton`·`SWHomeMenu`) 기준입니다. SW **뷰어 모드**(`SWViewer*`)의 토큰·상태는 §11과 [VIEWER.md](./VIEWER.md)를 봅니다. 모드 선택·혼합 금지는 [AI_USAGE.md](./AI_USAGE.md) §0.
- **모드:** §1~10과 §12·§13은 SW **일반 모드**(`SWHeader`·`SWPopup`·`SWToolButton`·`SWHomeMenu`·`SWFooter`) 기준입니다. SW **뷰어 모드**(`SWViewer*`)의 토큰·상태는 §11과 [VIEWER.md](./VIEWER.md)를 봅니다. 모드 선택·혼합 금지는 [AI_USAGE.md](./AI_USAGE.md) §0.
## 1. 크기·간격
@@ -173,3 +173,30 @@ Container `hm-sw-header` 기준 Breakpoint: **< 1800** 선/문자/치수스타
- Home On(Figma 662:67267, 2026-10-19 사용자 확정): 메뉴가 열려 있는 동안 SWHeader가 자동으로 로고에 노란 Hover 테두리를 유지하고, Header 전폭 상·하단에 `#5F4B0F` 2px 선(`--hm-sw-header-home-on-line-color` / `-width`)을 그립니다. 닫으면 기본 상태로 돌아갑니다. 크기·위치 변화가 없고 SW 일반 모드 전용입니다(뷰어 모드에는 없음). 소비 코드에서 별도 class나 상태를 추가하지 않습니다.
- 새 색·크기는 위 토큰만 `style`로 덮어씁니다. `hm-sw-home-menu*` class를 소비 코드에 쓰지 않습니다.
## 13. Footer (`SWFooter*`, Figma 26:10097) — SW 일반 모드 전용
원본 `src/theme/sw-footer.css`. 토큰은 `--hm-sw-footer-*`(`.hm-sw-header`, `.hm-sw-theme` 범위). 값은 Figma Plugin API로 읽은 값입니다(2026-10-20). 글꼴은 Header와 같이 Dotum 대신 Noto Sans KR, 글자 외곽선은 Header 토큰(`--hm-sw-header-text-outline` 1.3px / `--hm-sw-header-label-outline` 1px)을 공유하고, Figma의 자기 색 0.3 / 0.4px stroke는 Header 2026-10-16 수정과 같이 그리지 않습니다.
| 항목 | 값 | 토큰 |
|---|---|---|
| 바 | 높이 72, 배경 `#060606` | `--hm-sw-footer-height`, `-background` |
| 배치(1920, Figma) | 보조기능 버튼 0~74 → 2 → 보조기능 격자 76~392(위 2) → 4 → 명령 그룹 396~(위 1) → 명령어 전체보기(명령 칸 바로 뒤, 1552~1626) → 나머지 bg_vacant | — |
| 측면 버튼 | 74 × 71, 자산 면(그라데이션 + 그라데이션 테두리), 그림자 8px 60% 검정(왼쪽 버튼 → 오른쪽 5px, 오른쪽 버튼 → 왼쪽 5px). 제목 14px 1.3px 외곽선, 부제 14px 1px 외곽선, 위 18 · 줄 간격 2 | `--hm-sw-footer-side-*` |
| 보조기능 | 104 × 33, gap 2, 테두리 1px `#886D35` radius 3, 면 `linear-gradient(0deg, #262218 8%, #4A3C2B 34%, #594C31 55%, #312A1B 90%)`, 아이콘 중심 (19.5, 16.5), 라벨 13px 왼쪽 36 폭 57 가운데 | `--hm-sw-footer-aux-*` |
| 명령 그룹 면 | 테두리 위·왼쪽·아래 1px `#886D35`, 왼쪽 위 radius 4, 면 `linear-gradient(0deg, #2F281A 0%, #312A1B 5%, #332B1C 36%, #463C29 58%, #38301F 79%, #19160E 98%)`. 마지막 명령 뒤(빈 칸·나머지)는 bg_vacant `linear-gradient(0deg, #18150E 28%, #262015 41%, #332B1C 58%, #38301F 81%, #18150E 100%)` | `--hm-sw-footer-group-*`, `--hm-sw-footer-vacant-background` |
| 명령 칸 | 68 × 47, 라벨 14px `#F6F4F2` 위 6, 아이콘 18 × 18 위 26 가운데. 칸 사이 구분선 1 × 37(위 5) `linear-gradient(0deg, #20190E, #8A7A5B 45%, #F3DBA8 70%, #423625)` + `-1px 0 1px #000` | `--hm-sw-footer-slot-*`, `--hm-sw-footer-divider*` |
| 명령 상태 | Hover = 선택(Figma Variant2 = Variant3): 글자 `#FF5C00` + 주황 아이콘. Disabled 40%(Figma 없음) | `--hm-sw-footer-accent` |
| 빈 칸 | 68 × 46(위 1), + `#BEB5A6`. Hover: 주황 + 와 주황 그라데이션 점선 링(자산) | — |
| 가이드 | 208 × 149, 빈 칸 위 4px 가운데, 1px `#DA6813` radius 2, 면 `#0E3C2E`, 미리보기 194 × 111(5, 5, 검정 1px), 제목 14px 외곽선, 캡션 12px `#B6D0C9` 1px 외곽선 | `--hm-sw-footer-guide-*` |
| 분류 행 | 높이 24, 면 `#282216` → 위 14% `#574B30`, 안쪽 1px 테두리 그라데이션, 현재 탭 Pocket(좌우 7px 곡선, 아래 radius 2, 가장자리 `#675A3B`). 탭 14px `#D4CBBD`, 자간 10%, 위 4, 좌우 padding 8, 최소 43. 구분선 1 × 10 검정(위 7). Hover·현재 `#FF5C00` | `--hm-sw-footer-menu-*` |
| Layout 탭 | 높이 22, 12px 자간 2%. Default 면 `#C3B79E` 글자 `#161410`. On: `linear-gradient(0deg, #7C6C4C, #7C6C4C 85%, #A6946F)` 흰 글자. 이름 편집: `#0F0D0A` 빨강 `#E60403` 주황 테두리. Hover·On·편집 글자 아래 70% 검정 1.3px 외곽선. 둥근 탭: 1px `rgba(0,0,0,.05)`(Hover·편집 `#DA6813`), 위 radius 4, 안쪽 그림자 `inset 0 -3px 4px rgba(56,48,31,.25)`, ×(16) Hover 노랑. 사선 탭(마지막): 97 × 22 자산, Hover = 주황 면 + 검정 글자, On hover = 베이지 면 + 주황 글자, ×(28, 빨강 사선) | `--hm-sw-footer-tab-*` |
| Model 탭 | 73 × 22 자산(왼쪽 사선), 글자 왼쪽 22, Hover 주황, On 갈색. 앞 사선 탭과 8px 겹침 | — |
| 명령창 | 566 × 71 / 270, 위 radius 4, 위 71px `linear-gradient(180deg, #635537, #262218 5%, #281F16 10%, #282216)` + 아래 `#282216`. 12px 자간 10% 줄 14, 1px 외곽선. 입력 칸 23(좌우 4) `#16130D` + `0 -1px 2px rgba(255,255,255,.25)`, `inset 0 1px 4px #000`. 화살표 18 × 18 검정 radius 2(4, 4 오른쪽 위) | `--hm-sw-footer-command-*` |
| 바 안 명령창(실제 화면) | 보조기능 뒤 4px, 남는 폭(최소 280), 바 아래에 붙어 펼치면 위로 270 | `--hm-sw-footer-command-min-width` |
| 좌표(실제 화면) | 바 위 왼쪽, 12px `#F6F4F2` 1px 외곽선, 아래 3px | — |
| 보조기능 pressed(실제 화면) | 테두리 `#DA6813` + 글자 `#FF5C00` | `--hm-sw-footer-aux-pressed-border-color` |
| 좁은 폭(실제 화면) | 빈 칸이 먼저 줄고, 넘치는 명령은 마지막 칸 ▲(ico_arrow 18 × 18, 오른쪽 위 4) → 명령 칸 위 패널(같은 칸, 오른쪽 맞춤, 테두리 1px `#886D35`·아래 없음·위 radius 4, 명령 칸 면). 분류 탭 넘침 = 탭 영역 잘림 + ◀ ▶(13 × 16, 6 × 8 삼각형 `#F6F4F2`, Hover `#FF5C00`) + +. 바 1279px 이하 보조기능 아이콘만 45 × 33 | `--hm-sw-footer-aux-compact-width` |
- Figma에 없는 값: 실제 화면에서 가져온 값(바 안 명령창 배치, 좌표, 보조기능 pressed, ▲ 패널, 분류 ◀ ▶, 보조기능 아이콘만)과 AI 구성 값 — 디자인 확인 필요(보조기능·측면 버튼 Hover 글자 `#FF5C00`, Disabled 40%, 명령창 최소 280px, 보조기능 아이콘 전환 1279px, Layout 탭 이름 편집 입력의 동작).
- 새 하단 UI는 위 토큰을 `var(--…)`로 읽고 `hm-sw-footer*` class를 쓰거나 덮어쓰지 않습니다.
+113
View File
@@ -0,0 +1,113 @@
# SW Footer — `SWFooter` 계열 (SW 일반 모드)
SW 프로그램 하단 명령 막대입니다. 기준은 Figma 이지빔-SW `레이아웃 분리`(26:10097) 하단 72px과 부품 노드 34개(Plugin API로 읽은 값, 2026-10-20)이고, 바 안 명령창·좌표·좁은 폭 동작은 사용자 제공 실제 화면(2026-10-20)을 따릅니다. 두 기준이 다르면 Figma가 우선입니다(예: 선택된 Model 탭은 Figma On 갈색). **SW 일반 모드(편집 화면) 전용**이며 뷰어 모드(`SWViewer*`) 화면에는 쓰지 않습니다([AI_USAGE.md](./AI_USAGE.md) §0).
컴포넌트는 **표시와 사용자 입력 전달**만 합니다. CAD 명령 실행, 명령 등록, 레이아웃 생성·삭제, 명령 해석은 소비 프로그램이 Callback을 받아 처리하고 Props를 갱신합니다.
## 1. AI 작업 순서 (Footer 요청을 받았을 때)
1. **존재 확인:** `node_modules/@hm-design/sw/ai/FOOTER.md`(이 문서)와 [COMPONENT_CATALOG.md](./COMPONENT_CATALOG.md) §10이 있는지 확인합니다. 있으면 Footer를 새로 만들지 않습니다.
2. **Export 확인:** `node_modules/@hm-design/sw/dist/index.d.ts`에서 `SWFooter`·`SWFooterLayoutTabs`·`SWFooterCommandLine`·`swFooterToolIcons`가 Export되는지 확인합니다. CSS는 `@hm-design/sw/styles.css` 한 개(이미 Import했다면 추가 없음).
3. **기존 Footer 우선:** 하단 막대 전체 → `SWFooter`. 일부만(보조기능만, 분류 탭만 …) → 아래 §3 부품을 `<SWTheme>` 안에서 조합.
4. **Props로 구성:** 분류·명령·보조기능·문구·아이콘은 데이터(`categories`, `auxItems`, `auxAll`, `commandAll`, `slotGuide`)로 넣습니다. 선택 상태(`categoryId`, `activeToolId`, `pressed`, `value`)는 호스트 상태로 제어합니다.
5. **Variant:** Figma 상태는 Props로만 나옵니다(§4 표). Hover·Focus는 CSS가 처리합니다.
6. **재구현 금지:** Footer의 배경·크기·Pocket 탭·구분선·글자 외곽선을 `div`와 CSS로 다시 그리지 않습니다. `hm-sw-footer*` class를 소비 CSS에서 재정의하거나 복사하지 않습니다. 내용(명령·메뉴·아이콘·문구)만 바꿉니다.
판단을 결과 보고에 남깁니다: "SWFooter 재사용 — categories 3개, 보조기능 6개, Layout 탭 연결, 명령 실행은 호스트 onToolSelect".
## 2. `SWFooter` (완성형)
```tsx
import { SWFooter, SWFooterLayoutTabs, SW_FOOTER_MODEL, swFooterToolIcons, swFooterAuxIcons } from "@hm-design/sw"
import "@hm-design/sw/styles.css"
<SWFooter
categories={[{ id: "shape", label: "도형", tools: [{ id: "line", label: "Line", ...swFooterToolIcons.line }] }]}
categoryId={categoryId} onCategoryChange={setCategoryId}
activeToolId={running} onToolSelect={(toolId, categoryId, tool) => runCommand(toolId)}
onSlotAdd={(slotIndex, categoryId) => openRegisterDialog(slotIndex)}
auxItems={[{ id: "snap", label: "객체스냅", icon: swFooterAuxIcons.objectSnap, pressed: snap }]}
onAuxPressedChange={(id, pressed) => setSnap(pressed)}
commandAll={{ onClick: toggleCommandList, pressed: listOpen }}
status={`${x}, ${y}, ${z}`}
command={<SWFooterCommandLine history={history} onSubmit={runCommandText} expanded={listOpen} onExpandedChange={setListOpen} />}
layoutTabs={<SWFooterLayoutTabs layouts={layouts} value={space} onValueChange={setSpace} onAdd={addLayout} onRemove={removeLayout} onRename={renameLayout} />}
/>
```
| Prop | 기본값 | 설명 |
|---|---|---|
| `categories` | (필수) | `SWFooterCategory[]` — `{ id, label, tools: SWFooterTool[], disabled? }`. 아래 분류 행과 위 명령 칸 |
| `categoryId` / `defaultCategoryId` / `onCategoryChange` | 첫 분류 | 현재 분류(Controlled / Uncontrolled) |
| `activeToolId` / `onToolSelect(toolId, categoryId, tool)` | `null` | 실행 중 명령 = 주황(Figma Variant3, `aria-pressed`) |
| `slotCount` | `17` / `command`가 있으면 명령 수 + 2 | 명령 + 빈 칸(+) 수. 바 폭이 좁으면 빈 칸부터 줄고, 그래도 넘치는 명령은 마지막 칸의 ▲ 패널로 이동(§4.1) |
| `onSlotAdd(slotIndex, categoryId)` | 없음 | 빈 칸 클릭(명령 등록). **없으면 빈 칸은 모양만 있고 반응하지 않음** |
| `slotGuide` | Figma 가이드 | 빈 칸 Hover·키보드 Focus 시 위에 뜨는 208 × 149 안내 `{ title = "명령어 등록", caption = "빠른사용", media? }`. `media`는 194 × 111 영상·이미지(프로그램 제공). `false` = 없음 |
| `onCategoryAdd` | 없음 | 분류 행 끝 + 버튼 표시 |
| `auxItems` / `onAuxPressedChange(id, pressed)` / `onAuxItemClick(id)` | 없음 | 3 × 2 보조기능 토글(`SWFooterAuxItem`: `id, label, icon?, pressed?, title?, disabled?`). `pressed`를 주면 토글 |
| `auxAll` / `commandAll` | 표시 | 양끝 74 × 71 버튼 `{ title, subtitle, tooltip, onClick, pressed, disabled }`. 기본 문구 `보조기능 / 전체보기`, `명령어 / 전체보기`. `false` = 숨김 |
| `command` | 없음 | 바 안 명령창(보통 `<SWFooterCommandLine>`, 실제 화면 구성). 보조기능 격자와 명령 칸 사이의 남는 폭(최소 280px)을 쓰고, 명령 칸은 오른쪽 `명령어 전체보기` 바로 앞으로 붙습니다. 펼치면 위(도면 영역)로 커집니다 |
| `status` | 없음 | 바 위 왼쪽 글자(커서 좌표 등, 12px) |
| `layoutTabs` | 없음 | 바 위 오른쪽 끝에 붙는 탭(보통 `SWFooterLayoutTabs`) |
| `trailing` | 없음 | 바 오른쪽 빈 영역 콘텐츠(좌표·상태) |
| `label` | `명령 도구` | `<footer>` 접근성 이름 |
`SWFooterTool`: `{ id, label, icon?, activeIcon?, title?, ariaLabel?, disabled? }`. `icon` = 기본(18 × 18), `activeIcon` = Hover·선택 아이콘(없으면 `icon` 유지). 줄인 라벨(`Rect...`)은 `ariaLabel`로 전체 이름을 줍니다. Figma 명령 아이콘은 `swFooterToolIcons.line / pline / arc / circle / ellipse / rect / poly / point / hatch / ncopy`(각 `{ icon, activeIcon }`), 보조기능 아이콘은 `swFooterAuxIcons.objectSnap / selectionEffect / orthogonal / gridSnap / gridBackground / lineWeight`.
## 3. 부품 (조합용, `<SWTheme>` 안)
| 컴포넌트 | 크기 | 주요 Props |
|---|---|---|
| `SWFooterSideButton` | 74 × 71 | `title`(필수), `subtitle`, `side`(`start`/`end`, 그림자 방향), `tooltip`, `button` 속성 |
| `SWFooterAuxGroup` | 104 × 33 칸, gap 2 | `items`(필수), `onPressedChange`, `onItemClick`, `columns`(3), `label` |
| `SWFooterAuxButton` | 104 × 33 | `label`(필수), `icon`, `pressed`, `button` 속성 |
| `SWFooterToolButton` | 68 × 47 칸 | `label`(필수), `icon`, `activeIcon`, `pressed`, `button` 속성. 연속 배치하면 칸 사이 1px 금색 구분선 |
| `SWFooterAddSlot` | 68 × 47 칸(+ 68 × 46) | `guide`(기본 Figma 가이드 / `false`), `guideOpen`(Controlled), `onGuideOpenChange`, `button` 속성 |
| `SWFooterGuide` | 208 × 149 | `title`, `caption`, `media`, `div` 속성. 단독 카드로 둘 때 `data-standalone=""` |
| `SWFooterMenu` | 높이 24 | `items`(`{ id, label, disabled? }[]`, 필수), `value`(필수), `onValueChange`, `onAdd`, `addLabel`, `label`, `panelId`, `tabIdPrefix`. 현재 탭은 위 칸과 이어지는 Pocket 모양(SVG, 위치 자동 계산) |
| `SWFooterLayoutTabs` | 높이 22 | `layouts`(`SWFooterLayout[]` = `{ id, label, locked?, shape? }`, 필수), `value`(layout id 또는 `SW_FOOTER_MODEL`), `onValueChange`, `onAdd`, `onRemove`, `onRename`, `editingId`/`onEditingChange`, `modelLabel`(`"Model"` / `false`), `label`, `addLabel`, `removeLabel` |
| `SWFooterCommandLine` | 566 × 71 / 270 (`SWFooter command` 안에서는 남는 폭) | `history`(`ReactNode[]`, 필수), `value`/`onValueChange`, `onSubmit`, `prompt`(`Command:`), `expanded`/`defaultExpanded`/`onExpandedChange`, `inputProps`, `historyLabel`, `expandLabel`, `collapseLabel` |
- `SWFooter`·`SWFooterCommandLine`은 루트에 `hm-sw-theme`이 있어 `<SWTheme>`가 필요 없습니다. 나머지 부품은 `<SWTheme>` 안에 둡니다.
- `SWFooterMenu`의 Pocket은 부모 배경(명령 칸 면)을 비춰 보입니다. 단독으로 둘 때 부모에 `background: var(--hm-sw-footer-group-background)`를 줍니다.
- `SWFooterLayoutTabs`의 마지막 탭은 사선 모양(Figma layout01, × = btn_close2), 나머지는 둥근 모양(layout02/03, × = btn_delete)입니다. `shape`로 바꿀 수 있습니다. 탭 순서는 배열 순서 그대로(Figma 예시: `+`, Layout 03, 02, 01, Model).
- `SWFooterCommandLine`은 `SWFooter`의 `command`에 넣는 것이 기본 구성입니다(실제 화면). 바 밖에 두면 위치(보통 도면 영역 왼쪽 아래)는 소비 레이아웃이 정합니다. 접힘 = 마지막 3줄, 펼침 = 전체 기록 스크롤.
## 4. 상태 (Figma Variant ↔ Props)
| 요소 | Figma | 구현 |
|---|---|---|
| 명령 | Default `#F6F4F2` / Variant2 Hover = Variant3 선택: `#FF5C00` + 주황 아이콘 | Hover는 CSS, 선택은 `activeToolId`·`pressed` |
| 분류 탭 | Default `#D4CBBD` / Hover·현재 `#FF5C00`, 현재 탭은 Pocket | `categoryId`·`value` |
| 빈 칸 | Default + / Hover 주황 점선 링 + 주황 + | Hover·Focus 시 `slotGuide` 표시 |
| Layout 탭 | Default 베이지 `#C3B79E` / Hover(×) / On 갈색 그라데이션·흰 글자 / On hover / Variant5 이름 편집(`#0F0D0A`, 빨강 `#E60403`) / Variant6 편집 + Hover | `value`, 편집 = 더블클릭·F2 또는 `editingId` |
| Model 탭 | Default / Hover 주황 / On 갈색 | `value === SW_FOOTER_MODEL` |
| 명령창 화살표 | Default ↑ / Hover 주황 ↑ / On ↓ / On hover 주황 ↓ | `expanded` |
| Disabled | Figma 없음 | SW 공통 40%(명령·빈 칸·탭·보조기능) |
| 보조기능 pressed | Figma는 Default만 | 실제 화면: 주황 테두리 `#DA6813` + 글자 `#FF5C00`(아이콘 색은 자산 그대로) |
| 보조기능·측면 버튼 Hover | Figma 없음 | Footer Hover 규칙 재사용: 글자 `#FF5C00` (디자인 확인 필요) |
### 4.1 좁은 폭 (Figma 없음, 실제 화면 2026-10-20)
1. 빈 칸(+)이 먼저 줄어듭니다.
2. 명령이 남는 칸보다 많으면 들어가는 명령만 두고 **마지막 칸이 ▲**(Figma ico_arrow)가 됩니다. ▲를 누르면 나머지 명령이 명령 칸 바로 위(오른쪽 맞춤, 같은 칸 모양)에 펼쳐지고 ▼가 됩니다. 명령을 고르거나 Escape·바깥 클릭이면 닫힙니다. 숨은 명령이 선택 중이면 ▲가 주황입니다.
3. 분류 탭이 넘치면 탭 영역 안에서 잘리고 ◀ ▶(스크롤)와 +가 오른쪽에 붙습니다. 현재 탭과 Focus 탭은 자동으로 보이게 스크롤됩니다.
4. 바 폭 1279px 이하에서 보조기능은 아이콘만(45 × 33, 이름은 접근성 이름·tooltip)입니다.
5. 바 안 명령창은 최소 280px을 유지합니다. 측면 버튼·Layout 탭은 크기가 바뀌지 않습니다(▲ 패널은 Layout 탭 위에 겹침).
## 5. 키보드·접근성
- 명령 칸: `role="toolbar"`, 칸 하나만 Tab 정지, ←/→/Home/End 이동. 명령 버튼 `aria-pressed`. ▲ = `aria-expanded`, 열면 첫 명령으로 Focus, Escape = 닫고 ▲로 Focus.
- 분류 행: `role="tablist"`, ←/→/Home/End = 이동 + 선택, 명령 칸이 `tabpanel`. 분류 + 버튼은 tablist 밖의 일반 버튼. ◀ ▶는 마우스용(`tabIndex -1`, 키보드는 탭 이동이 자동 스크롤).
- 빈 칸: 이름 `명령어 등록 (n번 칸)`, 가이드가 보이면 `aria-describedby`.
- Layout 탭: `role="tablist"`, ←/→ 선택, **Delete** = 삭제(`onRemove`), **F2**·더블클릭 = 이름 편집(Enter 저장, Escape 취소, 바깥 클릭 저장). ×는 마우스 전용(`tabIndex -1`).
- 명령창: 기록 `role="log"`(`aria-live="polite"`), 입력 `<label>` 연결, Enter = `onSubmit`(한글 조합 중 Enter 무시), 화살표 `aria-expanded`.
- Focus: SW 공통 2px `#F0F7F5`.
## 6. 하지 말 것
- 뷰어 모드 화면에 `SWFooter`를 넣지 않습니다(뷰어는 `SWViewerHeader` 탭·버튼으로 Model/Layout을 다룸).
- 명령 실행·명령 등록 로직을 컴포넌트 안에 넣으려고 래핑하지 않습니다. Callback에서 처리합니다.
- 칸 크기(68 × 47)·바 높이(72)·탭 높이(22)를 바꾸려고 class를 덮어쓰지 않습니다. 필요한 경우 공개 토큰(`--hm-sw-footer-*`)을 `style`로 바꾸고 결과 보고에 "디자인 확인 필요"로 남깁니다.
- 아이콘을 Figma 자산 대신 임의 SVG로 바꿔 "Figma 아이콘"이라고 보고하지 않습니다. 새 아이콘은 프로그램 제공 18 × 18(보조 14~18) 요소입니다.
- 이 문서에 없는 Props(`items`, `tools` 최상위, `onCommand` 등)는 없습니다.
@@ -0,0 +1,52 @@
// Example I — SW Footer (general mode): the bottom command bar with its command line, layout tabs and data.
// Commands are not run here: the host receives callbacks and updates the props.
import { useState } from "react"
import {
SWFooter, SWFooterCommandLine, SWFooterLayoutTabs, SW_FOOTER_MODEL, swFooterAuxIcons, swFooterToolIcons,
type SWFooterAuxItem, type SWFooterCategory, type SWFooterLayout,
} from "@hm-design/sw"
const categories: SWFooterCategory[] = [
{ id: "shape", label: "도형", tools: [
{ id: "line", label: "LINE", ...swFooterToolIcons.line },
{ id: "pline", label: "PLINE", ariaLabel: "Polyline", ...swFooterToolIcons.pline },
{ id: "circle", label: "CIRCLE", ...swFooterToolIcons.circle },
{ id: "rect", label: "RECTA...", ariaLabel: "Rectangle", ...swFooterToolIcons.rect },
] },
{ id: "modify", label: "수정", tools: [{ id: "ncopy", label: "NCOPY", ...swFooterToolIcons.ncopy }] },
{ id: "layer", label: "레이어", tools: [] },
]
export function ExampleIFooter({ onEvent }: { onEvent?: (name: string) => void }) {
const [categoryId, setCategoryId] = useState("shape")
const [running, setRunning] = useState<string | null>(null)
const [aux, setAux] = useState<SWFooterAuxItem[]>([
{ id: "objectSnap", label: "객체스냅", icon: swFooterAuxIcons.objectSnap, pressed: true },
{ id: "orthogonal", label: "직각교차", icon: swFooterAuxIcons.orthogonal, pressed: false },
{ id: "gridSnap", label: "격자스냅", icon: swFooterAuxIcons.gridSnap, pressed: false },
])
const [layouts, setLayouts] = useState<SWFooterLayout[]>([{ id: "l2", label: "Layout 02" }, { id: "l1", label: "Layout 01" }])
const [space, setSpace] = useState(SW_FOOTER_MODEL)
const [history, setHistory] = useState<string[]>(["Command: LINE", "Specify first point:"])
const [listOpen, setListOpen] = useState(false)
const run = (command: string) => { setHistory(lines => [...lines, `Command: ${command}`]); onEvent?.(`command:${command}`) }
return (
<div className="example-screen">
<main className="example-canvas" />
<SWFooter
status="0.0000, 0.0000, 0.0000"
command={<SWFooterCommandLine history={history} onSubmit={run} expanded={listOpen} onExpandedChange={setListOpen} />}
categories={categories} categoryId={categoryId} onCategoryChange={setCategoryId}
activeToolId={running} onToolSelect={toolId => { setRunning(toolId); run(toolId.toUpperCase()) }}
onSlotAdd={(slotIndex, category) => onEvent?.(`register:${category}#${slotIndex}`)}
auxItems={aux} onAuxPressedChange={(id, pressed) => setAux(items => items.map(item => (item.id === id ? { ...item, pressed } : item)))}
auxAll={{ onClick: () => onEvent?.("auxAll") }}
commandAll={{ onClick: () => setListOpen(open => !open), pressed: listOpen }}
layoutTabs={<SWFooterLayoutTabs layouts={layouts} value={space} onValueChange={setSpace}
onAdd={() => setLayouts(list => [{ id: `l${list.length + 1}`, label: `Layout 0${list.length + 1}` }, ...list])}
onRemove={id => { setLayouts(list => list.filter(item => item.id !== id)); if (space === id) setSpace(SW_FOOTER_MODEL) }}
onRename={(id, label) => setLayouts(list => list.map(item => (item.id === id ? { ...item, label } : item)))} />}
/>
</div>
)
}
@@ -0,0 +1,229 @@
import { forwardRef, useEffect, useId, useLayoutEffect, useRef, useState, type HTMLAttributes, type ReactNode } from "react"
import {
SWFooterAddSlot, SWFooterAuxGroup, SWFooterGuide, SWFooterMenu, SWFooterSideButton, SWFooterToolButton, moveFocus,
type SWFooterGuideContent,
} from "./SWFooterParts"
import type { SWFooterAuxItem, SWFooterCategory, SWFooterTool } from "./types"
import "../../theme/sw-footer.css"
/** Side button of the bar ends: texts and click. `false` hides it. */
export interface SWFooterSideAction {
/** Default `보조기능` (start) / `명령어` (end). */
title?: string
/** Default `전체보기`. */
subtitle?: string
tooltip?: string
onClick?: () => void
/** aria-pressed (e.g. the full list is open). */
pressed?: boolean
disabled?: boolean
}
export interface SWFooterProps extends Omit<HTMLAttributes<HTMLElement>, "children"> {
/** Categories of the bottom row and their commands, in order (Figma 도형 수정 편집 …). */
categories: SWFooterCategory[]
/** Current category (controlled). */
categoryId?: string
/** Initial category when uncontrolled. Default: the first. */
defaultCategoryId?: string
onCategoryChange?: (categoryId: string) => void
/** Selected command (the running one) of the current category: orange (Figma Variant3). `null`: none. */
activeToolId?: string | null
/** A command was clicked. The host runs it and updates `activeToolId`. */
onToolSelect?: (toolId: string, categoryId: string, tool: SWFooterTool) => void
/**
* Slots of the command row (commands + empty + slots). Default 17 (Figma 1920px frame), shrinking to the bar width.
* With `command` the row keeps its own width: default commands + 2 empty slots.
*/
slotCount?: number
/** An empty + slot was clicked (register a command). `slotIndex` is 0-based in the row. Omitted: the empty slots are inert. */
onSlotAdd?: (slotIndex: number, categoryId: string) => void
/** Guide over a hovered / focused empty slot. Default: the Figma guide (`명령어 등록` / `빠른사용`). `false`: none. */
slotGuide?: SWFooterGuideContent | false
/** Shows the + after the categories. */
onCategoryAdd?: () => void
/** 3 × 2 auxiliary toggles (객체스냅 …). Omitted or empty: no grid. */
auxItems?: SWFooterAuxItem[]
onAuxPressedChange?: (id: string, pressed: boolean) => void
onAuxItemClick?: (id: string) => void
/** Left button `보조기능 / 전체보기`. Default shown; `false` hides it. */
auxAll?: SWFooterSideAction | false
/** Button after the slots `명령어 / 전체보기`. Default shown; `false` hides it. */
commandAll?: SWFooterSideAction | false
/**
* Command line inside the bar, between the auxiliary grid and the command slots (usually `<SWFooterCommandLine>`,
* reference screen 2026-10-20). It takes the free width; the slots then sit right before 명령어 전체보기 at the end.
* Expanded, it grows upwards over the drawing area.
*/
command?: ReactNode
/** Text above the bar, left (cursor coordinates …). */
status?: ReactNode
/** Layout tabs above the bar, right aligned (usually `<SWFooterLayoutTabs>`). */
layoutTabs?: ReactNode
/** Free content at the right end of the bar (status, coordinates …). */
trailing?: ReactNode
/** Accessible name of the bar. Default `명령 도구`. */
label?: string
}
const SLOT = 68
/**
* HMDesign SW Footer (Figma "레이아웃 분리" 26:10097 bottom bar, 72px): 보조기능 button, auxiliary toggle grid,
* command slots over the category row, 명령어 전체보기 button and an optional layout tab row above. Data in, user
* actions out: the component never runs CAD commands. Has `hm-sw-theme` on its root (no <SWTheme> needed).
*/
export const SWFooter = forwardRef<HTMLElement, SWFooterProps>(function SWFooter({
categories, categoryId, defaultCategoryId, onCategoryChange, activeToolId = null, onToolSelect,
slotCount, onSlotAdd, slotGuide = {}, onCategoryAdd,
auxItems, onAuxPressedChange, onAuxItemClick, auxAll = {}, commandAll = {},
command, status, layoutTabs, trailing, label = "명령 도구", className, ...rest
}, ref) {
const [ownCategory, setOwnCategory] = useState(defaultCategoryId ?? categories[0]?.id)
const requested = categoryId ?? ownCategory
const category = categories.find(item => item.id === requested) ?? categories[0]
const tools = category?.tools ?? []
const uid = useId()
const panelId = `${uid}panel`
const tabPrefix = `${uid}tab`
// Room for slots, measured (Figma 26:10097 has no narrow layout; the reference screens of 2026-10-20 do):
// empty slots go first, then the commands that do not fit move to the ▲ panel above the last slot.
const rootRef = useRef<HTMLElement | null>(null)
const groupRef = useRef<HTMLDivElement>(null)
const auxRef = useRef<HTMLDivElement>(null)
const embedded = command !== undefined && command !== null && command !== false
const [fit, setFit] = useState(Number.POSITIVE_INFINITY)
useLayoutEffect(() => {
const root = rootRef.current
const group = groupRef.current
if (!root || !group) return
const measure = () => {
const end = group.querySelector<HTMLElement>(":scope > .hm-sw-footer-side")?.offsetWidth ?? 0
let room = group.clientWidth - end
if (embedded) {
const aux = auxRef.current
const auxWidth = aux ? aux.offsetWidth + parseFloat(getComputedStyle(aux).marginRight || "0") : 0
const commandMin = parseFloat(getComputedStyle(root).getPropertyValue("--hm-sw-footer-command-min-width")) || 0
room = root.clientWidth - auxWidth - commandMin - 4 - end - 2
}
setFit(Math.max(1, Math.floor((room - 1) / SLOT)))
}
measure()
const observer = new ResizeObserver(measure)
observer.observe(root)
observer.observe(group)
if (auxRef.current) observer.observe(auxRef.current)
return () => observer.disconnect()
}, [embedded])
const overflow = tools.length > fit
const visible = overflow ? tools.slice(0, Math.max(0, fit - 1)) : tools
const hidden = overflow ? tools.slice(visible.length) : []
const wanted = embedded ? slotCount ?? tools.length + 2 : slotCount ?? 17
const slots = overflow ? fit : Math.max(tools.length, Math.min(wanted, fit))
// ▲ panel of the commands that do not fit (ico_arrow 26:10023: ▲ closed, ▼ open).
const [moreOpen, setMoreOpen] = useState(false)
const moreRef = useRef<HTMLButtonElement>(null)
const morePanelRef = useRef<HTMLDivElement>(null)
const moreId = `${uid}more`
useEffect(() => { if (!overflow) setMoreOpen(false) }, [overflow])
useEffect(() => {
if (!moreOpen) return
const close = (event: PointerEvent) => {
const target = event.target as Node
if (!morePanelRef.current?.contains(target) && !moreRef.current?.contains(target)) setMoreOpen(false)
}
document.addEventListener("pointerdown", close)
morePanelRef.current?.querySelector<HTMLButtonElement>("button:not(:disabled)")?.focus()
return () => document.removeEventListener("pointerdown", close)
}, [moreOpen])
// The guide is placed on the bar (the slot row clips its children).
const [guideLeft, setGuideLeft] = useState<number | null>(null)
const guideId = `${uid}guide`
const guideOpenChange = (open: boolean, button: HTMLButtonElement) => {
const root = rootRef.current
if (!open || !root) { setGuideLeft(null); return }
const slot = button.getBoundingClientRect()
setGuideLeft(slot.left - root.getBoundingClientRect().left + slot.width / 2)
}
const selectCategory = (id: string) => { if (categoryId === undefined) setOwnCategory(id); onCategoryChange?.(id); setGuideLeft(null); setMoreOpen(false) }
const side = (action: SWFooterSideAction | false, position: "start" | "end") => action && (
<SWFooterSideButton side={position} title={action.title ?? (position === "start" ? "보조기능" : "명령어")} subtitle={action.subtitle ?? "전체보기"}
tooltip={action.tooltip} aria-pressed={action.pressed} disabled={action.disabled} onClick={action.onClick} />
)
const activeIndex = visible.findIndex(item => item.id === activeToolId)
const toolButton = (tool: SWFooterTool, tabIndex: number, after?: () => void) => category && (
<SWFooterToolButton key={`${category.id}:${tool.id}`} label={tool.label} icon={tool.icon} activeIcon={tool.activeIcon}
pressed={tool.id === activeToolId} disabled={tool.disabled || category.disabled} title={tool.title} aria-label={tool.ariaLabel}
tabIndex={tabIndex} onClick={() => { onToolSelect?.(tool.id, category.id, tool); after?.() }} />
)
return (
<footer ref={node => { rootRef.current = node; if (typeof ref === "function") ref(node); else if (ref) ref.current = node }}
aria-label={label} className={`hm-sw-theme hm-sw-footer${className ? ` ${className}` : ""}`} data-command={embedded ? "" : undefined} {...rest}>
{status !== undefined && status !== null && <div className="hm-sw-footer__status">{status}</div>}
{layoutTabs && <div className="hm-sw-footer__tabs">{layoutTabs}</div>}
{(auxAll || (auxItems && auxItems.length > 0)) && (
<div ref={auxRef} className="hm-sw-footer__aux-area">
{side(auxAll, "start")}
{auxItems && auxItems.length > 0 && <SWFooterAuxGroup items={auxItems} onPressedChange={onAuxPressedChange} onItemClick={onAuxItemClick} />}
</div>
)}
{embedded && <div className="hm-sw-footer__command">{command}</div>}
<div ref={groupRef} className="hm-sw-footer__group">
<div className="hm-sw-footer__scroll">
<div className="hm-sw-footer__panel" style={{ width: slots * SLOT + 1 }}>
<div className="hm-sw-footer__vacant" style={{ left: (visible.length + (overflow ? 1 : 0)) * SLOT }} aria-hidden="true" />
<div id={panelId} role="tabpanel" aria-labelledby={category ? `${tabPrefix}-${category.id}` : undefined}>
<div role="toolbar" aria-label={category?.label ?? label} className="hm-sw-footer__slots"
onKeyDown={event => moveFocus(event, ":scope > .hm-sw-footer-slot > :is(.hm-sw-footer-tool, .hm-sw-footer-add, .hm-sw-footer-more)")}>
{visible.map((tool, index) => toolButton(tool, index === Math.max(0, activeIndex) ? 0 : -1))}
{overflow && (
<div className="hm-sw-footer-slot">
<button ref={moreRef} type="button" className="hm-sw-footer-more" aria-expanded={moreOpen} aria-controls={moreOpen ? moreId : undefined}
aria-label={`명령 더보기 (${hidden.length}개)`} title={`명령 더보기 (${hidden.length}개)`} tabIndex={visible.length === 0 ? 0 : -1}
data-active={hidden.some(item => item.id === activeToolId) ? "" : undefined} onClick={() => setMoreOpen(open => !open)}>
<span className="hm-sw-footer-more__icon" aria-hidden="true" />
</button>
</div>
)}
{!overflow && Array.from({ length: slots - visible.length }, (_, offset) => {
const index = visible.length + offset
if (!onSlotAdd) {
return <div key={`empty:${index}`} className="hm-sw-footer-slot"><span className="hm-sw-footer-add" data-inert="" aria-hidden="true" /></div>
}
return (
<SWFooterAddSlot key={`empty:${index}`} guide={false} disabled={category?.disabled}
aria-label={`${slotGuide ? slotGuide.title ?? "명령어 등록" : "명령어 등록"} (${index + 1}번 칸)`}
aria-describedby={guideLeft !== null && slotGuide ? guideId : undefined}
tabIndex={visible.length === 0 && offset === 0 ? 0 : -1}
onGuideOpenChange={slotGuide ? guideOpenChange : undefined}
onClick={() => category && onSlotAdd?.(index, category.id)} />
)
})}
</div>
</div>
<SWFooterMenu items={categories} value={category?.id ?? ""} onValueChange={selectCategory} onAdd={onCategoryAdd}
panelId={panelId} tabIdPrefix={tabPrefix} style={{ width: slots * SLOT + 1 }} />
{overflow && moreOpen && (
<div ref={morePanelRef} id={moreId} role="group" aria-label={`${category?.label ?? label} 더보기`} className="hm-sw-footer__more-panel"
style={{ width: Math.min(hidden.length, slots) * SLOT + 2 }}
onKeyDown={event => {
if (event.key === "Escape") { event.preventDefault(); setMoreOpen(false); moreRef.current?.focus() }
else moveFocus(event, ".hm-sw-footer-tool")
}}>
{hidden.map((tool, index) => toolButton(tool, index === 0 ? 0 : -1, () => setMoreOpen(false)))}
</div>
)}
</div>
</div>
{side(commandAll, "end")}
{(!embedded || trailing) && <div className="hm-sw-footer__trailing">{trailing}</div>}
</div>
{slotGuide && guideLeft !== null && <SWFooterGuide id={guideId} role="tooltip" className="hm-sw-footer__guide" style={{ left: guideLeft }} {...slotGuide} />}
</footer>
)
})
@@ -0,0 +1,64 @@
import { forwardRef, useEffect, useId, useRef, useState, type HTMLAttributes, type InputHTMLAttributes, type ReactNode } from "react"
import "../../theme/sw-footer.css"
export interface SWFooterCommandLineProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSubmit"> {
/** Output lines, oldest first. Collapsed shows the last 3 (Figma 26:10048), expanded scrolls them all (26:10056). */
history: ReactNode[]
/** Input text (controlled). Omit for an uncontrolled input. */
value?: string
onValueChange?: (value: string) => void
/** Enter in the input. The host runs the command and appends to `history`; an uncontrolled input is cleared. */
onSubmit?: (command: string) => void
/** Prompt label before the input. Default `Command:`. */
prompt?: string
/** Expanded (270px) or collapsed (71px). Controlled with `onExpandedChange`. */
expanded?: boolean
defaultExpanded?: boolean
onExpandedChange?: (expanded: boolean) => void
/** Native input attributes (placeholder, autoFocus, …). */
inputProps?: Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange">
/** Accessible names. */
historyLabel?: string
expandLabel?: string
collapseLabel?: string
}
/**
* Command line window (Figma command 26:10048 collapsed / 26:10056 expanded, ico_arrow 26:10023): 566px, 12px text
* with 10% tracking, history above a 23px input well. The host places it (usually above the footer, left).
*/
export const SWFooterCommandLine = forwardRef<HTMLDivElement, SWFooterCommandLineProps>(function SWFooterCommandLine({
history, value, onValueChange, onSubmit, prompt = "Command:", expanded, defaultExpanded = false, onExpandedChange,
inputProps, historyLabel = "명령 기록", expandLabel = "명령 기록 펼치기", collapseLabel = "명령 기록 접기", className, ...rest
}, ref) {
const [ownExpanded, setOwnExpanded] = useState(defaultExpanded)
const [ownValue, setOwnValue] = useState("")
const open = expanded ?? ownExpanded
const text = value ?? ownValue
const inputId = useId()
const historyRef = useRef<HTMLOListElement>(null)
// Newest line in view (collapsed: the last 3 lines; expanded: scrolled to the end).
useEffect(() => { const list = historyRef.current; if (list) list.scrollTop = list.scrollHeight }, [history.length, open])
const toggle = () => { const next = !open; if (expanded === undefined) setOwnExpanded(next); onExpandedChange?.(next) }
return (
<div ref={ref} className={`hm-sw-theme hm-sw-footer-command${className ? ` ${className}` : ""}`} data-expanded={open ? "" : undefined} {...rest}>
<ol ref={historyRef} className="hm-sw-footer-command__history" role="log" aria-label={historyLabel} aria-live="polite">
{/* Collapsed: the last 3 lines (Figma 26:10048); expanded: every line, scrolled to the newest. */}
{(open ? history : history.slice(-3)).map((line, index) => <li key={open ? index : history.length - 3 + index}>{line}</li>)}
</ol>
<button type="button" className="hm-sw-footer-command__toggle" aria-expanded={open} aria-label={open ? collapseLabel : expandLabel} title={open ? collapseLabel : expandLabel} onClick={toggle} />
<div className="hm-sw-footer-command__prompt">
<label className="hm-sw-footer-command__label" htmlFor={inputId}>{prompt}</label>
<input id={inputId} className="hm-sw-footer-command__input" autoComplete="off" spellCheck={false} {...inputProps} value={text}
onChange={event => { if (value === undefined) setOwnValue(event.target.value); onValueChange?.(event.target.value) }}
onKeyDown={event => {
inputProps?.onKeyDown?.(event)
if (event.defaultPrevented || event.key !== "Enter" || event.nativeEvent.isComposing) return
event.preventDefault()
onSubmit?.(text)
if (value === undefined) setOwnValue("")
}} />
</div>
</div>
)
})
@@ -0,0 +1,112 @@
import { forwardRef, useEffect, useRef, useState, type HTMLAttributes, type KeyboardEvent } from "react"
import { moveFocus } from "./SWFooterParts"
import type { SWFooterLayout } from "./types"
import "../../theme/sw-footer.css"
/** `value` of the Model tab. */
export const SW_FOOTER_MODEL = "model"
export interface SWFooterLayoutTabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
/** Layout tabs, left to right (Figma 26:9942: `+`, Layout 03, Layout 02, Layout 01, Model). */
layouts: SWFooterLayout[]
/** Current space: a layout id or `SW_FOOTER_MODEL` (`"model"`). */
value: string
onValueChange?: (value: string) => void
/** Shows the + before the tabs (btn_add_layout 26:9776). */
onAdd?: () => void
/** Shows the × of the hovered tab (btn_delete / btn_close2) and enables the Delete key. */
onRemove?: (layoutId: string) => void
/** Double click or F2 edits the name (Figma Variant5 / Variant6: #0F0D0A, red text); Enter saves, Escape cancels. */
onRename?: (layoutId: string, label: string) => void
/** Layout whose name is being edited (controlled). Default: internal (double click / F2). */
editingId?: string | null
onEditingChange?: (layoutId: string | null) => void
/** Model tab text. Default `Model`. `false` hides the Model tab. */
modelLabel?: string | false
/** Accessible names. */
label?: string
addLabel?: string
removeLabel?: (layout: SWFooterLayout) => string
}
/**
* Drawing space tabs above the footer (Figma layout01 26:9794, layout02 26:9837, layout03 26:9885, model 26:9914,
* btn_add_layout 26:9776, btn_delete 26:9785, btn_close2 26:9931, 탭 상태변화 26:9942). The last tab (next to Model, or the
* right end without Model as in 26:10097) takes the slanted layout01 shape; the others the rounded layout02 / 03 shape. States: Default, Hover, On (current),
* On hover, editing name. Tablist: Arrow / Home / End select, Delete removes, F2 renames.
*/
export const SWFooterLayoutTabs = forwardRef<HTMLDivElement, SWFooterLayoutTabsProps>(function SWFooterLayoutTabs({
layouts, value, onValueChange, onAdd, onRemove, onRename, editingId, onEditingChange, modelLabel = "Model",
label = "도면 공간", addLabel = "레이아웃 추가", removeLabel = layout => `${layout.label} 삭제`, className, onKeyDown, ...rest
}, ref) {
const [ownEditing, setOwnEditing] = useState<string | null>(null)
const editing = editingId !== undefined ? editingId : ownEditing
const setEditing = (next: string | null) => { if (editingId === undefined) setOwnEditing(next); onEditingChange?.(next) }
const [draft, setDraft] = useState("")
const inputRef = useRef<HTMLInputElement>(null)
useEffect(() => {
if (!editing) return
setDraft(layouts.find(layout => layout.id === editing)?.label ?? "")
inputRef.current?.select()
// Only when another tab starts editing (a renamed label must not reset the draft).
}, [editing])
useEffect(() => { if (editing && !layouts.some(layout => layout.id === editing)) setEditing(null) }, [editing, layouts])
const startEdit = (layout: SWFooterLayout) => { if (!onRename || layout.locked) return; setEditing(layout.id) }
const commit = (layout: SWFooterLayout, keep = true) => {
const next = draft.trim()
setEditing(null)
if (keep && next && next !== layout.label) onRename?.(layout.id, next)
}
const tabKeys = (event: KeyboardEvent<HTMLButtonElement>, layout: SWFooterLayout) => {
if (event.key === "F2") { event.preventDefault(); startEdit(layout) }
if (event.key === "Delete" && onRemove && !layout.locked) { event.preventDefault(); onRemove(layout.id) }
}
return (
<div ref={ref} className={`hm-sw-footer-tabs${className ? ` ${className}` : ""}`} {...rest}>
{onAdd && <button type="button" className="hm-sw-footer-tabs__add" aria-label={addLabel} title={addLabel} onClick={onAdd} />}
<div role="tablist" aria-label={label} className="hm-sw-footer-tabs__list"
onKeyDown={event => { onKeyDown?.(event); if (!event.defaultPrevented && !editing) moveFocus(event, '[role="tab"]', true) }}>
{layouts.map((layout, index) => {
const selected = layout.id === value
const isEditing = editing === layout.id
const shape = layout.shape ?? (index === layouts.length - 1 ? "slant" : "rounded")
const removable = !!onRemove && !layout.locked
return (
<div key={layout.id} className="hm-sw-footer-tab" data-shape={shape} data-state={isEditing ? "editing" : selected ? "on" : "off"}>
{isEditing ? (
<span className="hm-sw-footer-tab__select">
<input ref={inputRef} className="hm-sw-footer-tab__input" value={draft} aria-label={`${layout.label} 이름`}
onChange={event => setDraft(event.target.value)} onBlur={() => commit(layout)}
onKeyDown={event => {
event.stopPropagation()
if (event.key === "Enter") { event.preventDefault(); commit(layout) }
if (event.key === "Escape") { event.preventDefault(); commit(layout, false) }
}} />
</span>
) : (
<button type="button" role="tab" className="hm-sw-footer-tab__select" aria-selected={selected} tabIndex={selected ? 0 : -1}
title={layout.label} aria-keyshortcuts={[removable && "Delete", onRename && !layout.locked && "F2"].filter(Boolean).join(" ") || undefined}
onClick={() => { if (!selected) onValueChange?.(layout.id) }} onDoubleClick={() => startEdit(layout)} onKeyDown={event => tabKeys(event, layout)}>
<span className="hm-sw-footer-tab__label">{layout.label}</span>
</button>
)}
{/* Figma keeps the × place in every state (opacity 0 until hover). Mouse only: the tab owns Delete. */}
<button type="button" className="hm-sw-footer-tab__remove" tabIndex={-1} aria-label={removeLabel(layout)} title={removable ? removeLabel(layout) : undefined}
disabled={!removable} aria-hidden={!removable || undefined} onClick={() => onRemove?.(layout.id)} />
</div>
)
})}
{modelLabel !== false && (
<div className="hm-sw-footer-tab hm-sw-footer-tab--model" data-state={value === SW_FOOTER_MODEL ? "on" : "off"}>
<button type="button" role="tab" className="hm-sw-footer-tab__select" aria-selected={value === SW_FOOTER_MODEL} tabIndex={value === SW_FOOTER_MODEL ? 0 : -1}
onClick={() => { if (value !== SW_FOOTER_MODEL) onValueChange?.(SW_FOOTER_MODEL) }}>
<span className="hm-sw-footer-tab__label">{modelLabel}</span>
</button>
</div>
)}
</div>
</div>
)
})
@@ -0,0 +1,347 @@
import {
forwardRef, useCallback, useId, useLayoutEffect, useRef, useState,
type ButtonHTMLAttributes, type HTMLAttributes, type KeyboardEvent, type ReactNode,
} from "react"
import type { SWFooterAuxItem } from "./types"
import "../../theme/sw-footer.css"
const cx = (...names: (string | false | undefined)[]) => names.filter(Boolean).join(" ")
/** Arrow / Home / End focus move between the enabled buttons matching `selector` (roving focus). */
export function moveFocus(event: KeyboardEvent<HTMLElement>, selector: string, select = false) {
const keys = ["ArrowRight", "ArrowLeft", "Home", "End"]
if (!keys.includes(event.key)) return
const items = Array.from(event.currentTarget.querySelectorAll<HTMLButtonElement>(selector)).filter(item => !item.disabled)
const index = items.indexOf(document.activeElement as HTMLButtonElement)
if (index < 0 || !items.length) return
event.preventDefault()
const next = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1
: (index + (event.key === "ArrowRight" ? 1 : -1) + items.length) % items.length
items[next].focus()
if (select) items[next].click()
}
// ---------- Side button (보조기능 전체보기 / 명령어 전체보기) ----------
export interface SWFooterSideButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "title"> {
/** First line, 14px with the black outline (e.g. `보조기능`, `명령어`). */
title: string
/** Second line, 14px (e.g. `전체보기`). */
subtitle?: string
/** `start` (left of the bar, shadow to the right) or `end` (shadow to the left). Default `start`. */
side?: "start" | "end"
/** Native tooltip. */
tooltip?: string
}
/** 74 × 71 button of the bar ends (Figma 26:10189 / 26:10192). */
export const SWFooterSideButton = forwardRef<HTMLButtonElement, SWFooterSideButtonProps>(function SWFooterSideButton(
{ title, subtitle, side = "start", tooltip, className, type = "button", ...rest }, ref,
) {
return (
<button ref={ref} type={type} className={cx("hm-sw-footer-side", className)} data-side={side} title={tooltip} {...rest}>
<span className="hm-sw-footer-side__title">{title}</span>
{subtitle && <span className="hm-sw-footer-side__subtitle">{subtitle}</span>}
</button>
)
})
// ---------- Auxiliary toggles ----------
export interface SWFooterAuxButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
label: string
icon?: ReactNode
/** On / off (aria-pressed). Omit for a plain button. */
pressed?: boolean
}
/** 104 × 33 auxiliary toggle (Figma btn_객체스냅 …). */
export const SWFooterAuxButton = forwardRef<HTMLButtonElement, SWFooterAuxButtonProps>(function SWFooterAuxButton(
{ label, icon, pressed, className, type = "button", ...rest }, ref,
) {
return (
<button ref={ref} type={type} className={cx("hm-sw-footer-aux__button", className)} aria-pressed={pressed} {...rest}>
{icon !== undefined && <span className="hm-sw-footer-aux__icon" aria-hidden="true">{icon}</span>}
<span className="hm-sw-footer-aux__label">{label}</span>
</button>
)
})
export interface SWFooterAuxGroupProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
items: SWFooterAuxItem[]
/** A toggle (`pressed` defined) was clicked: the next state. The host updates `items`. */
onPressedChange?: (id: string, pressed: boolean) => void
/** Every click (toggles and plain buttons). */
onItemClick?: (id: string) => void
/** Columns of the grid. Default 3 (Figma 3 × 2). */
columns?: number
/** Accessible name. Default `보조 기능`. */
label?: string
}
/** The 3 × 2 auxiliary toggle grid (Figma 26:10104, gap 2px). */
export const SWFooterAuxGroup = forwardRef<HTMLDivElement, SWFooterAuxGroupProps>(function SWFooterAuxGroup(
{ items, onPressedChange, onItemClick, columns = 3, label = "보조 기능", className, style, ...rest }, ref,
) {
return (
<div ref={ref} role="group" aria-label={label} className={cx("hm-sw-footer-aux", className)}
style={columns === 3 ? style : { ...style, ["--hm-sw-footer-aux-columns" as string]: columns }} {...rest}>
{items.map(item => (
<SWFooterAuxButton key={item.id} label={item.label} icon={item.icon} pressed={item.pressed} title={item.title} disabled={item.disabled}
onClick={() => { onItemClick?.(item.id); if (item.pressed !== undefined) onPressedChange?.(item.id, !item.pressed) }} />
))}
</div>
)
})
// ---------- Command slot ----------
export interface SWFooterToolButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
label: string
icon?: ReactNode
/** Hover / selected icon (orange). Default: `icon`. */
activeIcon?: ReactNode
/** Selected (the running command): aria-pressed and the orange look of Figma Variant3. */
pressed?: boolean
}
/** 68 × 47 command slot (Figma line / Pline / Arc …). Consecutive slots draw the 1px gold divider between them. */
export const SWFooterToolButton = forwardRef<HTMLButtonElement, SWFooterToolButtonProps>(function SWFooterToolButton(
{ label, icon, activeIcon, pressed, className, type = "button", ...rest }, ref,
) {
return (
<div className="hm-sw-footer-slot">
<button ref={ref} type={type} className={cx("hm-sw-footer-tool", className)} aria-pressed={pressed ?? false}
data-active-icon={activeIcon !== undefined ? "" : undefined} {...rest}>
<span className="hm-sw-footer-tool__label">{label}</span>
{icon !== undefined && <span className="hm-sw-footer-tool__icon hm-sw-footer-tool__icon--default" aria-hidden="true">{icon}</span>}
{activeIcon !== undefined && <span className="hm-sw-footer-tool__icon hm-sw-footer-tool__icon--active" aria-hidden="true">{activeIcon}</span>}
</button>
</div>
)
})
// ---------- Guide (가이드 영상 보기 / 바로가기 등록) ----------
export interface SWFooterGuideContent {
/** 14px title at the bottom left. Default `명령어 등록`. */
title?: string
/** 12px caption at the bottom right. Default `빠른사용`. */
caption?: string
/** 194 × 111 preview (guide video, image or any element). Default: an empty black well. */
media?: ReactNode
}
export interface SWFooterGuideProps extends SWFooterGuideContent, Omit<HTMLAttributes<HTMLDivElement>, "title"> {}
/**
* 208 × 149 quick-use guide of an empty slot (Figma 26:9242 Variant2 / guide_바로가기 등록 26:9096). `SWFooter` and
* `SWFooterAddSlot` show it above the hovered (focused) empty slot; rendered alone it is a static card.
*/
export const SWFooterGuide = forwardRef<HTMLDivElement, SWFooterGuideProps>(function SWFooterGuide(
{ title = "명령어 등록", caption = "빠른사용", media, className, ...rest }, ref,
) {
return (
<div ref={ref} className={cx("hm-sw-footer-guide", className)} {...rest}>
<div className="hm-sw-footer-guide__media">{media}</div>
<span className="hm-sw-footer-guide__title">{title}</span>
<span className="hm-sw-footer-guide__caption">{caption}</span>
</div>
)
})
export interface SWFooterAddSlotProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
/**
* Guide shown above the slot while it is hovered or focused. Default: the Figma guide (`명령어 등록` / `빠른사용`,
* empty preview). `false`: no guide.
*/
guide?: SWFooterGuideContent | false
/** Shows the guide (controlled). Default: while hovered or keyboard focused. */
guideOpen?: boolean
/** Hover / focus of the slot (SWFooter places its own guide with it). */
onGuideOpenChange?: (open: boolean, button: HTMLButtonElement) => void
}
/** 68 × 46 empty command slot (Figma btn_add_menu 26:9971): the + well; hover draws the orange dashed ring. */
export const SWFooterAddSlot = forwardRef<HTMLButtonElement, SWFooterAddSlotProps>(function SWFooterAddSlot(
{ guide = {}, guideOpen, onGuideOpenChange, className, type = "button", onMouseEnter, onMouseLeave, onFocus, onBlur, ...rest }, ref,
) {
const [hovered, setOpen] = useState(false)
const open = guideOpen ?? hovered
const guideId = useId()
const change = (next: boolean, button: HTMLButtonElement) => { setOpen(next); onGuideOpenChange?.(next, button) }
return (
<div className="hm-sw-footer-slot">
<button ref={ref} type={type} className={cx("hm-sw-footer-add", className)} aria-expanded={guide ? open : undefined} aria-label={rest["aria-label"] ?? (guide ? guide.title ?? "명령어 등록" : "명령어 등록")}
aria-describedby={guide && open ? guideId : rest["aria-describedby"]}
onMouseEnter={event => { onMouseEnter?.(event); change(true, event.currentTarget) }}
onMouseLeave={event => { onMouseLeave?.(event); change(false, event.currentTarget) }}
onFocus={event => { onFocus?.(event); if (event.currentTarget.matches(":focus-visible")) change(true, event.currentTarget) }}
onBlur={event => { onBlur?.(event); change(false, event.currentTarget) }}
{...rest} />
{guide && open && !rest.disabled && <SWFooterGuide id={guideId} role="tooltip" {...guide} />}
</div>
)
})
// ---------- Category row ----------
export interface SWFooterMenuItem { id: string; label: string; disabled?: boolean }
export interface SWFooterMenuProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
items: SWFooterMenuItem[]
/** Current category (Figma Variant3, #FF5C00 in the open pocket). */
value: string
onValueChange?: (id: string) => void
/** Shows the + after the last tab (btn_add 26:9451). */
onAdd?: () => void
/** Accessible name of +. Default `카테고리 추가`. */
addLabel?: string
/** Accessible name of the tab list. Default `명령어 분류`. */
label?: string
/** Id of the panel the tabs control (the slots). */
panelId?: string
/** Id prefix of the tabs (`<prefix>-<item id>`), for `aria-labelledby` of the panel. */
tabIdPrefix?: string
}
/** Pocket outline of menu_bg (26:10127) around the current tab [a, a + w] of a row `width` wide (Figma path, offset). */
function pocketPath(a: number, w: number, width: number) {
const r = a + w
return `M0 0H${a - 7}C${a - 3.135} 0.003 ${a} 8.956 ${a} 20V21C${a} 22.105 ${a + 0.895} 23 ${a + 2} 23H${r - 2}`
+ `C${r - 0.895} 23 ${r} 22.105 ${r} 21V20C${r} 8.954 ${r + 3.134} 0 ${r + 7} 0H${width}V24H0Z`
}
function pocketEdge(a: number, w: number) {
const r = a + w
return `M${a - 7} 0C${a - 3.135} 0.003 ${a} 8.956 ${a} 20V21C${a} 22.105 ${a + 0.895} 23 ${a + 2} 23H${r - 2}`
+ `C${r - 0.895} 23 ${r} 22.105 ${r} 21V20C${r} 8.954 ${r + 3.134} 0 ${r + 7} 0`
}
/**
* The 24px category row under the slots (Figma menu_bg 26:10127 + text_도형 … 26:9459–26:9522). The current tab
* is a pocket opened into the slots: 7px curved edges instead of the 1px dividers. Tablist: Arrow / Home / End select.
*/
export const SWFooterMenu = forwardRef<HTMLDivElement, SWFooterMenuProps>(function SWFooterMenu(
{ items, value, onValueChange, onAdd, addLabel = "카테고리 추가", label = "명령어 분류", panelId, tabIdPrefix, className, onKeyDown, ...rest }, ref,
) {
const rowRef = useRef<HTMLDivElement | null>(null)
const viewportRef = useRef<HTMLDivElement>(null)
const [geometry, setGeometry] = useState<{ a: number; w: number; width: number } | null>(null)
const [steps, setSteps] = useState<{ prev: boolean; next: boolean } | null>(null)
const uid = useId()
const prefix = tabIdPrefix ?? `${uid}tab`
const current = value
const roving = items.some(item => item.id === value && !item.disabled) ? value : items.find(item => !item.disabled)?.id
const measure = useCallback(() => {
const row = rowRef.current
const viewport = viewportRef.current
if (!row || !viewport) return
const width = row.clientWidth
const tab = viewport.querySelector<HTMLElement>('[role="tab"][aria-selected="true"]')
const start = viewport.offsetLeft
const a = tab ? start + tab.offsetLeft - viewport.scrollLeft : -100
const w = tab ? tab.offsetWidth : 0
// The pocket is drawn only while its tab is fully inside the viewport (it never opens under ◀ ▶ or +).
const inside = tab && a >= start && a + w <= start + viewport.clientWidth + 0.5
setGeometry(previous => {
const next = inside ? { a, w, width } : { a: -100, w: 0, width }
return previous && previous.a === next.a && previous.w === next.w && previous.width === next.width ? previous : next
})
const overflow = viewport.scrollWidth > viewport.clientWidth + 1
const prev = viewport.scrollLeft > 0.5
const nextStep = viewport.scrollLeft + viewport.clientWidth < viewport.scrollWidth - 0.5
setSteps(previous => {
const next = overflow ? { prev, next: nextStep } : null
return previous?.prev === next?.prev && previous?.next === next?.next ? previous : next
})
}, [])
useLayoutEffect(() => {
const row = rowRef.current
const viewport = viewportRef.current
if (!row || !viewport) return
measure()
const observer = new ResizeObserver(measure)
observer.observe(row)
viewport.querySelectorAll('[role="tab"]').forEach(tab => observer.observe(tab))
return () => observer.disconnect()
}, [current, items, measure])
// Width props (style) and font loading change the row without a resize of the observed boxes in some hosts.
useLayoutEffect(measure)
// Keeps the focused / current tab in view inside the viewport (no page scroll).
const reveal = (tab: HTMLElement) => {
const viewport = viewportRef.current
if (!viewport) return
const left = tab.offsetLeft - 7
const right = tab.offsetLeft + tab.offsetWidth + 7
if (left < viewport.scrollLeft) viewport.scrollLeft = left
else if (right > viewport.scrollLeft + viewport.clientWidth) viewport.scrollLeft = right - viewport.clientWidth
}
useLayoutEffect(() => {
const tab = viewportRef.current?.querySelector<HTMLElement>('[role="tab"][aria-selected="true"]')
if (tab) reveal(tab)
}, [current])
const step = (direction: 1 | -1) => {
const viewport = viewportRef.current
if (viewport) viewport.scrollBy({ left: direction * Math.max(43, viewport.clientWidth * 0.6), behavior: "smooth" })
}
const gradientId = `${uid}menu-fill`
const edgeId = `${uid}menu-edge`
const clipId = `${uid}menu-clip`
const shape = geometry ? pocketPath(geometry.a, geometry.w, geometry.width) : undefined
return (
<div ref={node => { rowRef.current = node; if (typeof ref === "function") ref(node); else if (ref) ref.current = node }}
className={cx("hm-sw-footer-menu", className)} {...rest}>
{shape && geometry && (
<svg className="hm-sw-footer-menu__surface" width={geometry.width} height={24} viewBox={`0 0 ${geometry.width} 24`} aria-hidden="true" focusable="false">
<defs>
<linearGradient id={gradientId} x1="0" y1="24" x2="0" y2="0" gradientUnits="userSpaceOnUse">
<stop style={{ stopColor: "var(--hm-sw-footer-menu-bottom)" }} />
<stop offset="0.86" style={{ stopColor: "var(--hm-sw-footer-menu-bottom)" }} />
<stop offset="1" style={{ stopColor: "var(--hm-sw-footer-menu-top)" }} />
</linearGradient>
<linearGradient id={edgeId} x1="0" y1="24.5" x2="0" y2="0" gradientUnits="userSpaceOnUse">
<stop style={{ stopColor: "var(--hm-sw-footer-menu-bottom)" }} />
<stop offset="1" style={{ stopColor: "var(--hm-sw-footer-menu-edge-top)" }} />
</linearGradient>
<clipPath id={clipId}><path d={shape} fillRule="evenodd" /></clipPath>
</defs>
<path d={shape} fill={`url(#${gradientId})`} fillRule="evenodd" />
{/* Inside strokes (Figma 2px stroke masked by the shape = 1px inside). */}
<path d={shape} fill="none" stroke={`url(#${edgeId})`} strokeWidth={2} clipPath={`url(#${clipId})`} />
{geometry.w > 0 && <path d={pocketEdge(geometry.a, geometry.w)} fill="none" style={{ stroke: "var(--hm-sw-footer-menu-pocket-edge)" }} strokeWidth={2} strokeLinejoin="round" clipPath={`url(#${clipId})`} />}
</svg>
)}
<div ref={viewportRef} className="hm-sw-footer-menu__viewport" onScroll={measure}>
{/* display: contents keeps the tabs and dividers in the viewport row; + stays outside the tablist. */}
<div role="tablist" aria-label={label} className="hm-sw-footer-menu__tabs"
onFocus={event => { if ((event.target as HTMLElement).getAttribute("role") === "tab") reveal(event.target as HTMLElement) }}
onKeyDown={event => { onKeyDown?.(event); if (!event.defaultPrevented) moveFocus(event, '[role="tab"]', true) }}>
{items.map((item, index) => {
const selected = item.id === current
const previousSelected = index > 0 && items[index - 1].id === current
return [
index > 0 && !selected && !previousSelected && <span key={`${item.id}:divider`} className="hm-sw-footer-menu__divider" aria-hidden="true" />,
<button key={item.id} type="button" role="tab" id={`${prefix}-${item.id}`} className="hm-sw-footer-menu__tab"
aria-selected={selected} aria-controls={panelId} tabIndex={item.id === roving ? 0 : -1} disabled={item.disabled}
onClick={() => { if (!selected) onValueChange?.(item.id) }}>
{item.label}
</button>,
]
})}
</div>
</div>
{steps && [
<button key="prev" type="button" className="hm-sw-footer-menu__step" aria-label="이전 분류" title="이전 분류" tabIndex={-1} disabled={!steps.prev} onClick={() => step(-1)}>
<svg width="6" height="8" viewBox="0 0 6 8" aria-hidden="true"><path d="M6 0V8L0 4Z" fill="currentColor" /></svg>
</button>,
<button key="next" type="button" className="hm-sw-footer-menu__step" aria-label="다음 분류" title="다음 분류" tabIndex={-1} disabled={!steps.next} onClick={() => step(1)}>
<svg width="6" height="8" viewBox="0 0 6 8" aria-hidden="true"><path d="M0 0V8L6 4Z" fill="currentColor" /></svg>
</button>,
]}
{onAdd && [
!steps && items.length > 0 && items[items.length - 1].id !== current && <span key="add:divider" className="hm-sw-footer-menu__divider" aria-hidden="true" />,
<button key="add" type="button" className="hm-sw-footer-menu__add" aria-label={addLabel} title={addLabel} onClick={onAdd} />,
]}
</div>
)
})
@@ -0,0 +1,30 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="inner" filter="url(#filter0_i_0_6)">
<rect width="16" height="16" rx="1" fill="url(#paint0_linear_0_6)"/>
<g id="outlined line (Stroke)">
<use xlink:href="#stroke0_0_6" fill="#FF5C00"/>
<mask id="stroke1_0_6" maskUnits="userSpaceOnUse" x="1.5" y="3.5" width="13" height="10" fill="black">
<rect fill="white" x="1.5" y="3.5" width="13" height="10"/>
<use xlink:href="#stroke0_0_6"/>
</mask>
<path d="M8 11.5L13.3536 6.14645C13.5488 5.95118 13.5488 5.6346 13.3536 5.43934C13.1583 5.24408 12.8417 5.24408 12.6464 5.43934L8 10.0858L3.35355 5.43934C3.15829 5.24408 2.84171 5.24408 2.64645 5.43934C2.45118 5.6346 2.45118 5.95118 2.64645 6.14645L8 11.5Z" stroke-width="2" stroke-linecap="round" stroke="black" mask="url(#stroke1_0_6)"/>
</g>
</g>
<defs>
<filter id="filter0_i_0_6" x="0" y="0" width="16" height="16" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_0_6"/>
</filter>
<linearGradient id="paint0_linear_0_6" x1="8" y1="0" x2="8" y2="16" gradientUnits="userSpaceOnUse">
<stop stop-color="#281F16"/>
<stop offset="1" stop-color="#352E25"/>
</linearGradient>
<path id="stroke0_0_6" d="M8 11.5L13.3536 6.14645C13.5488 5.95118 13.5488 5.6346 13.3536 5.43934C13.1583 5.24408 12.8417 5.24408 12.6464 5.43934L8 10.0858L3.35355 5.43934C3.15829 5.24408 2.84171 5.24408 2.64645 5.43934C2.45118 5.6346 2.45118 5.95118 2.64645 6.14645L8 11.5Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,31 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="inner" filter="url(#filter0_i_0_4)">
<rect width="16" height="16" rx="1" fill="url(#paint0_linear_0_4)"/>
<rect width="16" height="16" rx="1" fill="black" fill-opacity="0.2"/>
<g id="outlined line (Stroke)">
<use xlink:href="#stroke0_0_4" fill="#FF5C00"/>
<mask id="stroke1_0_4" maskUnits="userSpaceOnUse" x="1.5" y="3.5" width="13" height="10" fill="black">
<rect fill="white" x="1.5" y="3.5" width="13" height="10"/>
<use xlink:href="#stroke0_0_4"/>
</mask>
<path d="M8 11.5L13.3536 6.14645C13.5488 5.95118 13.5488 5.6346 13.3536 5.43934C13.1583 5.24408 12.8417 5.24408 12.6464 5.43934L8 10.0858L3.35355 5.43934C3.15829 5.24408 2.84171 5.24408 2.64645 5.43934C2.45118 5.6346 2.45118 5.95118 2.64645 6.14645L8 11.5Z" stroke-width="2" stroke-linecap="round" stroke="black" mask="url(#stroke1_0_4)"/>
</g>
</g>
<defs>
<filter id="filter0_i_0_4" x="0" y="0" width="16" height="16" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.6 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_0_4"/>
</filter>
<linearGradient id="paint0_linear_0_4" x1="8" y1="0" x2="8" y2="16" gradientUnits="userSpaceOnUse">
<stop stop-color="#281F16"/>
<stop offset="1" stop-color="#352E25"/>
</linearGradient>
<path id="stroke0_0_4" d="M8 11.5L13.3536 6.14645C13.5488 5.95118 13.5488 5.6346 13.3536 5.43934C13.1583 5.24408 12.8417 5.24408 12.6464 5.43934L8 10.0858L3.35355 5.43934C3.15829 5.24408 2.84171 5.24408 2.64645 5.43934C2.45118 5.6346 2.45118 5.95118 2.64645 6.14645L8 11.5Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -0,0 +1,30 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="inner" filter="url(#filter0_i_0_8)">
<rect width="16" height="16" rx="1" fill="url(#paint0_linear_0_8)"/>
<g id="outlined line (Stroke)">
<use xlink:href="#stroke0_0_8" fill="#FF5C00"/>
<mask id="stroke1_0_8" maskUnits="userSpaceOnUse" x="1.5" y="2.29297" width="13" height="10" fill="black">
<rect fill="white" x="1.5" y="2.29297" width="13" height="10"/>
<use xlink:href="#stroke0_0_8"/>
</mask>
<path d="M8 4.29297L13.3536 9.64652C13.5488 9.84178 13.5488 10.1584 13.3536 10.3536C13.1583 10.5489 12.8417 10.5489 12.6464 10.3536L8 5.70718L3.35355 10.3536C3.15829 10.5489 2.84171 10.5489 2.64645 10.3536C2.45118 10.1584 2.45118 9.84178 2.64645 9.64652L8 4.29297Z" stroke-width="2" stroke-linecap="round" stroke="black" mask="url(#stroke1_0_8)"/>
</g>
</g>
<defs>
<filter id="filter0_i_0_8" x="0" y="0" width="16" height="16" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_0_8"/>
</filter>
<linearGradient id="paint0_linear_0_8" x1="8" y1="0" x2="8" y2="16" gradientUnits="userSpaceOnUse">
<stop stop-color="#281F16"/>
<stop offset="1" stop-color="#352E25"/>
</linearGradient>
<path id="stroke0_0_8" d="M8 4.29297L13.3536 9.64652C13.5488 9.84178 13.5488 10.1584 13.3536 10.3536C13.1583 10.5489 12.8417 10.5489 12.6464 10.3536L8 5.70718L3.35355 10.3536C3.15829 10.5489 2.84171 10.5489 2.64645 10.3536C2.45118 10.1584 2.45118 9.84178 2.64645 9.64652L8 4.29297Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,30 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="inner" filter="url(#filter0_i_0_10)">
<rect width="16" height="16" rx="1" fill="url(#paint0_linear_0_10)"/>
<g id="outlined line (Stroke)">
<use xlink:href="#stroke0_0_10" fill="#D4CBBD"/>
<mask id="stroke1_0_10" maskUnits="userSpaceOnUse" x="1.5" y="2.29297" width="13" height="10" fill="black">
<rect fill="white" x="1.5" y="2.29297" width="13" height="10"/>
<use xlink:href="#stroke0_0_10"/>
</mask>
<path d="M8 4.29297L13.3536 9.64652C13.5488 9.84178 13.5488 10.1584 13.3536 10.3536C13.1583 10.5489 12.8417 10.5489 12.6464 10.3536L8 5.70718L3.35355 10.3536C3.15829 10.5489 2.84171 10.5489 2.64645 10.3536C2.45118 10.1584 2.45118 9.84178 2.64645 9.64652L8 4.29297Z" stroke-width="2" stroke-linecap="round" stroke="black" mask="url(#stroke1_0_10)"/>
</g>
</g>
<defs>
<filter id="filter0_i_0_10" x="0" y="0" width="16" height="16" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.15 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_0_10"/>
</filter>
<linearGradient id="paint0_linear_0_10" x1="8" y1="0" x2="8" y2="16" gradientUnits="userSpaceOnUse">
<stop stop-color="#281F16"/>
<stop offset="1" stop-color="#352E25"/>
</linearGradient>
<path id="stroke0_0_10" d="M8 4.29297L13.3536 9.64652C13.5488 9.84178 13.5488 10.1584 13.3536 10.3536C13.1583 10.5489 12.8417 10.5489 12.6464 10.3536L8 5.70718L3.35355 10.3536C3.15829 10.5489 2.84171 10.5489 2.64645 10.3536C2.45118 10.1584 2.45118 9.84178 2.64645 9.64652L8 4.29297Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,13 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="&#234;&#183;&#184;&#235;&#166;&#172;&#235;&#147;&#156;&#236;&#138;&#164;&#235;&#131;&#133;">
<use xlink:href="#stroke0_0_121" fill="#F1EDE5"/>
<mask id="stroke1_0_121" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16" fill="black">
<rect fill="white" width="16" height="16"/>
<use xlink:href="#stroke0_0_121"/>
</mask>
<path d="M1 1H3V3H1V1ZM1 5H3V7H1V5ZM3 9H1V11H3V9ZM1 13H3V15H1V13ZM7 1H5V3H7V1ZM5 5H7V7H5V5ZM7 9H5V11H7V9ZM5 13H7V15H5V13ZM11 1H9V3H11V1ZM9 5H11V7H9V5ZM11 9H9V11H11V9ZM9 13H11V15H9V13ZM15 1H13V3H15V1ZM13 5H15V7H13V5ZM15 9H13V11H15V9ZM13 13H15V15H13V13Z" stroke-width="2" stroke="black" stroke-opacity="0.9" mask="url(#stroke1_0_121)"/>
</g>
<defs>
<path id="stroke0_0_121" d="M3 1H1V3H3V1ZM3 5H1V7H3V5ZM1 9H3V11H1V9ZM3 13H1V15H3V13ZM5 1H7V3H5V1ZM7 5H5V7H7V5ZM5 9H7V11H5V9ZM7 13H5V15H7V13ZM9 1H11V3H9V1ZM11 5H9V7H11V5ZM9 9H11V11H9V9ZM11 13H9V15H11V13ZM13 1H15V3H13V1ZM15 5H13V7H15V5ZM13 9H15V11H13V9ZM15 13H13V15H15V13Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,13 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="&#234;&#183;&#184;&#235;&#166;&#172;&#235;&#147;&#156;">
<use xlink:href="#stroke0_0_149" fill="#F1EDE5"/>
<mask id="stroke1_0_149" maskUnits="userSpaceOnUse" x="0" y="0" width="17" height="17" fill="black">
<rect fill="white" width="17" height="17"/>
<use xlink:href="#stroke0_0_149"/>
</mask>
<path d="M3 1H4V3L8 3V1H9V3L13 3V1H14V3L16 3V4H14V8H16V9H14V13H16V14H14V16H13V14H9V16H8V14H4V16H3V14H1V13H3V9H1V8H3V4H1V3L3 3V1ZM13 13V9H9V13H13ZM8 13V9H4V13H8ZM4 8H8V4H4V8ZM9 8H13V4H9V8Z" stroke-width="2" stroke="black" stroke-opacity="0.9" mask="url(#stroke1_0_149)"/>
</g>
<defs>
<path id="stroke0_0_149" d="M4 1H3V3L1 3V4H3V8H1V9H3V13H1V14H3V16H4V14H8V16H9V14H13V16H14V14H16V13H14V9H16V8H14V4H16V3L14 3V1H13V3L9 3V1H8V3L4 3V1ZM13 13V9H9V13H13ZM8 13V9H4V13H8ZM8 8H4V4H8V8ZM13 8H9V4H13V8Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -0,0 +1,10 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="16" height="15" viewBox="0 0 16 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="linewight">
<path d="M1 1H15V3H1V1ZM1 5H15V8H1V5ZM15 10H1V14H15V10Z" fill-rule="evenodd" clip-rule="evenodd" fill="#F1EDE5"/>
<mask id="stroke0_0_145" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="15" fill="black">
<rect fill="white" width="16" height="15"/>
<path d="M1 1H15V3H1V1ZM1 5H15V8H1V5ZM15 10H1V14H15V10Z" fill-rule="evenodd" clip-rule="evenodd"/>
</mask>
<path d="M15 1H1V3H15V1ZM15 5H1V8H15V5ZM1 10H15V14H1V10Z" stroke-width="2" stroke="black" stroke-opacity="0.9" mask="url(#stroke0_0_145)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 703 B

@@ -0,0 +1,10 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="object snap">
<path d="M5 1H1V5H2V16H16V2H5V1ZM3 5H5V3H15V15H3V5Z" fill-rule="evenodd" clip-rule="evenodd" fill="#F1EDE5"/>
<mask id="stroke0_0_187" maskUnits="userSpaceOnUse" x="0" y="0" width="17" height="17" fill="black">
<rect fill="white" width="17" height="17"/>
<path d="M5 1H1V5H2V16H16V2H5V1ZM3 5H5V3H15V15H3V5Z" fill-rule="evenodd" clip-rule="evenodd"/>
</mask>
<path d="M1 1H5V2H16V16H2V5H1V1ZM5 5H3V15H15V3H5V5Z" stroke-width="2" stroke="black" stroke-opacity="0.9" mask="url(#stroke0_0_187)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 692 B

@@ -0,0 +1,10 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="orthogonal">
<path d="M1 1H2V12H6V16H17V17H6H2H1V16V12V1Z" fill-rule="evenodd" clip-rule="evenodd" fill="#F1EDE5"/>
<mask id="stroke0_0_159" maskUnits="userSpaceOnUse" x="0" y="0" width="18" height="18" fill="black">
<rect fill="white" width="18" height="18"/>
<path d="M1 1H2V12H6V16H17V17H6H2H1V16V12V1Z" fill-rule="evenodd" clip-rule="evenodd"/>
</mask>
<path d="M2 1H1V12V16V17H2H6H17V16H6V12H2V1Z" stroke-width="2" stroke="black" stroke-opacity="0.9" mask="url(#stroke0_0_159)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 670 B

@@ -0,0 +1,10 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Union">
<path d="M15 15V1H1V15H15ZM14 14H2V2.70718L9.29282 10H7V11H10H11V10V7H10V9.29297L2.70703 2H14V14Z" fill-rule="evenodd" clip-rule="evenodd" fill="#F1EDE5"/>
<mask id="stroke0_0_163" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16" fill="black">
<rect fill="white" width="16" height="16"/>
<path d="M15 15V1H1V15H15ZM14 14H2V2.70718L9.29282 10H7V11H10H11V10V7H10V9.29297L2.70703 2H14V14Z" fill-rule="evenodd" clip-rule="evenodd"/>
</mask>
<path d="M15 1V15H1V1H15ZM2 14H14V2H2.70703L10 9.29297V7H11V10V11H10H7V10H9.29282L2 2.70718V14Z" stroke-width="2" stroke="black" stroke-opacity="0.9" mask="url(#stroke0_0_163)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 822 B

@@ -0,0 +1,12 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="28" height="12" viewBox="0 0 28 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="btn_add">
<g id="Union">
<path d="M15 5V1H13L13 5H9V7H13L13 11H15V7H19V5H15Z" fill-rule="evenodd" clip-rule="evenodd" fill="#D4CBBD"/>
<mask id="stroke0_0_402" maskUnits="userSpaceOnUse" x="8" y="0" width="12" height="12" fill="black">
<rect fill="white" x="8" width="12" height="12"/>
<path d="M15 5V1H13L13 5H9V7H13L13 11H15V7H19V5H15Z" fill-rule="evenodd" clip-rule="evenodd"/>
</mask>
<path d="M15 1V5H19V7H15V11H13L13 7H9V5H13L13 1H15Z" stroke-width="2" stroke="black" mask="url(#stroke0_0_402)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 693 B

@@ -0,0 +1,21 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="74" height="71" viewBox="0 0 74 71" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Vector" d="M0.5 0.5H73.5V70.5H0.5V0.5Z" fill-rule="evenodd" clip-rule="evenodd" fill="url(#paint0_linear_0_21)" stroke="url(#paint1_linear_0_21)"/>
<defs>
<linearGradient id="paint0_linear_0_21" x1="0" y1="71.0004" x2="-1.95986e-06" y2="0" gradientUnits="userSpaceOnUse">
<stop stop-color="#2B261C"/>
<stop offset="0.12" stop-color="#15120C"/>
<stop offset="0.28" stop-color="#282216"/>
<stop offset="0.37" stop-color="#322B1D"/>
<stop offset="0.51" stop-color="#3C3423"/>
<stop offset="0.89" stop-color="#2B2417"/>
<stop offset="1" stop-color="#433A25"/>
</linearGradient>
<linearGradient id="paint1_linear_0_21" x1="37" y1="0" x2="37" y2="71" gradientUnits="userSpaceOnUse">
<stop stop-color="#886D35"/>
<stop offset="0.0884568" stop-color="#423625"/>
<stop offset="0.26169" stop-color="#F3DBA8"/>
<stop offset="0.837918" stop-color="#0E0B06"/>
<stop offset="1" stop-color="#574B30"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,24 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="68" height="46" viewBox="0 0 68 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="btn_add_menu">
<mask id="stroke0_0_10" fill="white">
<path d="M0 0H68V46H0V0Z"/>
</mask>
<path id="Rectangle 7507" d="M0 0V-0.5C-0.276142 -0.5 -0.5 -0.276142 -0.5 0L0 0ZM68 0H68.5C68.5 -0.276142 68.2761 -0.5 68 -0.5V0ZM68 46V46.5C68.2761 46.5 68.5 46.2761 68.5 46H68ZM0 46H-0.5C-0.5 46.2761 -0.276142 46.5 0 46.5L0 46ZM0 0H-0.5V1.91667H0H0.5V0H0ZM0 5.75H-0.5V9.58333H0H0.5V5.75H0ZM0 13.4167H-0.5V17.25H0H0.5V13.4167H0ZM0 21.0833H-0.5V24.9167H0H0.5V21.0833H0ZM0 28.75H-0.5V32.5833H0H0.5V28.75H0ZM0 36.4167H-0.5V40.25H0H0.5V36.4167H0ZM0 44.0833H-0.5V46H0H0.5V44.0833H0ZM0 46V46.5H1.88889V46V45.5H0V46ZM5.66667 46V46.5H9.44444V46V45.5H5.66667V46ZM13.2222 46V46.5H17V46V45.5H13.2222V46ZM20.7778 46V46.5H24.5556V46V45.5H20.7778V46ZM28.3333 46V46.5H32.1111V46V45.5H28.3333V46ZM35.8889 46V46.5H39.6667V46V45.5H35.8889V46ZM43.4445 46V46.5H47.2222V46V45.5H43.4445V46ZM51 46V46.5H54.7778V46V45.5H51V46ZM58.5556 46V46.5H62.3333V46V45.5H58.5556V46ZM66.1111 46V46.5H68V46V45.5H66.1111V46ZM68 46H68.5V44.0833H68H67.5V46H68ZM68 40.25H68.5V36.4167H68H67.5V40.25H68ZM68 32.5833H68.5V28.75H68H67.5V32.5833H68ZM68 24.9167H68.5V21.0833H68H67.5V24.9167H68ZM68 17.25H68.5V13.4167H68H67.5V17.25H68ZM68 9.58333H68.5V5.75H68H67.5V9.58333H68ZM68 1.91667H68.5V0H68H67.5V1.91667H68ZM68 0V-0.5H66.1111V0V0.5H68V0ZM62.3333 0V-0.5H58.5556V0V0.5H62.3333V0ZM54.7778 0V-0.5H51V0V0.5H54.7778V0ZM47.2222 0V-0.5H43.4444V0V0.5H47.2222V0ZM39.6667 0V-0.5H35.8889V0V0.5H39.6667V0ZM32.1111 0V-0.5H28.3333V0V0.5H32.1111V0ZM24.5556 0V-0.5H20.7778V0V0.5H24.5556V0ZM17 0V-0.5H13.2222V0V0.5H17V0ZM9.44444 0V-0.5H5.66666V0V0.5H9.44444V0ZM1.88888 0V-0.5H0V0V0.5H1.88888V0ZM0 0V-1C-0.552285 -1 -1 -0.552285 -1 0L0 0ZM68 0H69C69 -0.552285 68.5523 -1 68 -1V0ZM68 46V47C68.5523 47 69 46.5523 69 46H68ZM0 46H-1C-1 46.5523 -0.552285 47 0 47L0 46ZM0 0H-1V1.91667H0H1V0H0ZM0 5.75H-1V9.58333H0H1V5.75H0ZM0 13.4167H-1V17.25H0H1V13.4167H0ZM0 21.0833H-1V24.9167H0H1V21.0833H0ZM0 28.75H-1V32.5833H0H1V28.75H0ZM0 36.4167H-1V40.25H0H1V36.4167H0ZM0 44.0833H-1V46H0H1V44.0833H0ZM0 46V47H1.88889V46V45H0V46ZM5.66667 46V47H9.44444V46V45H5.66667V46ZM13.2222 46V47H17V46V45H13.2222V46ZM20.7778 46V47H24.5556V46V45H20.7778V46ZM28.3333 46V47H32.1111V46V45H28.3333V46ZM35.8889 46V47H39.6667V46V45H35.8889V46ZM43.4445 46V47H47.2222V46V45H43.4445V46ZM51 46V47H54.7778V46V45H51V46ZM58.5556 46V47H62.3333V46V45H58.5556V46ZM66.1111 46V47H68V46V45H66.1111V46ZM68 46H69V44.0833H68H67V46H68ZM68 40.25H69V36.4167H68H67V40.25H68ZM68 32.5833H69V28.75H68H67V32.5833H68ZM68 24.9167H69V21.0833H68H67V24.9167H68ZM68 17.25H69V13.4167H68H67V17.25H68ZM68 9.58333H69V5.75H68H67V9.58333H68ZM68 1.91667H69V0H68H67V1.91667H68ZM68 0V-1H66.1111V0V1H68V0ZM62.3333 0V-1H58.5556V0V1H62.3333V0ZM54.7778 0V-1H51V0V1H54.7778V0ZM47.2222 0V-1H43.4444V0V1H47.2222V0ZM39.6667 0V-1H35.8889V0V1H39.6667V0ZM32.1111 0V-1H28.3333V0V1H32.1111V0ZM24.5556 0V-1H20.7778V0V1H24.5556V0ZM17 0V-1H13.2222V0V1H17V0ZM9.44444 0V-1H5.66666V0V1H9.44444V0ZM1.88888 0V-1H0V0V1H1.88888V0Z" fill="url(#paint0_linear_0_10)" mask="url(#stroke0_0_10)"/>
<g id="Union">
<path d="M35 21V17H33L33 21H29V23H33L33 27H35V23H39V21H35Z" fill-rule="evenodd" clip-rule="evenodd" fill="#FF5C00"/>
<mask id="stroke1_0_10" maskUnits="userSpaceOnUse" x="28" y="16" width="12" height="12" fill="black">
<rect fill="white" x="28" y="16" width="12" height="12"/>
<path d="M35 21V17H33L33 21H29V23H33L33 27H35V23H39V21H35Z" fill-rule="evenodd" clip-rule="evenodd"/>
</mask>
<path d="M35 17V21H39V23H35V27H33L33 23H29V21H33L33 17H35Z" stroke-width="2" stroke="black" mask="url(#stroke1_0_10)"/>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_0_10" x1="34" y1="0" x2="34" y2="46" gradientUnits="userSpaceOnUse">
<stop offset="0.0852474" stop-color="#DA6813"/>
<stop offset="0.379274" stop-color="#FFBB89"/>
<stop offset="0.718536" stop-color="#E5690D"/>
<stop offset="0.927747" stop-color="#AE3607"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

@@ -0,0 +1,12 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="68" height="46" viewBox="0 0 68 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="btn_add_menu">
<g id="Union">
<path d="M35 21V17H33L33 21H29V23H33L33 27H35V23H39V21H35Z" fill-rule="evenodd" clip-rule="evenodd" fill="#BEB5A6"/>
<mask id="stroke0_0_411" maskUnits="userSpaceOnUse" x="28" y="16" width="12" height="12" fill="black">
<rect fill="white" x="28" y="16" width="12" height="12"/>
<path d="M35 21V17H33L33 21H29V23H33L33 27H35V23H39V21H35Z" fill-rule="evenodd" clip-rule="evenodd"/>
</mask>
<path d="M35 17V21H39V23H35V27H33L33 23H29V21H33L33 17H35Z" stroke-width="2" stroke="black" mask="url(#stroke0_0_411)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 729 B

@@ -0,0 +1,32 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="24" height="22" viewBox="0 0 24 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="btn_add_layout" filter="url(#filter0_i_0_8)">
<path d="M0 4C0 1.79086 1.79086 0 4 0H20C22.2091 0 24 1.79086 24 4V22H0V4Z" fill="#C3B79E"/>
<path d="M4 0.5H20C21.933 0.5 23.5 2.067 23.5 4V21.5H0.5V4C0.5 2.12727 1.97083 0.598421 3.82031 0.504883L4 0.5Z" stroke="url(#paint0_linear_0_8)"/>
<g id="Union">
<path d="M13 7H11V10H8V12H11V15H13V12H16V10H13V7Z" fill-rule="evenodd" clip-rule="evenodd" fill="#FF5C00"/>
<mask id="stroke0_0_8" maskUnits="userSpaceOnUse" x="7" y="6" width="10" height="10" fill="black">
<rect fill="white" x="7" y="6" width="10" height="10"/>
<path d="M13 7H11V10H8V12H11V15H13V12H16V10H13V7Z" fill-rule="evenodd" clip-rule="evenodd"/>
</mask>
<path d="M11 7H13V10H16V12H13V15H11V12H8V10H11V7Z" stroke-width="2" stroke="black" stroke-opacity="0.7" mask="url(#stroke0_0_8)"/>
</g>
</g>
<defs>
<filter id="filter0_i_0_8" x="0" y="-3" width="24" height="25" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-3"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.188235 0 0 0 0 0.121569 0 0 0 0.25 0"/>
<feBlend mode="multiply" in2="shape" result="effect1_innerShadow_0_8"/>
</filter>
<linearGradient id="paint0_linear_0_8" x1="12" y1="0" x2="12" y2="22" gradientUnits="userSpaceOnUse">
<stop offset="0.0852474" stop-color="#DA6813"/>
<stop offset="0.379274" stop-color="#FFBB89"/>
<stop offset="0.718536" stop-color="#E5690D"/>
<stop offset="0.927747" stop-color="#AE3607"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,23 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="24" height="22" viewBox="0 0 24 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="btn_add_layout" filter="url(#filter0_i_0_16)">
<path d="M0 4C0 1.79086 1.79086 0 4 0H20C22.2091 0 24 1.79086 24 4V22H0V4Z" fill="#C3B79E"/>
<path d="M4 0.5H20C21.933 0.5 23.5 2.067 23.5 4V21.5H0.5V4C0.5 2.12727 1.97083 0.598421 3.82031 0.504883L4 0.5Z" stroke="url(#paint0_linear_0_16)"/>
<path id="Union" d="M13 7H11V10H8V12H11V15H13V12H16V10H13V7Z" fill-rule="evenodd" clip-rule="evenodd" fill="#161410"/>
</g>
<defs>
<filter id="filter0_i_0_16" x="0" y="-3" width="24" height="25" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-3"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.188235 0 0 0 0 0.121569 0 0 0 0.25 0"/>
<feBlend mode="multiply" in2="shape" result="effect1_innerShadow_0_16"/>
</filter>
<linearGradient id="paint0_linear_0_16" x1="12" y1="0" x2="12" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-color="#C8BA9C"/>
<stop offset="1" stop-color="#918670"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,26 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="28" height="22" viewBox="0 0 28 22" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="btn_close2">
<g id="Rectangle 7506">
<use xlink:href="#stroke0_0_9" fill="url(#paint0_linear_0_9)"/>
<use xlink:href="#stroke0_0_9" fill="black" fill-opacity="0.2"/>
</g>
<g id="Union">
<use xlink:href="#stroke1_0_9" fill="#FFC600"/>
<mask id="stroke2_0_9" maskUnits="userSpaceOnUse" x="4" y="6.31798" width="10" height="10" fill="black">
<rect fill="white" x="4" y="6.31798" width="10" height="10"/>
<use xlink:href="#stroke1_0_9"/>
</mask>
<path d="M5.1472 8.17154C4.95194 7.97627 4.95194 7.65969 5.1472 7.46443C5.34246 7.26917 5.65905 7.26917 5.85431 7.46443L8.6824 10.2925L11.5104 7.4645C11.7057 7.26924 12.0223 7.26924 12.2175 7.4645C12.4128 7.65977 12.4128 7.97635 12.2175 8.17161L9.3895 10.9996L12.2183 13.8284C12.4135 14.0237 12.4135 14.3402 12.2183 14.5355C12.023 14.7308 11.7064 14.7308 11.5112 14.5355L8.6824 11.7067L5.85355 14.5356C5.65829 14.7308 5.34171 14.7308 5.14645 14.5356C4.95118 14.3403 4.95118 14.0237 5.14645 13.8285L7.97529 10.9996L5.1472 8.17154Z" stroke-width="2" stroke="black" stroke-opacity="0.7" mask="url(#stroke2_0_9)"/>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_0_9" x1="14" y1="0" x2="14" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-color="#700000"/>
<stop offset="0.472245" stop-color="#BE0000"/>
<stop offset="0.837245" stop-color="#620808"/>
<stop offset="1" stop-color="#590000"/>
</linearGradient>
<path id="stroke0_0_9" d="M0 0H7.9262C10.4857 0 12.8907 1.22467 14.3961 3.29463L28 22H0V0Z"/>
<path id="stroke1_0_9" d="M5.1472 7.46443C4.95194 7.65969 4.95194 7.97627 5.1472 8.17154L7.97529 10.9996L5.14645 13.8285C4.95118 14.0237 4.95118 14.3403 5.14645 14.5356C5.34171 14.7308 5.65829 14.7308 5.85355 14.5356L8.6824 11.7067L11.5112 14.5355C11.7064 14.7308 12.023 14.7308 12.2183 14.5355C12.4135 14.3402 12.4135 14.0237 12.2183 13.8284L9.3895 10.9996L12.2175 8.17161C12.4128 7.97635 12.4128 7.65977 12.2175 7.4645C12.0223 7.26924 11.7057 7.26924 11.5104 7.4645L8.6824 10.2925L5.85431 7.46443C5.65905 7.26917 5.34246 7.26917 5.1472 7.46443Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -0,0 +1,22 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="28" height="22" viewBox="0 0 28 22" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="btn_close2">
<path id="Rectangle 7506" d="M0 0H7.9262C10.4857 0 12.8907 1.22467 14.3961 3.29463L28 22H0V0Z" fill="url(#paint0_linear_0_43)"/>
<g id="Union">
<use xlink:href="#stroke0_0_43" fill="white"/>
<mask id="stroke1_0_43" maskUnits="userSpaceOnUse" x="4" y="6.31798" width="10" height="10" fill="black">
<rect fill="white" x="4" y="6.31798" width="10" height="10"/>
<use xlink:href="#stroke0_0_43"/>
</mask>
<path d="M5.1472 8.17154C4.95194 7.97627 4.95194 7.65969 5.1472 7.46443C5.34246 7.26917 5.65905 7.26917 5.85431 7.46443L8.6824 10.2925L11.5104 7.4645C11.7057 7.26924 12.0223 7.26924 12.2175 7.4645C12.4128 7.65977 12.4128 7.97635 12.2175 8.17161L9.3895 10.9996L12.2183 13.8284C12.4135 14.0237 12.4135 14.3402 12.2183 14.5355C12.023 14.7308 11.7064 14.7308 11.5112 14.5355L8.6824 11.7067L5.85355 14.5356C5.65829 14.7308 5.34171 14.7308 5.14645 14.5356C4.95118 14.3403 4.95118 14.0237 5.14645 13.8285L7.97529 10.9996L5.1472 8.17154Z" stroke-width="2" stroke="black" stroke-opacity="0.7" mask="url(#stroke1_0_43)"/>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_0_43" x1="14" y1="0" x2="14" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-color="#700000"/>
<stop offset="0.472245" stop-color="#BE0000"/>
<stop offset="0.837245" stop-color="#620808"/>
<stop offset="1" stop-color="#590000"/>
</linearGradient>
<path id="stroke0_0_43" d="M5.1472 7.46443C4.95194 7.65969 4.95194 7.97627 5.1472 8.17154L7.97529 10.9996L5.14645 13.8285C4.95118 14.0237 4.95118 14.3403 5.14645 14.5356C5.34171 14.7308 5.65829 14.7308 5.85355 14.5356L8.6824 11.7067L11.5112 14.5355C11.7064 14.7308 12.023 14.7308 12.2183 14.5355C12.4135 14.3402 12.4135 14.0237 12.2183 13.8284L9.3895 10.9996L12.2175 8.17161C12.4128 7.97635 12.4128 7.65977 12.2175 7.4645C12.0223 7.26924 11.7057 7.26924 11.5104 7.4645L8.6824 10.2925L5.85431 7.46443C5.65905 7.26917 5.34246 7.26917 5.1472 7.46443Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,27 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="16" height="22" viewBox="0 0 16 22" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="btn_delete">
<mask id="path-1-inside-1_0_8" fill="white">
<path d="M0 0H12C14.2091 0 16 1.79086 16 4V22H0V0Z"/>
</mask>
<path d="M0 0H12C14.2091 0 16 1.79086 16 4V22H0V0Z" fill="url(#paint0_linear_0_8)"/>
<path d="M0 0H12C14.2091 0 16 1.79086 16 4V22H0V0Z" fill="black" fill-opacity="0.2"/>
<path d="M0 0H16H0ZM16 22H0H16ZM-1 22V0H1V22H-1ZM16 0V22V0Z" fill="black" fill-opacity="0.4" mask="url(#path-1-inside-1_0_8)"/>
<g id="Union">
<use xlink:href="#stroke0_0_8" fill="#FFC600"/>
<mask id="stroke1_0_8" maskUnits="userSpaceOnUse" x="3.31764" y="6.31798" width="10" height="10" fill="black">
<rect fill="white" x="3.31764" y="6.31798" width="10" height="10"/>
<use xlink:href="#stroke0_0_8"/>
</mask>
<path d="M4.46484 8.17154C4.26958 7.97627 4.26958 7.65969 4.46484 7.46443C4.66011 7.26917 4.97669 7.26917 5.17195 7.46443L8.00004 10.2925L10.828 7.4645C11.0233 7.26924 11.3399 7.26924 11.5352 7.4645C11.7304 7.65977 11.7304 7.97635 11.5352 8.17161L8.70714 10.9996L11.5359 13.8284C11.7312 14.0237 11.7312 14.3402 11.5359 14.5355C11.3406 14.7308 11.0241 14.7308 10.8288 14.5355L8.00004 11.7067L5.1712 14.5356C4.97593 14.7308 4.65935 14.7308 4.46409 14.5356C4.26883 14.3403 4.26883 14.0237 4.46409 13.8285L7.29293 10.9996L4.46484 8.17154Z" stroke-width="2" stroke="black" stroke-opacity="0.7" mask="url(#stroke1_0_8)"/>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_0_8" x1="8" y1="0" x2="8" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-color="#700000"/>
<stop offset="0.472245" stop-color="#BE0000"/>
<stop offset="0.837245" stop-color="#620808"/>
<stop offset="1" stop-color="#590000"/>
</linearGradient>
<path id="stroke0_0_8" d="M4.46484 7.46443C4.26958 7.65969 4.26958 7.97627 4.46484 8.17154L7.29293 10.9996L4.46409 13.8285C4.26883 14.0237 4.26883 14.3403 4.46409 14.5356C4.65935 14.7308 4.97593 14.7308 5.1712 14.5356L8.00004 11.7067L10.8288 14.5355C11.0241 14.7308 11.3406 14.7308 11.5359 14.5355C11.7312 14.3402 11.7312 14.0237 11.5359 13.8284L8.70714 10.9996L11.5352 8.17161C11.7304 7.97635 11.7304 7.65977 11.5352 7.4645C11.3399 7.26924 11.0233 7.26924 10.828 7.4645L8.00004 10.2925L5.17195 7.46443C4.97669 7.26917 4.66011 7.26917 4.46484 7.46443Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,26 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="16" height="22" viewBox="0 0 16 22" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="btn_delete">
<mask id="path-1-inside-1_0_17" fill="white">
<path d="M0 0H12C14.2091 0 16 1.79086 16 4V22H0V0Z"/>
</mask>
<path d="M0 0H12C14.2091 0 16 1.79086 16 4V22H0V0Z" fill="url(#paint0_linear_0_17)"/>
<path d="M0 0H16H0ZM16 22H0H16ZM-1 22V0H1V22H-1ZM16 0V22V0Z" fill="black" fill-opacity="0.4" mask="url(#path-1-inside-1_0_17)"/>
<g id="Union">
<use xlink:href="#stroke0_0_17" fill="white"/>
<mask id="stroke1_0_17" maskUnits="userSpaceOnUse" x="3.31764" y="6.31798" width="10" height="10" fill="black">
<rect fill="white" x="3.31764" y="6.31798" width="10" height="10"/>
<use xlink:href="#stroke0_0_17"/>
</mask>
<path d="M4.46484 8.17154C4.26958 7.97627 4.26958 7.65969 4.46484 7.46443C4.66011 7.26917 4.97669 7.26917 5.17195 7.46443L8.00004 10.2925L10.828 7.4645C11.0233 7.26924 11.3399 7.26924 11.5352 7.4645C11.7304 7.65977 11.7304 7.97635 11.5352 8.17161L8.70714 10.9996L11.5359 13.8284C11.7312 14.0237 11.7312 14.3402 11.5359 14.5355C11.3406 14.7308 11.0241 14.7308 10.8288 14.5355L8.00004 11.7067L5.1712 14.5356C4.97593 14.7308 4.65935 14.7308 4.46409 14.5356C4.26883 14.3403 4.26883 14.0237 4.46409 13.8285L7.29293 10.9996L4.46484 8.17154Z" stroke-width="2" stroke="black" stroke-opacity="0.7" mask="url(#stroke1_0_17)"/>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_0_17" x1="8" y1="0" x2="8" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-color="#700000"/>
<stop offset="0.472245" stop-color="#BE0000"/>
<stop offset="0.837245" stop-color="#620808"/>
<stop offset="1" stop-color="#590000"/>
</linearGradient>
<path id="stroke0_0_17" d="M4.46484 7.46443C4.26958 7.65969 4.26958 7.97627 4.46484 8.17154L7.29293 10.9996L4.46409 13.8285C4.26883 14.0237 4.26883 14.3403 4.46409 14.5356C4.65935 14.7308 4.97593 14.7308 5.1712 14.5356L8.00004 11.7067L10.8288 14.5355C11.0241 14.7308 11.3406 14.7308 11.5359 14.5355C11.7312 14.3402 11.7312 14.0237 11.5359 13.8284L8.70714 10.9996L11.5352 8.17161C11.7304 7.97635 11.7304 7.65977 11.5352 7.4645C11.3399 7.26924 11.0233 7.26924 10.828 7.4645L8.00004 10.2925L5.17195 7.46443C4.97669 7.26917 4.66011 7.26917 4.46484 7.46443Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1,30 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="73" height="22" viewBox="0 0 73 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="bg">
<g filter="url(#filter0_i_0_4)">
<path d="M13.6039 3.29463C15.1093 1.22467 17.5143 0 20.0738 0H73V22H0L13.6039 3.29463Z" fill="#C3B79E"/>
<path d="M13.6039 3.29463C15.1093 1.22467 17.5143 0 20.0738 0H73V22H0L13.6039 3.29463Z" fill="url(#paint0_linear_0_4)"/>
</g>
<path d="M20.0742 0.5H72.5V21.5H0.981445L14.0078 3.58887C15.4191 1.64829 17.6747 0.5 20.0742 0.5Z" stroke="url(#paint1_linear_0_4)"/>
</g>
<defs>
<filter id="filter0_i_0_4" x="0" y="-3" width="73" height="25" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-3"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.188235 0 0 0 0 0.121569 0 0 0 0.25 0"/>
<feBlend mode="multiply" in2="shape" result="effect1_innerShadow_0_4"/>
</filter>
<linearGradient id="paint0_linear_0_4" x1="0" y1="21.8948" x2="0" y2="0" gradientUnits="userSpaceOnUse">
<stop stop-color="#7C6C4C"/>
<stop offset="0.85" stop-color="#7C6C4C"/>
<stop offset="1" stop-color="#A6946F"/>
</linearGradient>
<linearGradient id="paint1_linear_0_4" x1="38.5" y1="0" x2="38.5" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.05"/>
<stop offset="1" stop-opacity="0.3"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1,24 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="73" height="22" viewBox="0 0 73 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="bg">
<g filter="url(#filter0_i_0_5)">
<path d="M13.6039 3.29463C15.1093 1.22467 17.5143 0 20.0738 0H73V22H0L13.6039 3.29463Z" fill="#C3B79E"/>
</g>
<path d="M20.0742 0.5H72.5V21.5H0.981445L14.0078 3.58887C15.4191 1.64829 17.6747 0.5 20.0742 0.5Z" stroke="url(#paint0_linear_0_5)"/>
</g>
<defs>
<filter id="filter0_i_0_5" x="0" y="-3" width="73" height="25" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-3"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.188235 0 0 0 0 0.121569 0 0 0 0.25 0"/>
<feBlend mode="multiply" in2="shape" result="effect1_innerShadow_0_5"/>
</filter>
<linearGradient id="paint0_linear_0_5" x1="38.5" y1="0" x2="38.5" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.05"/>
<stop offset="1" stop-opacity="0.3"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,17 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="97" height="22" viewBox="0 0 97 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="bg" filter="url(#filter0_i_0_58)">
<path d="M13.6039 3.29463C15.1093 1.22467 17.5143 0 20.0738 0H93C95.2091 0 97 1.79086 97 4V22H0L13.6039 3.29463Z" fill="#0F0D0A"/>
</g>
<defs>
<filter id="filter0_i_0_58" x="0" y="-3" width="97" height="25" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-3"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.188235 0 0 0 0 0.121569 0 0 0 0.25 0"/>
<feBlend mode="multiply" in2="shape" result="effect1_innerShadow_0_58"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -0,0 +1,26 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="97" height="22" viewBox="0 0 97 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="bg">
<g filter="url(#filter0_i_0_81)">
<path d="M13.6039 3.29463C15.1093 1.22467 17.5143 0 20.0738 0H93C95.2091 0 97 1.79086 97 4V22H0L13.6039 3.29463Z" fill="#0F0D0A"/>
</g>
<path d="M20.0742 0.5H93C94.933 0.5 96.5 2.067 96.5 4V21.5H0.981445L14.0078 3.58887C15.4191 1.64829 17.6747 0.5 20.0742 0.5Z" stroke="url(#paint0_linear_0_81)"/>
</g>
<defs>
<filter id="filter0_i_0_81" x="0" y="-3" width="97" height="25" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-3"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.188235 0 0 0 0 0.121569 0 0 0 0.25 0"/>
<feBlend mode="multiply" in2="shape" result="effect1_innerShadow_0_81"/>
</filter>
<linearGradient id="paint0_linear_0_81" x1="48.5" y1="0" x2="48.5" y2="22" gradientUnits="userSpaceOnUse">
<stop offset="0.0852474" stop-color="#DA6813"/>
<stop offset="0.379274" stop-color="#FFBB89"/>
<stop offset="0.718536" stop-color="#E5690D"/>
<stop offset="0.927747" stop-color="#AE3607"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,25 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="97" height="22" viewBox="0 0 97 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="bg">
<g filter="url(#filter0_i_0_82)">
<path d="M13.6039 3.29463C15.1093 1.22467 17.5143 0 20.0738 0H93C95.2091 0 97 1.79086 97 4V22H0L13.6039 3.29463Z" fill="#FF5C00"/>
<path d="M13.6039 3.29463C15.1093 1.22467 17.5143 0 20.0738 0H93C95.2091 0 97 1.79086 97 4V22H0L13.6039 3.29463Z" fill="black" fill-opacity="0.2"/>
</g>
<path d="M20.0742 0.5H93C94.933 0.5 96.5 2.067 96.5 4V21.5H0.981445L14.0078 3.58887C15.4191 1.64829 17.6747 0.5 20.0742 0.5Z" stroke="url(#paint0_linear_0_82)"/>
</g>
<defs>
<filter id="filter0_i_0_82" x="0" y="-3" width="97" height="25" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-3"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.188235 0 0 0 0 0.121569 0 0 0 0.25 0"/>
<feBlend mode="multiply" in2="shape" result="effect1_innerShadow_0_82"/>
</filter>
<linearGradient id="paint0_linear_0_82" x1="56" y1="0" x2="56" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.05"/>
<stop offset="1" stop-opacity="0.3"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,29 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="97" height="22" viewBox="0 0 97 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="bg">
<g filter="url(#filter0_i_0_59)">
<path d="M13.6039 3.29463C15.1093 1.22467 17.5143 0 20.0738 0H93C95.2091 0 97 1.79086 97 4V22H0L13.6039 3.29463Z" fill="url(#paint0_linear_0_59)"/>
</g>
<path d="M20.0742 0.5H93C94.933 0.5 96.5 2.067 96.5 4V21.5H0.981445L14.0078 3.58887C15.4191 1.64829 17.6747 0.5 20.0742 0.5Z" stroke="url(#paint1_linear_0_59)"/>
</g>
<defs>
<filter id="filter0_i_0_59" x="0" y="-3" width="97" height="25" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-3"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.188235 0 0 0 0 0.121569 0 0 0 0.25 0"/>
<feBlend mode="multiply" in2="shape" result="effect1_innerShadow_0_59"/>
</filter>
<linearGradient id="paint0_linear_0_59" x1="2" y1="21.8948" x2="2" y2="0" gradientUnits="userSpaceOnUse">
<stop stop-color="#7C6C4C"/>
<stop offset="0.85" stop-color="#7C6C4C"/>
<stop offset="1" stop-color="#A6946F"/>
</linearGradient>
<linearGradient id="paint1_linear_0_59" x1="56" y1="0" x2="56" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.05"/>
<stop offset="1" stop-opacity="0.3"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,14 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="97" height="22" viewBox="0 0 97 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="stroke0_0_60" fill="white">
<path d="M13.6039 3.29463C15.1093 1.22467 17.5143 0 20.0738 0H93C95.2091 0 97 1.79086 97 4V22H0L13.6039 3.29463Z"/>
</mask>
<path id="bg" d="M97 22V4C97 1.79086 95.2091 0 93 0H20.0738C17.5143 0 15.1093 1.22467 13.6039 3.29463L0 22H97Z" stroke-width="2" stroke="url(#paint0_linear_0_60)" mask="url(#stroke0_0_60)"/>
<defs>
<linearGradient id="paint0_linear_0_60" x1="48.5" y1="0" x2="48.5" y2="22" gradientUnits="userSpaceOnUse">
<stop offset="0.0852474" stop-color="#DA6813"/>
<stop offset="0.379274" stop-color="#FFBB89"/>
<stop offset="0.718536" stop-color="#E5690D"/>
<stop offset="0.927747" stop-color="#AE3607"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 855 B

@@ -0,0 +1,24 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="97" height="22" viewBox="0 0 97 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="bg">
<g filter="url(#filter0_i_0_83)">
<path d="M13.6039 3.29463C15.1093 1.22467 17.5143 0 20.0738 0H93C95.2091 0 97 1.79086 97 4V22H0L13.6039 3.29463Z" fill="#C3B79E"/>
</g>
<path d="M20.0742 0.5H93C94.933 0.5 96.5 2.067 96.5 4V21.5H0.981445L14.0078 3.58887C15.4191 1.64829 17.6747 0.5 20.0742 0.5Z" stroke="url(#paint0_linear_0_83)"/>
</g>
<defs>
<filter id="filter0_i_0_83" x="0" y="-3" width="97" height="25" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="-3"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.188235 0 0 0 0 0.121569 0 0 0 0.25 0"/>
<feBlend mode="multiply" in2="shape" result="effect1_innerShadow_0_83"/>
</filter>
<linearGradient id="paint0_linear_0_83" x1="56" y1="0" x2="56" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.05"/>
<stop offset="1" stop-opacity="0.3"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319865">
<g id="Union">
<use xlink:href="#stroke0_0_10" fill="#FF5C00"/>
<mask id="stroke1_0_10" maskUnits="userSpaceOnUse" x="0.373037" y="0.369787" width="17" height="18" fill="black">
<rect fill="white" x="0.373037" y="0.369787" width="17" height="18"/>
<use xlink:href="#stroke0_0_10"/>
</mask>
<path d="M2 4.5C2 4.8109 2.09459 5.09972 2.25656 5.33923C1.9301 5.95091 1.68907 6.60669 1.54202 7.28832C1.30721 8.37679 1.3173 9.50407 1.57236 10.588C1.82743 11.6718 2.32093 12.6856 3.01645 13.5551C3.57402 14.252 4.25081 14.8424 5.01322 15.3C5.0045 15.3654 5 15.4322 5 15.5C5 16.3284 5.67157 17 6.5 17C7.32843 17 8 16.3284 8 15.5C8 14.6716 7.32843 14 6.5 14C6.10177 14 5.73978 14.1552 5.47121 14.4084C4.83335 14.0168 4.26638 13.5173 3.79687 12.9303C3.19412 12.1768 2.7667 11.2984 2.54563 10.3591C2.3246 9.41981 2.31585 8.44269 2.51928 7.49944C2.6365 6.95608 2.82255 6.43138 3.07174 5.93798C3.20745 5.97833 3.35119 6 3.5 6C4.32843 6 5 5.32843 5 4.5C5 4.24225 4.93499 3.99968 4.82047 3.7878C5.1619 3.51569 5.53092 3.27783 5.92231 3.07842C6.78221 2.64036 7.7293 2.39909 8.69398 2.37248C9.65852 2.3459 10.617 2.53388 11.4996 2.92377C12.0844 3.18213 12.6263 3.52477 13.1086 3.9392C13.0386 4.11262 13 4.30213 13 4.50065C13 5.32907 13.6716 6.00065 14.5 6.00065C15.3284 6.00065 16 5.32907 16 4.50065C16 3.67222 15.3284 3.00065 14.5 3.00065C14.2352 3.00065 13.9865 3.06923 13.7706 3.18961C13.2116 2.70755 12.583 2.30929 11.9041 2.00935C10.8856 1.5594 9.77952 1.34198 8.66651 1.37264C7.55342 1.40334 6.46075 1.68132 5.46856 2.18676C4.967 2.44227 4.4975 2.75275 4.06808 3.11131C3.89287 3.03956 3.70105 3 3.5 3C2.67157 3 2 3.67157 2 4.5Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_10)"/>
</g>
</g>
<defs>
<path id="stroke0_0_10" d="M2.25656 5.33923C2.09459 5.09972 2 4.8109 2 4.5C2 3.67157 2.67157 3 3.5 3C3.70105 3 3.89287 3.03956 4.06808 3.11131C4.4975 2.75275 4.967 2.44227 5.46856 2.18676C6.46075 1.68132 7.55342 1.40334 8.66651 1.37264C9.77952 1.34198 10.8856 1.5594 11.9041 2.00935C12.583 2.30929 13.2116 2.70755 13.7706 3.18961C13.9865 3.06923 14.2352 3.00065 14.5 3.00065C15.3284 3.00065 16 3.67222 16 4.50065C16 5.32907 15.3284 6.00065 14.5 6.00065C13.6716 6.00065 13 5.32907 13 4.50065C13 4.30213 13.0386 4.11262 13.1086 3.9392C12.6263 3.52477 12.0844 3.18213 11.4996 2.92377C10.617 2.53388 9.65852 2.3459 8.69398 2.37248C7.7293 2.39909 6.78221 2.64036 5.92231 3.07842C5.53092 3.27783 5.1619 3.51569 4.82047 3.7878C4.93499 3.99968 5 4.24225 5 4.5C5 5.32843 4.32843 6 3.5 6C3.35119 6 3.20745 5.97833 3.07174 5.93798C2.82255 6.43138 2.6365 6.95608 2.51928 7.49944C2.31585 8.44269 2.3246 9.41981 2.54563 10.3591C2.7667 11.2984 3.19412 12.1768 3.79687 12.9303C4.26638 13.5173 4.83335 14.0168 5.47121 14.4084C5.73978 14.1552 6.10177 14 6.5 14C7.32843 14 8 14.6716 8 15.5C8 16.3284 7.32843 17 6.5 17C5.67157 17 5 16.3284 5 15.5C5 15.4322 5.0045 15.3654 5.01322 15.3C4.25081 14.8424 3.57402 14.252 3.01645 13.5551C2.32093 12.6856 1.82743 11.6718 1.57236 10.588C1.3173 9.50407 1.30721 8.37679 1.54202 7.28832C1.68907 6.60669 1.9301 5.95091 2.25656 5.33923Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319865">
<g id="Union">
<use xlink:href="#stroke0_0_304" fill="#D4CBBD"/>
<mask id="stroke1_0_304" maskUnits="userSpaceOnUse" x="0.373037" y="0.369787" width="17" height="18" fill="black">
<rect fill="white" x="0.373037" y="0.369787" width="17" height="18"/>
<use xlink:href="#stroke0_0_304"/>
</mask>
<path d="M2 4.5C2 4.8109 2.09459 5.09972 2.25656 5.33923C1.9301 5.95091 1.68907 6.60669 1.54202 7.28832C1.30721 8.37679 1.3173 9.50407 1.57236 10.588C1.82743 11.6718 2.32093 12.6856 3.01645 13.5551C3.57402 14.252 4.25081 14.8424 5.01322 15.3C5.0045 15.3654 5 15.4322 5 15.5C5 16.3284 5.67157 17 6.5 17C7.32843 17 8 16.3284 8 15.5C8 14.6716 7.32843 14 6.5 14C6.10177 14 5.73978 14.1552 5.47121 14.4084C4.83335 14.0168 4.26638 13.5173 3.79687 12.9303C3.19412 12.1768 2.7667 11.2984 2.54563 10.3591C2.3246 9.41981 2.31585 8.44269 2.51928 7.49944C2.6365 6.95608 2.82255 6.43138 3.07174 5.93798C3.20745 5.97833 3.35119 6 3.5 6C4.32843 6 5 5.32843 5 4.5C5 4.24225 4.93499 3.99968 4.82047 3.7878C5.1619 3.51569 5.53092 3.27783 5.92231 3.07842C6.78221 2.64036 7.7293 2.39909 8.69398 2.37248C9.65852 2.3459 10.617 2.53388 11.4996 2.92377C12.0844 3.18213 12.6263 3.52477 13.1086 3.9392C13.0386 4.11262 13 4.30213 13 4.50065C13 5.32907 13.6716 6.00065 14.5 6.00065C15.3284 6.00065 16 5.32907 16 4.50065C16 3.67222 15.3284 3.00065 14.5 3.00065C14.2352 3.00065 13.9865 3.06923 13.7706 3.18961C13.2116 2.70755 12.583 2.30929 11.9041 2.00935C10.8856 1.5594 9.77952 1.34198 8.66651 1.37264C7.55342 1.40334 6.46075 1.68132 5.46856 2.18676C4.967 2.44227 4.4975 2.75275 4.06808 3.11131C3.89287 3.03956 3.70105 3 3.5 3C2.67157 3 2 3.67157 2 4.5Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_304)"/>
</g>
</g>
<defs>
<path id="stroke0_0_304" d="M2.25656 5.33923C2.09459 5.09972 2 4.8109 2 4.5C2 3.67157 2.67157 3 3.5 3C3.70105 3 3.89287 3.03956 4.06808 3.11131C4.4975 2.75275 4.967 2.44227 5.46856 2.18676C6.46075 1.68132 7.55342 1.40334 8.66651 1.37264C9.77952 1.34198 10.8856 1.5594 11.9041 2.00935C12.583 2.30929 13.2116 2.70755 13.7706 3.18961C13.9865 3.06923 14.2352 3.00065 14.5 3.00065C15.3284 3.00065 16 3.67222 16 4.50065C16 5.32907 15.3284 6.00065 14.5 6.00065C13.6716 6.00065 13 5.32907 13 4.50065C13 4.30213 13.0386 4.11262 13.1086 3.9392C12.6263 3.52477 12.0844 3.18213 11.4996 2.92377C10.617 2.53388 9.65852 2.3459 8.69398 2.37248C7.7293 2.39909 6.78221 2.64036 5.92231 3.07842C5.53092 3.27783 5.1619 3.51569 4.82047 3.7878C4.93499 3.99968 5 4.24225 5 4.5C5 5.32843 4.32843 6 3.5 6C3.35119 6 3.20745 5.97833 3.07174 5.93798C2.82255 6.43138 2.6365 6.95608 2.51928 7.49944C2.31585 8.44269 2.3246 9.41981 2.54563 10.3591C2.7667 11.2984 3.19412 12.1768 3.79687 12.9303C4.26638 13.5173 4.83335 14.0168 5.47121 14.4084C5.73978 14.1552 6.10177 14 6.5 14C7.32843 14 8 14.6716 8 15.5C8 16.3284 7.32843 17 6.5 17C5.67157 17 5 16.3284 5 15.5C5 15.4322 5.0045 15.3654 5.01322 15.3C4.25081 14.8424 3.57402 14.252 3.01645 13.5551C2.32093 12.6856 1.82743 11.6718 1.57236 10.588C1.3173 9.50407 1.30721 8.37679 1.54202 7.28832C1.68907 6.60669 1.9301 5.95091 2.25656 5.33923Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319867">
<g id="Union">
<use xlink:href="#stroke0_0_10" fill="#FF5C00"/>
<mask id="stroke1_0_10" maskUnits="userSpaceOnUse" x="0.5" y="2" width="17" height="14" fill="black">
<rect fill="white" x="0.5" y="2" width="17" height="14"/>
<use xlink:href="#stroke0_0_10"/>
</mask>
<path d="M9.95101 4.11831C12.5065 4.54155 14.5482 6.06008 15.2435 8.01967C15.9561 8.13601 16.5 8.75449 16.5 9.5C16.5 10.2455 15.9561 10.864 15.2435 10.9803C14.4208 13.2989 11.7133 15 8.5 15C4.63401 15 1.5 12.5376 1.5 9.5C1.5 6.85332 3.8793 4.64328 7.04899 4.11831C7.21783 3.47475 7.80347 3 8.5 3C9.19653 3 9.78218 3.47475 9.95101 4.11831ZM9.86843 5.11524C12.0436 5.4914 13.6367 6.7438 14.2376 8.20795C13.7961 8.46903 13.5 8.94994 13.5 9.5C13.5 10.0501 13.7961 10.531 14.2376 10.7921C13.5168 12.5484 11.3683 14 8.5 14C4.9482 14 2.5 11.7741 2.5 9.5C2.5 7.53144 4.3346 5.59893 7.13157 5.11524C7.36645 5.63686 7.89082 6 8.5 6C9.10918 6 9.63355 5.63686 9.86843 5.11524ZM10 9H9V8H8V9H7V10H8V11H9V10H10V9Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_10)"/>
</g>
</g>
<defs>
<path id="stroke0_0_10" d="M15.2435 8.01967C14.5482 6.06008 12.5065 4.54155 9.95101 4.11831C9.78218 3.47475 9.19653 3 8.5 3C7.80347 3 7.21783 3.47475 7.04899 4.11831C3.8793 4.64328 1.5 6.85332 1.5 9.5C1.5 12.5376 4.63401 15 8.5 15C11.7133 15 14.4208 13.2989 15.2435 10.9803C15.9561 10.864 16.5 10.2455 16.5 9.5C16.5 8.75449 15.9561 8.13601 15.2435 8.01967ZM14.2376 8.20795C13.6367 6.7438 12.0436 5.4914 9.86843 5.11524C9.63355 5.63686 9.10918 6 8.5 6C7.89082 6 7.36645 5.63686 7.13157 5.11524C4.3346 5.59893 2.5 7.53144 2.5 9.5C2.5 11.7741 4.9482 14 8.5 14C11.3683 14 13.5168 12.5484 14.2376 10.7921C13.7961 10.531 13.5 10.0501 13.5 9.5C13.5 8.94994 13.7961 8.46903 14.2376 8.20795ZM9 9H10V10H9V11H8V10H7V9H8V8H9V9Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319867">
<g id="Union">
<use xlink:href="#stroke0_0_297" fill="#D4CBBD"/>
<mask id="stroke1_0_297" maskUnits="userSpaceOnUse" x="0.5" y="2" width="17" height="14" fill="black">
<rect fill="white" x="0.5" y="2" width="17" height="14"/>
<use xlink:href="#stroke0_0_297"/>
</mask>
<path d="M9.95101 4.11831C12.5065 4.54155 14.5482 6.06008 15.2435 8.01967C15.9561 8.13601 16.5 8.75449 16.5 9.5C16.5 10.2455 15.9561 10.864 15.2435 10.9803C14.4208 13.2989 11.7133 15 8.5 15C4.63401 15 1.5 12.5376 1.5 9.5C1.5 6.85332 3.8793 4.64328 7.04899 4.11831C7.21783 3.47475 7.80347 3 8.5 3C9.19653 3 9.78218 3.47475 9.95101 4.11831ZM9.86843 5.11524C12.0436 5.4914 13.6367 6.7438 14.2376 8.20795C13.7961 8.46903 13.5 8.94994 13.5 9.5C13.5 10.0501 13.7961 10.531 14.2376 10.7921C13.5168 12.5484 11.3683 14 8.5 14C4.9482 14 2.5 11.7741 2.5 9.5C2.5 7.53144 4.3346 5.59893 7.13157 5.11524C7.36645 5.63686 7.89082 6 8.5 6C9.10918 6 9.63355 5.63686 9.86843 5.11524ZM10 9H9V8H8V9H7V10H8V11H9V10H10V9Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_297)"/>
</g>
</g>
<defs>
<path id="stroke0_0_297" d="M15.2435 8.01967C14.5482 6.06008 12.5065 4.54155 9.95101 4.11831C9.78218 3.47475 9.19653 3 8.5 3C7.80347 3 7.21783 3.47475 7.04899 4.11831C3.8793 4.64328 1.5 6.85332 1.5 9.5C1.5 12.5376 4.63401 15 8.5 15C11.7133 15 14.4208 13.2989 15.2435 10.9803C15.9561 10.864 16.5 10.2455 16.5 9.5C16.5 8.75449 15.9561 8.13601 15.2435 8.01967ZM14.2376 8.20795C13.6367 6.7438 12.0436 5.4914 9.86843 5.11524C9.63355 5.63686 9.10918 6 8.5 6C7.89082 6 7.36645 5.63686 7.13157 5.11524C4.3346 5.59893 2.5 7.53144 2.5 9.5C2.5 11.7741 4.9482 14 8.5 14C11.3683 14 13.5168 12.5484 14.2376 10.7921C13.7961 10.531 13.5 10.0501 13.5 9.5C13.5 8.94994 13.7961 8.46903 14.2376 8.20795ZM9 9H10V10H9V11H8V10H7V9H8V8H9V9Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,18 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319869" clip-path="url(#clip0_0_10)">
<g id="Union">
<use xlink:href="#stroke0_0_10" fill="#FF5C00"/>
<mask id="stroke1_0_10" maskUnits="userSpaceOnUse" x="-2.31371" y="-2.31371" width="22.6274" height="22.6274" fill="black">
<rect fill="white" x="-2.31371" y="-2.31371" width="22.6274" height="22.6274"/>
<use xlink:href="#stroke0_0_10"/>
</mask>
<path d="M13.5817 5.12543L10.3539 8.35325C10.6202 8.91081 10.5225 9.59884 10.0607 10.0607C9.47487 10.6464 8.52513 10.6464 7.93934 10.0607C7.35355 9.47487 7.35355 8.52513 7.93934 7.93934C8.40116 7.47752 9.08919 7.37979 9.64675 7.64614L12.8746 4.41833C10.5172 2.42026 6.98144 2.53327 4.75736 4.75736C2.41421 7.1005 2.41421 10.8995 4.75736 13.2426C7.1005 15.5858 10.8995 15.5858 13.2426 13.2426C15.4667 11.0186 15.5797 7.48282 13.5817 5.12543ZM4.05025 4.05025C1.31658 6.78392 1.31658 11.2161 4.05025 13.9497C6.78392 16.6834 11.2161 16.6834 13.9497 13.9497C16.6834 11.2161 16.6834 6.78392 13.9497 4.05025C11.2161 1.31658 6.78392 1.31658 4.05025 4.05025Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_10)"/>
</g>
</g>
<defs>
<clipPath id="clip0_0_10">
<rect width="18" height="18" fill="white"/>
</clipPath>
<path id="stroke0_0_10" d="M13.5817 5.12543L10.3539 8.35325C10.6202 8.91081 10.5225 9.59884 10.0607 10.0607C9.47487 10.6464 8.52513 10.6464 7.93934 10.0607C7.35355 9.47487 7.35355 8.52513 7.93934 7.93934C8.40116 7.47752 9.08919 7.37979 9.64675 7.64614L12.8746 4.41833C10.5172 2.42026 6.98144 2.53327 4.75736 4.75736C2.41421 7.1005 2.41421 10.8995 4.75736 13.2426C7.1005 15.5858 10.8995 15.5858 13.2426 13.2426C15.4667 11.0186 15.5797 7.48282 13.5817 5.12543ZM4.05025 13.9497C1.31658 11.2161 1.31658 6.78392 4.05025 4.05025C6.78392 1.31658 11.2161 1.31658 13.9497 4.05025C16.6834 6.78392 16.6834 11.2161 13.9497 13.9497C11.2161 16.6834 6.78392 16.6834 4.05025 13.9497Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,18 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319869" clip-path="url(#clip0_0_332)">
<g id="Union">
<use xlink:href="#stroke0_0_332" fill="#D4CBBD"/>
<mask id="stroke1_0_332" maskUnits="userSpaceOnUse" x="-2.31371" y="-2.31371" width="22.6274" height="22.6274" fill="black">
<rect fill="white" x="-2.31371" y="-2.31371" width="22.6274" height="22.6274"/>
<use xlink:href="#stroke0_0_332"/>
</mask>
<path d="M13.5817 5.12543L10.3539 8.35325C10.6202 8.91081 10.5225 9.59884 10.0607 10.0607C9.47487 10.6464 8.52513 10.6464 7.93934 10.0607C7.35355 9.47487 7.35355 8.52513 7.93934 7.93934C8.40116 7.47752 9.08919 7.37979 9.64675 7.64614L12.8746 4.41833C10.5172 2.42026 6.98144 2.53327 4.75736 4.75736C2.41421 7.1005 2.41421 10.8995 4.75736 13.2426C7.1005 15.5858 10.8995 15.5858 13.2426 13.2426C15.4667 11.0186 15.5797 7.48282 13.5817 5.12543ZM4.05025 4.05025C1.31658 6.78392 1.31658 11.2161 4.05025 13.9497C6.78392 16.6834 11.2161 16.6834 13.9497 13.9497C16.6834 11.2161 16.6834 6.78392 13.9497 4.05025C11.2161 1.31658 6.78392 1.31658 4.05025 4.05025Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_332)"/>
</g>
</g>
<defs>
<clipPath id="clip0_0_332">
<rect width="18" height="18" fill="white"/>
</clipPath>
<path id="stroke0_0_332" d="M13.5817 5.12543L10.3539 8.35325C10.6202 8.91081 10.5225 9.59884 10.0607 10.0607C9.47487 10.6464 8.52513 10.6464 7.93934 10.0607C7.35355 9.47487 7.35355 8.52513 7.93934 7.93934C8.40116 7.47752 9.08919 7.37979 9.64675 7.64614L12.8746 4.41833C10.5172 2.42026 6.98144 2.53327 4.75736 4.75736C2.41421 7.1005 2.41421 10.8995 4.75736 13.2426C7.1005 15.5858 10.8995 15.5858 13.2426 13.2426C15.4667 11.0186 15.5797 7.48282 13.5817 5.12543ZM4.05025 13.9497C1.31658 11.2161 1.31658 6.78392 4.05025 4.05025C6.78392 1.31658 11.2161 1.31658 13.9497 4.05025C16.6834 6.78392 16.6834 11.2161 13.9497 13.9497C11.2161 16.6834 6.78392 16.6834 4.05025 13.9497Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319878">
<g id="Union">
<use xlink:href="#stroke0_0_10" fill="#FF5C00"/>
<mask id="stroke1_0_10" maskUnits="userSpaceOnUse" x="2" y="2" width="14" height="14" fill="black">
<rect fill="white" x="2" y="2" width="14" height="14"/>
<use xlink:href="#stroke0_0_10"/>
</mask>
<path d="M4 4L4 3H5V4H13V3H14V4H15V5H14L14 13H15V14H14V15H13V14H5V15H4L4 14H3V13H4L4 5H3V4H4ZM13 11.4904V13H11.4904L13 11.4904ZM13 10.0762L10.0762 13H7.41421L13 7.41422L13 10.0762ZM5 13H6L13 6.00001V5H11.4142L5 11.4142V13ZM5 10L9.99995 5H7.41419L5 7.41421L5 10ZM5.99998 5H5V5.99999L5.99998 5Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_10)"/>
</g>
</g>
<defs>
<path id="stroke0_0_10" d="M4 3L4 4H3V5H4L4 13H3V14H4L4 15H5V14H13V15H14V14H15V13H14L14 5H15V4H14V3H13V4H5V3H4ZM13 13V11.4904L11.4904 13H13ZM10.0762 13L13 10.0762L13 7.41422L7.41421 13H10.0762ZM6 13H5V11.4142L11.4142 5H13V6.00001L6 13ZM9.99995 5L5 10L5 7.41421L7.41419 5H9.99995ZM5.99998 5H5V5.99999L5.99998 5Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319878">
<g id="Union">
<use xlink:href="#stroke0_0_276" fill="#D9CFBB"/>
<mask id="stroke1_0_276" maskUnits="userSpaceOnUse" x="2" y="2" width="14" height="14" fill="black">
<rect fill="white" x="2" y="2" width="14" height="14"/>
<use xlink:href="#stroke0_0_276"/>
</mask>
<path d="M4 4L4 3H5V4H13V3H14V4H15V5H14L14 13H15V14H14V15H13V14H5V15H4L4 14H3V13H4L4 5H3V4H4ZM13 11.4904V13H11.4904L13 11.4904ZM13 10.0762L10.0762 13H7.41421L13 7.41422L13 10.0762ZM5 13H6L13 6.00001V5H11.4142L5 11.4142V13ZM5 10L9.99995 5H7.41419L5 7.41421L5 10ZM5.99998 5H5V5.99999L5.99998 5Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_276)"/>
</g>
</g>
<defs>
<path id="stroke0_0_276" d="M4 3L4 4H3V5H4L4 13H3V14H4L4 15H5V14H13V15H14V14H15V13H14L14 5H15V4H14V3H13V4H5V3H4ZM13 13V11.4904L11.4904 13H13ZM10.0762 13L13 10.0762L13 7.41422L7.41421 13H10.0762ZM6 13H5V11.4142L11.4142 5H13V6.00001L6 13ZM9.99995 5L5 10L5 7.41421L7.41419 5H9.99995ZM5.99998 5H5V5.99999L5.99998 5Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319865">
<g id="Union">
<use xlink:href="#stroke0_0_10" fill="#FF5C00"/>
<mask id="stroke1_0_10" maskUnits="userSpaceOnUse" x="0.585786" y="-0.849242" width="18.3848" height="18.3848" fill="black">
<rect fill="white" x="0.585786" y="-0.849242" width="18.3848" height="18.3848"/>
<use xlink:href="#stroke0_0_10"/>
</mask>
<path d="M5.18198 15.0607C5.6438 14.5988 5.74153 13.9108 5.47518 13.3532L14.082 4.74644C14.6396 5.01279 15.3276 4.91506 15.7894 4.45324C16.3752 3.86745 16.3752 2.9177 15.7894 2.33192C15.2036 1.74613 14.2539 1.74613 13.6681 2.33192C13.2063 2.79374 13.1085 3.48177 13.3749 4.03933L4.76807 12.6461C4.21051 12.3798 3.52248 12.4775 3.06066 12.9393C2.47487 13.5251 2.47487 14.4749 3.06066 15.0607C3.64645 15.6464 4.59619 15.6464 5.18198 15.0607Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_10)"/>
</g>
</g>
<defs>
<path id="stroke0_0_10" d="M5.47518 13.3532C5.74153 13.9108 5.6438 14.5988 5.18198 15.0607C4.59619 15.6464 3.64645 15.6464 3.06066 15.0607C2.47487 14.4749 2.47487 13.5251 3.06066 12.9393C3.52248 12.4775 4.21051 12.3798 4.76807 12.6461L13.3749 4.03933C13.1085 3.48177 13.2063 2.79374 13.6681 2.33192C14.2539 1.74613 15.2036 1.74613 15.7894 2.33192C16.3752 2.9177 16.3752 3.86745 15.7894 4.45324C15.3276 4.91506 14.6396 5.01279 14.082 4.74644L5.47518 13.3532Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319865">
<g id="Union">
<use xlink:href="#stroke0_0_337" fill="#D4CBBD"/>
<mask id="stroke1_0_337" maskUnits="userSpaceOnUse" x="0.585786" y="-0.849242" width="18.3848" height="18.3848" fill="black">
<rect fill="white" x="0.585786" y="-0.849242" width="18.3848" height="18.3848"/>
<use xlink:href="#stroke0_0_337"/>
</mask>
<path d="M5.18198 15.0607C5.6438 14.5988 5.74153 13.9108 5.47518 13.3532L14.082 4.74644C14.6396 5.01279 15.3276 4.91506 15.7894 4.45324C16.3752 3.86745 16.3752 2.9177 15.7894 2.33192C15.2036 1.74613 14.2539 1.74613 13.6681 2.33192C13.2063 2.79374 13.1085 3.48177 13.3749 4.03933L4.76807 12.6461C4.21051 12.3798 3.52248 12.4775 3.06066 12.9393C2.47487 13.5251 2.47487 14.4749 3.06066 15.0607C3.64645 15.6464 4.59619 15.6464 5.18198 15.0607Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_337)"/>
</g>
</g>
<defs>
<path id="stroke0_0_337" d="M5.47518 13.3532C5.74153 13.9108 5.6438 14.5988 5.18198 15.0607C4.59619 15.6464 3.64645 15.6464 3.06066 15.0607C2.47487 14.4749 2.47487 13.5251 3.06066 12.9393C3.52248 12.4775 4.21051 12.3798 4.76807 12.6461L13.3749 4.03933C13.1085 3.48177 13.2063 2.79374 13.6681 2.33192C14.2539 1.74613 15.2036 1.74613 15.7894 2.33192C16.3752 2.9177 16.3752 3.86745 15.7894 4.45324C15.3276 4.91506 14.6396 5.01279 14.082 4.74644L5.47518 13.3532Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,9 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame 427319878">
<path id="Rectangle 7565" d="M1.5 1.5H9.5V7.5H1.5V1.5Z" stroke="#FF5C00"/>
<path id="Ellipse 438" d="M12.5 8C12.5 9.38071 11.3807 10.5 10 10.5C8.61929 10.5 7.5 9.38071 7.5 8C7.5 6.61929 8.61929 5.5 10 5.5C11.3807 5.5 12.5 6.61929 12.5 8Z" stroke="#FF5C00"/>
<path id="Union" d="M5.66016 14.2334C6.0848 14.4093 6.54037 14.5 7 14.5H7.56537L7.08398 15.2227L7.91602 15.7773L8.91602 14.2773L9.10059 14L8.91602 13.7227L7.91602 12.2227L7.08398 12.7773L7.56537 13.5H7C6.67169 13.5 6.34628 13.4352 6.04297 13.3096C5.73978 13.1839 5.46449 12.9996 5.23242 12.7676C5.00036 12.5355 4.81606 12.2602 4.69043 11.957C4.56479 11.6537 4.5 11.3283 4.5 11H3.5C3.5 11.4596 3.59071 11.9152 3.7666 12.3398C3.94249 12.7643 4.2005 13.1497 4.52539 13.4746C4.85028 13.7995 5.23569 14.0575 5.66016 14.2334Z" fill-rule="evenodd" clip-rule="evenodd" fill="#FF5C00"/>
<path id="Union_2" d="M0 0H1H10H11V1V4.12602C12.7252 4.57006 14 6.13616 14 8C14 8.74455 13.7966 9.4416 13.4423 10.0386C13.6245 10.0131 13.8107 10 14 10C16.2091 10 18 11.7909 18 14C18 16.2091 16.2091 18 14 18C11.9331 18 10.2324 16.4324 10.0219 14.4213L8.19338 17.1641L5.69722 15.5L5.80495 15.3384C5.62607 15.2891 5.45005 15.2288 5.27792 15.1575C4.73196 14.9313 4.23588 14.5998 3.81802 14.182C3.40016 13.7641 3.06869 13.268 2.84254 12.7221C2.6164 12.1761 2.5 11.5909 2.5 11V10H5.5V11C5.5 11.197 5.5388 11.392 5.61418 11.574C5.68956 11.756 5.80005 11.9214 5.93934 12.0607C5.99624 12.1176 6.05749 12.1697 6.12245 12.2165L7.62208 11.2168C6.89569 10.6789 6.35788 9.90086 6.12602 9H1H0V8V1V0ZM8.85947 11.835L9.74808 13.1679L10.0219 13.5787C10.0833 12.9927 10.2711 12.4443 10.5577 11.9614C10.3755 11.9869 10.1893 12 10 12C9.60372 12 9.22091 11.9424 8.85947 11.835ZM10 5V4V2V1H9H2H1V2V7V8H2H6H7C7 9.65685 8.34315 11 10 11C11.6569 11 13 9.65685 13 8C13 6.34315 11.6569 5 10 5ZM8.26756 9C8.61337 9.5978 9.25972 10 10 10C11.1046 10 12 9.10457 12 8C12 7.25972 11.5978 6.61337 11 6.26756C10.7058 6.09739 10.3643 6 10 6V7V8H9H8C8 8.36429 8.09739 8.70583 8.26756 9ZM14 17C15.6569 17 17 15.6569 17 14C17 12.3431 15.6569 11 14 11C12.3431 11 11 12.3431 11 14C11 15.6569 12.3431 17 14 17ZM7.56574 13.5L7.08397 12.7774L7.91603 12.2227L9.10093 14L7.91603 15.7773L7.08397 15.2227L7.56574 14.5H7C6.79793 14.5 6.59672 14.4825 6.3986 14.4479C6.14608 14.4039 5.89856 14.3321 5.66061 14.2336C5.23597 14.0577 4.85013 13.7999 4.52513 13.4749C4.4845 13.4342 4.44492 13.3927 4.40643 13.3502C4.13695 13.0528 3.92033 12.711 3.76642 12.3394C3.72036 12.2282 3.68016 12.1149 3.6459 12C3.54933 11.6761 3.5 11.3393 3.5 11H4.5C4.5 11.3283 4.56466 11.6534 4.6903 11.9567C4.81594 12.26 5.00009 12.5356 5.23223 12.7678C5.46438 12.9999 5.73998 13.1841 6.04329 13.3097C6.12621 13.344 6.21076 13.3738 6.29655 13.399C6.52459 13.4659 6.76145 13.5 7 13.5H7.56574Z" fill-rule="evenodd" clip-rule="evenodd" fill="black" fill-opacity="0.3"/>
<path id="Ellipse 440" d="M16.5 14C16.5 15.3807 15.3807 16.5 14 16.5C12.6193 16.5 11.5 15.3807 11.5 14C11.5 12.6193 12.6193 11.5 14 11.5C15.3807 11.5 16.5 12.6193 16.5 14Z" stroke="#FF5C00"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1,9 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame 427319878">
<path id="Rectangle 7565" d="M1.5 1.5H9.5V7.5H1.5V1.5Z" stroke="#D4CBBD"/>
<path id="Ellipse 438" d="M12.5 8C12.5 9.38071 11.3807 10.5 10 10.5C8.61929 10.5 7.5 9.38071 7.5 8C7.5 6.61929 8.61929 5.5 10 5.5C11.3807 5.5 12.5 6.61929 12.5 8Z" stroke="#D4CBBD"/>
<path id="Union" d="M5.66016 14.2334C6.0848 14.4093 6.54037 14.5 7 14.5H7.56537L7.08398 15.2227L7.91602 15.7773L8.91602 14.2773L9.10059 14L8.91602 13.7227L7.91602 12.2227L7.08398 12.7773L7.56537 13.5H7C6.67169 13.5 6.34628 13.4352 6.04297 13.3096C5.73978 13.1839 5.46449 12.9996 5.23242 12.7676C5.00036 12.5355 4.81606 12.2602 4.69043 11.957C4.56479 11.6537 4.5 11.3283 4.5 11H3.5C3.5 11.4596 3.59071 11.9152 3.7666 12.3398C3.94249 12.7643 4.2005 13.1497 4.52539 13.4746C4.85028 13.7995 5.23569 14.0575 5.66016 14.2334Z" fill-rule="evenodd" clip-rule="evenodd" fill="#D4CBBD"/>
<path id="Union_2" d="M0 0H1H10H11V1V4.12602C12.7252 4.57006 14 6.13616 14 8C14 8.74455 13.7966 9.4416 13.4423 10.0386C13.6245 10.0131 13.8107 10 14 10C16.2091 10 18 11.7909 18 14C18 16.2091 16.2091 18 14 18C11.9331 18 10.2324 16.4324 10.0219 14.4213L8.19338 17.1641L5.69722 15.5L5.80495 15.3384C5.62607 15.2891 5.45005 15.2288 5.27792 15.1575C4.73196 14.9313 4.23588 14.5998 3.81802 14.182C3.40016 13.7641 3.06869 13.268 2.84254 12.7221C2.6164 12.1761 2.5 11.5909 2.5 11V10H5.5V11C5.5 11.197 5.5388 11.392 5.61418 11.574C5.68956 11.756 5.80005 11.9214 5.93934 12.0607C5.99624 12.1176 6.05749 12.1697 6.12245 12.2165L7.62208 11.2168C6.89569 10.6789 6.35788 9.90086 6.12602 9H1H0V8V1V0ZM8.85947 11.835L9.74808 13.1679L10.0219 13.5787C10.0833 12.9927 10.2711 12.4443 10.5577 11.9614C10.3755 11.9869 10.1893 12 10 12C9.60372 12 9.22091 11.9424 8.85947 11.835ZM10 5V4V2V1H9H2H1V2V7V8H2H6H7C7 9.65685 8.34315 11 10 11C11.6569 11 13 9.65685 13 8C13 6.34315 11.6569 5 10 5ZM8.26756 9C8.61337 9.5978 9.25972 10 10 10C11.1046 10 12 9.10457 12 8C12 7.25972 11.5978 6.61337 11 6.26756C10.7058 6.09739 10.3643 6 10 6V7V8H9H8C8 8.36429 8.09739 8.70583 8.26756 9ZM14 17C15.6569 17 17 15.6569 17 14C17 12.3431 15.6569 11 14 11C12.3431 11 11 12.3431 11 14C11 15.6569 12.3431 17 14 17ZM7.56574 13.5L7.08397 12.7774L7.91603 12.2227L9.10093 14L7.91603 15.7773L7.08397 15.2227L7.56574 14.5H7C6.79793 14.5 6.59672 14.4825 6.3986 14.4479C6.14608 14.4039 5.89856 14.3321 5.66061 14.2336C5.23597 14.0577 4.85013 13.7999 4.52513 13.4749C4.4845 13.4342 4.44492 13.3927 4.40643 13.3502C4.13695 13.0528 3.92033 12.711 3.76642 12.3394C3.72036 12.2282 3.68016 12.1149 3.6459 12C3.54933 11.6761 3.5 11.3393 3.5 11H4.5C4.5 11.3283 4.56466 11.6534 4.6903 11.9567C4.81594 12.26 5.00009 12.5356 5.23223 12.7678C5.46438 12.9999 5.73998 13.1841 6.04329 13.3097C6.12621 13.344 6.21076 13.3738 6.29655 13.399C6.52459 13.4659 6.76145 13.5 7 13.5H7.56574Z" fill-rule="evenodd" clip-rule="evenodd" fill="black" fill-opacity="0.3"/>
<path id="Ellipse 440" d="M16.5 14C16.5 15.3807 15.3807 16.5 14 16.5C12.6193 16.5 11.5 15.3807 11.5 14C11.5 12.6193 12.6193 11.5 14 11.5C15.3807 11.5 16.5 12.6193 16.5 14Z" stroke="#D4CBBD"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319865">
<g id="Union">
<use xlink:href="#stroke0_0_10" fill="#FF5C00"/>
<mask id="stroke1_0_10" maskUnits="userSpaceOnUse" x="0.62132" y="1.5" width="17" height="15" fill="black">
<rect fill="white" x="0.62132" y="1.5" width="17" height="15"/>
<use xlink:href="#stroke0_0_10"/>
</mask>
<path d="M12.182 5.06066C12.282 4.9606 12.365 4.84993 12.4309 4.73226C14.2147 5.33008 15.5 7.01494 15.5 9C15.5 10.9851 14.2147 12.6699 12.4309 13.2677C12.365 13.1501 12.282 13.0394 12.182 12.9393C11.5962 12.3536 10.6464 12.3536 10.0607 12.9393C9.89691 13.1031 9.77894 13.2953 9.70674 13.5H4.5359C4.4637 13.2953 4.34573 13.1031 4.18198 12.9393C3.59619 12.3536 2.64645 12.3536 2.06066 12.9393C1.47487 13.5251 1.47487 14.4749 2.06066 15.0607C2.64645 15.6464 3.59619 15.6464 4.18198 15.0607C4.34573 14.8969 4.4637 14.7047 4.5359 14.5H9.70674C9.77894 14.7047 9.89691 14.8969 10.0607 15.0607C10.6464 15.6464 11.5962 15.6464 12.182 15.0607C12.4074 14.8353 12.5461 14.556 12.598 14.2643C14.8562 13.5797 16.5 11.4818 16.5 9C16.5 6.5182 14.8562 4.42034 12.598 3.73574C12.5461 3.44403 12.4074 3.16474 12.182 2.93934C11.5962 2.35355 10.6464 2.35355 10.0607 2.93934C9.47487 3.52513 9.47487 4.47487 10.0607 5.06066C10.6464 5.64645 11.5962 5.64645 12.182 5.06066Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_10)"/>
</g>
</g>
<defs>
<path id="stroke0_0_10" d="M12.4309 4.73226C12.365 4.84993 12.282 4.9606 12.182 5.06066C11.5962 5.64645 10.6464 5.64645 10.0607 5.06066C9.47487 4.47487 9.47487 3.52513 10.0607 2.93934C10.6464 2.35355 11.5962 2.35355 12.182 2.93934C12.4074 3.16474 12.5461 3.44403 12.598 3.73574C14.8562 4.42034 16.5 6.5182 16.5 9C16.5 11.4818 14.8562 13.5797 12.598 14.2643C12.5461 14.556 12.4074 14.8353 12.182 15.0607C11.5962 15.6464 10.6464 15.6464 10.0607 15.0607C9.89691 14.8969 9.77894 14.7047 9.70674 14.5H4.5359C4.4637 14.7047 4.34573 14.8969 4.18198 15.0607C3.59619 15.6464 2.64645 15.6464 2.06066 15.0607C1.47487 14.4749 1.47487 13.5251 2.06066 12.9393C2.64645 12.3536 3.59619 12.3536 4.18198 12.9393C4.34573 13.1031 4.4637 13.2953 4.5359 13.5H9.70674C9.77894 13.2953 9.89691 13.1031 10.0607 12.9393C10.6464 12.3536 11.5962 12.3536 12.182 12.9393C12.282 13.0394 12.365 13.1501 12.4309 13.2677C14.2147 12.6699 15.5 10.9851 15.5 9C15.5 7.01494 14.2147 5.33008 12.4309 4.73226Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319865">
<g id="Union">
<use xlink:href="#stroke0_0_286" fill="#D4CBBD"/>
<mask id="stroke1_0_286" maskUnits="userSpaceOnUse" x="0.62132" y="1.5" width="17" height="15" fill="black">
<rect fill="white" x="0.62132" y="1.5" width="17" height="15"/>
<use xlink:href="#stroke0_0_286"/>
</mask>
<path d="M12.182 5.06066C12.282 4.9606 12.365 4.84993 12.4309 4.73226C14.2147 5.33008 15.5 7.01494 15.5 9C15.5 10.9851 14.2147 12.6699 12.4309 13.2677C12.365 13.1501 12.282 13.0394 12.182 12.9393C11.5962 12.3536 10.6464 12.3536 10.0607 12.9393C9.89691 13.1031 9.77894 13.2953 9.70674 13.5H4.5359C4.4637 13.2953 4.34573 13.1031 4.18198 12.9393C3.59619 12.3536 2.64645 12.3536 2.06066 12.9393C1.47487 13.5251 1.47487 14.4749 2.06066 15.0607C2.64645 15.6464 3.59619 15.6464 4.18198 15.0607C4.34573 14.8969 4.4637 14.7047 4.5359 14.5H9.70674C9.77894 14.7047 9.89691 14.8969 10.0607 15.0607C10.6464 15.6464 11.5962 15.6464 12.182 15.0607C12.4074 14.8353 12.5461 14.556 12.598 14.2643C14.8562 13.5797 16.5 11.4818 16.5 9C16.5 6.5182 14.8562 4.42034 12.598 3.73574C12.5461 3.44403 12.4074 3.16474 12.182 2.93934C11.5962 2.35355 10.6464 2.35355 10.0607 2.93934C9.47487 3.52513 9.47487 4.47487 10.0607 5.06066C10.6464 5.64645 11.5962 5.64645 12.182 5.06066Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_286)"/>
</g>
</g>
<defs>
<path id="stroke0_0_286" d="M12.4309 4.73226C12.365 4.84993 12.282 4.9606 12.182 5.06066C11.5962 5.64645 10.6464 5.64645 10.0607 5.06066C9.47487 4.47487 9.47487 3.52513 10.0607 2.93934C10.6464 2.35355 11.5962 2.35355 12.182 2.93934C12.4074 3.16474 12.5461 3.44403 12.598 3.73574C14.8562 4.42034 16.5 6.5182 16.5 9C16.5 11.4818 14.8562 13.5797 12.598 14.2643C12.5461 14.556 12.4074 14.8353 12.182 15.0607C11.5962 15.6464 10.6464 15.6464 10.0607 15.0607C9.89691 14.8969 9.77894 14.7047 9.70674 14.5H4.5359C4.4637 14.7047 4.34573 14.8969 4.18198 15.0607C3.59619 15.6464 2.64645 15.6464 2.06066 15.0607C1.47487 14.4749 1.47487 13.5251 2.06066 12.9393C2.64645 12.3536 3.59619 12.3536 4.18198 12.9393C4.34573 13.1031 4.4637 13.2953 4.5359 13.5H9.70674C9.77894 13.2953 9.89691 13.1031 10.0607 12.9393C10.6464 12.3536 11.5962 12.3536 12.182 12.9393C12.282 13.0394 12.365 13.1501 12.4309 13.2677C14.2147 12.6699 15.5 10.9851 15.5 9C15.5 7.01494 14.2147 5.33008 12.4309 4.73226Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@@ -0,0 +1,8 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame 427319876">
<g id="Ellipse 437">
<circle cx="9" cy="9" r="2" fill="#FF5C00"/>
<circle cx="9" cy="9" r="2.5" stroke="black" stroke-opacity="0.3"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 342 B

@@ -0,0 +1,8 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame 427319876">
<g id="Ellipse 437">
<circle cx="9" cy="9" r="2" fill="#D4CBBD"/>
<circle cx="9" cy="9" r="2.5" stroke="black" stroke-opacity="0.3"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 342 B

@@ -0,0 +1,6 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame 427319874">
<path id="Polygon 353" d="M9 3L15.6574 7.83688L13.1145 15.6631H4.8855L2.3426 7.83688L9 3Z" stroke-width="3" stroke="black" stroke-opacity="0.3"/>
<path id="Polygon 354" d="M9 3L15.6574 7.83688L13.1145 15.6631H4.8855L2.3426 7.83688L9 3Z" stroke="#FF5C00"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 459 B

@@ -0,0 +1,6 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Frame 427319874">
<path id="Polygon 353" d="M9 3L15.6574 7.83688L13.1145 15.6631H4.8855L2.3426 7.83688L9 3Z" stroke-width="3" stroke="black" stroke-opacity="0.3"/>
<path id="Polygon 354" d="M9 3L15.6574 7.83688L13.1145 15.6631H4.8855L2.3426 7.83688L9 3Z" stroke="#D4CBBD"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 459 B

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319871">
<g id="Union">
<use xlink:href="#stroke0_0_10" fill="#FF5C00"/>
<mask id="stroke1_0_10" maskUnits="userSpaceOnUse" x="1" y="1" width="16" height="16" fill="black">
<rect fill="white" x="1" y="1" width="16" height="16"/>
<use xlink:href="#stroke0_0_10"/>
</mask>
<path d="M3 4.91465C2.4174 4.70873 2 4.15311 2 3.5C2 2.67157 2.67157 2 3.5 2C4.15311 2 4.70873 2.4174 4.91465 3H15V13.0854C15.5826 13.2913 16 13.8469 16 14.5C16 15.3284 15.3284 16 14.5 16C13.8469 16 13.2913 15.5826 13.0854 15H3V4.91465ZM14 13.0854V4H4.91465C4.764 4.42621 4.42621 4.764 4 4.91465V14H13.0854C13.236 13.5738 13.5738 13.236 14 13.0854Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_10)"/>
</g>
</g>
<defs>
<path id="stroke0_0_10" d="M2 3.5C2 4.15311 2.4174 4.70873 3 4.91465V15H13.0854C13.2913 15.5826 13.8469 16 14.5 16C15.3284 16 16 15.3284 16 14.5C16 13.8469 15.5826 13.2913 15 13.0854V3H4.91465C4.70873 2.4174 4.15311 2 3.5 2C2.67157 2 2 2.67157 2 3.5ZM14 13.0854V4H4.91465C4.764 4.42621 4.42621 4.764 4 4.91465V14H13.0854C13.236 13.5738 13.5738 13.236 14 13.0854Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,15 @@
<svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Frame 427319871">
<g id="Union">
<use xlink:href="#stroke0_0_317" fill="#D4CBBD"/>
<mask id="stroke1_0_317" maskUnits="userSpaceOnUse" x="1" y="1" width="16" height="16" fill="black">
<rect fill="white" x="1" y="1" width="16" height="16"/>
<use xlink:href="#stroke0_0_317"/>
</mask>
<path d="M3 4.91465C2.4174 4.70873 2 4.15311 2 3.5C2 2.67157 2.67157 2 3.5 2C4.15311 2 4.70873 2.4174 4.91465 3H15V13.0854C15.5826 13.2913 16 13.8469 16 14.5C16 15.3284 15.3284 16 14.5 16C13.8469 16 13.2913 15.5826 13.0854 15H3V4.91465ZM14 13.0854V4H4.91465C4.764 4.42621 4.42621 4.764 4 4.91465V14H13.0854C13.236 13.5738 13.5738 13.236 14 13.0854Z" stroke-width="2" stroke="black" stroke-opacity="0.3" mask="url(#stroke1_0_317)"/>
</g>
</g>
<defs>
<path id="stroke0_0_317" d="M2 3.5C2 4.15311 2.4174 4.70873 3 4.91465V15H13.0854C13.2913 15.5826 13.8469 16 14.5 16C15.3284 16 16 15.3284 16 14.5C16 13.8469 15.5826 13.2913 15 13.0854V3H4.91465C4.70873 2.4174 4.15311 2 3.5 2C2.67157 2 2 2.67157 2 3.5ZM14 13.0854V4H4.91465C4.764 4.42621 4.42621 4.764 4 4.91465V14H13.0854C13.236 13.5738 13.5738 13.236 14 13.0854Z" fill-rule="evenodd" clip-rule="evenodd"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,60 @@
import type { ReactElement } from "react"
import line from "./assets/tool-line.svg"
import lineOn from "./assets/tool-line-on.svg"
import pline from "./assets/tool-pline.svg"
import plineOn from "./assets/tool-pline-on.svg"
import arc from "./assets/tool-arc.svg"
import arcOn from "./assets/tool-arc-on.svg"
import circle from "./assets/tool-circle.svg"
import circleOn from "./assets/tool-circle-on.svg"
import ellipse from "./assets/tool-ellipse.svg"
import ellipseOn from "./assets/tool-ellipse-on.svg"
import rect from "./assets/tool-rect.svg"
import rectOn from "./assets/tool-rect-on.svg"
import poly from "./assets/tool-poly.svg"
import polyOn from "./assets/tool-poly-on.svg"
import point from "./assets/tool-point.svg"
import pointOn from "./assets/tool-point-on.svg"
import hatch from "./assets/tool-hatch.svg"
import hatchOn from "./assets/tool-hatch-on.svg"
import ncopy from "./assets/tool-ncopy.svg"
import ncopyOn from "./assets/tool-ncopy-on.svg"
import objectSnap from "./assets/aux-object-snap.svg"
import selectionEffect from "./assets/aux-selection.svg"
import orthogonal from "./assets/aux-orthogonal.svg"
import gridSnap from "./assets/aux-grid-snap.svg"
import gridBackground from "./assets/aux-grid.svg"
import lineWeight from "./assets/aux-lineweight.svg"
const img = (src: string, width: number, height = width): ReactElement => <img src={src} width={width} height={height} alt="" draggable={false} />
export type SWFooterToolIconName = "line" | "pline" | "arc" | "circle" | "ellipse" | "rect" | "poly" | "point" | "hatch" | "ncopy"
/**
* Figma command icons (26:9529 … 26:9739): `icon` = Default (#D4CBBD), `activeIcon` = Variant2 / Variant3 (#FF5C00).
* 18 × 18. Spread into a `SWFooterTool`: `{ id: "line", label: "Line", ...swFooterToolIcons.line }`.
*/
export const swFooterToolIcons: Record<SWFooterToolIconName, { icon: ReactElement; activeIcon: ReactElement }> = {
line: { icon: img(line, 18), activeIcon: img(lineOn, 18) },
pline: { icon: img(pline, 18), activeIcon: img(plineOn, 18) },
arc: { icon: img(arc, 18), activeIcon: img(arcOn, 18) },
circle: { icon: img(circle, 18), activeIcon: img(circleOn, 18) },
ellipse: { icon: img(ellipse, 18), activeIcon: img(ellipseOn, 18) },
rect: { icon: img(rect, 18), activeIcon: img(rectOn, 18) },
poly: { icon: img(poly, 18), activeIcon: img(polyOn, 18) },
point: { icon: img(point, 18), activeIcon: img(pointOn, 18) },
hatch: { icon: img(hatch, 18), activeIcon: img(hatchOn, 18) },
ncopy: { icon: img(ncopy, 18), activeIcon: img(ncopyOn, 18) },
}
export type SWFooterAuxIconName = "objectSnap" | "selectionEffect" | "orthogonal" | "gridSnap" | "gridBackground" | "lineWeight"
/** Figma auxiliary toggle icons (26:10104: 객체스냅, 선택효과, 직각교차, 격자스냅, 격자배경, 선가중치). */
export const swFooterAuxIcons: Record<SWFooterAuxIconName, ReactElement> = {
objectSnap: img(objectSnap, 17),
selectionEffect: img(selectionEffect, 16),
orthogonal: img(orthogonal, 18),
gridSnap: img(gridSnap, 16),
gridBackground: img(gridBackground, 17),
lineWeight: img(lineWeight, 16, 15),
}
@@ -0,0 +1,10 @@
export { SWFooter, type SWFooterProps, type SWFooterSideAction } from "./SWFooter"
export {
SWFooterSideButton, SWFooterAuxGroup, SWFooterAuxButton, SWFooterToolButton, SWFooterAddSlot, SWFooterGuide, SWFooterMenu,
type SWFooterSideButtonProps, type SWFooterAuxGroupProps, type SWFooterAuxButtonProps, type SWFooterToolButtonProps,
type SWFooterAddSlotProps, type SWFooterGuideProps, type SWFooterGuideContent, type SWFooterMenuProps, type SWFooterMenuItem,
} from "./SWFooterParts"
export { SWFooterLayoutTabs, SW_FOOTER_MODEL, type SWFooterLayoutTabsProps } from "./SWFooterLayoutTabs"
export { SWFooterCommandLine, type SWFooterCommandLineProps } from "./SWFooterCommandLine"
export { swFooterToolIcons, swFooterAuxIcons, type SWFooterToolIconName, type SWFooterAuxIconName } from "./icons"
export type { SWFooterTool, SWFooterCategory, SWFooterAuxItem, SWFooterLayout } from "./types"
@@ -0,0 +1,51 @@
import type { ReactNode } from "react"
/** One command button of the footer slots (Figma line / Pline / Arc … 68 × 47). */
export interface SWFooterTool {
/** Unique within its category. Reported by `onToolSelect`. */
id: string
/** 14px label above the icon (e.g. `Line`, `Rect...`). Also the accessible name unless `ariaLabel` is set. */
label: string
/** 18 × 18 icon (Figma Default). Use `swFooterToolIcons` for the Figma draw tools, or any element. */
icon?: ReactNode
/** Icon shown on hover and while selected (Figma Variant2 / Variant3, orange). Default: `icon`. */
activeIcon?: ReactNode
/** Tooltip (native `title`). */
title?: string
/** Accessible name when the label is abbreviated (e.g. `Rectangle` for `Rect...`). */
ariaLabel?: string
/** 40% and not clickable. */
disabled?: boolean
}
/** One category of the bottom row (도형, 수정, 편집 …) and its commands. */
export interface SWFooterCategory {
id: string
/** Tab text (14px). */
label: string
/** Commands of this category, in slot order. Slots after the last command are empty (+) slots. */
tools: SWFooterTool[]
disabled?: boolean
}
/** One auxiliary toggle of the 3 × 2 grid (객체스냅, 선택효과 … 104 × 33). */
export interface SWFooterAuxItem {
id: string
label: string
/** Icon at the left (Figma 14–16px). Use `swFooterAuxIcons` or any element. */
icon?: ReactNode
/** On / off state (aria-pressed). Omit for a plain button. */
pressed?: boolean
title?: string
disabled?: boolean
}
/** One drawing space tab (Figma layout01 / layout02 / layout03). */
export interface SWFooterLayout {
id: string
label: string
/** No × (remove) button and no rename. */
locked?: boolean
/** Tab shape. Default: `slant` (layout01) for the last tab, `rounded` (layout02 / 03) for the others. */
shape?: "rounded" | "slant"
}
+3
View File
@@ -14,3 +14,6 @@ export * from "./components/sw-home-menu"
// SW viewer mode (2026-10-19): viewer header, dropdown, popup panel and panel controls.
export * from "./components/sw-viewer"
// SW footer (2026-10-20): Figma 26:10097 bottom bar, command slots, category row, layout tabs and command line.
export * from "./components/sw-footer"
+533
View File
@@ -0,0 +1,533 @@
/* @hm-design/sw — SW Footer (Figma "레이아웃 분리" 26:10097 bottom bar, components 26:9459 … 26:10097, read
through the Figma Plugin API 2026-10-20). Footer-only tokens. Text keeps the SW typography of sw.css
(Noto Sans KR instead of Dotum, black OUTSIDE outline as 16 text-shadows, own-color 0.3 / 0.4px strokes removed
like the 2026-10-16 header fix). Never touches global selectors, SWHeader or SWPopup classes. */
.hm-sw-header,
.hm-sw-theme {
/* Bar (26:10098 background, 72px high, #060606) */
--hm-sw-footer-height: 72px;
--hm-sw-footer-background: #060606;
--hm-sw-footer-text: #f6f4f2;
--hm-sw-footer-muted: #d4cbbd;
--hm-sw-footer-accent: #ff5c00;
--hm-sw-footer-red: #e60403;
--hm-sw-footer-disabled-opacity: 0.4;
/* Side buttons 74 × 71 (26:10189 보조기능 / 26:10192 명령어 전체보기): side-bg.svg + 8px 60% black drop shadow
towards the bar content. */
--hm-sw-footer-side-width: 74px;
--hm-sw-footer-side-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
--hm-sw-footer-side-shadow-offset: 5px;
/* Auxiliary toggles 104 × 33 (26:10104, 3 × 2, gap 2px) */
--hm-sw-footer-aux-width: 104px;
--hm-sw-footer-aux-height: 33px;
--hm-sw-footer-aux-gap: 2px;
--hm-sw-footer-aux-background: linear-gradient(0deg, #262218 8%, #4a3c2b 34%, #594c31 55%, #312a1b 90%);
--hm-sw-footer-aux-border-color: #886d35;
--hm-sw-footer-aux-radius: 3px;
--hm-sw-footer-aux-font-size: 13px;
/* Not in Figma (Default only): the reference screen (2026-10-20) draws an ON toggle with an orange border and text. */
--hm-sw-footer-aux-pressed-border-color: #da6813;
/* Command group (26:10111 groop명령어): 68 × 47 slots over a 24px category row, 71px high */
--hm-sw-footer-slot-width: 68px;
--hm-sw-footer-slot-height: 47px;
--hm-sw-footer-group-background: linear-gradient(0deg, #2f281a 0%, #312a1b 5%, #332b1c 36%, #463c29 58%, #38301f 79%, #19160e 98%);
--hm-sw-footer-group-border-color: #886d35;
--hm-sw-footer-group-radius: 4px;
/* bg_vacant 26:10113: the darker surface after the last command (empty slots and the rest of the bar). */
--hm-sw-footer-vacant-background: linear-gradient(0deg, #18150e 28%, #262015 41%, #332b1c 58%, #38301f 81%, #18150e 100%);
--hm-sw-footer-divider: linear-gradient(0deg, #20190e 0.27%, #8a7a5b 45.15%, #f3dba8 70.08%, #423625 100%);
--hm-sw-footer-divider-shadow: -1px 0 1px #000;
/* Category row (menu_bg 26:10127): the active tab is a pocket opened into the slots above */
--hm-sw-footer-menu-height: 24px;
--hm-sw-footer-menu-top: #574b30;
--hm-sw-footer-menu-bottom: #282216;
--hm-sw-footer-menu-edge-top: #685a3b;
--hm-sw-footer-menu-pocket-edge: #675a3b;
--hm-sw-footer-menu-divider: #000;
--hm-sw-footer-menu-tab-min-width: 43px;
/* Empty-slot guide (26:9242 Variant2 / guide_바로가기 등록 26:9096): 208 × 149, 4px above the slot */
--hm-sw-footer-guide-width: 208px;
--hm-sw-footer-guide-height: 149px;
--hm-sw-footer-guide-background: #0e3c2e;
--hm-sw-footer-guide-border-color: #da6813;
--hm-sw-footer-guide-caption: #b6d0c9;
/* Layout tabs (26:9942 탭 상태변화, layout01 / layout02 / layout03 / model / btn_add_layout) */
--hm-sw-footer-tab-height: 22px;
--hm-sw-footer-tab-font-size: 12px;
--hm-sw-footer-tab-text: #161410;
--hm-sw-footer-tab-background: #c3b79e;
--hm-sw-footer-tab-hover-overlay: rgba(0, 0, 0, 0.2);
--hm-sw-footer-tab-on-background: linear-gradient(0deg, #7c6c4c 0.48%, #7c6c4c 85.07%, #a6946f 100%);
--hm-sw-footer-tab-editing-background: #0f0d0a;
--hm-sw-footer-tab-border-color: rgba(0, 0, 0, 0.05);
--hm-sw-footer-tab-hover-border-color: #da6813;
--hm-sw-footer-tab-inset-shadow: inset 0 -3px 4px rgba(56, 48, 31, 0.25);
/* 12px tab text: rgba(0, 0, 0, .7) 1.3px OUTSIDE stroke under the hover / on / editing text. */
--hm-sw-footer-tab-outline-color: rgba(0, 0, 0, 0.7);
/* Command line (26:10048 / 26:10056): 566px, 71px collapsed, 270px expanded */
--hm-sw-footer-command-width: 566px;
--hm-sw-footer-command-height: 71px;
--hm-sw-footer-command-expanded-height: 270px;
--hm-sw-footer-command-background: linear-gradient(180deg, #635537 0%, #262218 5%, #281f16 10%, #282216 100%);
--hm-sw-footer-command-expanded-background: #282216;
--hm-sw-footer-command-input-background: #16130d;
--hm-sw-footer-command-input-shadow: 0 -1px 2px rgba(255, 255, 255, 0.25), inset 0 1px 4px #000;
--hm-sw-footer-command-font-size: 12px;
--hm-sw-footer-command-line-height: 14px;
/* Not in Figma (reference screens 2026-10-20): the command line inside the bar keeps at least this width. */
--hm-sw-footer-command-min-width: 280px;
/* Not in Figma: below this bar width the auxiliary toggles show their icon only (reference screen 2026-10-20). */
--hm-sw-footer-aux-compact-width: 45px;
}
.hm-sw-footer-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* ---------- Bar ---------- */
.hm-sw-footer {
container: hm-sw-footer / inline-size;
position: relative;
display: flex;
align-items: stretch;
width: 100%;
min-width: 0;
height: var(--hm-sw-footer-height);
background: var(--hm-sw-footer-background);
color: var(--hm-sw-footer-text);
}
/* Layout tabs sit on top of the bar, right aligned (26:10190 at 1823, 986 in the 1920 frame). */
.hm-sw-footer__tabs { position: absolute; right: 0; bottom: 100%; z-index: 1; display: flex; }
.hm-sw-footer__aux-area { flex: 0 0 auto; display: flex; gap: 2px; margin-right: 4px; }
/* 26:10111 starts 1px below the bar top and ends at the bar bottom. */
.hm-sw-footer__group {
position: relative;
flex: 1 1 auto;
min-width: 0;
display: flex;
margin-top: 1px;
border: 1px solid var(--hm-sw-footer-group-border-color);
border-right: 0;
border-radius: var(--hm-sw-footer-group-radius) 0 0 0;
background: var(--hm-sw-footer-group-background);
}
.hm-sw-footer__vacant { position: absolute; top: 1px; right: 0; bottom: 0; background: var(--hm-sw-footer-vacant-background); pointer-events: none; }
.hm-sw-footer__scroll {
position: relative;
flex: 0 1 auto;
min-width: 0;
margin: -1px 0 -1px -1px;
}
.hm-sw-footer__panel { position: relative; display: flex; flex-direction: column; width: max-content; }
.hm-sw-footer__slots { display: flex; height: var(--hm-sw-footer-slot-height); }
.hm-sw-footer__group > .hm-sw-footer-side { margin-top: -1px; }
/* The rest of the bar is bg_vacant too (26:10113 runs to the frame edge and covers the bottom border). */
.hm-sw-footer__trailing { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; justify-content: flex-end; margin-bottom: -1px; background: var(--hm-sw-footer-vacant-background); }
/* Command line in the bar (reference screen 2026-10-20): free width between the auxiliary grid and the slots,
bottom aligned so an expanded line grows upwards. The slots keep their own width before 명령어 전체보기. */
.hm-sw-footer__command { position: relative; z-index: 2; flex: 1 1 0; min-width: var(--hm-sw-footer-command-min-width); margin: 1px 4px 0 0; }
.hm-sw-footer__command > .hm-sw-footer-command { position: absolute; left: 0; right: 0; bottom: 0; width: auto; max-width: none; }
.hm-sw-footer[data-command] .hm-sw-footer__group { flex: 0 1 auto; }
.hm-sw-footer[data-command] .hm-sw-footer__trailing { flex: 0 0 auto; }
/* Status text above the bar, left (cursor coordinates in the reference screen). */
.hm-sw-footer__status { position: absolute; left: 0; bottom: 100%; z-index: 1; padding: 0 0 3px 2px; font-size: 12px; line-height: 14px; color: var(--hm-sw-footer-text); white-space: nowrap; text-shadow: var(--hm-sw-header-label-outline); }
/* Bar-level guide: 4px above the empty slot (which starts 2px below the bar top). */
.hm-sw-footer > .hm-sw-footer__guide { bottom: calc(100% + 2px); }
/* ---------- Side buttons (보조기능 / 명령어 전체보기) ---------- */
.hm-sw-footer-side {
position: relative;
z-index: 1;
flex: 0 0 var(--hm-sw-footer-side-width);
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
width: var(--hm-sw-footer-side-width);
height: 71px;
padding-top: 18px;
background: url("../components/sw-footer/assets/side-bg.svg") 0 0 / 74px 71px no-repeat;
filter: drop-shadow(var(--hm-sw-footer-side-shadow-offset) 0 8px rgba(0, 0, 0, 0.6));
color: var(--hm-sw-footer-text);
font-size: 14px;
letter-spacing: 0.04em;
line-height: 16px;
text-align: center;
white-space: nowrap;
}
.hm-sw-footer-side[data-side="end"] { filter: drop-shadow(calc(-1 * var(--hm-sw-footer-side-shadow-offset)) 0 8px rgba(0, 0, 0, 0.6)); }
.hm-sw-footer-side__title { text-shadow: var(--hm-sw-header-text-outline); }
.hm-sw-footer-side__subtitle { text-shadow: var(--hm-sw-header-label-outline); }
/* Not in Figma: hover / selected reuse the footer accent (#FF5C00) of the slot and category hover. */
.hm-sw-footer-side:hover:not(:disabled) .hm-sw-footer-side__title,
.hm-sw-footer-side[aria-pressed="true"] .hm-sw-footer-side__title,
.hm-sw-footer-side[aria-expanded="true"] .hm-sw-footer-side__title { color: var(--hm-sw-footer-accent); }
/* ---------- Auxiliary toggles (객체스냅 …) ---------- */
.hm-sw-footer-aux {
display: grid;
grid-template-columns: repeat(var(--hm-sw-footer-aux-columns, 3), var(--hm-sw-footer-aux-width));
grid-auto-rows: var(--hm-sw-footer-aux-height);
align-content: center;
gap: var(--hm-sw-footer-aux-gap);
}
.hm-sw-footer-aux__button {
position: relative;
width: var(--hm-sw-footer-aux-width);
height: var(--hm-sw-footer-aux-height);
border: 1px solid var(--hm-sw-footer-aux-border-color);
border-radius: var(--hm-sw-footer-aux-radius);
background: var(--hm-sw-footer-aux-background);
color: var(--hm-sw-footer-text);
font-size: var(--hm-sw-footer-aux-font-size);
letter-spacing: 0.04em;
}
/* Icon box: Figma icons sit at 12px, 8–10px (14–16px boxes); a 17px box at 10, 7 inside the 1px border centers them. */
.hm-sw-footer-aux__icon { position: absolute; left: 10px; top: 7px; display: grid; place-items: center; width: 17px; height: 17px; }
.hm-sw-footer-aux__icon :is(img, svg) { display: block; flex: none; }
.hm-sw-footer-aux__label {
position: absolute;
left: 35px;
right: 10px;
top: 7px;
overflow: hidden;
line-height: 17px;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
text-shadow: var(--hm-sw-header-text-outline);
}
/* Not in Figma (only Default is drawn): hover = the footer accent text; pressed = accent text + orange border
(reference screen 2026-10-20). */
.hm-sw-footer-aux__button:hover:not(:disabled) .hm-sw-footer-aux__label,
.hm-sw-footer-aux__button[aria-pressed="true"] .hm-sw-footer-aux__label { color: var(--hm-sw-footer-accent); }
.hm-sw-footer-aux__button[aria-pressed="true"] { border-color: var(--hm-sw-footer-aux-pressed-border-color); }
/* ---------- Command slots (Line, PLine … / 빈 칸) ---------- */
.hm-sw-footer-slot {
position: relative;
flex: 0 0 var(--hm-sw-footer-slot-width);
width: var(--hm-sw-footer-slot-width);
height: var(--hm-sw-footer-slot-height);
}
/* 1px divider between slots (Rectangle 7461 …: x = 68n, 5 → 42). */
.hm-sw-footer-slot + .hm-sw-footer-slot::before {
content: "";
position: absolute;
z-index: 1;
left: 0;
top: 5px;
width: 1px;
height: 37px;
background: var(--hm-sw-footer-divider);
box-shadow: var(--hm-sw-footer-divider-shadow);
pointer-events: none;
}
.hm-sw-footer-tool {
position: absolute;
inset: 0;
color: var(--hm-sw-footer-text);
font-size: 14px;
letter-spacing: 0.04em;
}
.hm-sw-footer-tool__label {
position: absolute;
left: 2px;
right: 2px;
top: 6px;
overflow: hidden;
line-height: 16px;
text-align: center;
text-overflow: clip;
white-space: nowrap;
text-shadow: var(--hm-sw-header-text-outline);
}
.hm-sw-footer-tool__icon { position: absolute; left: 50%; top: 26px; display: grid; place-items: center; width: 18px; height: 18px; transform: translateX(-50%); }
.hm-sw-footer-tool__icon :is(img, svg) { display: block; flex: none; }
.hm-sw-footer-tool__icon--active { display: none; }
/* Variant2 (hover) and Variant3 (selected) are the same in Figma: #FF5C00 text and the orange icon. */
.hm-sw-footer-tool:is(:hover:not(:disabled), [aria-pressed="true"]) { color: var(--hm-sw-footer-accent); }
.hm-sw-footer-tool[data-active-icon]:is(:hover:not(:disabled), [aria-pressed="true"]) .hm-sw-footer-tool__icon--default { display: none; }
.hm-sw-footer-tool[data-active-icon]:is(:hover:not(:disabled), [aria-pressed="true"]) .hm-sw-footer-tool__icon--active { display: grid; }
.hm-sw-footer-tool:disabled { opacity: var(--hm-sw-footer-disabled-opacity); }
/* Last slot when the commands do not fit (reference screens 2026-10-20): ico_arrow 26:10023 ▲ opens the panel of
the other commands above the slots, ▼ while open. */
.hm-sw-footer-more { position: absolute; inset: 0; }
.hm-sw-footer-more__icon { position: absolute; top: 4px; right: 4px; width: 18px; height: 18px; border-radius: 2px; background: #000 url("../components/sw-footer/assets/arrow-up.svg") 1px 1px / 16px 16px no-repeat; }
.hm-sw-footer-more:is(:hover, [data-active]) .hm-sw-footer-more__icon { background-image: url("../components/sw-footer/assets/arrow-up-hover.svg"); }
.hm-sw-footer-more[aria-expanded="true"] .hm-sw-footer-more__icon { background-image: url("../components/sw-footer/assets/arrow-down.svg"); }
.hm-sw-footer-more[aria-expanded="true"]:is(:hover, [data-active]) .hm-sw-footer-more__icon { background-image: url("../components/sw-footer/assets/arrow-down-hover.svg"); }
.hm-sw-footer__more-panel {
position: absolute;
z-index: 4;
right: 0;
bottom: 100%;
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
border: 1px solid var(--hm-sw-footer-group-border-color);
border-bottom: 0;
border-radius: var(--hm-sw-footer-group-radius) var(--hm-sw-footer-group-radius) 0 0;
background: var(--hm-sw-footer-group-background);
}
/* Empty slot (btn_add_menu 26:9971): 68 × 46 one pixel below the slot top; hover adds the orange dashed ring. */
.hm-sw-footer-add {
position: absolute;
left: 0;
top: 1px;
width: var(--hm-sw-footer-slot-width);
height: 46px;
background: url("../components/sw-footer/assets/slot-add.svg") 0 0 / 68px 46px no-repeat;
}
.hm-sw-footer-add:is(:hover:not(:disabled), [aria-expanded="true"]) { background-image: url("../components/sw-footer/assets/slot-add-hover.svg"); }
.hm-sw-footer-add:disabled { opacity: var(--hm-sw-footer-disabled-opacity); }
/* Without onSlotAdd the + wells are drawn but inert (no ring, no guide). */
.hm-sw-footer-add[data-inert] { pointer-events: none; }
.hm-sw-footer-slot:has(.hm-sw-footer-add:hover:not(:disabled))::before { visibility: hidden; }
/* Guide (가이드 영상 보기): centered over the slot, 4px above it. */
.hm-sw-footer-guide {
position: absolute;
z-index: 3;
left: 50%;
bottom: calc(100% + 3px);
width: var(--hm-sw-footer-guide-width);
height: var(--hm-sw-footer-guide-height);
overflow: hidden;
transform: translateX(-50%);
border: 1px solid var(--hm-sw-footer-guide-border-color);
border-radius: 2px;
background: var(--hm-sw-footer-guide-background);
pointer-events: none;
}
.hm-sw-footer-guide[data-standalone] { position: relative; left: auto; bottom: auto; transform: none; }
.hm-sw-footer-guide__media { position: absolute; left: 5px; top: 5px; width: 194px; height: 111px; overflow: hidden; border: 1px solid #000; background: #000; }
.hm-sw-footer-guide__media > :is(img, video) { display: block; width: 100%; height: 100%; object-fit: cover; }
.hm-sw-footer-guide__title { position: absolute; left: 5px; top: 123px; font-size: 14px; letter-spacing: 0.04em; line-height: 16px; color: var(--hm-sw-footer-text); text-shadow: var(--hm-sw-header-text-outline); white-space: nowrap; }
.hm-sw-footer-guide__caption { position: absolute; right: 6px; top: 124px; font-size: 12px; letter-spacing: 0.02em; line-height: 14px; color: var(--hm-sw-footer-guide-caption); text-shadow: var(--hm-sw-header-label-outline); white-space: nowrap; }
/* Narrow bar (reference screen 2026-10-20): icon-only toggles, 45 × 33. The label stays as the accessible name. */
@container hm-sw-footer (max-width: 1279px) {
.hm-sw-footer-aux { --hm-sw-footer-aux-width: var(--hm-sw-footer-aux-compact-width); }
.hm-sw-footer-aux__icon { left: 13px; }
.hm-sw-footer-aux__label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}
/* ---------- Category row (도형 수정 편집 …) ---------- */
.hm-sw-footer-menu { position: relative; display: flex; align-items: flex-start; height: var(--hm-sw-footer-menu-height); }
/* Tabs that do not fit scroll inside the viewport with ◀ ▶ (reference screens 2026-10-20). */
.hm-sw-footer-menu__viewport { position: relative; flex: 0 1 auto; min-width: 0; display: flex; align-items: flex-start; height: 100%; padding-left: 7px; overflow: hidden; scrollbar-width: none; }
.hm-sw-footer-menu__viewport::-webkit-scrollbar { display: none; }
.hm-sw-footer-menu__step { position: relative; flex: 0 0 13px; display: grid; place-items: center; width: 13px; height: 16px; margin-top: 4px; color: var(--hm-sw-footer-text); }
.hm-sw-footer-menu__step svg { display: block; filter: drop-shadow(0 0 1px #000); }
.hm-sw-footer-menu__step:hover:not(:disabled) { color: var(--hm-sw-footer-accent); }
.hm-sw-footer-menu__step:disabled { opacity: var(--hm-sw-footer-disabled-opacity); }
.hm-sw-footer-menu__step + .hm-sw-footer-menu__step { margin-right: 4px; }
.hm-sw-footer-menu__tabs { display: contents; }
.hm-sw-footer-menu__surface { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.hm-sw-footer-menu__tab {
position: relative;
flex: 0 0 auto;
min-width: var(--hm-sw-footer-menu-tab-min-width);
height: 16px;
margin-top: 4px;
padding: 0 8px;
color: var(--hm-sw-footer-muted);
font-size: 14px;
/* Figma 1.4px (10%) on 레이어 … 구조/배근 (0.56px on the first four): with it the Noto widths meet the Figma
boxes (45 / 61 / 43 / 84px). */
letter-spacing: 0.1em;
line-height: 16px;
text-align: center;
white-space: nowrap;
text-shadow: var(--hm-sw-header-text-outline);
}
/* Variant2 (hover) and Variant3 (current) are #FF5C00 in Figma. */
.hm-sw-footer-menu__tab:is(:hover:not(:disabled), [aria-selected="true"]) { color: var(--hm-sw-footer-accent); }
/* 1px × 10px black divider (Rectangle 7480 …) between tabs; the current tab has its 7px pocket edges instead. */
.hm-sw-footer-menu__divider { position: relative; flex: 0 0 1px; width: 1px; height: 10px; margin-top: 7px; background: var(--hm-sw-footer-menu-divider); }
.hm-sw-footer-menu__tab[aria-selected="true"] { margin-right: 7px; }
.hm-sw-footer-menu__tab[aria-selected="true"]:not(:first-of-type) { margin-left: 7px; }
.hm-sw-footer-menu__add { position: relative; flex: 0 0 28px; width: 28px; height: 12px; margin-top: 6px; background: url("../components/sw-footer/assets/menu-add.svg") 0 0 / 28px 12px no-repeat; }
/* Not in Figma: only Default of btn_add (26:9451) exists; hover keeps the asset (no invented look). */
/* ---------- Layout tabs ---------- */
.hm-sw-footer-tabs { display: flex; align-items: flex-end; gap: 1px; height: var(--hm-sw-footer-tab-height); font-size: var(--hm-sw-footer-tab-font-size); letter-spacing: 0.02em; }
.hm-sw-footer-tabs__list { display: contents; }
.hm-sw-footer-tabs__add { flex: 0 0 24px; width: 24px; height: 22px; background: url("../components/sw-footer/assets/tab-add.svg") 0 0 / 24px 22px no-repeat; }
.hm-sw-footer-tabs__add:hover:not(:disabled) { background-image: url("../components/sw-footer/assets/tab-add-hover.svg"); }
.hm-sw-footer-tab { position: relative; flex: 0 0 auto; display: flex; height: 22px; color: var(--hm-sw-footer-tab-text); }
.hm-sw-footer-tab__select {
position: relative;
z-index: 1;
display: flex;
align-items: center;
height: 22px;
min-width: 69px;
padding: 0 4px 0 8px;
white-space: nowrap;
line-height: 14px;
}
.hm-sw-footer-tab__label { display: block; }
.hm-sw-footer-tab__input {
width: 57px;
height: 16px;
margin: 0;
padding: 0;
border: 0;
outline: none;
background: transparent;
color: inherit;
font: inherit;
letter-spacing: inherit;
}
.hm-sw-footer-tab__remove { position: relative; z-index: 1; flex: 0 0 auto; height: 22px; opacity: 0; }
.hm-sw-footer-tab__remove:focus-visible { opacity: 1; }
/* Rounded tab (layout02 / layout03): 1px border, 4px top radius, -3px inset shadow. */
/* The Figma border is an inside stroke drawn over the content (it does not widen the 85px tab): ::after. */
.hm-sw-footer-tab[data-shape="rounded"] {
overflow: hidden;
border-radius: 4px 4px 0 0;
background: var(--hm-sw-footer-tab-background);
}
.hm-sw-footer-tab[data-shape="rounded"]::after {
content: "";
position: absolute;
z-index: 2;
inset: 0;
border: 1px solid var(--hm-sw-footer-tab-border-color);
border-radius: inherit;
box-shadow: var(--hm-sw-footer-tab-inset-shadow);
pointer-events: none;
}
.hm-sw-footer-tab[data-shape="rounded"] .hm-sw-footer-tab__label { max-width: 12em; overflow: hidden; text-overflow: ellipsis; }
.hm-sw-footer-tab[data-shape="rounded"] .hm-sw-footer-tab__remove { width: 16px; background: url("../components/sw-footer/assets/tab-delete.svg") 0 0 / 16px 22px no-repeat; }
.hm-sw-footer-tab[data-shape="rounded"] .hm-sw-footer-tab__remove:hover { background-image: url("../components/sw-footer/assets/tab-delete-hover.svg"); }
.hm-sw-footer-tab[data-shape="rounded"][data-state="on"] { background: var(--hm-sw-footer-tab-on-background); }
.hm-sw-footer-tab[data-shape="rounded"][data-state="editing"] { background: var(--hm-sw-footer-tab-editing-background); }
.hm-sw-footer-tab[data-shape="rounded"]:is(:hover, [data-state="editing"])::after { border-color: var(--hm-sw-footer-tab-hover-border-color); }
.hm-sw-footer-tab[data-shape="rounded"][data-state="off"]:hover { background: linear-gradient(var(--hm-sw-footer-tab-hover-overlay), var(--hm-sw-footer-tab-hover-overlay)), var(--hm-sw-footer-tab-background); }
/* Slanted tab (layout01, next to Model): 97 × 22 mirrored bg asset + the 28px red close piece. */
.hm-sw-footer-tab[data-shape="slant"] { width: 97px; }
.hm-sw-footer-tab[data-shape="slant"] .hm-sw-footer-tab__label { width: 57px; overflow: hidden; text-overflow: ellipsis; }
.hm-sw-footer-tab[data-shape="slant"]::before,
.hm-sw-footer-tab[data-shape="slant"]::after {
content: "";
position: absolute;
inset: 0;
transform: scaleX(-1);
background: url("../components/sw-footer/assets/tab-slant.svg") 0 0 / 97px 22px no-repeat;
pointer-events: none;
}
.hm-sw-footer-tab[data-shape="slant"]::after { z-index: 2; background-image: none; }
.hm-sw-footer-tab[data-shape="slant"] .hm-sw-footer-tab__remove { width: 28px; background: url("../components/sw-footer/assets/tab-close.svg") 0 0 / 28px 22px no-repeat; }
.hm-sw-footer-tab[data-shape="slant"] .hm-sw-footer-tab__remove:hover { background-image: url("../components/sw-footer/assets/tab-close-hover.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="on"]::before { background-image: url("../components/sw-footer/assets/tab-slant-on.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="editing"]::before { background-image: url("../components/sw-footer/assets/tab-slant-editing.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="off"]:hover::before { background-image: url("../components/sw-footer/assets/tab-slant-hover.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="off"]:hover::after { background-image: url("../components/sw-footer/assets/tab-slant-ring.svg"); }
/* layout01 Variant6 (editing + hover): the fill without its own ring and the orange ring on top. */
.hm-sw-footer-tab[data-shape="slant"][data-state="editing"]:hover::before { background-image: url("../components/sw-footer/assets/tab-slant-editing-fill.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="editing"]:hover::after { background-image: url("../components/sw-footer/assets/tab-slant-ring.svg"); }
.hm-sw-footer-tab[data-shape="slant"] + .hm-sw-footer-tab--model { margin-left: -9px; }
/* Text per state (Figma layout01 / layout02 variants). */
.hm-sw-footer-tab:is([data-state="on"], [data-state="editing"], :hover) .hm-sw-footer-tab__select {
text-shadow:
1.3px 0 0 var(--hm-sw-footer-tab-outline-color), 0.92px 0.92px 0 var(--hm-sw-footer-tab-outline-color),
0 1.3px 0 var(--hm-sw-footer-tab-outline-color), -0.92px 0.92px 0 var(--hm-sw-footer-tab-outline-color),
-1.3px 0 0 var(--hm-sw-footer-tab-outline-color), -0.92px -0.92px 0 var(--hm-sw-footer-tab-outline-color),
0 -1.3px 0 var(--hm-sw-footer-tab-outline-color), 0.92px -0.92px 0 var(--hm-sw-footer-tab-outline-color);
}
.hm-sw-footer-tab[data-state="on"] { color: var(--hm-sw-footer-text); }
.hm-sw-footer-tab[data-state="editing"] { color: var(--hm-sw-footer-red); }
.hm-sw-footer-tab[data-shape="rounded"]:not([data-state="editing"]):hover,
.hm-sw-footer-tab[data-shape="slant"][data-state="on"]:hover,
.hm-sw-footer-tab--model:hover:not([data-state="on"]) { color: var(--hm-sw-footer-accent); }
/* layout01 hover (not selected): orange tab, black text with the 70% outline. */
.hm-sw-footer-tab[data-shape="slant"][data-state="off"]:hover { color: #000; }
.hm-sw-footer-tab[data-shape="slant"][data-state="off"]:hover .hm-sw-footer-tab__select { text-shadow: none; }
/* layout01 "on hover" draws the default beige surface under the orange text. */
.hm-sw-footer-tab[data-shape="slant"][data-state="on"]:hover::before { background-image: url("../components/sw-footer/assets/tab-slant.svg"); }
/* The × appears on hover (all shapes) and in no other state (Figma opacity 0). */
.hm-sw-footer-tab:hover .hm-sw-footer-tab__remove { opacity: 1; }
.hm-sw-footer-tab .hm-sw-footer-tab__remove:disabled { opacity: 0; cursor: default; }
/* Model (26:9914): 73 × 22 asset, label 22px from the left. */
.hm-sw-footer-tab--model { width: 73px; background: url("../components/sw-footer/assets/tab-model.svg") 0 0 / 73px 22px no-repeat; }
.hm-sw-footer-tab--model[data-state="on"] { background-image: url("../components/sw-footer/assets/tab-model-on.svg"); }
.hm-sw-footer-tab--model .hm-sw-footer-tab__select { min-width: 0; width: 100%; padding: 0 16px 0 22px; }
/* ---------- Command line ---------- */
.hm-sw-footer-command {
position: relative;
display: flex;
flex-direction: column;
width: var(--hm-sw-footer-command-width);
max-width: 100%;
height: var(--hm-sw-footer-command-height);
overflow: hidden;
border-radius: 4px 4px 0 0;
background: var(--hm-sw-footer-command-background) 0 0 / 100% var(--hm-sw-footer-command-height) no-repeat, var(--hm-sw-footer-command-expanded-background);
color: var(--hm-sw-footer-text);
font-size: var(--hm-sw-footer-command-font-size);
letter-spacing: 0.1em;
line-height: var(--hm-sw-footer-command-line-height);
}
.hm-sw-footer-command[data-expanded] { height: var(--hm-sw-footer-command-expanded-height); }
.hm-sw-footer-command__history {
flex: 1 1 auto;
min-height: 0;
margin: 0;
padding: 4px 30px 2px 8px;
overflow-y: auto;
list-style: none;
scrollbar-width: thin;
scrollbar-color: #574b30 transparent;
text-shadow: var(--hm-sw-header-label-outline);
}
.hm-sw-footer-command__history > li { white-space: pre; }
.hm-sw-footer-command:not([data-expanded]) .hm-sw-footer-command__history { overflow: hidden; }
.hm-sw-footer-command__history > li { overflow: hidden; text-overflow: ellipsis; }
.hm-sw-footer-command__toggle { position: absolute; top: 4px; right: 4px; width: 18px; height: 18px; border-radius: 2px; background: #000 url("../components/sw-footer/assets/arrow-up.svg") 1px 1px / 16px 16px no-repeat; }
.hm-sw-footer-command__toggle:hover { background-image: url("../components/sw-footer/assets/arrow-up-hover.svg"); }
.hm-sw-footer-command__toggle[aria-expanded="true"] { background-image: url("../components/sw-footer/assets/arrow-down.svg"); }
.hm-sw-footer-command__toggle[aria-expanded="true"]:hover { background-image: url("../components/sw-footer/assets/arrow-down-hover.svg"); }
.hm-sw-footer-command__prompt {
flex: 0 0 23px;
display: flex;
align-items: center;
gap: 5px;
height: 23px;
margin: 0 4px;
padding: 0 6px;
background: var(--hm-sw-footer-command-input-background);
box-shadow: var(--hm-sw-footer-command-input-shadow);
}
.hm-sw-footer-command__label { flex: 0 0 auto; text-shadow: var(--hm-sw-header-label-outline); }
.hm-sw-footer-command__input {
flex: 1 1 auto;
min-width: 0;
height: 16px;
margin: 0;
padding: 0;
border: 0;
outline: none;
background: transparent;
color: inherit;
font: inherit;
letter-spacing: inherit;
text-shadow: var(--hm-sw-header-label-outline);
caret-color: var(--hm-sw-footer-text);
}
.hm-sw-footer-command__prompt:has(.hm-sw-footer-command__input:focus-visible) { outline: 1px solid var(--hm-sw-header-focus-color); outline-offset: -1px; }
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff. Show more