커뮤니티 스킨과 팩
누구나 FolderSkin을 쓰는 모든 사람과 스킨을 무료로 공유할 수 있어요. 공유하는 스킨 묶음을 팩이라고 하고, 스킨 하나만 있어도 하나의 팩이에요. 팩은 별도의 저장소인 folderskin-community의 packs/ 아래에 있고, 팩을 추가할 때 계정은 필요 없어요. FolderSkin에는 기본으로 들어 있는 스킨이 없어요. 스킨은 모두 팩이나 내 이미지, AI 생성 결과에서 나와요.
팩 추가하기
섹션 제목: “팩 추가하기”FolderSkin을 처음 열면 라이브러리를 채울 첫 팩을 추천해 줘요. 그다음부터는 앱에서 커뮤니티를 열면 돼요. 위쪽의 필터는 팩에 붙은 태그예요. 추가를 누르면 팩의 스킨이 팩의 태그와 함께 라이브러리에 들어가요. 각 스킨의 ⋯ 메뉴에서 어느 팩에서 왔는지, 누가 공유했는지 볼 수 있어요. 제거를 누르면 팩 전체를 다시 빼요. 이미 그 팩의 스킨을 쓰고 있는 폴더는 아이콘이 그대로 남아요. 아이콘은 폴더 안에 저장되기 때문이에요.
폴더에서 추가는 내 컴퓨터에 있는 팩 폴더로 같은 일을 해요. 팩을 공유하기 전에 미리 써 볼 때도 이 방법을 써요.
folderskin.app의 갤러리에는 모든 팩에 설치 버튼이 있어요. 누르면 FolderSkin이 열리면서 커뮤니티에서 그 팩을 보여 주고 추가해요. 팩의 추가 버튼을 누른 것과 똑같아요(아래 설치 링크 참고). 공식 표시가 붙은 팩은 메인테이너가 보증하는 팩이에요.
팩은 통째로 추가되거나, 아예 추가되지 않거나 둘 중 하나예요. 먼저 모든 이미지를 내려받아 검사한 다음 한 번에 저장하기 때문에, 연결이 끊기거나 디스크가 가득 차도 팩이 반만 라이브러리에 남는 일은 없어요. 스킨은 팩에서 정한 순서대로 나타나요.
내 스킨 공유하기
섹션 제목: “내 스킨 공유하기”공유는 앱에서 해요. FolderSkin이 팩을 커뮤니티 서비스(community.folderskin.app)로 보내면, 메인테이너가 거기서 검토해요. GitHub 계정은 필요 없어요. FolderSkin 0.1.6 이하에서는 GitHub에 풀 리퀘스트를 대신 열어 주는 방법도 있었지만, 0.1.7부터 이 방법은 없어졌어요.
- 공유하고 싶은 스킨에 태그를 붙여요(⋯ → 태그). 스킨 하나만 공유하려면 ⋯ → 커뮤니티에 공유를 쓰세요. 여러 개를 공유하려면 커뮤니티 → 스킨 공유를 열고 태그를 고르세요.
- 팩 이름, 태그, 라이선스를 입력하고, 이미지의 출처를 적은 다음, 내가 공유할 권리가 있는 이미지라는 항목에 체크해요.
- 처음 한 번은 FolderSkin이 브라우저에서 이 컴퓨터를 인증해요. 이때 쓰는 이름이 팩의 제작자로 표시돼요. 인증은 컴퓨터마다 한 번만 해요.
- 팩을 보내요. FolderSkin은 무엇이든 컴퓨터 밖으로 나가기 전에 팩이 아래 규격에 맞는지 검사해요. 제출 내역에서 보낸 팩을 모두 볼 수 있고, 거절된 팩은 그 이유도 볼 수 있어요.
모든 이미지는 무손실로 공유되기 때문에, 완성된 폴더의 투명한 가장자리까지 만든 그대로 보여요. FolderSkin은 보내기 전에 이미지를 하나씩 무손실 WebP로 바꿔요. 이미지 한 장에 몇 초씩 걸리고, 준비된 이미지 수를 세면서 진행해요. 1024px에서 1.5MB에 들어가지 않을 만큼 정밀한 이미지는 무손실 그대로 896px로, 그래도 안 되면 768px로 줄이고, 어떤 이미지를 줄였는지 알려 줘요. 팩 하나의 이미지는 모두 합쳐 64MB까지예요. 그보다 크면 거절되고, 팩을 두 개로 나누라는 제안이 함께 나와요.
모든 팩은 다른 사람에게 보이기 전에 사람이 직접 살펴봐요. 승인되면 15분 안팎으로 자동 공개돼요(아래 승인하면 공개되는 과정 참고). 이름이 같은 팩이 여러 개 있어도 괜찮아요. 내가 고른 이름이 그대로 모두에게 보이고, 팩에는 따로 고유한 ID가 붙어요(팩 ID).
팩은 직접 추가할 수도 있어요. folderskin-community에 packs/ 아래 폴더 하나를 추가하는 풀 리퀘스트를 보내면 돼요. 폴더는 packs make로 만들어요(이미지로 팩 만들기). 그러면 생성된 ID가 붙고, 풀 리퀘스트에서는 앱과 똑같은 검사가 실행돼요. 앱의 폴더로 저장을 써도 아래 규칙을 모두 지키는 팩 폴더를 만들 수 있어요.
팩 ID
섹션 제목: “팩 ID”모든 팩에는 ID가 있어요. ID는 팩 폴더의 이름이고, 팩으로 가는 모든 링크에 들어가요. ID는 팩을 만들 때 한 번, 이름과 무작위 문자 6개로 만들어요. 예를 들어 Classic Art라는 팩은 classic-art-k7q2mx 같은 ID를 받아요. 이름은 얼마든지 겹쳐도 되기 때문에 Classic Art라는 팩이 100개 있어도 괜찮고, 고유해야 하는 건 ID뿐이에요. 직접 만드는 팩의 ID는 packs make가, 앱에서 공유한 팩의 ID는 커뮤니티 서비스가 만들어요. 한번 정해진 ID는 팩 이름이 바뀌어도 바뀌지 않아요.
무작위 부분은 a부터 z까지, 2부터 7까지의 문자 중 6개로, 시스템의 안전한 난수 소스에서 뽑아요. 새 ID는 packs/에 있는 폴더 이름이나 moved.json에 있는 옛 ID와 절대 겹치지 않아요.
moved.json
섹션 제목: “moved.json”ID를 생성하기 전에 만든 팩은 classic-art처럼 이름만으로 만든 ID를 갖고 있었어요. 이런 팩은 packs rename으로 생성된 ID를 새로 받았고, packs/ 옆에 있는 moved.json에 각 옛 ID와 그 팩의 현재 ID가 기록돼 있어요.
{ "version": 1, "moved": { "classic-art": "classic-art-k7q2mx" } }- 옛 ID는 모두
packs/의 어떤 폴더도 쓰지 않는 팩 ID이고, 새 팩에 주어지는 일도 없어요. - 새 ID는 모두
packs/에 있는 팩을 가리켜요. 다른 옛 ID로 이어지는 일은 없어요. 팩 ID를 다시 바꾸면, 그 팩으로 이어지던 것이 모두 가장 새로운 ID를 가리키도록 바뀌어요. packs index와packs catalog는 이 대응표를index.json과head.json에"moved"로 복사해요. 그래서 앱, 웹사이트, 커뮤니티 서비스가 옛 ID로 팩을 찾아갈 수 있어요. 0.1.7부터 앱은 옛 ID로 추가한 팩을 새 ID로 옮기고, 옛 ID가 든 설치 링크로도 팩을 찾을 수 있어요.- ID를 바꾼 팩도 처음 공개된 날짜를 그대로 유지해요.
packs index는 그 팩의 ID 중 하나라도 추가한 가장 이른 커밋을 기준으로 날짜를 매겨요. pack.json에는 아무것도 넣지 않아요.pack.json은 규격에 없는 필드를 받지 않고, 넣으면 앱 0.1.4부터 0.1.6까지는 그 팩을 거절해요.
packs rename
섹션 제목: “packs rename”cargo run -p folderskin-tools -- packs rename --dir ../folderskin-community --allcargo run -p folderskin-tools -- packs rename --dir ../folderskin-community classic-artcargo run -p folderskin-tools -- packs rename --dir ../folderskin-community classic-art --to classic-art-k7q2mxfolderskin-community의 git 체크아웃 안에서 팩에 생성된 ID를 붙여요. 폴더는 각각 git mv로 옮기기 때문에 히스토리도 함께 따라가요. featured.json과 official.json은 순서를 그대로 둔 채 새 ID로 다시 쓰이고, 모든 이동은 moved.json에 기록돼요(파일이 없으면 새로 만들어요). 모든 변경은 스테이징되어 바로 커밋할 수 있는 상태가 돼요.
--all은 ID가 생성된 형식이 아닌 팩을 모두 바꾸고, 나머지는 그대로 둬요. 판단은 ID의 모양만 보고 해요. 그래서 data-structures-in-bricks처럼 마지막 단어가 우연히 여섯 글자인 옛 ID는 생성된 ID처럼 보이니, 그런 팩은 ID를 직접 지정해서 바꾸세요. ID를 지정한 팩은 지금 ID가 무엇이든 새로 생성된 ID로 옮겨지고, --to를 쓰면 그 ID로 옮겨져요. 이때 --to의 값은 생성된 형식의 ID이면서, 지금도 예전에도 어떤 팩도 쓴 적이 없는 ID여야 해요. 다시 실행해도 아무것도 바뀌지 않아요. 이미 옮긴 팩은 moved.json에서 찾아서, 이미 옮겼다고 알려 줘요.
승인하면 공개되는 과정
섹션 제목: “승인하면 공개되는 과정”팩을 승인하면 그대로 공개돼요. 누가 손으로 파일을 복사하는 일은 없어요.
- 메인테이너가 팩을 승인하면 커뮤니티 서비스가 팩에 ID를 붙여요. 서비스에 GitHub 토큰(
GITHUB_DISPATCH_TOKEN)이 있으면,pack-approvedrepository dispatch로 folderskin-community의 Packs 워크플로도 곧바로 시작해요. - 토큰이 없으면 워크플로가 직접 팩을 찾아요. 15분마다
https://community.folderskin.app/v1/exports/pending에 승인된 채 기다리는 팩이 몇 개인지 물어보고(몇 초면 끝나요), 기다리는 팩이 있을 때만 나머지 작업을 해요. 기다리는 팩이 있든 없든 매일, 매주 실행되고, 메인테이너가 직접 시작할 때도 실행돼요. community pull --no-done이 승인된 팩을 하나씩packs/에 쓰고, 모든 파일의 크기와 SHA-256을 업로드할 때 서비스가 기록한 값과 대조해요. 서비스가 준 ID는 생성된 형식이어야 하고, 이미 있는 폴더를 덮어쓰거나 번호를 붙여 다른 이름으로 만드는 일은 없어요. 팩의 완성된 폴더는 검사 전에 한 가지 모양으로 맞춰요(팩의 폴더를 한 가지 모양으로). 그 모양에서 너무 벗어난 폴더는 그대로 두고, 실행 로그에 경고로 남겨요.packs check가 풀 리퀘스트 때와 똑같이 모든 팩을 검사해요.- 각 팩은 github-actions[bot]이
Add the <name> pack이라는 메시지로 커밋하고main에 푸시해요. - 그다음에야
community done이 서비스에 팩이 공개됐다고 알려요. 검사나 푸시에 실패한 팩은 서비스에서 계속 기다리고, 다음 실행 때 다시 시도해요. - 같은 실행에서
index.json, 미리 보기,v2/를 다시 만들고,v2/를 미러로 복사한 다음(아래 참고) 커밋해요. 워크플로 자체 토큰으로 한 푸시는 다른 워크플로를 시작하지 않기 때문에, 이 모든 걸 한 번의 실행에서 처리해요.
그래서 팩은 승인 후 15분 안팎이면 공개되고, 서비스가 워크플로를 직접 시작하면 몇 분 안에 공개돼요. 실패한 실행은 아무것도 공개하지 않고, GitHub이 워크플로가 실패했다는 이메일을 메인테이너에게 보내요. GitHub은 바쁠 때 예약 실행을 늦게 시작할 수 있고, 60일 동안 활동이 없는 저장소에서는 예약을 꺼 버려요. repository dispatch는 이 두 가지 모두와 상관없어요.
워크플로에는 메인테이너의 서명 키, 즉 community keygen이 만든 파일 전체가 저장소 시크릿 FOLDERSKIN_ADMIN_KEY로 있어야 해요. 없으면 승인된 팩은 서비스에서 계속 기다리고, 실행 로그에도 그렇게 표시돼요. 저장소 변수 REQUIRE_GENERATED_IDS를 true로 설정하면, 모든 검사에서 생성된 ID가 없는 팩을 거절해요.
직접 가져오는 방법도 여전히 쓸 수 있어요. community pull은 팩을 쓰고 나서 곧바로 서비스에 알려요. 실행한 사람이 쓴 내용을 커밋한다고 보기 때문이에요. --no-done --pulled pulled.json을 붙이면 community done --from pulled.json을 실행할 때까지 알림을 미뤄요. 워크플로는 이 방식으로 실행해요. 서비스에 두 번 알려도 문제는 없어요.
앱은 전체 트리를 https://packs.folderskin.app에서 읽을 수 있어요. 이곳은 저장소와 같은 v2/를 담고 있는 Cloudflare R2 버킷이에요. 버킷에 쓰는 건 커뮤니티 서비스라서, 워크플로에는 따로 Cloudflare 토큰이 필요 없어요. community mirror가 각 파일을 메인테이너의 키로 서명해서 서비스를 통해 보내요.
cargo run -p folderskin-tools -- community mirror --tree ../folderskin-community \ --public https://packs.folderskin.app --api https://community.folderskin.app --key ~/folderskin-admin.key모든 파일에 대해 미러에 HEAD 요청을 보내고, 이미 같은 길이로 제공되고 있는 파일은 그대로 둬요. head.json을 제외한 모든 파일 이름은 내용의 해시이기 때문이에요. 나머지 파일은 PUT /v1/admin/tree/<path>로 올리고, 버킷이 확인할 수 있도록 각 파일의 SHA-256을 X-Content-SHA256에 담아 보내요. head.json은 다른 파일이 모두 올라간 뒤 마지막에 보내기 때문에, 미러가 갖고 있지 않은 카탈로그를 가리키는 일은 없어요. 시간이 지나면 통과할 수도 있는 요청(응답 없음, 5xx, 429)은 대기 시간을 1초부터 두 배씩 늘리며 모두 다섯 번까지 시도하고, Retry-After는 30초까지 따라요. 워크플로는 커밋하기 전에 이 명령을 실행하므로, GitHub의 head.json은 거기 적힌 모든 것이 미러에 올라간 다음에야 바뀌어요. 미러는 저장소 변수 COMMUNITY_MIRROR_URL로 켜고, head.json에는 미러가 켜져 있는 동안에만 미러가 들어가요.
팩 규격
섹션 제목: “팩 규격”팩은 폴더 하나예요.
packs/night-prints-h4x2qe/ pack.json koi.webp fox-in-the-rain.webppack.json은 이렇게 생겼어요.
{ "version": 1, "name": "Night prints", "author": "your-github-name", "license": "CC0-1.0", "tags": ["woodblock", "night"], "skins": [ { "file": "koi.webp", "name": "Koi over the wave", "tags": ["animals"] }, { "file": "fox-in-the-rain.webp", "name": "Fox in the rain" } ]}| 필드 | 규칙 |
|---|---|
version | 1 |
name | 1~40자 |
author | 내 GitHub 사용자 이름 |
license | CC0-1.0, CC-BY-4.0, MIT 중 하나 |
tags | 태그 1~5개. 팩의 모든 스킨에 붙고, 첫 번째 태그가 모두의 필터에서 이 팩을 나타내요 |
skins | 항목 1~50개 |
skins[].file | 폴더 안의 이미지 |
skins[].name | 1~60자 |
skins[].tags | 선택 사항. 그 스킨에만 최대 3개까지 더 붙일 수 있어요 |
다른 필드는 쓸 수 없어요. 그래서 "tag" 같은 오타는 무시되지 않고 검사에서 걸려요.
| 제한 | |
|---|---|
| 팩의 스킨 수 | 1~50개 |
| 이미지 한 장 | 무손실(PNG 또는 무손실 WebP). 최대 1.5MB |
| 팩의 이미지 합계 | 최대 64MB |
| 이미지 변의 길이 | 256~1024px |
| 파일 이름 | 영문자, 숫자, ., -, _만 쓰고 .png나 .webp로 끝나야 해요 |
| ID(폴더 이름) | 이름과 무작위 문자 6개. 예: night-prints-h4x2qe(팩 ID). 소문자 영문자와 숫자로 된 단어를 하이픈 하나로 이어 붙이고, 최대 40자 |
| 태그 | 소문자 영문자, 숫자, 공백, 하이픈으로 최대 24자 |
pack.json | 최대 64KB |
50개와 64MB인 이유는, 팩이 주제가 있는 세트이고 팩을 추가하는 사람은 전부를 내려받기 때문이에요. 스킨 50개면 검토도 금방 끝나고, 64MB면 한 장에 1.3MB인 이미지 50장, 가장 큰 크기라면 42장이 들어가요.
FolderSkin 0.1.7 이전에 공개된 팩은 PNG, JPEG 또는 모든 종류의 WebP로 한 장에 2MB까지라는 기준으로 만들어졌고, 앱은 지금도 이 팩들을 읽을 수 있어요. packs make로 다시 만들면 위 규칙을 따르게 돼요. packs check에 --require-lossless를 붙이면 팩을 이 규칙으로 검사하고(팩 직접 검사하기), 커뮤니티 서비스는 이 규칙에 맞는 팩만 받아요.
이미지
섹션 제목: “이미지”이미지는 두 종류 중 하나이고, 창에 끌어다 놓은 이미지와 같은 방법으로 구분해요.
- 완성된 폴더: 투명한 배경이나 단색 마젠타
#FF00FF배경 위에 그린 폴더예요. 마젠타는 FolderSkin이 잘라 내요. 그린 그대로 아이콘이 돼요. - 그 밖의 모든 이미지: FolderSkin의 폴더에 씌워져요. 폴더가 이미지의 어디를 잘라 내는지는 SKINS.md에 나와 있으니, 주제가 잘리지 않게 할 수 있어요.
세 운영체제 중 어디서든 가장 큰 아이콘은 1024px이라서, 그보다 큰 이미지는 아무 의미가 없어요.
모든 이미지는 무손실이라서 팩은 만든 그대로 보여요. 그라데이션에 블록 노이즈가 생기지 않고, 글자 주변에 링잉이 생기지 않으며, 완성된 폴더의 가장자리도 그린 그대로 깔끔해요. 무손실 WebP는 같은 이미지의 PNG보다 3분의 1 정도 작아서, 앱과 packs make는 WebP로 저장해요. 정밀한 1024px 이미지는 0.6MB에서 1.5MB 사이이고, 대부분은 800KB 정도예요. 1.5MB에 들어가지 않는 이미지는 흐리게 만들어 욱여넣지 않고, 무손실 그대로 896px, 그다음 768px로 줄여요.
팩의 폴더를 한 가지 모양으로
섹션 제목: “팩의 폴더를 한 가지 모양으로”FolderSkin은 완성된 폴더 이미지 전체를 아이콘에 맞춰 넣어요. 하나씩 만든 폴더는 저마다 자기 윤곽에 딱 맞게 잘려 나오고, 가로세로 비율이 똑같은 렌더링은 하나도 없어요. 어떤 팩은 폭이 높이의 1.03배에서 1.30배까지 제각각이었어요. Finder에서 나란히 놓으면 납작한 폴더가 길쭉한 폴더보다 작아 보였죠. 그래서 팩 안의 완성된 폴더는 한 가지 모양으로 맞춰요.
- 팩의 모양은 각 폴더 모양의 중앙값이에요. 폴더는 이미지에서 불투명도가 절반을 넘는 부분의 폭 ÷ 높이로 재기 때문에, 부드러운 가장자리나 희미한 그림자는 치지 않아요.
- 각 폴더를 정확히 그 모양으로 다시 그려요. 폴더 부분만 잘라 낸 다음, 폭은 962px(1024px 템플릿에서 FolderSkin 자체 폴더의 폭으로,
folderskin-tools template으로 볼 수 있어요), 높이는 그 모양에 맞는 값으로 크기를 바꿔요. 그런 다음 투명한 1024 × 1024 이미지 안에서 템플릿의 폴더와 같은 자리, 즉 같은 왼쪽 가장자리와 같은 기준선 위에 놓고 무손실 WebP로 저장해요. PNG는 이름이 같은.webp가 되고,pack.json도 그에 맞게 바뀌어요. - 모양은 최대 8%까지만 바꿔요. 그 정도는 아무도 알아채지 못해요. 그보다 많이 바꿔야 하는 폴더는 이상치예요. 그만큼 늘리면 글자와 얼굴이 찌그러져 보이기 때문에 모양을 바꾸지 않아요. 이상치를 어떻게 처리할지는 명령에 따라 다르고, 결정은 사람이 해요.
- 아트워크는 건드리지 않아요. FolderSkin이 자체 폴더에 씌우기 때문에 맞출 모양이 애초에 없어요. 완성된 폴더가 하나뿐인 팩도 마찬가지예요.
packs make는 만드는 모든 팩에, community pull은 가져오는 모든 팩에, packs normalize는 이미 packs/에 있는 팩에 이 작업을 해요. 두 번 해도 달라지는 건 없어요. 이미 팩의 모양으로 제자리에 있는 폴더는 다시 그리지 않고, 쓰려는 내용이 이미 들어 있는 파일은 다시 쓰지 않아요.
| 명령 | 이상치 처리 |
|---|---|
packs make | 팩에서 빼고 목록에 표시해요. --keep-outliers를 붙이면 그대로 남겨요 |
packs normalize | 목록에 표시하고 그대로 둬요. --drop-outliers를 붙이면 pack.json에서 빼고 이미지도 지워요 |
community pull | 그대로 두고 경고를 출력해요(Packs 워크플로 로그에 표시돼요). 누군가 공유한 스킨이 사람의 결정 없이 빠지는 일은 없어요 |
cargo run -p folderskin-tools -- packs normalize --dir ../folderskin-communitycargo run -p folderskin-tools -- packs normalize --dir ../folderskin-community classic-art-5rxas2 --tolerance 0.3cargo run -p folderskin-tools -- packs normalize --dir ../folderskin-community dreamscapes-ppfia6 --drop-outlierspacks normalize는 모든 팩, 또는 지정한 팩을 차례로 처리해요. 팩마다 모양과 다시 그린 폴더를 보여 주고, 이상치는 얼마나 벗어났는지와 함께 모두 알려 줘요. packs check를 통과하지 못하는 팩은 통과할 때까지 손대지 않아요. --tolerance는 8%라는 값을 바꿔요. Classic Art의 모나리자처럼, 이상치라도 팩의 모양에 맞추고 싶을 때 써요. --drop-outliers는 메인테이너용이에요. 이것 말고는 스킨을 지우는 건 아무것도 없어요. packs check --require-one-shape는 폴더끼리 1% 넘게 차이 나는 팩을 거절해요(팩 직접 검사하기). 한 가지 모양으로 다시 그린 폴더는 거절되는 일이 없어요.
라이선스
섹션 제목: “라이선스”공유하는 스킨에는 크리에이티브 커먼즈나 MIT 라이선스를 써요.
CC0-1.0: 누구나 어떤 목적으로든 쓸 수 있어요. 대부분의 스킨은 AI로 만들고, CC0는 권리를 가장 적게 주장하기 때문에 이게 기본값이에요.CC-BY-4.0: 누구나 쓸 수 있고, 쓰는 사람은 나를 만든 사람으로 표시해요.MIT: 누구나 쓸 수 있고, 내 이름이 함께 남아요.
직접 만들었거나 공유해도 되는 이미지만 공유하세요.
이미지로 팩 만들기
섹션 제목: “이미지로 팩 만들기”packs make는 이미지 모델에서 저장한 렌더링 같은 이미지 폴더를, folderskin-community 체크아웃의 packs/ 아래에 검사를 바로 통과하는 팩으로 만들어요. 아래 명령은 이 저장소에서, 옆에 folderskin-community를 체크아웃한 상태로 실행해요.
cargo run -p folderskin-tools -- packs make ~/Downloads/3d-renders --dir ../folderskin-community \ --name "3D" --tags 3d,glossy --author your-github-name --preview /tmp/3d.png팩에는 이름과 무작위 문자 6개로 된 3d-k7q2mx 같은 고유 ID가 붙고, 이게 폴더 이름이 돼요. ID는 보고서에 나와요. packs/에 있는 폴더 이름이나 moved.json의 옛 ID와 겹치는 일은 없어요.
이미지는 앱에서 이미지를 추가할 때와 똑같이 나뉘어요. PROMPTS.md의 채팅 프롬프트대로 마젠타 위에 그렸거나 실제로 투명한 배경 위에 그린 완성된 폴더는 잘라 내서 그대로 아이콘으로 써요. 그 밖의 이미지는 FolderSkin 폴더에 쓰일 아트워크가 돼요. 모든 이미지는 1024px로 줄여 무손실 WebP로 저장하는데, 앱이 팩을 공유할 때 쓰는 인코더가 내장되어 있어서 따로 설치할 건 없어요. 그래도 1.5MB가 넘는 이미지는 896px, 그다음 768px로 줄이고, 보고서에 그렇게 표시해요. --max-kb를 쓰면 이미지를 1.5MB보다 더 작게 제한할 수 있어요. 이미지를 모두 합쳐 64MB가 넘으면 거절하고, 팩을 두 개로 나누라고 제안해요. libwebp의 가장 꼼꼼한 설정은 이미지 한 장에 몇 초가 걸리기 때문에, 모든 코어에서 한꺼번에 처리해요. 형식에 대한 자세한 내용은 SKINS.md에 있어요. 각 이미지가 어느 쪽으로 처리됐는지는 보고서에 나와요. 스킨 이름은 파일 이름에서 따오니, 파일 이름을 먼저 정리하거나 나중에 pack.json에서 이름을 고치세요. --preview를 쓰면 모든 스킨을 폴더 모양으로 PNG 한 장에 그려 주니 한눈에 확인할 수 있어요.
완성된 폴더가 둘 이상이면 한 가지 모양으로 맞추고(팩의 폴더를 한 가지 모양으로), 어떤 폴더를 다시 그렸는지 보고서에 표시해요. 다른 폴더의 모양에서 8% 넘게 벗어난 폴더는 팩에서 빼고, 얼마나 벗어났는지 보고서에 표시해요. --keep-outliers를 붙이면 빼지 않고 그대로 둬요. 이상치를 남기지 않고 만든 팩은 packs check --require-one-shape를 통과해요.
--id를 쓰면 이미 있는 팩을 새 이미지로 다시 만들 수 있어요. --id 3d-k7q2mx는 packs/3d-k7q2mx/의 내용을 모두 바꾸고, 팩의 ID는 그대로 유지되기 때문에 이 팩을 추가한 모든 사람이 새 버전을 업데이트로 받아요. 새 폴더는 먼저 다른 곳에서 만들어 검사하고, 통과할 때만 옛 폴더를 대신해요. --id 없이 실행하면 packs make는 항상 새 팩을 만들어요.
이미지 모델에 #FF00FF를 요청해도 대신 라즈베리색이나 핫핑크로 고르게 칠하는 경우가 많아요(Classic Art 팩을 만들 때 Grok이 그랬어요). --flat-backdrop은 어떤 색이든 단색 배경이면 잘라 내요. 이미지마다 배경색을 직접 재고, 가장자리까지 이어진 부분만 지우고(그래서 폴더 안의 빨간 망토는 남아요), 부드러운 그림자도 함께 지운 다음, 가장자리에는 분홍 테두리 대신 그림의 색을 입혀요. 무늬 없는 회색이나 검은 배경에서는 그 배경 자체의 노이즈 범위 안에서만 지우고 위쪽으로 번지지 않기 때문에, 폴더 가장자리에 닿은 어두운 코트나 잉크 선을 배경으로 착각하지 않아요. 나중에 --preview 시트를 확인하세요(밝은 회색 위에 그리기 때문에 구멍이 있으면 보여요). 단색 배경이 없는 이미지는 여전히 아트워크로 처리돼요.
이미지 한 장이 앱에서 어떻게 보일지 보려면, render로 폴더 모양으로 그려 보세요.
cargo run -p folderskin-tools -- render ../folderskin-community/packs/3d-k7q2mx/glass.webp --out /tmp/glass.png --size 512팩 직접 검사하기
섹션 제목: “팩 직접 검사하기”이 저장소에서, 옆에 folderskin-community를 체크아웃한 상태로 실행해요.
cargo run -p folderskin-tools -- packs check --dir ../folderskin-communitypacks/의 모든 폴더를 앱과 같은 규칙으로 검사하고, 문제가 있으면 하나하나 문장으로 알려 줘요. folderskin-community 체크아웃 안에서 실행하면 --dir은 생략해도 돼요. 도구가 기본으로 현재 폴더를 보기 때문이에요. 이미지는 한 장에 2MB(0.1.7 이전에 공개된 팩의 기준)까지인지, 팩 하나의 이미지는 합쳐서 64MB까지인지 검사해요. --require-lossless를 붙이면 새 팩이 따르는 규칙으로 모든 이미지를 검사해요. PNG 또는 무손실 WebP에 최대 1.5MB라는 규칙으로, 커뮤니티 서비스가 받고 packs make가 만드는 형식이에요. 예전 팩을 모두 다시 만들 때까지는 따로 요청하지 않으면 꺼져 있어요. --max-kb를 쓰면 이미지를 더 작게 제한할 수 있어요.
ID끼리의 관계도 검사해요. 대소문자만 다른 두 폴더 이름은 macOS와 Windows에서 같은 폴더가 돼 버려요. 또 팩은 moved.json에 있는 옛 ID를 쓸 수 없고, moved.json 자체도 정해진 규칙을 따라야 해요(moved.json). 이름은 겹쳐도 되기 때문에 비교하지 않아요. --require-generated-ids는 ID가 생성된 형식이 아닌 팩을 모두 거절해요. 따로 요청하지 않으면 꺼져 있고, folderskin-community의 워크플로는 변수 REQUIRE_GENERATED_IDS가 true일 때 이 옵션을 켜요.
--require-one-shape는 완성된 폴더의 모양이 하나로 맞지 않는 팩, 즉 폭 ÷ 높이가 1% 넘게 차이 나는 폴더가 두 개 있는 팩을 거절해요. 한 가지 모양으로 다시 그린 폴더는 항상 이 범위 안에 있으므로, 모양을 한 번도 맞추지 않은 팩이나 누군가 남겨 둔 이상치를 잡아낼 수 있어요. 오류에는 가장 많이 차이 나는 두 폴더의 이름과 실행할 명령이 나와요(팩의 폴더를 한 가지 모양으로). 따로 요청하지 않으면 꺼져 있고, folderskin-community의 워크플로를 위한 옵션이에요.
앱이 팩을 읽는 방식
섹션 제목: “앱이 팩을 읽는 방식”커뮤니티에는 목록 보기와 갤러리 보기가 있고, 팩에서 보기를 누르면 그 팩이 열려요. 아무것도 추가하기 전에 모든 스킨이 이름과 함께 폴더에 입혀진 모습으로 보여요. 새로 고침을 누르면 목록을 다시 읽어요. 추가한 뒤에 바뀐 팩에는 업데이트가 표시되고, 누르면 스킨이 새 버전으로 바뀌어요. 폴더의 아이콘은 그대로 남고, 두 버전에 모두 있는 이미지를 즐겨찾기해 두었다면 즐겨찾기도 그대로 남아요.
- folderskin-community의
index.json에는 모든 팩이 나열돼 있어요. ID, 이름, 제작자, 라이선스, 태그, 스킨 수, 그리고 정확한 내용(pack.json과 모든 이미지)의 해시예요. 앱은 추가한 스킨과 함께 이 해시를 저장해 두고, 이걸로 팩에 업데이트가 있는지 알아요. 각 항목에는 팩이 처음 공개된 시각("added". Unix 초 단위로, 첫 ID로pack.json을 추가한 커밋의 시각)과,official.json에 있는 팩이면"official": true도 들어 있어요. 팩 목록 옆의"moved"는 moved.json이에요.index.json은folderskin-tools packs index가 만들고, 이때 팩의 처음 스킨 4개를 폴더 모양으로 나란히 그린previews/<id>.png도 함께 만들어요. 둘 다 folderskin-community의main에서 생성되니 절대 직접 고치지 마세요. packs catalog가 만드는v2/는 같은 팩들을 앱이 내 컴퓨터에서 검색하는 카탈로그 형태로 담은 거예요. 여기 있는head.json은 현재 카탈로그를 가리키고,featured와official팩을 나열하고,moved를 담고,https://packs.folderskin.app처럼 같은 트리를 제공하는 미러를 나열해요(미러). 앱은 각 파일을 먼저 미러에서 가져오고, 실패하면 GitHub에서 가져와요. 어느 쪽이든 모든 파일을 해시로 확인해요. 0.1.7부터는head.json자체도 먼저https://packs.folderskin.app에서 읽어요.- 앱은 팩을 추가할 때만 팩의 이미지를 내려받아요. 한 번에 네 장씩 받으면서 몇 장이 도착했는지 보여 줘요. 모든 이미지를 위의 제한에 비추어 검사하고, 하나라도 통과하지 못하면 아무것도 저장하지 않아요. 모두 통과하면 한꺼번에 저장하기 때문에 팩이 반만 추가되는 일은 없어요.
FOLDERSKIN_COMMUNITY_URL을 설정하면 앱이 folderskin-community의 다른 사본을 보게 돼요. 예를 들어 체크아웃의 루트에서python3 -m http.server로 서버를 띄우고 이 변수를http://localhost:8000으로 설정하면, 팩을 처음부터 끝까지 시험해 볼 수 있어요.
index.json과 head.json에는 시간이 지나면서 필드가 늘어나지만, 어떤 버전의 앱이든 아는 필드만 읽고 나머지는 건너뛰어요. pack.json은 그 반대예요. 규격에 없는 필드를 받지 않기 때문에, 앞으로도 아무것도 추가할 수 없어요. 팩에 관한 새로운 정보는 인덱스에 넣어요.
추천 팩과 공식 팩
섹션 제목: “추천 팩과 공식 팩”folderskin-community의 루트에는 packs/ 옆에 목록 두 개가 있고, 메인테이너만 이 목록을 고쳐요. 둘 다 ["classic-art-k7q2mx", "colours-a2b3c4"] 같은 팩 ID의 JSON 목록이에요.
| 파일 | 역할 |
|---|---|
featured.json | 첫 실행 때 추천하고 커뮤니티에서 맨 먼저 보여 주는 팩(이 순서대로) |
official.json | 커뮤니티, 팩 뷰어, 웹사이트에서 공식 표시가 붙는 팩 |
두 파일 모두 없어도 돼요. 모든 ID는 packs/에 있는 팩이어야 하고, 두 번 나온 ID는 한 번으로 쳐요. 둘 중 하나라도 없는 팩을 가리키면 packs index와 packs catalog는 멈추고 아무것도 쓰지 않아요. 그래서 ID를 바꾸거나 지운 팩이 빈자리로 남는 일은 없어요. 풀 리퀘스트 검사에서 packs catalog를 실행하기 때문에 병합 전에 잡아낼 수 있어요. packs rename은 두 목록을 알아서 고쳐 줘요. folderskin-community의 Packs 워크플로는 paths에 있는 파일이 바뀌면 인덱스를 다시 만들어요. 그래서 두 목록은 packs/**와 함께 paths에 들어 있어야 하고, moved.json도 마찬가지예요.
설치 링크
섹션 제목: “설치 링크”folderskin://install?pack=<id>를 열면 FolderSkin이 커뮤니티에서 <id> 팩을 보여 주고 추가해요. 팩의 추가 버튼과 똑같이 동작하고, 진행 표시와 마지막 메시지도 같아요. 먼저 창이 맨 앞으로 나와요. GitHub에 다시 물어봐도 커뮤니티에 그 ID의 팩이 없으면, FolderSkin이 그렇다고 알려 주고 검색해 보라고 제안해요. 이미 라이브러리에 있는 팩이면 팩을 열고 이미 있다고 알려 줘요. 0.1.7부터는 옛 ID가 든 링크를 열면 옮겨 간 팩이 열려요(moved.json).
FolderSkin은 정확히 다음 형식인 링크만 받아요. 스킴은 folderskin이고, install이 호스트(folderskin://install?…)이거나 경로 전체(folderskin:install?…)여야 해요. 사용자, 비밀번호, 포트는 없어야 하고, pack이 정확히 하나 있어야 하며, 그 값은 팩 ID(소문자 영문자와 숫자로 된 단어를 하이픈 하나로 이어 붙인 최대 40자)여야 해요. 다른 매개변수는 건너뛰고, 그 밖의 링크는 무시해요.
스킴은 설치 프로그램이 등록해요. macOS는 앱의 Info.plist, Windows는 설치 프로그램, Linux는 .deb와 .rpm의 데스크톱 항목이에요. AppImage는 설치하는 과정이 없어서 실행할 때 직접 등록해요. Windows와 Linux에서는 링크를 열면 두 번째 FolderSkin이 실행되어 이미 실행 중인 FolderSkin에 링크를 넘기고 종료하기 때문에, 항상 하나만 실행돼요.
시험해 보려면:
| macOS | 앱을 빌드하고(pnpm tauri build --bundles app) target/release/bundle/macos/FolderSkin.app을 한 번 열어 macOS에 스킴을 등록해요(/Applications에 복사한 사본이 가장 확실해요). 그런 다음 open 'folderskin://install?pack=classic-art'를 실행해요. macOS는 번들로 만든 앱에만 링크를 보내기 때문에 pnpm tauri dev는 링크를 받지 못해요 |
| Windows | 빌드를 설치하거나 pnpm tauri dev를 실행해요(개발 빌드는 스스로 스킴을 등록해요). 그런 다음 명령 프롬프트에서 start "" "folderskin://install?pack=classic-art"를 실행하거나, 실행 창(Windows+R)에 같은 링크를 입력해요 |
| Linux | .deb나 .rpm을 설치하거나, AppImage를 한 번 실행하거나, pnpm tauri dev를 실행해요. 그런 다음 xdg-open 'folderskin://install?pack=classic-art'를 실행해요 |
| 브라우저 미리 보기 | pnpm dev를 실행하고 http://localhost:14200/?install=classic-art를 열어요 |
Windows나 Linux에서 pnpm tauri dev를 실행하기 전에 설치된 FolderSkin을 종료하세요. 이미 실행 중인 FolderSkin이 있으면 새로 실행한 쪽이 링크를 넘기고 종료해 버려요. 스킴을 등록한 개발 빌드는 설치 프로그램이나 다른 빌드가 다시 등록할 때까지 그 등록을 유지해요.
설치 수
섹션 제목: “설치 수”커뮤니티의 팩이 추가되면 FolderSkin은 커뮤니티 서비스에 그 팩의 ID를 알려요. 본문 없이 POST https://community.folderskin.app/v1/packs/<id>/installs를 보내는 게 전부예요. 계정도, 기기 ID도, 라이브러리나 폴더에 관한 정보도 보내지 않아요(FolderSkin의 다른 모든 요청처럼 User-Agent에는 앱 버전이 들어가요). 이 요청은 팩을 저장한 다음에 보내고, 5초가 지나면 포기해요. 이 요청을 기다리는 것도 없고, 실패해도 알리지 않아요. 개발 빌드와, 다른 사본에서 팩을 읽는 빌드(FOLDERSKIN_COMMUNITY_URL)는 FOLDERSKIN_COMMUNITY_API로 보낼 서비스를 지정하지 않는 한 아무것도 보내지 않아요.
서비스는 네트워크와 팩마다 하루에 한 번만 추가를 세고, 공개된 index.json에 있는 팩만 대상으로 해요. 옛 ID로 추가한 것은 옮겨 간 팩으로 세기 때문에, 0.1.7 이전 앱에서 추가한 것도 계속 세어져요. 서비스는 팩별 횟수와, 그날(UTC)이 끝날 때까지 요청이 온 네트워크의 솔트 처리된 해시를 보관해요. 그래서 같은 날 같은 팩을 다시 추가해도 두 번 세지 않아요. 이 해시는 매일 하는 정리 작업에서 지워져요. 주소는 저장하지 않아요.
folderskin.app은 GET https://community.folderskin.app/v1/packs/installs에서 횟수를 읽어요. 응답은 {"version": 1, "installs": {"classic-art-k7q2mx": 42}} 형태이고, 5분 동안 캐시돼요. 자세한 내용은 services/community/README.md에 있어요.