diff --git a/.Jules/palette.md b/.Jules/palette.md index 3723067..31a238c 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -5,3 +5,7 @@ ## 2024-06-21 - Added skip-to-content link **Learning:** Found a missing skip-to-content link, which is a key accessibility feature to help keyboard and screen reader users bypass navigation. Additionally learned that giving `
` `tabindex="-1"` and removing its outline when `:focus-visible` ensures proper focus handling after clicking the skip link without disruptive visual outlines. **Action:** Always include a skip-to-content link near the start of the `body` and manage target focus appropriately. + +## 2026-06-26 - Added scroll-padding-top for sticky header +**Learning:** Anchor links on pages with a fixed or sticky header often scroll the target element behind the header, hiding the content. This is particularly problematic in single-page structures with heavy internal navigation. +**Action:** Use `scroll-padding-top` on the `html` element with a value equal to the sticky header's height to ensure anchor links scroll to a visible position. diff --git a/.gitignore b/.gitignore index 3c3629e..fafe757 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +venv/ diff --git a/.jules/bolt.md b/.jules/bolt.md index f74ef2f..8de11f5 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,3 +1,6 @@ ## 2024-06-20 - Unnecessary initial DOM updates for default language **Learning:** The simple static i18n implementation runs `node.textContent = dict[node.dataset.i18n]` for every translatable node on the initial script load, even when the HTML is already written in the target language (Korean). This creates unnecessary layout/paint operations and blocking time on the main thread for elements that don't need text changes. **Action:** Always check if the current value matches the desired value before updating the DOM (`node.textContent !== newText`), and add early exits when setting state to the same value to avoid redundant DOM traversal and writes. +## 2024-06-27 - 초기 언어 로드 시 불필요한 DOM 탐색 제거 +**Learning:** 초기 로드 시 요청된 언어가 HTML의 기본 언어(ko)와 동일한 경우, 모든 DOM 텍스트 노드를 탐색하고 치환하는 불필요한 작업을 생략하면 성능이 향상됨을 확인했습니다. +**Action:** `isInitialDefault` 조건을 추가하여 초기 로드 시 불필요한 DOM 순회 코드가 실행되지 않도록 개선했습니다. diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 499037d..e30db0d 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -10,3 +10,7 @@ **Vulnerability:** Unhandled exceptions when accessing `localStorage` in strict browser privacy modes (e.g., when cookies are blocked). **Learning:** Browsers throw a `SecurityError` when `localStorage` is accessed and the user has blocked third-party cookies or is in a strict privacy mode. If unhandled, this crashes the executing script, leading to a degraded user experience (DoS-like behavior for privacy-conscious users). **Prevention:** Always wrap `localStorage.getItem` and `localStorage.setItem` in `try-catch` blocks to fail securely and fall back to sensible defaults. +## 2026-06-27 - 외부 링크의 reverse tabnabbing 취약점 완화 +**Vulnerability:** 외부 링크(특히 참조문헌 링크 등)에 `target="_blank"` 속성을 사용하거나 새 탭으로 여는 동작을 유도할 때, `rel="noopener noreferrer"` 속성이 누락되어 Reverse Tabnabbing 공격에 노출될 수 있음. +**Learning:** `rel="noopener noreferrer"`가 없으면 새로 열린 탭의 페이지가 `window.opener` 객체를 통해 원래 페이지의 `location`을 악의적인 사이트로 변경할 수 있습니다. +**Prevention:** 외부 링크를 새 탭으로 열기 위해 `target="_blank"`를 사용할 때만 `rel="noopener noreferrer"`를 함께 추가하여 부모 창에 대한 접근을 차단해야 합니다. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f930ee6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# 변경 이력 (CHANGELOG) + +## [Unreleased] +### 추가됨 +- `styles.css`에 `scroll-padding-top: 81px` 추가하여 고정 헤더와 앵커 링크 대상 간의 겹침 현상을 해결했습니다. +- 로컬 테스트 환경을 위해 `.gitignore`에 `venv/` 디렉터리를 무시하도록 설정했습니다. +- **성능 개선**: `i18n.js`에서 초기 로드 시 기본 언어가 한국어(ko)인 경우 불필요한 DOM 순회 및 텍스트 업데이트를 생략하도록 개선했습니다. +- **테스트 추가**: 다국어 처리 로직의 무결성을 검증하기 위해 `test_i18n.html` 테스트 파일을 추가했습니다. diff --git a/i18n.js b/i18n.js index 2393774..00e14cb 100644 --- a/i18n.js +++ b/i18n.js @@ -315,8 +315,7 @@ function setLanguage(lang) { const dict = messages[lang] || messages.ko; - if (!i18nNodes) { - i18nNodes = document.querySelectorAll("[data-i18n]"); + if (!langButtons) { langButtons = document.querySelectorAll("[data-lang]"); metaDesc = document.querySelector('meta[name="description"]'); ogDesc = document.querySelector('meta[property="og:description"]'); @@ -346,13 +345,22 @@ function setLanguage(lang) { } } - // Only update textContent if it actually changed to avoid layout recalculations - i18nNodes.forEach((node) => { - const newText = dict[node.dataset.i18n]; - if (newText && node.textContent !== newText) { - node.textContent = newText; + // ⚡ Bolt: 기본 언어로 초기 로드 시 불필요한 DOM 텍스트 읽기 및 탐색 생략 (성능 개선) + const isInitialDefault = lang === "ko" && !i18nNodes; + + if (!isInitialDefault) { + if (!i18nNodes) { + i18nNodes = document.querySelectorAll("[data-i18n]"); } - }); + + // Only update textContent if it actually changed to avoid layout recalculations + i18nNodes.forEach((node) => { + const newText = dict[node.dataset.i18n]; + if (newText && node.textContent !== newText) { + node.textContent = newText; + } + }); + } langButtons.forEach((button) => { const pressed = String(button.dataset.lang === lang); diff --git a/index.html b/index.html index dbb0157..3014dd8 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,7 @@ + 맥락지혜 연구실 | Contextual Wisdom Lab 프로젝트 Fork 작업 - GitHub + GitHub
@@ -57,7 +58,7 @@

맥락지혜 연구실

구슬이 서 말이어도 꿰어야 보배이듯, 문서, 메일, 로그, 회의록을 맥락 안에서 엮어 사람이 무엇을 결정하고 무엇을 실행할지 보이게 하는 AI 의사결정 지원 시스템을 연구하고 만듭니다.

@@ -251,19 +252,19 @@

참고문헌

  1. Ackoff, R. L. (1989). From data to wisdom. Journal of Applied Systems Analysis, 16(1), 3-9. - https://faculty.ung.edu/kmelton/documents/datawisdom.pdf + https://faculty.ung.edu/kmelton/documents/datawisdom.pdf
  2. Baskarada, S., & Koronios, A. (2013). Data, information, knowledge, wisdom (DIKW): A semiotic theoretical and empirical exploration of the hierarchy and its quality dimension. Australasian Journal of Information Systems, 18(1). - https://doi.org/10.3127/ajis.v18i1.748 + https://doi.org/10.3127/ajis.v18i1.748
  3. Frické, M. (2009). The knowledge pyramid: A critique of the DIKW hierarchy. Journal of Information Science, 35(2), 131-142. - https://doi.org/10.1177/0165551508094050 + https://doi.org/10.1177/0165551508094050
  4. Brienza, J. P., Kung, F. Y. H., Santos, H. C., Bobocel, D. R., & Grossmann, I. (2018). Wisdom, bias, and balance: Toward a process-sensitive measurement of wisdom-related cognition. Journal of Personality and Social Psychology, 115(6), 1093-1126. - https://doi.org/10.1037/pspp0000171 + https://doi.org/10.1037/pspp0000171
@@ -324,31 +325,31 @@

공개 프로젝트

-

Naruon

+

Naruon

메일, 첨부, 일정, 작업을 맥락으로 묶어 판단과 실행으로 연결하는 AI 이메일 워크스페이스입니다.

-

pg-erd-cloud

+

pg-erd-cloud

PostgreSQL 스키마를 리버스 엔지니어링하고 ERD와 DDL 공유 흐름으로 관리하는 클라우드 MVP입니다.

-

BandScope

+

BandScope

곡을 섹션, 역할, 템포, 연습 우선순위로 분석하는 로컬 우선 리허설 앱입니다.

-

codec-carver

+

codec-carver

긴 녹음을 메타데이터를 보존한 FLAC/Opus 조각으로 변환하는 Python CLI입니다.

-

newsdom-api

+

newsdom-api

스캔된 일본어 신문 PDF를 기사, 제목, 본문, 이미지 구조의 DOM형 JSON으로 파싱하는 API입니다.

-

scopeweave

+

scopeweave

트리 편집, 진행률 계산, CSV/JSON, 주간 Gantt를 지원하는 정적 HTML/CSS/JS WBS 플래너입니다.

-

VibeSec

+

VibeSec

바이브코딩 앱을 위한 보안 가드레일입니다. AI 개발 도구 규칙, 정적 점검, 리뷰와 수정 프롬프트를 다룹니다.

@@ -419,7 +420,7 @@

연구에서 제품으로

>

Founded by - Seongho Bae. + Seongho Bae. Context into judgment. Judgment into action.

diff --git a/scripts/ci/collect_failed_check_evidence.sh b/scripts/ci/collect_failed_check_evidence.sh deleted file mode 100755 index 76c8a09..0000000 --- a/scripts/ci/collect_failed_check_evidence.sh +++ /dev/null @@ -1,517 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -if [ "$#" -ne 1 ]; then - echo "usage: $0 " >&2 - exit 2 -fi - -: "${GH_REPOSITORY:?GH_REPOSITORY is required}" -: "${PR_NUMBER:?PR_NUMBER is required}" -: "${HEAD_SHA:?HEAD_SHA is required}" - -OUTPUT_FILE="$1" -FAILED_CHECK_LOG_LINES="${FAILED_CHECK_LOG_LINES:-180}" - -strip_ansi() { - perl -pe 's/\x1b\[[0-9;?]*[A-Za-z]//g' -} - -emit_bounded_file() { - local file_path="$1" - local max_lines="$2" - local total_lines - local head_lines - local tail_lines - - total_lines="$(wc -l <"$file_path" | tr -d '[:space:]')" - if [ -z "$total_lines" ] || [ "$total_lines" -le "$max_lines" ]; then - sed -n "1,${max_lines}p" "$file_path" - return 0 - fi - - head_lines=$((max_lines / 2)) - tail_lines=$((max_lines - head_lines)) - sed -n "1,${head_lines}p" "$file_path" - printf '\n... truncated %s middle log lines ...\n\n' "$((total_lines - max_lines))" - tail -n "$tail_lines" "$file_path" -} - -emit_failure_signal_summary() { - local log_file="$1" - local summary_tmp - - summary_tmp="$(mktemp)" - tmp_files+=("$summary_tmp") - - awk ' - /FAIL:/ || - /::error::/ || - /##\[error\]/ || - /Process completed with exit code/ || - /LLM CONNECTION FAILED/ || - /RateLimitError/ || - /Too many requests/ || - /HTTPStatusError/ || - /401 Unauthorized/ || - /api\.deepseek\.com/ || - /Authentication Fails/ || - /budget limit/ || - /Configured model and fallback models were unavailable/ || - /provider infrastructure/ || - /[Ff]atal/ || - /[Dd]enied/ || - /[Tt]imeout/ || - /[Ww]arn/ { - if (!seen[$0]++) { - print - } - } - ' "$log_file" >"$summary_tmp" - - if [ ! -s "$summary_tmp" ]; then - return 1 - fi - - printf '### Failed log signal summary\n\n' - printf '```text\n' - emit_bounded_file "$summary_tmp" 120 - printf '\n```\n\n' -} - -emit_strix_vulnerability_evidence() { - local log_file="$1" - local summary_tmp - local ranges_tmp - local merged_ranges_tmp - local report_index=0 - local start_line - local end_line - - summary_tmp="$(mktemp)" - ranges_tmp="$(mktemp)" - merged_ranges_tmp="$(mktemp)" - tmp_files+=("$summary_tmp" "$ranges_tmp" "$merged_ranges_tmp") - - awk ' - /Strix run failed for model/ || - /Primary model unavailable; retrying with fallback/ || - /Strix fallback model/ || - /LLM CONNECTION FAILED/ || - /RateLimitError/ || - /Too many requests/ || - /HTTPStatusError/ || - /401 Unauthorized/ || - /api\.deepseek\.com/ || - /Authentication Fails/ || - /budget limit/ || - /Configured model and fallback models were unavailable/ || - /Below-threshold findings detected/ || - /Unable to map Strix findings/ || - /Model [[:alnum:]_.\/-]+/ || - /Vulnerabilities[[:space:]]+[0-9]/ || - /Vulnerabilities[[:space:]]+.*Total/ || - /(CRITICAL|HIGH|MEDIUM|LOW):[[:space:]]+[0-9]/ { - if (!seen[$0]++) { - print - } - } - ' "$log_file" >"$summary_tmp" - - awk ' - /Vulnerability Report/ { - start = NR - 12 - if (start < 1) { - start = 1 - } - end = NR + 190 - print start, end - } - ' "$log_file" >"$ranges_tmp" - - if [ ! -s "$summary_tmp" ] && [ ! -s "$ranges_tmp" ]; then - return 1 - fi - - printf '### Strix model attempt and finding summary\n\n' - if [ -s "$summary_tmp" ]; then - printf '```text\n' - emit_bounded_file "$summary_tmp" 180 - printf '\n```\n\n' - else - printf 'No model summary lines were detected in the failed Strix log.\n\n' - fi - - if [ ! -s "$ranges_tmp" ]; then - printf 'No Strix vulnerability report windows were detected in the failed log.\n\n' - return 0 - fi - - awk ' - NR == 1 { - start = $1 - end = $2 - next - } - $1 <= end + 5 { - if ($2 > end) { - end = $2 - } - next - } - { - print start, end - start = $1 - end = $2 - } - END { - if (start != "") { - print start, end - } - } - ' "$ranges_tmp" >"$merged_ranges_tmp" - - while read -r start_line end_line; do - report_index=$((report_index + 1)) - printf '### Strix vulnerability report window %s (log lines %s-%s)\n\n' "$report_index" "$start_line" "$end_line" - printf '```text\n' - sed -n "${start_line},${end_line}p" "$log_file" - printf '\n```\n\n' - done <"$merged_ranges_tmp" -} - -owner="${GH_REPOSITORY%%/*}" -repo="${GH_REPOSITORY#*/}" -failed_contexts="$(mktemp)" -workflow_run_contexts="$(mktemp)" -active_failed_contexts="$(mktemp)" -manual_success_contexts="$(mktemp)" -superseded_failed_contexts="$(mktemp)" -tmp_files=( - "$failed_contexts" - "$workflow_run_contexts" - "$active_failed_contexts" - "$manual_success_contexts" - "$superseded_failed_contexts" -) -cleanup() { - rm -f "${tmp_files[@]}" -} -trap cleanup EXIT - -manual_success_for_label() { - local label="$1" - local key - - key="${label##*/}" - key="$(printf '%s' "$key" | tr '[:upper:]' '[:lower:]')" - awk -F '\t' -v key="$key" ' - tolower($1) == key { - print - found = 1 - exit - } - END { - exit found ? 0 : 1 - } - ' "$manual_success_contexts" -} - -# shellcheck disable=SC2016 -gh api graphql \ - -f owner="$owner" \ - -f name="$repo" \ - -F number="$PR_NUMBER" \ - -f query=' - query($owner:String!,$name:String!,$number:Int!) { - repository(owner:$owner,name:$name) { - pullRequest(number:$number) { - statusCheckRollup { - contexts(first: 100) { - nodes { - __typename - ... on CheckRun { - databaseId - name - status - conclusion - detailsUrl - checkSuite { - workflowRun { - databaseId - workflow { - name - } - } - } - } - ... on StatusContext { - context - state - targetUrl - } - } - } - } - } - } - } - ' \ - --jq ' - (.data.repository.pullRequest.statusCheckRollup.contexts.nodes // []) - | map( - if .__typename == "CheckRun" then - select((.status // "") == "COMPLETED") - | select((.conclusion // "" | ascii_upcase) as $c | ["FAILURE","TIMED_OUT","ACTION_REQUIRED","CANCELLED","STARTUP_FAILURE"] | index($c)) - | [ - "check_run", - (((.checkSuite.workflowRun.workflow.name // "") + "/" + (.name // "check")) | gsub("^/"; "")), - (.conclusion // "unknown"), - (.detailsUrl // ""), - ((.checkSuite.workflowRun.databaseId // "") | tostring), - ((.databaseId // "") | tostring) - ] - elif .__typename == "StatusContext" then - select((.state // "" | ascii_upcase) as $s | ["FAILURE","ERROR"] | index($s)) - | [ - "status_context", - (.context // "status"), - (.state // "unknown"), - (.targetUrl // ""), - "", - "" - ] - else - empty - end - ) - | .[] - | @tsv - ' >"$failed_contexts" - - env HEAD_SHA="$HEAD_SHA" gh run list \ - --repo "$GH_REPOSITORY" \ - --commit "$HEAD_SHA" \ - --limit 100 \ - --json databaseId,workflowName,status,conclusion,url,event,headSha \ - --jq ' - .[] - | select((.event // "") == "pull_request_target" or (.event // "") == "workflow_dispatch") - | select((.headSha // "") == env.HEAD_SHA) - | select((.workflowName // "") == "Strix Security Scan" or (.workflowName // "") == "Strix") - | select((.status // "") == "completed") - | select((.conclusion // "" | ascii_downcase) as $c | ["failure","timed_out","action_required","cancelled","startup_failure"] | index($c)) - | select(((.event // "") == "workflow_dispatch" and (.conclusion // "" | ascii_downcase) == "cancelled") | not) - | [ - "workflow_run", - (if (.workflowName // "") != "" then .workflowName else "workflow run" end), - (.conclusion // "unknown"), - (.url // ""), - ((.databaseId // "") | tostring), - "" - ] - | @tsv - ' >"$workflow_run_contexts" - -if ! gh api -X GET "repos/${GH_REPOSITORY}/commits/${HEAD_SHA}/status" \ - --jq ' - (.statuses // []) - | map( - select((.context // "") != "") - | . + {__context_key: (.context // "" | ascii_downcase)} - ) - | sort_by(.__context_key, (.created_at // "")) - | group_by(.__context_key) - | map(last) - | map( - select((.state // "" | ascii_downcase) == "success") - | select((.description // "") | contains("Manual workflow_dispatch Strix evidence passed")) - | select((.target_url // "") | test("/actions/runs/[0-9]+")) - | [ - (.__context_key // ""), - (.target_url // ""), - (.description // "") - ] - ) - | .[] - | @tsv - ' >"$manual_success_contexts"; then - : >"$manual_success_contexts" -fi - -while IFS=$'\t' read -r kind label conclusion details_url run_id check_run_id; do - if [ -z "$run_id" ]; then - continue - fi - if awk -F '\t' -v run_id="$run_id" '$5 == run_id { found = 1 } END { exit found ? 0 : 1 }' "$failed_contexts"; then - continue - fi - printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$kind" "$label" "$conclusion" "$details_url" "$run_id" "$check_run_id" >>"$failed_contexts" -done <"$workflow_run_contexts" - -while IFS=$'\t' read -r kind label conclusion details_url run_id check_run_id; do - if success_line="$(manual_success_for_label "$label")"; then - IFS=$'\t' read -r success_context success_url success_description <<<"$success_line" - printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ - "$kind" \ - "$label" \ - "$conclusion" \ - "$details_url" \ - "$run_id" \ - "$check_run_id" \ - "$success_context" \ - "$success_url" \ - "$success_description" >>"$superseded_failed_contexts" - continue - fi - printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$kind" "$label" "$conclusion" "$details_url" "$run_id" "$check_run_id" >>"$active_failed_contexts" -done <"$failed_contexts" - -{ - printf '# Failed GitHub Check Evidence\n\n' - printf -- '- PR: #%s\n' "$PR_NUMBER" - printf -- '- Head SHA: `%s`\n' "$HEAD_SHA" - printf -- '- Repository: `%s`\n\n' "$GH_REPOSITORY" - printf '## Line-specific repair contract\n\n' - printf -- '- Treat the check logs and annotations below as diagnostic evidence, not as a complete review.\n' - printf -- '- For each actionable failed check, inspect the local source or diff and identify the exact file line that must change.\n' - printf -- '- OpenCode `REQUEST_CHANGES` findings must include `path`, `line`, `root_cause`, `fix_direction`, `regression_test_direction`, and `suggested_diff`.\n' - printf -- '- Do not request changes with only a GitHub Actions URL or a generic check name.\n\n' - printf -- '- When Strix logs contain multiple `Vulnerability Report` or `Model ... Vulnerabilities ...` sections, include every model-reported vulnerability in the review evidence and findings, including model name, title, severity, endpoint, and Code Locations/path:line evidence when present.\n' - printf -- '- Create one OpenCode finding per Strix model vulnerability report; do not satisfy two model reports with one combined finding, even when titles or locations match.\n\n' - - if [ -s "$superseded_failed_contexts" ]; then - printf '## Superseded failed checks\n\n' - while IFS=$'\t' read -r kind label conclusion details_url run_id check_run_id success_context success_url success_description; do - printf -- '- `%s` `%s` was superseded by current-head manual workflow_dispatch status `%s`.' "$label" "$conclusion" "$success_context" - if [ -n "$success_url" ]; then - printf ' Evidence: %s.' "$success_url" - fi - if [ -n "$success_description" ]; then - printf ' Description: %s.' "$success_description" - fi - printf '\n' - done <"$superseded_failed_contexts" - printf '\n' - fi - - if [ ! -s "$active_failed_contexts" ]; then - if [ -s "$superseded_failed_contexts" ]; then - printf 'No active failed GitHub Checks remained after superseded checks were classified.\n' - else - printf 'No completed failed GitHub Checks were present when evidence was collected.\n' - fi - exit 0 - fi - - while IFS=$'\t' read -r kind label conclusion details_url run_id check_run_id; do - printf '## Failed check: %s\n\n' "$label" - printf -- '- Type: `%s`\n' "$kind" - printf -- '- Conclusion: `%s`\n' "$conclusion" - if [ -n "$details_url" ]; then - printf -- '- Details URL: %s\n' "$details_url" - fi - if [ -n "$run_id" ]; then - printf -- '- Workflow run id: `%s`\n' "$run_id" - fi - if [ -n "$check_run_id" ]; then - printf -- '- Check run id: `%s`\n' "$check_run_id" - fi - printf '\n' - - if [ "$kind" = "workflow_run" ] && [ -n "$run_id" ]; then - log_file="$(mktemp)" - stripped_log_file="$(mktemp)" - tmp_files+=("$log_file" "$stripped_log_file") - if gh run view "$run_id" --repo "$GH_REPOSITORY" --log-failed >"$log_file" 2>&1; then - strip_ansi <"$log_file" >"$stripped_log_file" - if [ -s "$stripped_log_file" ]; then - emit_failure_signal_summary "$stripped_log_file" || true - printf '### Failed workflow run log excerpt\n\n' - printf '```text\n' - emit_bounded_file "$stripped_log_file" "$FAILED_CHECK_LOG_LINES" - printf '\n```\n\n' - if [[ "$label" == *Strix* ]]; then - emit_strix_vulnerability_evidence "$stripped_log_file" || true - fi - else - printf 'No GitHub Actions job log is available for this failed workflow run.\n\n' - if [ "$conclusion" = "cancelled" ]; then - printf 'The workflow run completed as cancelled before GitHub emitted a failed job log. Treat this as missing current-head security evidence, not as a source-code vulnerability report.\n\n' - fi - fi - else - strip_ansi <"$log_file" >"$stripped_log_file" - printf 'No GitHub Actions job log is available for this failed workflow run.\n\n' - printf '```text\n' - emit_bounded_file "$stripped_log_file" 60 - printf '\n```\n\n' - fi - continue - fi - - if [ "$kind" != "check_run" ] || [ -z "$check_run_id" ]; then - printf 'No GitHub Actions job log is available for this status context.\n\n' - continue - fi - - job_json="$(mktemp)" - tmp_files+=("$job_json") - if gh api -X GET "repos/${GH_REPOSITORY}/actions/jobs/${check_run_id}" >"$job_json" 2>/dev/null; then - failed_steps="$( - jq -r ' - (.steps // []) - | map(select((.conclusion // "" | ascii_downcase) as $c | ["failure","timed_out","cancelled","startup_failure"] | index($c))) - | .[] - | "- step " + ((.number // 0) | tostring) + ": " + (.name // "step") + " (" + (.conclusion // "unknown") + ")" - ' "$job_json" - )" - if [ -n "$failed_steps" ]; then - printf '### Failed job steps\n\n' - printf '%s\n\n' "$failed_steps" - fi - fi - - annotations_tmp="$(mktemp)" - tmp_files+=("$annotations_tmp") - if gh api -X GET "repos/${GH_REPOSITORY}/check-runs/${check_run_id}/annotations" --paginate \ - --jq ' - .[]? - | "- " + (.path // "unknown") + ":" + ((.start_line // 0) | tostring) + "-" + ((.end_line // .start_line // 0) | tostring) + " [" + (.annotation_level // "annotation") + "] " + ((.message // .title // "") | gsub("\r|\n"; " ")) - ' >"$annotations_tmp" 2>/dev/null; then - if [ -s "$annotations_tmp" ]; then - printf '### Check annotations\n\n' - emit_bounded_file "$annotations_tmp" 40 - printf '\n' - fi - fi - - log_raw="$(mktemp)" - log_clean="$(mktemp)" - tmp_files+=("$log_raw" "$log_clean") - if [ -n "$run_id" ] && gh run view "$run_id" \ - --repo "$GH_REPOSITORY" \ - --job "$check_run_id" \ - --log-failed >"$log_raw" 2>&1; then - strip_ansi <"$log_raw" >"$log_clean" - if [ -s "$log_clean" ]; then - emit_failure_signal_summary "$log_clean" || true - if emit_strix_vulnerability_evidence "$log_clean"; then - printf '\n' - fi - printf '### Failed log excerpt\n\n' - printf '```text\n' - emit_bounded_file "$log_clean" "$FAILED_CHECK_LOG_LINES" - printf '\n```\n\n' - fi - else - printf '### Failed log excerpt\n\n' - printf 'The failed job log could not be collected with `gh run view --log-failed`.\n\n' - if [ -s "$log_raw" ]; then - printf '```text\n' - strip_ansi <"$log_raw" | sed -n '1,40p' - printf '\n```\n\n' - fi - fi - done <"$active_failed_contexts" -} >"$OUTPUT_FILE" diff --git a/styles.css b/styles.css index dfc509c..aada0d5 100644 --- a/styles.css +++ b/styles.css @@ -23,6 +23,7 @@ html { scroll-behavior: smooth; + scroll-padding-top: 81px; } body { diff --git a/test_i18n.html b/test_i18n.html new file mode 100644 index 0000000..c8e71fb --- /dev/null +++ b/test_i18n.html @@ -0,0 +1,63 @@ + + + + + i18n Test + + + + +
맥락지혜 연구실
+ + + + + + + +