diff --git "a/Ch\341\273\215n gi\341\272\245y ph\303\251p m\303\243 ngu\341\273\223n m\341\273\237 _ Ch\341\273\215n gi\341\272\245y ph\303\251p (1)" "b/Ch\341\273\215n gi\341\272\245y ph\303\251p m\303\243 ngu\341\273\223n m\341\273\237 _ Ch\341\273\215n gi\341\272\245y ph\303\251p (1)" new file mode 100644 index 0000000..e032049 --- /dev/null +++ "b/Ch\341\273\215n gi\341\272\245y ph\303\251p m\303\243 ngu\341\273\223n m\341\273\237 _ Ch\341\273\215n gi\341\272\245y ph\303\251p (1)" @@ -0,0 +1,999 @@ +From: +Snapshot-Content-Location: https://choosealicense.com/ +Subject: =?utf-8?Q?Ch=E1=BB=8Dn=20gi=E1=BA=A5y=20ph=C3=A9p=20m=C3=A3=20ngu=E1=BB?= + =?utf-8?Q?=93n=20m=E1=BB=9F=20|=20Ch=E1=BB=8Dn=20gi=E1=BA=A5y=20ph=C3=A9p?= +Date: Tue, 16 Jun 2026 13:08:20 +0700 +MIME-Version: 1.0 +Content-Type: multipart/related; + type="text/html"; + boundary="----MultipartBoundary--kxwvuFwyuMYcePAR51VZyTenwsfxKEx0HaVbZWicZr----" + + +------MultipartBoundary--kxwvuFwyuMYcePAR51VZyTenwsfxKEx0HaVbZWicZr---- +Content-Type: text/html +Content-ID: +Content-Transfer-Encoding: binary +Content-Location: https://choosealicense.com/ + + + + + + + + + + +Chọn giấy phép mã nguồn mở | Chọn giấy phép + + + + + + + + + + + + + + + + + +
+ + + + + +

Chọn giấy phép mã nguồn mở

+ + + +

Giấy phép mã nguồn mở bảo vệ cả người đóng góp và người dùng. Các doanh nghiệp và nhà phát triển am hiểu sẽ không tham gia vào một dự án nào nếu không có sự bảo vệ này.

+ +

+ { + Tình huống của bạn được mô tả chính xác nhất bằng các lựa chọn sau đây? + } +

+ +

+ { + Nếu không có giải pháp nào trong số này hiệu quả với tôi thì sao? + } +

+ + + + + +
Văn bản gốc
Đánh giá bản dịch này
Ý kiến phản hồi của bạn sẽ được dùng để góp phần cải thiện Google Dịch
+ + + + + + +------MultipartBoundary--kxwvuFwyuMYcePAR51VZyTenwsfxKEx0HaVbZWicZr---- +Content-Type: image/svg+xml +Content-Transfer-Encoding: binary +Content-Location: https://fonts.gstatic.com/s/i/productlogos/translate/v14/24px.svg + + +------MultipartBoundary--kxwvuFwyuMYcePAR51VZyTenwsfxKEx0HaVbZWicZr---- +Content-Type: text/css +Content-Transfer-Encoding: binary +Content-Location: https://www.gstatic.com/_/translate_http/_/ss/k=translate_http.tr.zZZZhVqDDCw.L.W.O/am=BBCABg/d=0/rs=AN8SPfrkE3FGhJXryfBRpx1gYoqebAENWQ/m=el_main_css + +@charset "utf-8"; + +.VIpgJd-ZVi9od-ORHb-OEVmcd { left: 0px; top: 0px; height: 39px; width: 100%; z-index: 10000001; position: fixed; border-width: medium medium 1px; border-style: none none solid; border-color: currentcolor currentcolor rgb(107, 144, 218); border-image: initial; margin: 0px; box-shadow: rgb(153, 153, 153) 0px 0px 8px 1px; } + +.VIpgJd-ZVi9od-xl07Ob-OEVmcd { z-index: 10000002; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; position: fixed; box-shadow: rgb(153, 153, 153) 0px 3px 8px 2px; } + +.VIpgJd-ZVi9od-SmfZ-OEVmcd { z-index: 10000000; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; margin: 0px; } + +.goog-te-gadget { font-family: arial; font-size: 11px; color: rgb(102, 102, 102); white-space: nowrap; } + +.goog-te-gadget img { vertical-align: middle; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; } + +.goog-te-gadget-simple { background-color: rgb(255, 255, 255); border-width: 1px; border-style: solid; border-color: rgb(155, 155, 155) rgb(213, 213, 213) rgb(232, 232, 232); font-size: 10pt; display: inline-block; padding-top: 1px; padding-bottom: 2px; cursor: pointer; } + +.goog-te-gadget-icon { margin-left: 2px; margin-right: 2px; width: 19px; height: 19px; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; vertical-align: middle; } + +.goog-te-combo { margin-left: 4px; margin-right: 4px; vertical-align: baseline; } + +.goog-te-gadget .goog-te-combo { margin: 4px 0px; } + +.VIpgJd-ZVi9od-l4eHX-hSRGPd, .VIpgJd-ZVi9od-l4eHX-hSRGPd:active, .VIpgJd-ZVi9od-l4eHX-hSRGPd:hover, .VIpgJd-ZVi9od-l4eHX-hSRGPd:link, .VIpgJd-ZVi9od-l4eHX-hSRGPd:visited { font-size: 12px; font-weight: 700; color: rgb(68, 68, 68); text-decoration: none; } + +.VIpgJd-ZVi9od-ORHb .VIpgJd-ZVi9od-l4eHX-hSRGPd, .VIpgJd-ZVi9od-TvD9Pc-hSRGPd { display: block; margin: 0px 10px; } + +.VIpgJd-ZVi9od-ORHb .VIpgJd-ZVi9od-l4eHX-hSRGPd { padding-top: 2px; padding-left: 4px; } + +.VIpgJd-ZVi9od-ORHb *, .VIpgJd-ZVi9od-SmfZ *, .VIpgJd-ZVi9od-l9xktf *, .VIpgJd-ZVi9od-vH1Gmf *, .VIpgJd-ZVi9od-xl07Ob *, .goog-te-combo { font-family: arial; font-size: 10pt; } + +.VIpgJd-ZVi9od-ORHb { margin: 0px; background-color: rgb(228, 239, 251); overflow: hidden; } + +.VIpgJd-ZVi9od-ORHb img { border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; } + +.VIpgJd-ZVi9od-ORHb-bN97Pc { color: rgb(0, 0, 0); } + +.VIpgJd-ZVi9od-ORHb-bN97Pc img { vertical-align: middle; } + +.VIpgJd-ZVi9od-ORHb-Tswv1b { color: rgb(102, 102, 102); vertical-align: top; margin-top: 0px; font-size: 7pt; } + +.VIpgJd-ZVi9od-ORHb-KE6vqe { width: 8px; } + +.VIpgJd-ZVi9od-LgbsSe { border-color: rgb(231, 231, 231); border-style: none solid solid none; border-width: 0px 1px 1px 0px; } + +.VIpgJd-ZVi9od-LgbsSe div { border-color: rgb(204, 204, 204) rgb(153, 153, 153) rgb(153, 153, 153) rgb(204, 204, 204); border-style: solid; border-width: 1px; height: 20px; } + +.VIpgJd-ZVi9od-LgbsSe button { background: transparent; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; cursor: pointer; height: 20px; overflow: hidden; margin: 0px; vertical-align: top; white-space: nowrap; } + +.VIpgJd-ZVi9od-LgbsSe button:active { background: none 0px 0px repeat scroll rgb(204, 204, 204); } + +.VIpgJd-ZVi9od-SmfZ { margin: 0px; background-color: rgb(255, 255, 255); white-space: nowrap; } + +.VIpgJd-ZVi9od-SmfZ-hSRGPd { text-decoration: none; font-weight: 700; font-size: 10pt; border: 1px outset rgb(136, 136, 136); padding: 6px 10px; white-space: nowrap; position: absolute; left: 0px; top: 0px; } + +.VIpgJd-ZVi9od-SmfZ-hSRGPd img { margin-left: 2px; margin-right: 2px; width: 19px; height: 19px; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; vertical-align: middle; } + +.VIpgJd-ZVi9od-SmfZ-hSRGPd span { text-decoration: underline; margin-left: 2px; margin-right: 2px; vertical-align: middle; } + +.goog-te-float-top .VIpgJd-ZVi9od-SmfZ-hSRGPd { padding: 2px; border-top-width: 0px; } + +.goog-te-float-bottom .VIpgJd-ZVi9od-SmfZ-hSRGPd { padding: 2px; border-bottom-width: 0px; } + +.VIpgJd-ZVi9od-xl07Ob-lTBxed { text-decoration: none; color: rgb(0, 0, 204); white-space: nowrap; margin-left: 4px; margin-right: 4px; } + +.VIpgJd-ZVi9od-xl07Ob-lTBxed span { text-decoration: underline; } + +.VIpgJd-ZVi9od-xl07Ob-lTBxed img { margin-left: 2px; margin-right: 2px; } + +.goog-te-gadget-simple .VIpgJd-ZVi9od-xl07Ob-lTBxed { color: rgb(0, 0, 0); } + +.goog-te-gadget-simple .VIpgJd-ZVi9od-xl07Ob-lTBxed span { text-decoration: none; } + +.VIpgJd-ZVi9od-xl07Ob { background-color: rgb(255, 255, 255); text-decoration: none; border: 2px solid rgb(195, 217, 255); overflow: hidden scroll; position: absolute; left: 0px; top: 0px; } + +.VIpgJd-ZVi9od-xl07Ob-ibnC6b { padding: 3px; text-decoration: none; } + +.VIpgJd-ZVi9od-xl07Ob-ibnC6b, .VIpgJd-ZVi9od-xl07Ob-ibnC6b:link { color: rgb(0, 0, 204); background: rgb(255, 255, 255); } + +.VIpgJd-ZVi9od-xl07Ob-ibnC6b:visited { color: rgb(85, 26, 139); } + +.VIpgJd-ZVi9od-xl07Ob-ibnC6b:hover { background: rgb(195, 217, 255); } + +.VIpgJd-ZVi9od-xl07Ob-ibnC6b:active { color: rgb(0, 0, 204); } + +.VIpgJd-ZVi9od-vH1Gmf { background-color: rgb(255, 255, 255); text-decoration: none; border: 1px solid rgb(107, 144, 218); overflow: hidden; padding: 4px; } + +.VIpgJd-ZVi9od-vH1Gmf-KrhPNb { width: 16px; } + +.VIpgJd-ZVi9od-vH1Gmf-hgDUwe { margin: 6px 0px; height: 1px; background-color: rgb(170, 170, 170); overflow: hidden; } + +.VIpgJd-ZVi9od-vH1Gmf-ibnC6b div, .VIpgJd-ZVi9od-vH1Gmf-ibnC6b-gk6SMd div { padding: 4px; } + +.VIpgJd-ZVi9od-vH1Gmf-ibnC6b .uDEFge { display: none; } + +.VIpgJd-ZVi9od-vH1Gmf-ibnC6b-gk6SMd .fmcmS { padding-left: 4px; padding-right: 4px; } + +.VIpgJd-ZVi9od-vH1Gmf-ibnC6b, .VIpgJd-ZVi9od-vH1Gmf-ibnC6b-gk6SMd { text-decoration: none; } + +.VIpgJd-ZVi9od-vH1Gmf-ibnC6b div, .VIpgJd-ZVi9od-vH1Gmf-ibnC6b:active div, .VIpgJd-ZVi9od-vH1Gmf-ibnC6b:link div, .VIpgJd-ZVi9od-vH1Gmf-ibnC6b:visited div { color: rgb(0, 0, 204); background: rgb(255, 255, 255); } + +.VIpgJd-ZVi9od-vH1Gmf-ibnC6b:hover div { color: rgb(255, 255, 255); background: rgb(51, 102, 204); } + +.VIpgJd-ZVi9od-vH1Gmf-ibnC6b-gk6SMd div, .VIpgJd-ZVi9od-vH1Gmf-ibnC6b-gk6SMd:active div, .VIpgJd-ZVi9od-vH1Gmf-ibnC6b-gk6SMd:hover div, .VIpgJd-ZVi9od-vH1Gmf-ibnC6b-gk6SMd:link div, .VIpgJd-ZVi9od-vH1Gmf-ibnC6b-gk6SMd:visited div { color: rgb(0, 0, 0); font-weight: 700; } + +.VIpgJd-ZVi9od-l9xktf { background-color: rgb(255, 255, 255); overflow: hidden; padding: 8px; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; border-radius: 10px; } + +.VIpgJd-ZVi9od-l9xktf-OEVmcd { background-color: rgb(255, 255, 255); border: 1px solid rgb(107, 144, 218); box-shadow: rgb(153, 153, 153) 0px 3px 8px 2px; border-radius: 8px; } + +.VIpgJd-ZVi9od-l9xktf img { border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; } + +.VIpgJd-ZVi9od-l9xktf-fmcmS { margin-top: 6px; } + +.VIpgJd-ZVi9od-l9xktf-VgwJlc { margin-top: 6px; white-space: nowrap; } + +.VIpgJd-ZVi9od-l9xktf-VgwJlc * { vertical-align: middle; } + +.VIpgJd-ZVi9od-l9xktf-VgwJlc .DUGJie { background-image: url("https://www&google.com/images/zippy_minus_sm.gif"); } + +.VIpgJd-ZVi9od-l9xktf-VgwJlc .TdyTDe { background-image: url("https://www&google.com/images/zippy_plus_sm.gif"); } + +.VIpgJd-ZVi9od-l9xktf-VgwJlc span { color: rgb(0, 0, 204); text-decoration: underline; cursor: pointer; margin: 0px 4px; } + +.VIpgJd-ZVi9od-l9xktf-I9GLp { margin: 6px 0px 0px; } + +.VIpgJd-ZVi9od-l9xktf-I9GLp form { margin: 0px; } + +.VIpgJd-ZVi9od-l9xktf-I9GLp form textarea { margin-bottom: 4px; width: 100%; } + +.VIpgJd-ZVi9od-l9xktf-yePe5c { margin: 6px 0px 4px; } + +.VIpgJd-ZVi9od-aZ2wEe-wOHMyf { z-index: 1000; position: fixed; transition-delay: 0.6s; left: -1000px; top: -1000px; } + +.VIpgJd-ZVi9od-aZ2wEe-wOHMyf-ti6hGc { transition-delay: 0s; left: -14px; top: -14px; } + +.VIpgJd-ZVi9od-aZ2wEe-OiiCO { display: flex; -webkit-box-align: center; align-items: center; -webkit-box-pack: center; justify-content: center; width: 104px; height: 104px; border-radius: 50px; background: url("https://www.gstatic.com/images/branding/product/2x/translate_24dp.png") 50% 50% no-repeat rgb(255, 255, 255); transition: 0.6s ease-in-out; transform: scale(0.4); opacity: 0; } + +.VIpgJd-ZVi9od-aZ2wEe-OiiCO-ti6hGc { transform: scale(0.5); opacity: 1; } + +.VIpgJd-ZVi9od-aZ2wEe { margin: 2px 0px 0px 2px; animation: 1.4s linear 0s infinite normal none running spinner-rotator; } + +@-webkit-keyframes spinner-rotator { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(270deg); } +} + +@keyframes spinner-rotator { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(270deg); } +} + +.VIpgJd-ZVi9od-aZ2wEe-Jt5cK { stroke-dasharray: 187; stroke-dashoffset: 0; stroke: rgb(66, 133, 244); transform-origin: center center; animation: 1.4s ease-in-out 0s infinite normal none running spinner-dash; } + +@-webkit-keyframes spinner-dash { + 0% { stroke-dashoffset: 187; } + 50% { stroke-dashoffset: 46.75; transform: rotate(135deg); } + 100% { stroke-dashoffset: 187; transform: rotate(450deg); } +} + +@keyframes spinner-dash { + 0% { stroke-dashoffset: 187; } + 50% { stroke-dashoffset: 46.75; transform: rotate(135deg); } + 100% { stroke-dashoffset: 187; transform: rotate(450deg); } +} + +.VIpgJd-yAWNEb-L7lbkb a, .VIpgJd-yAWNEb-L7lbkb body, .VIpgJd-yAWNEb-L7lbkb div, .VIpgJd-yAWNEb-L7lbkb form, .VIpgJd-yAWNEb-L7lbkb h1, .VIpgJd-yAWNEb-L7lbkb h2, .VIpgJd-yAWNEb-L7lbkb h3, .VIpgJd-yAWNEb-L7lbkb h4, .VIpgJd-yAWNEb-L7lbkb h5, .VIpgJd-yAWNEb-L7lbkb h6, .VIpgJd-yAWNEb-L7lbkb html, .VIpgJd-yAWNEb-L7lbkb iframe, .VIpgJd-yAWNEb-L7lbkb img, .VIpgJd-yAWNEb-L7lbkb li, .VIpgJd-yAWNEb-L7lbkb ol, .VIpgJd-yAWNEb-L7lbkb p, .VIpgJd-yAWNEb-L7lbkb span, .VIpgJd-yAWNEb-L7lbkb table, .VIpgJd-yAWNEb-L7lbkb tbody, .VIpgJd-yAWNEb-L7lbkb td, .VIpgJd-yAWNEb-L7lbkb tr, .VIpgJd-yAWNEb-L7lbkb ul { color-scheme: unset; forced-color-adjust: unset; math-depth: unset; position: unset; position-anchor: unset; text-size-adjust: unset; appearance: unset; color: unset; font-family: unset; font-feature-settings: unset; font-kerning: unset; font-language-override: unset; font-optical-sizing: unset; font-palette: unset; font-size-adjust: unset; font-stretch: unset; font-style: unset; font-synthesis: unset; font-variant: unset; font-variation-settings: unset; font-weight: unset; position-area: unset; text-orientation: unset; text-rendering: unset; text-spacing-trim: unset; -webkit-font-smoothing: unset; -webkit-locale: unset; -webkit-text-orientation: unset; -webkit-writing-mode: unset; writing-mode: unset; zoom: unset; accent-color: unset; place-content: unset; place-items: unset; place-self: unset; alignment-baseline: unset; anchor-name: unset; anchor-scope: unset; animation-composition: unset; animation: unset; animation-trigger: unset; app-region: unset; aspect-ratio: unset; backdrop-filter: unset; backface-visibility: unset; background: unset; background-blend-mode: unset; baseline-shift: unset; baseline-source: unset; block-size: unset; border-block: unset; border: unset; border-radius: unset; border-collapse: unset; border-end-end-radius: unset; border-end-start-radius: unset; border-inline: unset; border-shape: unset; border-start-end-radius: unset; border-start-start-radius: unset; inset: unset; box-decoration-break: unset; box-shadow: unset; box-sizing: unset; break-after: unset; break-before: unset; break-inside: unset; buffered-rendering: unset; caption-side: unset; caret-animation: unset; caret-color: unset; caret-shape: unset; clear: unset; clip: unset; clip-path: unset; clip-rule: unset; color-interpolation: unset; color-interpolation-filters: unset; color-rendering: unset; columns: unset; column-fill: unset; gap: unset; rule-break: unset; rule: unset; rule-inset: unset; rule-visibility-items: unset; column-span: unset; contain: unset; contain-intrinsic-block-size: unset; contain-intrinsic-size: unset; contain-intrinsic-inline-size: unset; container: unset; content: unset; content-visibility: unset; corner-shape: unset; corner-block-end-shape: unset; corner-block-start-shape: unset; counter-increment: unset; counter-reset: unset; counter-set: unset; cursor: unset; cx: unset; cy: unset; d: unset; display: unset; dominant-baseline: unset; dynamic-range-limit: unset; empty-cells: unset; field-sizing: unset; fill: unset; fill-opacity: unset; fill-rule: unset; filter: unset; flex: unset; flex-flow: unset; float: unset; flood-color: unset; flood-opacity: unset; grid: unset; grid-area: unset; height: unset; hyphenate-character: unset; hyphenate-limit-chars: unset; hyphens: unset; image-orientation: unset; image-rendering: unset; initial-letter: unset; inline-size: unset; inset-block: unset; inset-inline: unset; interactivity: unset; interest-delay: unset; interpolate-size: unset; isolation: unset; letter-spacing: unset; lighting-color: unset; line-break: unset; list-style: unset; margin-block: unset; margin: unset; margin-inline: unset; marker: unset; mask: unset; mask-type: unset; math-shift: unset; math-style: unset; max-block-size: unset; max-height: unset; max-inline-size: unset; max-width: unset; min-block-size: unset; min-height: unset; min-inline-size: unset; min-width: unset; mix-blend-mode: unset; object-fit: unset; object-position: unset; object-view-box: unset; offset: unset; opacity: unset; order: unset; orphans: unset; outline: unset; outline-offset: unset; overflow-anchor: unset; overflow-block: unset; overflow-clip-margin: unset; overflow-inline: unset; overflow-wrap: unset; overflow: unset; overlay: unset; overscroll-behavior-block: unset; overscroll-behavior-inline: unset; overscroll-behavior: unset; padding-block: unset; padding: unset; padding-inline: unset; page: unset; page-orientation: unset; paint-order: unset; perspective: unset; perspective-origin: unset; pointer-events: unset; position-try: unset; position-visibility: unset; print-color-adjust: unset; quotes: unset; r: unset; reading-flow: unset; reading-order: unset; resize: unset; rotate: unset; ruby-align: unset; ruby-position: unset; rule-overlap: unset; rx: unset; ry: unset; scale: unset; scroll-behavior: unset; scroll-initial-target: unset; scroll-margin-block: unset; scroll-margin: unset; scroll-margin-inline: unset; scroll-marker-group: unset; scroll-padding-block: unset; scroll-padding: unset; scroll-padding-inline: unset; scroll-snap-align: unset; scroll-snap-stop: unset; scroll-snap-type: unset; scroll-target-group: unset; scroll-timeline: unset; scrollbar-color: unset; scrollbar-gutter: unset; scrollbar-width: unset; shape-image-threshold: unset; shape-margin: unset; shape-outside: unset; shape-rendering: unset; size: unset; speak: unset; stop-color: unset; stop-opacity: unset; stroke: unset; stroke-dasharray: unset; stroke-dashoffset: unset; stroke-linecap: unset; stroke-linejoin: unset; stroke-miterlimit: unset; stroke-opacity: unset; stroke-width: unset; tab-size: unset; table-layout: unset; text-align: unset; text-align-last: unset; text-anchor: unset; text-autospace: unset; text-box: unset; text-combine-upright: unset; text-decoration: unset; text-decoration-skip-ink: unset; text-emphasis: unset; text-emphasis-position: unset; text-indent: unset; text-justify: unset; text-overflow: unset; text-shadow: unset; text-transform: unset; text-underline-offset: unset; text-underline-position: unset; text-wrap: unset; timeline-scope: unset; timeline-trigger: unset; touch-action: unset; transform: unset; transform-box: unset; transform-origin: unset; transform-style: unset; transition: unset; translate: unset; trigger-scope: unset; user-select: unset; vector-effect: unset; vertical-align: unset; view-timeline: unset; view-transition-class: unset; view-transition-group: unset; view-transition-name: unset; view-transition-scope: unset; visibility: unset; border-spacing: unset; -webkit-box-align: unset; -webkit-box-decoration-break: unset; -webkit-box-direction: unset; -webkit-box-flex: unset; -webkit-box-ordinal-group: unset; -webkit-box-orient: unset; -webkit-box-pack: unset; -webkit-box-reflect: unset; -webkit-line-break: unset; -webkit-line-clamp: unset; -webkit-mask-box-image: unset; -webkit-rtl-ordering: unset; -webkit-ruby-position: unset; -webkit-tap-highlight-color: unset; -webkit-text-combine: unset; -webkit-text-decorations-in-effect: unset; -webkit-text-fill-color: unset; -webkit-text-security: unset; -webkit-text-stroke: unset; -webkit-user-drag: unset; white-space-collapse: unset; widows: unset; width: unset; will-change: unset; word-break: unset; word-spacing: unset; x: unset; y: unset; z-index: unset; font-size: 100%; line-height: normal; } + +.VIpgJd-yAWNEb-L7lbkb ol, .VIpgJd-yAWNEb-L7lbkb ul { list-style: none; } + +.VIpgJd-yAWNEb-L7lbkb table { border-collapse: collapse; border-spacing: 0px; } + +.VIpgJd-yAWNEb-L7lbkb caption, .VIpgJd-yAWNEb-L7lbkb td, .VIpgJd-yAWNEb-L7lbkb th { text-align: left; font-weight: 400; } + +div > .VIpgJd-yAWNEb-L7lbkb { padding: 10px 14px; } + +.VIpgJd-yAWNEb-L7lbkb { color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); border: 1px solid rgb(238, 238, 238); box-shadow: rgba(0, 0, 0, 0.2) 0px 4px 16px; display: none; font-family: arial; font-size: 10pt; width: 420px; padding: 12px; position: absolute; z-index: 10000; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-nVMfcd-fmcmS, .VIpgJd-yAWNEb-yAWNEb-Vy2Aqc-pbTTYe { clear: both; font-size: 10pt; position: relative; text-align: justify; width: 100%; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-r4nke { color: rgb(153, 153, 153); font-family: arial, sans-serif; margin: 4px 0px; text-align: left; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-TvD9Pc-LgbsSe { display: none; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-l4eHX { float: left; margin: 0px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-Z0Arqf-PLDbbf { display: inline-block; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-fw42Ze-Z0Arqf-haAclf { display: none; width: 100%; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-Z0Arqf-H9tDt { margin-top: 20px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-LK5yu { float: left; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-qwU8Me { float: right; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-cGMI2b { min-height: 15px; position: relative; height: 1%; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-jOfkMb-Ne3sFf { background: rgb(41, 145, 13); border-radius: 4px; box-shadow: rgb(30, 102, 9) 0px 2px 2px inset; color: white; font-size: 9pt; font-weight: bolder; margin-top: 12px; padding: 6px; text-shadow: rgb(30, 102, 9) 1px 1px 1px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-Z0Arqf-hSRGPd { color: rgb(17, 85, 204); cursor: pointer; font-family: arial; font-size: 11px; margin-right: 15px; text-decoration: none; } + +.VIpgJd-yAWNEb-L7lbkb > textarea { font-family: arial; resize: vertical; width: 100%; margin-bottom: 10px; border-radius: 1px; border-width: 1px; border-style: solid; border-color: silver rgb(217, 217, 217) rgb(217, 217, 217); border-image: initial; font-size: 13px; height: auto; overflow-y: auto; padding: 1px; } + +.VIpgJd-yAWNEb-L7lbkb textarea:focus { box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 2px inset; border: 1px solid rgb(77, 144, 254); outline: none; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-Z0Arqf-IbE0S { margin-right: 10px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp { min-height: 25px; vertical-align: middle; padding-top: 8px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp { margin-bottom: 0px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input { display: inline-block; min-width: 54px; border: 1px solid rgba(0, 0, 0, 0.1); text-align: center; color: rgb(68, 68, 68); font-size: 11px; font-weight: 700; height: 27px; outline: 0px; padding: 0px 8px; vertical-align: middle; line-height: 27px; margin: 0px 16px 0px 0px; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 2px; border-radius: 2px; transition: 0.218s; background-color: rgb(245, 245, 245); background-image: -webkit-linear-gradient(top, rgb(245, 245, 245), rgb(241, 241, 241)); user-select: none; cursor: default; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input:hover { border: 1px solid rgb(198, 198, 198); color: rgb(34, 34, 34); transition: all; background-color: rgb(248, 248, 248); background-image: -webkit-linear-gradient(top, rgb(248, 248, 248), rgb(241, 241, 241)); } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input:active { border: 1px solid rgb(198, 198, 198); color: rgb(51, 51, 51); background-color: rgb(246, 246, 246); background-image: -webkit-linear-gradient(top, rgb(246, 246, 246), rgb(241, 241, 241)); } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input:focus .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input.AHmuwe .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input:active, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input:focus:active { box-shadow: rgba(255, 255, 255, 0.5) 0px 0px 0px 1px inset; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input.AHmuwe, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input:focus { outline: none; border: 1px solid rgb(77, 144, 254); z-index: 4 !important; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input.gk6SMd { background-color: rgb(238, 238, 238); background-image: -webkit-linear-gradient(top, rgb(238, 238, 238), rgb(224, 224, 224)); box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 2px inset; border: 1px solid rgb(204, 204, 204); color: rgb(51, 51, 51); } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input .VIpgJd-yAWNEb-Z0Arqf-sFeBqf { color: white; border-color: rgb(48, 121, 237); background-color: rgb(77, 144, 254); background-image: -webkit-linear-gradient(top, rgb(77, 144, 254), rgb(71, 135, 237)); } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input .VIpgJd-yAWNEb-Z0Arqf-sFeBqf.AHmuwe .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input .VIpgJd-yAWNEb-Z0Arqf-sFeBqf:active, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input .VIpgJd-yAWNEb-Z0Arqf-sFeBqf:hover .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input .VIpgJd-yAWNEb-Z0Arqf-sFeBqf:focus { border-color: rgb(48, 121, 237); background-color: rgb(53, 122, 232); background-image: -webkit-linear-gradient(top, rgb(77, 144, 254), rgb(53, 122, 232)); } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input .VIpgJd-yAWNEb-Z0Arqf-sFeBqf:hover { box-shadow: rgb(255, 255, 255) 0px 0px 0px 1px inset, rgba(0, 0, 0, 0.1) 0px 1px 1px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input .VIpgJd-yAWNEb-Z0Arqf-sFeBqf.AHmuwe, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input .VIpgJd-yAWNEb-Z0Arqf-sFeBqf:active, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input .VIpgJd-yAWNEb-Z0Arqf-sFeBqf:focus, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input .VIpgJd-yAWNEb-Z0Arqf-sFeBqf:hover, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input.AHmuwe, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input:active, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input:focus, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-L4Nn5e-I9GLp .VIpgJd-yAWNEb-Z0Arqf-I9GLp input:hover { border-color: rgb(48, 121, 237); } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-mrxPge { color: rgb(153, 153, 153); font-family: arial, sans-serif; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-W0vJo-fmcmS { color: rgb(153, 153, 153); font-size: 11px; font-family: arial, sans-serif; margin: 15px 0px 5px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-u0pjoe-fmcmS { color: rgb(136, 0, 0); display: none; font-size: 9pt; } + +.VIpgJd-yAWNEb-VIpgJd-fmcmS-sn54Q { background-color: rgb(201, 215, 241); box-shadow: rgb(153, 153, 170) 2px 2px 4px; box-sizing: border-box; position: relative; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-xl07Ob .VIpgJd-yAWNEb-VIpgJd-xl07Ob { background: rgb(255, 255, 255); border: 1px solid rgb(221, 221, 221); box-shadow: rgb(153, 153, 170) 0px 2px 4px; min-width: 0px; outline: none; padding: 0px; position: absolute; z-index: 2000; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-xl07Ob .VIpgJd-yAWNEb-VIpgJd-j7LFlb { cursor: pointer; padding: 2px 5px 5px; margin-right: 0px; border-style: none; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-xl07Ob .VIpgJd-yAWNEb-VIpgJd-j7LFlb:hover { background: rgb(221, 221, 221); } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-xl07Ob .VIpgJd-yAWNEb-VIpgJd-j7LFlb h1 { font-size: 100%; font-weight: 700; margin: 4px 0px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-xl07Ob .VIpgJd-yAWNEb-VIpgJd-j7LFlb strong { color: rgb(52, 90, 173); } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-VIpgJd-eKm5Fc-hFsbo { text-align: right; position: absolute; right: 0px; left: auto; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-VIpgJd-j7LFlb-SIsrTd .VIpgJd-yAWNEb-VIpgJd-eKm5Fc-hFsbo { text-align: left; position: absolute; left: 0px; right: auto; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-TVLw9c-ppHlrf-sn54Q, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-yAWNEb-Vy2Aqc-fmcmS { background-color: rgb(241, 234, 0); border-radius: 4px; box-shadow: rgba(0, 0, 0, 0.5) 3px 3px 4px; box-sizing: border-box; color: rgb(241, 234, 0); cursor: pointer; margin: -2px -2px -2px -3px; padding: 2px 2px 2px 3px; position: relative; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-TVLw9c-ppHlrf-sn54Q { color: rgb(34, 34, 34); } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-yAWNEb-Vy2Aqc-pbTTYe { color: white; position: absolute !important; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-TVLw9c-ppHlrf, .VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-TVLw9c-ppHlrf .VIpgJd-yAWNEb-TVLw9c-ppHlrf-sn54Q { background-color: rgb(201, 215, 241); border-radius: 4px 4px 0px 0px; box-shadow: rgba(0, 0, 0, 0.5) 3px 3px 4px; box-sizing: border-box; cursor: pointer; margin: -2px -2px -2px -3px; padding: 2px 2px 3px 3px; position: relative; } + +.VIpgJd-yAWNEb-L7lbkb span:focus { outline: none; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-TVLw9c-DyVDA { background-color: transparent; border: 1px solid rgb(77, 144, 254); border-radius: 0px; margin: -2px; padding: 1px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-yAWNEb-TVLw9c-sn54Q-LzX3ef { border-left: 2px solid red; margin-left: -2px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-yAWNEb-TVLw9c-sn54Q-YIAiIb { border-right: 2px solid red; margin-right: -2px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-YPqjbf { padding: 2px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-YPqjbf-fmcmS { font-size: 11px; padding: 2px 2px 3px; margin: 0px; background-color: rgb(255, 255, 255); color: rgb(51, 51, 51); border-width: 1px; border-style: solid; border-color: rgb(192, 192, 192) rgb(217, 217, 217) rgb(217, 217, 217); border-image: initial; display: inline-block; vertical-align: top; height: 21px; box-sizing: border-box; border-radius: 1px; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-YPqjbf-fmcmS:hover { border-width: 1px; border-style: solid; border-color: rgb(160, 160, 160) rgb(185, 185, 185) rgb(185, 185, 185); border-image: initial; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 2px inset; } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-YPqjbf-fmcmS:focus { box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 2px inset; outline: none; border: 1px solid rgb(77, 144, 254); } + +.VIpgJd-yAWNEb-L7lbkb .VIpgJd-yAWNEb-IFdKyd-YPqjbf-sFeBqf { font-size: 11px; padding: 2px 6px 3px; margin: 0px 0px 0px 2px; height: 21px; } + +.VIpgJd-yAWNEb-L7lbkb > div { display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; flex-direction: column; font-family: "Google Sans", Arial, sans-serif; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-Ud7fr { display: flex; -webkit-box-align: end; align-items: end; margin: 14px; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-IuizWc-SIsrTd { margin-right: 14px; color: rgb(116, 119, 117); font-size: 14px; font-weight: 500; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-IuizWc-i3jM8c { margin-left: 14px; color: rgb(116, 119, 117); font-size: 14px; font-weight: 500; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-k77Iif { margin: 0px 16px 16px; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-axAV1 { width: auto; color: rgb(31, 31, 31); font-size: 16px; text-align: initial; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-axAV1 .VIpgJd-yAWNEb-SIsrTd { text-align: right; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-N7Eqid { border-radius: 0px 0px 12px 12px; margin: 0px; background: rgb(241, 244, 249); position: relative; min-height: 50px; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-N7Eqid .VIpgJd-yAWNEb-SIsrTd { text-align: right; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-N7Eqid-B7I4Od { display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; flex-direction: column; width: 77%; padding: 12px; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-N7Eqid-B7I4Od .VIpgJd-yAWNEb-SIsrTd { text-align: right; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-UTujCb { color: rgb(31, 31, 31); font-size: 12px; font-weight: 500; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-N7Eqid-B7I4Od .VIpgJd-yAWNEb-SIsrTd .VIpgJd-yAWNEb-hvhgNd-UTujCb { text-align: right; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-eO9mKe { color: rgb(68, 71, 70); font-size: 12px; padding-top: 4px; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-N7Eqid-B7I4Od .VIpgJd-yAWNEb-SIsrTd .VIpgJd-yAWNEb-hvhgNd-eO9mKe { text-align: right; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-xgov5 { position: absolute; top: 10px; right: 5px; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-xgov5 .VIpgJd-yAWNEb-SIsrTd { left: 5px; right: auto; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-THI6Vb { fill: rgb(11, 87, 208); } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-bgm6sf { margin: -4px 2px 0px 0px; padding: 2px 0px 0px; width: 48px; height: 48px; border-width: medium; border-style: none; border-color: currentcolor; border-image: initial; border-radius: 24px; cursor: pointer; background: none; } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-bgm6sf:hover { background: rgb(232, 235, 236); } + +.VIpgJd-yAWNEb-hvhgNd .VIpgJd-yAWNEb-hvhgNd-aXYTce { display: none; } + +sentinel { } +------MultipartBoundary--kxwvuFwyuMYcePAR51VZyTenwsfxKEx0HaVbZWicZr---- +Content-Type: image/png +Content-Transfer-Encoding: binary +Content-Location: https://choosealicense.com/assets/img/home-sprite@2x.png + +�PNG + + IHDR�l��\�PLTE�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ҋ(�tRNS@��fbKGD�H pHYs  ��tIME��s���IDATx��ۿ�UG�sN��jn%lq��j�m\Xa�B1�"q ���`%h�F+�X($&�H�Xjlbggg���?&?��ݽw�93gf�k��vߝ�>���ߗ�'~%�x%� %�,��e{"�����<��Gq����OZ{^Ȯx��sZ��w`��<�e0N���]�Ѹ���D�e}�A�ƕڞ�2�zN�d<󈜪�9/�8_�sV�q��縨�x �a1����b�;�=w�G�z��@�<"�yHR<,�y(��u�!��8Ѵ�?:0�As���A ���PhI�5 ZУ�3�� ZοdK����Z�u>7�W^���)���:��~��<�Y>��g��M�z�Z�o:f9��m�,t�L�:K�j����ȧ�ʀ.FA�[�\ � I�]� �M���X�<�D~s�E��IOТ���'��%/Џ���L �G �[�o�$G�A��4&҃��u-H�灴dٙr1R�^(�wuyt1Z�t�����@'~��T�Չ{�]�<�g�@7'��n���/�����[����Q��A���� z�lW��Me���tL�*��@����1h��oY��:C?k�?І�'��u�����-c��d󼫯hG~��`K�̣���A7�+[�r�xoAh%i��1 J��%J��=/�:}��ڔhth���9 k�y��@ϷyK!��8�����x�kc������c1� (���cMlc<�P��jiOTd��� �|<���d��\=D+��ϽןW�<_�����y�DELJ�C~6����,|� � ����#����+��ߕA��x�l� ��c�}���A������g��ȧ-���+���Q�K���#[z�N������"ru��J����ЇcE*��K�Y|p�r���=@^D��W?�#p���n��\. �Ya}5�D�8n��q��2��Pd"��z�h��~� +�ҳ�~m�Nġ.(j��;���A�E���\��D�w��nK��r��D���M����,�M���A�{�v�B�ޞ��II�a���,k�CH���)Z�4sRa�'�٢����>t����3���5X�/�I%����_`�������׏I `(X��k[J�\���/*�14���) ӓ�>�O�X{׆8$���%ꇜ�\�ڞ����w��@)b�ո`�$���\.�rL܇Z"Ž�s��wb�1q֥Wv�C��Yug�E?�&��OQ�Cx�8s���1�}`��W�3A�)4�b��x� b�鉯�:�z�l�[h8��;0�s]��t�Asо�h B�|� &0}� B�� �ݢ�8��=� a { text-decoration: none; } + +.triptych h3 { font-size: 1.375rem; margin: 0px auto 20px; width: 220px; } + +.situations .existing { margin-top: 16px; } + +.situations .whatever { margin-top: 16px; } + +.situations .copyleft { margin-top: 3px; } + +.situations .button { margin-top: 20px; } + +.triptych-sprite { background-image: url("../img/home-sprite.png"); background-repeat: no-repeat; display: inline-block; } + +.three-arrows { background-position: 0px 0px; width: 72px; height: 56px; } + +.community { background-position: 0px -57px; width: 72px; height: 56px; } + +.circular { background-position: 0px -115px; width: 72px; height: 68px; } + +.license-overview { clear: both; margin-bottom: 40px; } + +.license-overview-heading { float: left; width: 35%; } + +.license-details { box-sizing: border-box; float: left; width: 55%; padding-left: 20px; } + +.license-page-details { box-sizing: border-box; } + +.license-overview-name { font-size: 1.75rem; margin-top: 5px; } + +.license-overview-description { color: rgb(85, 85, 85); } + +.license-rules { border-bottom: 1px solid rgb(221, 221, 221); font-size: 0.8125rem; line-height: 1.3; margin-bottom: 12px; width: 100%; } + +.license-rules th, .license-rules td { width: 33%; } + +.license-rules th { font-size: 0.9375rem; padding: 5px 10px 5px 0px; vertical-align: bottom; } + +.license-rules th.summary { line-height: 1.4; } + +.license-rules .name { box-sizing: content-box; border-right: 1px solid rgb(221, 221, 221); padding-left: 0px; width: 280px; } + +.license-rules .name a { font-size: 1.75rem; } + +.license-rules .name small a { font-size: 1rem; } + +.license-rules td { padding: 4px 0px 12px; vertical-align: top; } + +.license-rules .label { font-weight: bold; } + +.license-rules li { position: relative; padding-left: 18px; margin-right: 15px; margin-bottom: 5px; } + +.license-rules li:hover { color: rgb(68, 68, 68); } + +.source-marker { display: inline-block; width: 16px; text-align: center; margin-right: 4px; font-size: 0.875rem; line-height: 1; } + +.license-marker { display: inline-block; width: 12px; text-align: center; font-weight: 700; line-height: 1; } + +.license-rules li .license-marker { position: absolute; left: 0px; top: 0.1em; } + +.license-types td .license-marker { display: block; margin: 0px auto; } + +.license-types td .license-marker, .license-types dd .license-marker { font-size: 0.8125rem; } + +.license-permissions .license-marker { color: rgb(41, 134, 37); } + +.license-conditions .license-marker { color: rgb(13, 100, 138); } + +.license-limitations .license-marker { color: rgb(129, 42, 40); } + +.license-permissions .lite .license-marker { color: rgba(41, 134, 37, 0.5); } + +.license-conditions .lite .license-marker { color: rgba(13, 100, 138, 0.5); } + +.license-limitations .lite .license-marker { color: rgba(129, 42, 40, 0.5); } + +.license-rules-sidebar li { float: none; padding-bottom: 3px; } + +.license-body { font-size: 0.9375rem; float: left; width: 700px; } + +.license-body pre { font-family: Consolas, Monaco, Courier, monospace; font-size: 0.875rem; background-color: rgb(255, 255, 255); border: 1px solid rgb(238, 238, 238); border-radius: 3px; padding: 20px; } + +.license-nickname { margin-top: 0px; } + +.sidebar { float: right; width: 220px; font-size: 0.75rem; } + +.sidebar a.button { margin-top: -110px; width: 100%; } + +.sidebar input#repository-url { width: 100%; padding: 5px 20px 5px 10px; box-sizing: border-box; margin-right: 12px; } + +.input-wrapper { position: relative; } + +.input-wrapper .status-indicator { position: absolute; right: 5px; top: 6px; height: 8px; width: 8px; border-radius: 50%; background: rgb(255, 255, 255); animation: auto ease 0s 1 normal none running none; border: 4px solid rgb(221, 221, 221); } + +.input-wrapper .status-indicator.fetching { border-right-color: transparent; animation: 0.8s linear 0s infinite normal none running rotate; } + +.input-wrapper .status-indicator.error { border: 4px solid rgb(198, 64, 61); } + +@keyframes rotate { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.note { color: rgb(104, 112, 114); } + +.button { border-radius: 3px; box-sizing: border-box; border-color: rgb(204, 204, 204) rgb(187, 187, 187) rgb(170, 170, 170); border-style: solid; border-width: 1px; color: rgb(68, 68, 68); box-shadow: rgba(0, 0, 0, 0.1) 0px 2px 4px, rgba(255, 255, 255, 0.8) 0px 1px 0px 0px inset; background-image: linear-gradient(rgb(238, 238, 238), rgb(221, 221, 221)); display: inline-block; font-size: 0.75rem; font-weight: normal; padding: 5px 10px; text-align: center; vertical-align: middle; } + +.button:hover { text-decoration: none; background-image: linear-gradient(rgb(238, 238, 238), rgb(204, 204, 204)); } + +.projects-with-license li { padding-bottom: 3px; } + +.site-footer { border-top: 1px solid rgb(238, 238, 238); margin-top: 30px; padding-top: 20px; color: rgb(85, 85, 85); font-size: 0.75rem; text-align: left; line-height: 1.5; } + +.site-footer a { color: rgb(68, 68, 68); font-weight: normal; } + +.site-footer p { float: left; margin-top: 0px; } + +.site-footer nav { float: right; } + +.site-footer nav a { display: inline-block; margin-left: 20px; } + +.bullets { list-style-type: disc; } + +.bullets > li { margin-left: 2em; } + +.small { font-size: 90%; } + +.override-hint-inline { display: block; } + +[class*="hint--"][aria-label]::after { font-size: 0.8rem; font-weight: 400; text-shadow: none; } + +.tooltip--permissions.hint--bottom::before { border-bottom-color: rgb(41, 134, 37); } + +.tooltip--conditions.hint--bottom::before { border-bottom-color: rgb(13, 100, 138); } + +.tooltip--error.hint--bottom::before, .tooltip--limitations.hint--bottom::before { border-bottom-color: rgb(129, 42, 40); } + +.tooltip--permissions::after { background-color: rgb(216, 244, 215); color: rgb(26, 88, 24); border-color: rgb(61, 198, 55); } + +.tooltip--conditions::after { background-color: rgb(208, 235, 246); border-color: rgb(20, 154, 212); color: rgb(13, 100, 138); } + +.tooltip--error::after, .tooltip--limitations::after { background-color: rgb(244, 217, 216); border-color: rgb(198, 64, 61); color: rgb(129, 42, 40); } + +.clearfix::before, .clearfix::after { content: ""; display: table; } + +.clearfix::after { clear: both; } + +.with-love { float: right; clear: right; } + +@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 200 / 100), only screen and (min-device-pixel-ratio: 2) { + .three-arrows, .community, .circular { background-image: url("../img/home-sprite@2x.png"); background-size: 72px 182px; } +} + +@media only screen and (max-width: 1050px) { + .container { width: 100%; padding: 0px 20px; box-sizing: border-box; } + .triptych { width: 100%; } + .triptych li { width: 33%; margin-left: 0px; padding: 0px 20px; box-sizing: border-box; } + .triptych h3 { width: auto; padding: 0px 30px; } + .home h2 { line-height: 30px; padding: 20px 100px; position: relative; } + .home h2 span { position: absolute; display: block; float: left; top: 50%; margin-top: -22px; } + .home h2 span:first-child { left: 65px; } + .home h2 span:last-child { right: 65px; } + .sidebar a.button { margin: 14px 0px 20px; } + .license-body { width: calc(100% - 250px); } + .hint--large::after { width: 150px; } +} + +@media only screen and (max-width: 800px), (max-device-width: 1050px) and (orientation: portrait) { + .triptych h3 { width: auto; padding: 0px 10px; margin-bottom: 10px; } + .sidebar { float: left; width: 50%; } + .license-body { width: 100%; } + .license-rules { border-bottom-width: medium; border-bottom-style: none; border-bottom-color: currentcolor; } + .license-details { width: 65%; } +} + +@media only screen and (max-width: 680px) { + .triptych li { float: none; width: 100%; margin-bottom: 50px; } + .home h2 { padding: 20px 70px; } + .home h2 span:first-child { left: 35px; } + .home h2 span:last-child { right: 35px; } + .license-overview { margin-bottom: 20px; } + .license-overview-heading { float: none; width: 100%; } + .license-details { float: none; width: 100%; padding-left: 0px; } + .site-footer { text-align: center; } + .site-footer nav, .site-footer p, .with-love { float: none; } + .site-footer nav { margin: 0px auto 10px; } +} + +@media only screen and (max-width: 481px) { + h1 { font-size: 30px; margin-bottom: 15px; } + .home h1 { font-size: 33px; } + .home h2 { font-size: 17px; line-height: 20px; padding: 20px 45px; } + .home h2 span:first-child { left: 0px; } + .home h2 span:last-child { right: 0px; } + .home h2 span { margin-top: -16px; } + .sidebar { width: 100%; } + .license-body { overflow-wrap: break-word; } + .license-body pre { font-size: 10px; } + .license-rules:not(.license-rules-sidebar) li { padding-left: 14px; margin-right: 5px; font-size: 10px; text-size-adjust: none; } + .license-rules:not(.license-rules-sidebar) .license-marker { width: 10px; font-size: 10px; left: 0px; top: 1px; position: absolute; } + .hint--large::after { width: 80px; } +} + +@media only screen and (max-width: 321px) { + .container { padding: 0px 10px; } +} +------MultipartBoundary--kxwvuFwyuMYcePAR51VZyTenwsfxKEx0HaVbZWicZr---- +Content-Type: text/css +Content-Transfer-Encoding: binary +Content-Location: https://fonts.googleapis.com/css?family=Chivo:900 + +@charset "utf-8"; + +@font-face { font-family: Chivo; font-style: normal; font-weight: 900; src: url("https://fonts.gstatic.com/s/chivo/v21/va9b4kzIxd1KFppkaRKvDRPJVDf_FRjen2rTN3vAiIsJ.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+300-301, U+303-304, U+308-309, U+323, U+329, U+1EA0-1EF9, U+20AB; } + +@font-face { font-family: Chivo; font-style: normal; font-weight: 900; src: url("https://fonts.gstatic.com/s/chivo/v21/va9b4kzIxd1KFppkaRKvDRPJVDf_FRjenmrTN3vAiIsJ.woff2") format("woff2"); unicode-range: U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } + +@font-face { font-family: Chivo; font-style: normal; font-weight: 900; src: url("https://fonts.gstatic.com/s/chivo/v21/va9b4kzIxd1KFppkaRKvDRPJVDf_FRjekGrTN3vAiA.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } +------MultipartBoundary--kxwvuFwyuMYcePAR51VZyTenwsfxKEx0HaVbZWicZr---- +Content-Type: text/html +Content-ID: +Content-Transfer-Encoding: binary + + +------MultipartBoundary--kxwvuFwyuMYcePAR51VZyTenwsfxKEx0HaVbZWicZr------ diff --git a/README.md b/README.md index 8b13789..ff1ebf4 100644 --- a/README.md +++ b/README.md @@ -1 +1,101 @@ +

+ GitBot logo +

+

GitBot

+ +

+ Nền tảng tự động hóa Pull Request, CI/CD và tài liệu vận hành dành cho đội kỹ thuật hiện đại. +

+ +

+ Tổng quan · + Tính năng · + Kiến trúc · + Chạy cục bộ · + Vercel +

+ +--- + +## ✨ Tổng quan + +**GitBot** là giao diện quản trị giúp nhóm phát triển theo dõi toàn bộ vòng đời Pull Request trong một workspace thống nhất: review diff, bình luận theo dòng, quan sát pipeline, đọc wiki nội bộ và kiểm tra readiness trước khi deploy. + +Thiết kế dự án tập trung vào ba nguyên tắc: + +- **Tối giản:** thông tin quan trọng được ưu tiên, ít nhiễu thị giác. +- **Hiện đại:** dark UI cho app vận hành, wiki sáng sạch theo phong cách documentation hub. +- **Thực dụng:** mọi màn hình đều phục vụ trực tiếp cho review, CI/CD, bảo mật và rollout. + +## 🚀 Tính năng chính + +| Khu vực | Mô tả | +| --- | --- | +| Wiki | Trang giới thiệu tài liệu, mục lục, hướng dẫn CI/CD và checklist release. | +| Dashboard | KPI repository, PR, pipeline, diff stats và tín hiệu hệ thống. | +| Code Review | Xem diff, chọn file, gửi bình luận theo ngữ cảnh PR. | +| Pipeline | Theo dõi stage lint/test/build/deploy và trạng thái workflow. | +| Mobile Simulator | Kiểm tra trải nghiệm GitBot trên khung di động mô phỏng. | +| Command Palette | Điều hướng nhanh bằng `⌘K` / `Ctrl K`. | + +## 🧭 Kiến trúc + +```mermaid +flowchart LR + GitHub[GitHub Webhook] --> API[GitBot API] + API --> Runner[GitBot Runner] + API --> DB[(PostgreSQL)] + Runner --> CI[CI/CD Jobs] + API --> UI[React Workspace] + UI --> Wiki[Wiki & Dashboard] +``` + +## 🧩 Logo + +Logo GitBot sử dụng biểu tượng robot kết hợp nhánh Git để thể hiện vai trò trợ lý tự động hóa kỹ thuật. Bảng màu xanh `sky` và `emerald` đồng bộ với giao diện sản phẩm, gợi cảm giác đáng tin cậy, realtime và thân thiện với developer. + +- File logo: [`public/gitbot-logo.svg`](public/gitbot-logo.svg) +- Có thể dùng trực tiếp cho GitHub README, header web app, favicon hoặc tài liệu nội bộ. + +## 🛠 Chạy cục bộ + +```bash +npm install +npm run dev +``` + +Ứng dụng mặc định chạy tại: + +```txt +http://localhost:3000 +``` + +## 📦 Build production + +```bash +npm run build +npm run start +``` + +## ▲ Triển khai Vercel + +Dự án đã chuẩn bị UI và build Vite phù hợp cho triển khai production. Khi môi trường có quyền registry và token Vercel, có thể deploy bằng: + +```bash +npx vercel --prod --yes +``` + +## 📚 Tài liệu trong app + +Mở ứng dụng và chọn tab **Wiki** để xem trang giới thiệu tài liệu được thiết kế riêng cho GitBot, bao gồm: + +1. Tổng quan dự án. +2. Bắt đầu nhanh. +3. Cấu hình `.gitbot-ci.yml` mẫu. +4. Quy ước review và release. +5. Checklist triển khai Vercel. + +## 📄 License + +Apache-2.0 diff --git a/gitbot/frontend/app/layout.tsx b/gitbot/frontend/app/layout.tsx index 322da33..7c3b812 100644 --- a/gitbot/frontend/app/layout.tsx +++ b/gitbot/frontend/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; +import { Analytics } from '@vercel/analytics/next'; import './globals.css'; const inter = Inter({ subsets: ['latin'], variable: '--font-sans' }); @@ -16,7 +17,10 @@ export default function RootLayout({ }) { return ( - {children} + + {children} + + ); } diff --git a/gitbot/frontend/package-lock.json b/gitbot/frontend/package-lock.json new file mode 100644 index 0000000..a500314 --- /dev/null +++ b/gitbot/frontend/package-lock.json @@ -0,0 +1,1013 @@ +{ + "name": "gitbot-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gitbot-frontend", + "version": "0.1.0", + "dependencies": { + "@vercel/analytics": "^2.0.1", + "lucide-react": "^0.371.0", + "next": "^15.0.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "@types/react": "^19.0.0", + "tailwindcss": "^4.0.0", + "typescript": "^5.0.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@next/env": { + "version": "15.5.19", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.19.tgz", + "integrity": "sha512-sWWluFvcv5v3Fxznmf2ZfjyoVQt/64oCnYqS90inQWGzMPK1VjvekPiz3OPHKmFT30EnHrjlbyaHLt3M0vWabw==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.5.19", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.19.tgz", + "integrity": "sha512-jx9wWlTKueHKPvVOndyr7WuaevWCkuYqsQ8gC0TMPKAVWG3MhcdMrjfo9tvIZNXd0QOUYXXvAcZ325y8Uq7uzg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.5.19", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.19.tgz", + "integrity": "sha512-291KFcsIQ3OenRdiUDFOR6W3wezzH4auENXm1gbm1Bjd4ANMMRgxPrWTUztQN43BnVoVuMnHCrLeECIMwgFKbA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.19.tgz", + "integrity": "sha512-WeH+nelQyyMeE2f8FxBRZNrGipya5zHZV2vjzfCOAYyiI6am+NbnWAAldOBFQBB2w0DjJcsvrKqoFT2b7+5YoA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.19.tgz", + "integrity": "sha512-5xTOE0lDlDCSSfp+BAif7j17VRRCjWp//ZPZy6NI0QpdrhxtQnsZguSx0xAAZ0c9XZLrLLwCe/XVe5YPrRilKw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.19.tgz", + "integrity": "sha512-LTxRmMgqqMv05Had879W00Fm53quiJd3Zuz8h1JSNJ3nGSlbZ/7Tjs1tKyScgN3Au3t3MyPsjPlq60fMmSHLsg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.19.tgz", + "integrity": "sha512-eoNQSpA5PQfB9wBO4RA47MTDXWz1fizy9Y3Z6e4DetYIF3dvjuu8sj7aIGn/bFCU6lnFzTK34NtCaffP4NsQ7Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.19", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.19.tgz", + "integrity": "sha512-6UNt2dFuCHOe446sm/Kp69nUe8/wIhnh9bm6Xcqw4qEWCOppLMOvhTBVgvM7invVUNr4SPpP6NOQsACtn2IN9Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.19", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.19.tgz", + "integrity": "sha512-PhmojAHyqMne56HBLGu9dhDnHPuFmEjrXSQMM/nW0J6j849lk3ESrVtqNJcCk8CKOV7brpTTbaYAjwKPzKM69w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.16.tgz", + "integrity": "sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@vercel/analytics": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-2.0.1.tgz", + "integrity": "sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==", + "license": "MIT", + "peerDependencies": { + "@remix-run/react": "^2", + "@sveltejs/kit": "^1 || ^2", + "next": ">= 13", + "nuxt": ">= 3", + "react": "^18 || ^19 || ^19.0.0-rc", + "svelte": ">= 4", + "vue": "^3", + "vue-router": "^4" + }, + "peerDependenciesMeta": { + "@remix-run/react": { + "optional": true + }, + "@sveltejs/kit": { + "optional": true + }, + "next": { + "optional": true + }, + "nuxt": { + "optional": true + }, + "react": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue": { + "optional": true + }, + "vue-router": { + "optional": true + } + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lucide-react": { + "version": "0.371.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.371.0.tgz", + "integrity": "sha512-FpUup3kaya7JpGHaoNYn1o0Z8IFvR9w4LeXr2ad20DTr3RFLBXD69bVXagV/I8yWyCJu3it1d1B3bNSa5ui4AA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "15.5.19", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.19.tgz", + "integrity": "sha512-xNOW6tYshGX1/Oi3F8uuk4gpDeWsSUE/1Z0G5uUMekIxaQ0xc03UXd9II0VQHYMWviMeA0OHpJFAKsHf8bTYVg==", + "license": "MIT", + "dependencies": { + "@next/env": "15.5.19", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.19", + "@next/swc-darwin-x64": "15.5.19", + "@next/swc-linux-arm64-gnu": "15.5.19", + "@next/swc-linux-arm64-musl": "15.5.19", + "@next/swc-linux-x64-gnu": "15.5.19", + "@next/swc-linux-x64-musl": "15.5.19", + "@next/swc-win32-arm64-msvc": "15.5.19", + "@next/swc-win32-x64-msvc": "15.5.19", + "sharp": "^0.34.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.7" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/gitbot/frontend/package.json b/gitbot/frontend/package.json index 6d34822..b1a9311 100644 --- a/gitbot/frontend/package.json +++ b/gitbot/frontend/package.json @@ -6,15 +6,16 @@ "dev": "next dev -p 3000" }, "dependencies": { - "react": "^19.0.0", - "react-dom": "^19.0.0", + "@vercel/analytics": "^2.0.1", + "lucide-react": "^0.371.0", "next": "^15.0.0", - "lucide-react": "^0.371.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { - "typescript": "^5.0.0", "@types/node": "^20.0.0", "@types/react": "^19.0.0", - "tailwindcss": "^4.0.0" + "tailwindcss": "^4.0.0", + "typescript": "^5.0.0" } } diff --git a/index.html b/index.html index 21dfe69..97229b4 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,8 @@ - My Google AI Studio App + GitBot — Developer Automation Workspace +
diff --git a/public/gitbot-logo.svg b/public/gitbot-logo.svg new file mode 100644 index 0000000..0304bbe --- /dev/null +++ b/public/gitbot-logo.svg @@ -0,0 +1,24 @@ + + GitBot logo + A modern Git branch robot mark for the GitBot developer automation platform. + + + + + + + + + + + + + + + + + + + + + diff --git a/src/App.tsx b/src/App.tsx index e0c7a4d..47d6452 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,27 +4,37 @@ */ import React, { useState, useEffect, useRef } from 'react'; -import { - GitBranch, - GitPullRequest, - Settings, - Bell, - Cpu, - Smartphone, - Monitor, - Database, - Network, - Calendar, - ArrowLeft, - Folder, - Github, - ChevronRight, - Star, - Menu, +import { + GitBranch, + GitPullRequest, + Settings, + Bell, + Smartphone, + Monitor, + Database, + Network, + Calendar, + ArrowLeft, + Folder, + Github, + ChevronRight, + Star, + Menu, MessageSquare, Sparkles, Info, - Search + Search, + Activity, + CheckCircle2, + Clock3, + ShieldCheck, + TrendingUp, + Zap, + BookOpen, + Layers3, + Code2, + LockKeyhole, + Rocket } from 'lucide-react'; import { Repository, PullRequest, PRComment, Pipeline, DiffFile } from './types'; @@ -39,7 +49,7 @@ import { Analytics } from '@vercel/analytics/react'; export default function App() { // Navigation / Viewport simulation mode const [viewportMode, setViewportMode] = useState<'desktop' | 'mobile'>('desktop'); - + // Data State const [currentRepoId, setCurrentRepoId] = useState(1); const pendingTargetFileRef = useRef(null); @@ -53,10 +63,10 @@ export default function App() { const [diffFiles, setDiffFiles] = useState([]); const [comments, setComments] = useState([]); const [pipelines, setPipelines] = useState([]); - + // GUI state const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); - const [activeWorkspaceTab, setActiveWorkspaceTab] = useState<'pr' | 'pipeline' | 'docs'>('pr'); + const [activeWorkspaceTab, setActiveWorkspaceTab] = useState<'wiki' | 'dashboard' | 'pr' | 'pipeline' | 'docs'>('wiki'); const [notificationMsg, setNotificationMsg] = useState(''); const [isCommandPaletteOpen, setIsCommandPaletteOpen] = useState(false); @@ -111,7 +121,7 @@ export default function App() { ]; const filtered = prList.filter(p => p.repo_id === repoId); setPrs(filtered); - + // Auto assign first PR if none is loaded or mismatched if (filtered.length > 0 && !filtered.some(p => p.id === currentPrId)) { setCurrentPrId(filtered[0].id); @@ -193,12 +203,33 @@ export default function App() { setTimeout(() => setNotificationMsg(""), 3500); }; + const activeOpenPRs = prs.filter(p => p.status === 'open').length; + const mergedPRs = prs.filter(p => p.status === 'merged').length; + const totalDiffAdditions = diffFiles.reduce((sum, file) => sum + file.additions, 0); + const totalDiffDeletions = diffFiles.reduce((sum, file) => sum + file.deletions, 0); + const pipelineSuccessRate = pipelines.length + ? Math.round((pipelines.filter(pipeline => pipeline.status === 'success').length / pipelines.length) * 100) + : 96; + const dashboardCards = [ + { label: 'Pull Requests mở', value: activeOpenPRs || 2, helper: `${mergedPRs} PR đã merge trong sprint`, icon: GitPullRequest, accentClass: 'border-emerald-500/20 bg-emerald-500/10 text-emerald-300' }, + { label: 'Pipeline ổn định', value: `${pipelineSuccessRate}%`, helper: `${pipelines.length || 4} workflow đang theo dõi`, icon: CheckCircle2, accentClass: 'border-indigo-500/20 bg-indigo-500/10 text-indigo-300' }, + { label: 'Dòng code thay đổi', value: `+${totalDiffAdditions || 248}`, helper: `-${totalDiffDeletions || 64} dòng được loại bỏ`, icon: Activity, accentClass: 'border-sky-500/20 bg-sky-500/10 text-sky-300' }, + { label: 'Bảo mật runtime', value: 'A+', helper: 'ReDoS Guard và SSH Gate active', icon: ShieldCheck, accentClass: 'border-amber-500/20 bg-amber-500/10 text-amber-300' }, + ]; + const wikiSections = [ + { title: 'Bắt đầu nhanh', desc: 'Kết nối repository, tạo webhook và chạy pipeline đầu tiên trong 10 phút.', icon: Rocket, tag: 'Onboarding' }, + { title: 'Kiến trúc hệ thống', desc: 'Runner, API gateway, Git storage, cache và luồng dữ liệu realtime.', icon: Layers3, tag: 'Architecture' }, + { title: 'Cấu hình CI/CD', desc: 'Chuẩn hóa .gitbot-ci.yml, stage, job, artifact và biến môi trường.', icon: Code2, tag: 'Pipeline' }, + { title: 'Bảo mật & quyền truy cập', desc: 'SSH gate, RBAC, secret rotation, audit log và ReDoS Guard.', icon: LockKeyhole, tag: 'Security' }, + ]; + const wikiToc = ['Tổng quan', 'Bắt đầu nhanh', 'Cấu hình mẫu', 'Quy ước review', 'Triển khai Vercel', 'FAQ']; + const selectedRepo = repos.find(r => r.id === currentRepoId) || repos[0]; const selectedPR = prs.find(p => p.id === currentPrId) || prs[0]; return (
- + {/* Dynamic Top bar Notification slide */} {notificationMsg && (
@@ -210,8 +241,8 @@ export default function App() { {/* Global Application Nav-Header */}
-
- +
+ GitBot logo
@@ -226,7 +257,7 @@ export default function App() { {/* Search Command Palette Trigger Button (Desktop Only) with Shortcuts Help */}
- - + {/* Tooltip dropdown on hover */}
@@ -282,27 +313,27 @@ export default function App() { {/* Viewport Simulation Mode Selector */}
- - +
{/* Inner Workspace Tabs container switch */} + {activeWorkspaceTab === 'wiki' && ( +
+
+
+
+
+ GitBot Wiki +
+

+ Trung tâm tài liệu vận hành +

+

+ Một trang wiki tối giản, hiện đại để đội kỹ thuật tra cứu kiến trúc, quy ước CI/CD, bảo mật và checklist triển khai Vercel. +

+
+
+
Version
+
v2.6 Docs
+
Cập nhật: 15/06/2026
+
+
+
+ +
+ + +
+
+
+
+ Overview +

GitBot giúp chuẩn hóa vòng đời pull request

+

+ Wiki này gom các hướng dẫn quan trọng vào một bề mặt sạch: cách chạy runner, review diff, kiểm tra pipeline và phát hành an toàn. +

+
+
+
Status
+
Production-ready
+
+
+
+ +
+ {wikiSections.map(section => { + const Icon = section.icon; + return ( +
+
+
+ +
+ {section.tag} +
+

{section.title}

+

{section.desc}

+
+ ); + })} +
+ +
+
+
+ Cấu hình mẫu +

.gitbot-ci.yml tối giản

+
+ Copy-ready +
+
{`stages: [lint, test, build, deploy]
+runner: gitbot-runner-core
+deploy:
+  provider: vercel
+  production: true`}
+
+ +
+ {[ + ['Review', 'Mỗi PR cần ít nhất 2 approval và không còn thread blocker.'], + ['Pipeline', 'Build production chỉ chạy khi lint, test và security scan hoàn tất.'], + ['Release', 'Canary 15% trước khi chuyển toàn bộ traffic sang production.'], + ].map(([title, body]) => ( +
+
{title}
+

{body}

+
+ ))} +
+
+
+
+ )} + + {activeWorkspaceTab === 'dashboard' && ( +
+
+
+
+
+
+
+ + Live Overview + +

+ Dashboard vận hành GitBot cho {selectedRepo?.name || 'workspace'} +

+

+ Theo dõi sức khỏe repository, PR, CI/CD, bảo mật và tiến độ rollout trong một màn hình tổng quan sẵn sàng triển khai Vercel. +

+
+
+ Release window +
Hôm nay
+
Canary → Production
+
+
+ +
+ {dashboardCards.map(card => { + const Icon = card.icon; + return ( +
+
+ {card.label} +
+ +
+
+
{card.value}
+

{card.helper}

+
+ ); + })} +
+
+
+ +
+
+
+
+

Luồng triển khai ưu tiên

+

Các bước quan trọng trước khi phát hành production.

+
+ +
+
+ {[ + ['Review PR trọng điểm', selectedPR?.title || 'Tối ưu parser yaml', 'Đang chờ 2 approval'], + ['CI/CD smoke test', 'Chạy regression trên runner core', 'Tự động trong 8 phút'], + ['Canary release', 'Đẩy 15% traffic qua Vercel edge', 'Theo dõi lỗi realtime'], + ].map(([title, desc, meta], index) => ( +
+
{index + 1}
+
+
{title}
+
{desc}
+
{meta}
+
+
+ ))} +
+
+ +
+
+ +

Tín hiệu hệ thống

+
+
+ {[ + ['API latency', '142ms', 'Ổn định'], + ['Webhook queue', '12 jobs', 'Đang xử lý'], + ['Uptime tháng này', '99.98%', 'SLA đạt'], + ['Cache hit-rate', '91%', 'Tối ưu'], + ].map(([label, value, status]) => ( +
+
+
{label}
+
{status}
+
+ {value} +
+ ))} +
+
+
+
+ )} + {activeWorkspaceTab === 'pr' && (
{/* File Dropdown Selector to switch files in diff files list */} @@ -469,8 +727,8 @@ export default function App() {
Xem tệp tin:
- setSelectedFilePath(e.target.value)} className="bg-transparent text-[10px] font-mono text-slate-300 outline-none border-none cursor-pointer outline-none" > @@ -764,7 +1110,7 @@ export default function App() {
{/* Mobile Diff code renderer */} - -
- { @@ -833,7 +1179,7 @@ export default function App() { } }} /> -