github-project-manager 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/backend/dist/modules/milestone/milestone.controller.d.ts +12 -0
- package/apps/backend/dist/modules/milestone/milestone.controller.js +59 -0
- package/apps/backend/dist/modules/milestone/milestone.controller.js.map +1 -0
- package/apps/backend/dist/modules/milestone/milestone.module.js +7 -2
- package/apps/backend/dist/modules/milestone/milestone.module.js.map +1 -1
- package/apps/backend/dist/modules/milestone/milestone.service.d.ts +15 -0
- package/apps/backend/dist/modules/milestone/milestone.service.js +75 -0
- package/apps/backend/dist/modules/milestone/milestone.service.js.map +1 -0
- package/apps/backend/dist/modules/project/project.controller.d.ts +1 -0
- package/apps/backend/dist/modules/project/project.controller.js +16 -0
- package/apps/backend/dist/modules/project/project.controller.js.map +1 -1
- package/apps/backend/dist/modules/project/project.entity.d.ts +2 -0
- package/apps/backend/dist/modules/project/project.entity.js +10 -0
- package/apps/backend/dist/modules/project/project.entity.js.map +1 -1
- package/apps/backend/dist/modules/project/project.service.d.ts +1 -0
- package/apps/backend/dist/modules/project/project.service.js +3 -0
- package/apps/backend/dist/modules/project/project.service.js.map +1 -1
- package/apps/backend/dist/modules/sync/github.service.d.ts +18 -0
- package/apps/backend/dist/modules/sync/github.service.js +14 -4
- package/apps/backend/dist/modules/sync/github.service.js.map +1 -1
- package/apps/backend/dist/modules/sync/sync.controller.d.ts +3 -2
- package/apps/backend/dist/modules/sync/sync.controller.js +29 -6
- package/apps/backend/dist/modules/sync/sync.controller.js.map +1 -1
- package/apps/backend/dist/modules/sync/sync.module.js +2 -1
- package/apps/backend/dist/modules/sync/sync.module.js.map +1 -1
- package/apps/backend/dist/modules/sync/sync.service.d.ts +5 -1
- package/apps/backend/dist/modules/sync/sync.service.js +80 -2
- package/apps/backend/dist/modules/sync/sync.service.js.map +1 -1
- package/apps/backend/dist/modules/task/task.controller.d.ts +6 -6
- package/apps/backend/dist/modules/task/task.controller.js +41 -27
- package/apps/backend/dist/modules/task/task.controller.js.map +1 -1
- package/apps/backend/dist/modules/task/task.entity.d.ts +10 -1
- package/apps/backend/dist/modules/task/task.entity.js +27 -0
- package/apps/backend/dist/modules/task/task.entity.js.map +1 -1
- package/apps/backend/dist/modules/task/task.service.js +2 -2
- package/apps/backend/dist/modules/task/task.service.js.map +1 -1
- package/apps/cli/dist/commands/hooks.d.ts +11 -0
- package/apps/cli/dist/commands/hooks.js +113 -0
- package/apps/cli/dist/commands/hooks.js.map +1 -0
- package/apps/cli/dist/commands/init.d.ts +4 -1
- package/apps/cli/dist/commands/init.js +16 -1
- package/apps/cli/dist/commands/init.js.map +1 -1
- package/apps/cli/dist/hooks/hook-registry.d.ts +9 -0
- package/apps/cli/dist/hooks/hook-registry.js +29 -0
- package/apps/cli/dist/hooks/hook-registry.js.map +1 -0
- package/apps/cli/dist/index.js +23 -2
- package/apps/cli/dist/index.js.map +1 -1
- package/apps/cli/dist/utils/settings.d.ts +10 -0
- package/apps/cli/dist/utils/settings.js +93 -0
- package/apps/cli/dist/utils/settings.js.map +1 -0
- package/apps/cli/dist/utils/template.d.ts +1 -0
- package/apps/cli/dist/utils/template.js +20 -0
- package/apps/cli/dist/utils/template.js.map +1 -0
- package/apps/frontend/dist/assets/index-C-VkfAuU.css +1 -0
- package/apps/frontend/dist/assets/index-CJ-smTcW.js +92 -0
- package/apps/frontend/dist/index.html +2 -2
- package/package.json +2 -2
- package/templates/claude-settings-hooks.json +35 -0
- package/templates/hooks/gpm-commit-linker.sh +72 -0
- package/templates/hooks/gpm-response-suggest.sh +63 -0
- package/templates/hooks/gpm-session-briefing.sh +104 -0
- package/apps/frontend/dist/assets/index-5kQElBCK.css +0 -1
- package/apps/frontend/dist/assets/index-DzMlDUgv.js +0 -60
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>GPM - GitHub Project Manager</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-CJ-smTcW.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-C-VkfAuU.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "github-project-manager",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "GitHub Project V2 manager - CLI + Web UI for task management with Claude Code",
|
|
5
5
|
"bin": {
|
|
6
6
|
"gpm": "./apps/cli/bin/gpm.js"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"apps/*"
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
|
-
"dev:backend": "yarn workspace @gpm/backend
|
|
19
|
+
"dev:backend": "yarn workspace @gpm/backend dev",
|
|
20
20
|
"dev:frontend": "yarn workspace @gpm/frontend dev",
|
|
21
21
|
"build:backend": "yarn workspace @gpm/backend build",
|
|
22
22
|
"build:frontend": "yarn workspace @gpm/frontend build",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"UserPromptSubmit": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": ".claude/hooks/gpm-session-briefing.sh"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"PostToolUse": [
|
|
14
|
+
{
|
|
15
|
+
"matcher": "Bash",
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"type": "command",
|
|
19
|
+
"command": ".claude/hooks/gpm-commit-linker.sh"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"Stop": [
|
|
25
|
+
{
|
|
26
|
+
"hooks": [
|
|
27
|
+
{
|
|
28
|
+
"type": "command",
|
|
29
|
+
"command": ".claude/hooks/gpm-response-suggest.sh"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -u
|
|
3
|
+
# GPM Commit Linker — PostToolUse hook (Bash)
|
|
4
|
+
# 커밋 감지 시 In Progress 태스크와 연결하고 완료 처리를 제안
|
|
5
|
+
# stderr → 사용자 알림으로 표시
|
|
6
|
+
# 필수: jq
|
|
7
|
+
|
|
8
|
+
# jq 없으면 스킵
|
|
9
|
+
command -v jq &>/dev/null || exit 0
|
|
10
|
+
|
|
11
|
+
# gpm CLI 실행 (gpm 직접 사용 → npx fallback)
|
|
12
|
+
gpm_cli() {
|
|
13
|
+
if command -v gpm &>/dev/null; then
|
|
14
|
+
gpm "$@"
|
|
15
|
+
else
|
|
16
|
+
npx github-project-manager "$@"
|
|
17
|
+
fi
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# stdin에서 hook input JSON 읽기
|
|
21
|
+
INPUT=$(cat)
|
|
22
|
+
|
|
23
|
+
# tool_name, command 추출
|
|
24
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
25
|
+
[ "$TOOL_NAME" != "Bash" ] && exit 0
|
|
26
|
+
|
|
27
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
28
|
+
|
|
29
|
+
# git commit 명령이 아니면 스킵
|
|
30
|
+
echo "$COMMAND" | grep -qE 'git\s+commit' || exit 0
|
|
31
|
+
|
|
32
|
+
# 프로젝트 루트 및 .gpmrc 확인
|
|
33
|
+
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "")"
|
|
34
|
+
GPMRC="${PROJECT_ROOT:-.}/.gpmrc"
|
|
35
|
+
[ ! -f "$GPMRC" ] && exit 0
|
|
36
|
+
|
|
37
|
+
# 최근 커밋 메시지 가져오기
|
|
38
|
+
COMMIT_MSG=$(cd "$PROJECT_ROOT" && git log -1 --format="%s" 2>/dev/null || echo "")
|
|
39
|
+
[ -z "$COMMIT_MSG" ] && exit 0
|
|
40
|
+
|
|
41
|
+
# In Progress 태스크 조회
|
|
42
|
+
cd "$PROJECT_ROOT" || exit 0
|
|
43
|
+
TASKS=$(gpm_cli task list --json --limit 30 2>/dev/null || echo "")
|
|
44
|
+
[ -z "$TASKS" ] || [ "$TASKS" = "[]" ] && exit 0
|
|
45
|
+
|
|
46
|
+
IN_PROGRESS=$(echo "$TASKS" | jq -r '
|
|
47
|
+
[.[] | select(.status == "In Progress")]
|
|
48
|
+
| if length > 0 then
|
|
49
|
+
map("#\(.number // .id): \(.title)")
|
|
50
|
+
| join(", ")
|
|
51
|
+
else "없음" end
|
|
52
|
+
' 2>/dev/null || echo "없음")
|
|
53
|
+
IN_PROGRESS_COUNT=$(echo "$TASKS" | jq '[.[] | select(.status == "In Progress")] | length' 2>/dev/null || echo 0)
|
|
54
|
+
|
|
55
|
+
# 커밋 마커 파일 생성 (Stop hook에서 참조)
|
|
56
|
+
PROJ_HASH=$(echo "$PROJECT_ROOT" | md5 -q 2>/dev/null || echo "$PROJECT_ROOT" | md5sum 2>/dev/null | cut -d' ' -f1)
|
|
57
|
+
PROJ_HASH="${PROJ_HASH:0:12}"
|
|
58
|
+
echo "$COMMIT_MSG" > "/tmp/gpm-commit-marker-${PROJ_HASH}"
|
|
59
|
+
|
|
60
|
+
# 브리핑 캐시 무효화 (다음 프롬프트에서 갱신되도록)
|
|
61
|
+
rm -f "/tmp/gpm-briefing-${PROJ_HASH}"
|
|
62
|
+
|
|
63
|
+
# 알림 출력 (stderr → 사용자에게 표시)
|
|
64
|
+
{
|
|
65
|
+
echo "[GPM] 커밋 감지: \"${COMMIT_MSG}\""
|
|
66
|
+
if [ "$IN_PROGRESS_COUNT" != "0" ] && [ "$IN_PROGRESS_COUNT" != "?" ]; then
|
|
67
|
+
echo "In Progress: ${IN_PROGRESS}"
|
|
68
|
+
echo "태스크를 완료했다면 /gpm done 을 실행하세요."
|
|
69
|
+
fi
|
|
70
|
+
} >&2
|
|
71
|
+
|
|
72
|
+
exit 0
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -u
|
|
3
|
+
# GPM Response Suggest — Stop hook
|
|
4
|
+
# 커밋 후 응답 완료 시 Claude가 태스크 완료/다음 작업을 제안하도록 유도
|
|
5
|
+
# stdout JSON {"decision": "block", "reason": "..."} → Claude 추가 응답 트리거
|
|
6
|
+
# 필수: jq
|
|
7
|
+
|
|
8
|
+
# jq 없으면 스킵
|
|
9
|
+
command -v jq &>/dev/null || exit 0
|
|
10
|
+
|
|
11
|
+
# gpm CLI 실행 (gpm 직접 사용 → npx fallback)
|
|
12
|
+
gpm_cli() {
|
|
13
|
+
if command -v gpm &>/dev/null; then
|
|
14
|
+
gpm "$@"
|
|
15
|
+
else
|
|
16
|
+
npx github-project-manager "$@"
|
|
17
|
+
fi
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
INPUT=$(cat)
|
|
21
|
+
|
|
22
|
+
# 무한 루프 방지: stop_hook_active가 true이면 즉시 종료 (Claude 중단 허용)
|
|
23
|
+
ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false' 2>/dev/null)
|
|
24
|
+
[ "$ACTIVE" = "true" ] && exit 0
|
|
25
|
+
|
|
26
|
+
# 프로젝트 확인
|
|
27
|
+
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "")"
|
|
28
|
+
GPMRC="${PROJECT_ROOT:-.}/.gpmrc"
|
|
29
|
+
[ ! -f "$GPMRC" ] && exit 0
|
|
30
|
+
|
|
31
|
+
# 커밋 마커 확인 (gpm-commit-linker.sh에서 생성)
|
|
32
|
+
PROJ_HASH=$(echo "$PROJECT_ROOT" | md5 -q 2>/dev/null || echo "$PROJECT_ROOT" | md5sum 2>/dev/null | cut -d' ' -f1)
|
|
33
|
+
PROJ_HASH="${PROJ_HASH:0:12}"
|
|
34
|
+
MARKER="/tmp/gpm-commit-marker-${PROJ_HASH}"
|
|
35
|
+
|
|
36
|
+
# 마커 없으면 조용히 종료 → Claude 정상 중단
|
|
37
|
+
[ ! -f "$MARKER" ] && exit 0
|
|
38
|
+
|
|
39
|
+
# 마커 읽고 삭제
|
|
40
|
+
COMMIT_MSG=$(cat "$MARKER")
|
|
41
|
+
rm -f "$MARKER"
|
|
42
|
+
|
|
43
|
+
# In Progress 태스크 조회
|
|
44
|
+
cd "$PROJECT_ROOT" || exit 0
|
|
45
|
+
TASKS=$(gpm_cli task list --json --limit 20 2>/dev/null || echo "")
|
|
46
|
+
|
|
47
|
+
IN_PROGRESS="확인 필요"
|
|
48
|
+
if [ -n "$TASKS" ] && [ "$TASKS" != "[]" ]; then
|
|
49
|
+
IN_PROGRESS=$(echo "$TASKS" | jq -r '
|
|
50
|
+
[.[] | select(.status == "In Progress")]
|
|
51
|
+
| if length > 0 then
|
|
52
|
+
map("#\(.number // .id) \(.title)")
|
|
53
|
+
| join(", ")
|
|
54
|
+
else "없음" end
|
|
55
|
+
' 2>/dev/null || echo "확인 필요")
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Claude에게 전달할 메시지 구성 + JSON 안전 출력
|
|
59
|
+
REASON="[GPM Stop Hook] 커밋이 감지되었습니다. In Progress 태스크: ${IN_PROGRESS}. 이 커밋으로 완료된 태스크가 있는지 사용자에게 간결하게(1-2줄) 물어보고, 완료했다면 /gpm done을 제안하세요. 다음 작업이 필요하면 /gpm next를 안내하세요."
|
|
60
|
+
|
|
61
|
+
jq -n --arg reason "$REASON" '{"decision": "block", "reason": $reason}'
|
|
62
|
+
|
|
63
|
+
exit 0
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -u
|
|
3
|
+
# GPM Session Briefing — UserPromptSubmit hook
|
|
4
|
+
# 세션 시작 시 In Progress 태스크와 마일스톤별 다음 추천 태스크를 표시
|
|
5
|
+
# stdout → Claude context에 주입
|
|
6
|
+
|
|
7
|
+
# gpm CLI 실행 (gpm 직접 사용 → npx fallback)
|
|
8
|
+
gpm_cli() {
|
|
9
|
+
if command -v gpm &>/dev/null; then
|
|
10
|
+
gpm "$@"
|
|
11
|
+
else
|
|
12
|
+
npx github-project-manager "$@"
|
|
13
|
+
fi
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "")"
|
|
17
|
+
GPMRC="${PROJECT_ROOT:-.}/.gpmrc"
|
|
18
|
+
|
|
19
|
+
# .gpmrc 없으면 스킵
|
|
20
|
+
[ ! -f "$GPMRC" ] && exit 0
|
|
21
|
+
|
|
22
|
+
# 프로젝트 해시 (캐시 키)
|
|
23
|
+
PROJ_HASH=$(echo "$PROJECT_ROOT" | md5 -q 2>/dev/null || echo "$PROJECT_ROOT" | md5sum 2>/dev/null | cut -d' ' -f1)
|
|
24
|
+
PROJ_HASH="${PROJ_HASH:0:12}"
|
|
25
|
+
CACHE_FILE="/tmp/gpm-briefing-${PROJ_HASH}"
|
|
26
|
+
|
|
27
|
+
# 30분 캐시 (1800초) — 빈번한 CLI 호출 방지
|
|
28
|
+
if [ -f "$CACHE_FILE" ]; then
|
|
29
|
+
CACHE_MOD=$(stat -f %m "$CACHE_FILE" 2>/dev/null || stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0)
|
|
30
|
+
CACHE_AGE=$(( $(date +%s) - CACHE_MOD ))
|
|
31
|
+
if [ "$CACHE_AGE" -lt 1800 ]; then
|
|
32
|
+
cat "$CACHE_FILE"
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# 태스크 목록 조회 (로컬 SQLite 캐시, GitHub API 호출 없음)
|
|
38
|
+
cd "$PROJECT_ROOT" || exit 0
|
|
39
|
+
TASKS=$(gpm_cli task list --json --limit 50 2>/dev/null || echo "")
|
|
40
|
+
|
|
41
|
+
# 빈 결과 또는 에러 시 스킵
|
|
42
|
+
if [ -z "$TASKS" ] || [ "$TASKS" = "[]" ]; then
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# jq 사용 가능 여부 확인
|
|
47
|
+
if ! command -v jq &>/dev/null; then
|
|
48
|
+
cat > "$CACHE_FILE" << 'NOJQ'
|
|
49
|
+
### GPM 브리핑
|
|
50
|
+
태스크 현황을 확인하려면 `/gpm status`를 실행하세요.
|
|
51
|
+
NOJQ
|
|
52
|
+
cat "$CACHE_FILE"
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# 태스크 파싱
|
|
57
|
+
IN_PROGRESS=$(echo "$TASKS" | jq -r '
|
|
58
|
+
[.[] | select(.status == "In Progress")]
|
|
59
|
+
| if length > 0 then
|
|
60
|
+
map("- #\(.number // .id): \(.title)\(if .milestone then " [\(.milestone)]" else "" end)")
|
|
61
|
+
| join("\n")
|
|
62
|
+
else empty end
|
|
63
|
+
' 2>/dev/null || echo "")
|
|
64
|
+
|
|
65
|
+
IN_PROGRESS_COUNT=$(echo "$TASKS" | jq '[.[] | select(.status == "In Progress")] | length' 2>/dev/null || echo 0)
|
|
66
|
+
|
|
67
|
+
TODO_LIST=$(echo "$TASKS" | jq -r '
|
|
68
|
+
[.[] | select(.status == "Todo" or .status == "TODO" or .status == "To Do")]
|
|
69
|
+
| sort_by(.milestone // "zzz")
|
|
70
|
+
| .[0:5]
|
|
71
|
+
| if length > 0 then
|
|
72
|
+
map("- #\(.number // .id): \(.title)\(if .milestone then " [\(.milestone)]" else "" end)")
|
|
73
|
+
| join("\n")
|
|
74
|
+
else empty end
|
|
75
|
+
' 2>/dev/null || echo "")
|
|
76
|
+
|
|
77
|
+
TODO_COUNT=$(echo "$TASKS" | jq '[.[] | select(.status == "Todo" or .status == "TODO" or .status == "To Do")] | length' 2>/dev/null || echo 0)
|
|
78
|
+
|
|
79
|
+
# 브리핑 생성
|
|
80
|
+
{
|
|
81
|
+
echo "### GPM 태스크 브리핑"
|
|
82
|
+
echo ""
|
|
83
|
+
|
|
84
|
+
if [ -n "$IN_PROGRESS" ] && [ "$IN_PROGRESS_COUNT" -gt 0 ] 2>/dev/null; then
|
|
85
|
+
echo "**In Progress (${IN_PROGRESS_COUNT})**:"
|
|
86
|
+
echo "$IN_PROGRESS"
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
if [ -n "$TODO_LIST" ] && [ "$TODO_COUNT" -gt 0 ] 2>/dev/null; then
|
|
90
|
+
echo ""
|
|
91
|
+
echo "**Todo 추천 (${TODO_COUNT}개 중 상위 5개, 마일스톤 순)**:"
|
|
92
|
+
echo "$TODO_LIST"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
if [ -z "$IN_PROGRESS" ] && [ -z "$TODO_LIST" ]; then
|
|
96
|
+
echo "등록된 태스크가 없습니다. \`/gpm sync\`로 GitHub과 동기화하세요."
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
echo ""
|
|
100
|
+
echo "> /gpm status | /gpm next | /gpm done"
|
|
101
|
+
} > "$CACHE_FILE"
|
|
102
|
+
|
|
103
|
+
cat "$CACHE_FILE"
|
|
104
|
+
exit 0
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-500:oklch(63.7% .237 25.331);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-white:#fff;--spacing:.25rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.mx-auto{margin-inline:auto}.mt-3{margin-top:calc(var(--spacing) * 3)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.block{display:block}.flex{display:flex}.grid{display:grid}.min-h-\[50vh\]{min-height:50vh}.min-h-screen{min-height:100vh}.max-w-7xl{max-width:var(--container-7xl)}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.rounded{border-radius:.25rem}.rounded-lg{border-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-gray-200{border-color:var(--color-gray-200)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-white{background-color:var(--color-white)}.p-5{padding:calc(var(--spacing) * 5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-8{padding-block:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-blue-500{color:var(--color-blue-500)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-red-500{color:var(--color-red-500)}.text-white{color:var(--color-white)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.hover\:border-blue-400:hover{border-color:var(--color-blue-400)}.hover\:text-blue-600:hover{color:var(--color-blue-600)}.hover\:text-blue-700:hover{color:var(--color-blue-700)}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}@media(min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}
|