diff --git a/.gitignore b/.gitignore index f854a44..78aa8ee 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules/ .DS_Store data.db data.db-* +archive/ .gstack/ diff --git a/app.css b/app.css index 66d5fde..b48ab74 100644 --- a/app.css +++ b/app.css @@ -18,6 +18,8 @@ } .view[hidden]{display:none} +.view{min-width:0} +#main{min-width:0;overflow-x:clip} body[data-view="macro"] #navLinks{display:flex} body:not([data-view="macro"]) #navLinks{display:none} @@ -485,7 +487,7 @@ body:not([data-view="macro"]) #navLinks{display:none} } .slb-head{display:flex;justify-content:space-between;gap:12px;align-items:baseline;flex-wrap:wrap;margin-bottom:12px} .slb-head b{font-size:.9rem}.slb-head span{font-size:.74rem;color:var(--text2)} -.slb-steps{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:8px} +.slb-steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(118px,1fr));gap:8px} .slb-steps button{ text-align:left;border:1px solid var(--border);background:#f9faf7;border-radius:12px;padding:11px 12px; color:var(--text);font:inherit;cursor:pointer;min-height:86px;transition:.15s; @@ -499,7 +501,7 @@ body:not([data-view="macro"]) #navLinks{display:none} .sub-tabs{ display:flex;gap:4px;background:rgba(0,0,0,.04);border-radius:12px; - padding:4px;margin:8px 0 20px;flex-wrap:wrap;width:fit-content; + padding:4px;margin:8px 0 20px;flex-wrap:wrap;width:100%;max-width:100%; } .sub-tabs a{ padding:10px 20px;border-radius:10px;font-size:.86rem;font-weight:600; @@ -508,6 +510,8 @@ body:not([data-view="macro"]) #navLinks{display:none} .sub-tabs a:hover{color:var(--text)} .sub-tabs a.active{background:var(--surface);color:var(--blue);box-shadow:0 1px 4px rgba(0,0,0,.08)} .stk-pane[hidden]{display:none} +#view-stock,#stkBody,.stk-pane{min-width:0;width:100%;max-width:100%;overflow-x:clip;box-sizing:border-box} +#view-stock .page{max-width:100%;overflow-x:clip} .metric-head{ display:flex;justify-content:space-between;align-items:center;gap:14px;flex-wrap:wrap; @@ -542,7 +546,8 @@ body:not([data-view="macro"]) #navLinks{display:none} background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:16px; box-shadow:var(--shadow); } -.metric-section-head{display:flex;align-items:baseline;gap:10px;margin-bottom:12px;flex-wrap:wrap} +.metric-section-head{display:flex;align-items:baseline;gap:10px;margin-bottom:12px;flex-wrap:wrap;min-width:0} +.metric-section-head span{flex:1 1 100%;min-width:0;word-break:break-word;line-height:1.45} .metric-section-head h3{font-size:.98rem;line-height:1.2} .metric-section-head span{font-size:.74rem;color:var(--text2)} .metric-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px} @@ -636,6 +641,87 @@ body:not([data-view="macro"]) #navLinks{display:none} cursor:pointer;font-size:1rem;line-height:1;padding:0;flex-shrink:0; } .watch-chip-x:hover{background:var(--red);color:#fff} + +/* ── 追蹤個股(分群)── */ +.watch-page .watch-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px} +.watch-status{font-size:.72rem;color:var(--text2);flex:1;min-width:120px} +.watch-msg{font-size:.76rem;padding:8px 12px;border-radius:10px;margin-bottom:10px;line-height:1.45} +.watch-msg.good{background:rgba(31,157,102,.08);color:var(--green);border:1px solid rgba(31,157,102,.2)} +.watch-msg.warn{background:rgba(200,138,29,.08);color:var(--orange);border:1px solid rgba(200,138,29,.2)} +.watch-msg.bad{background:rgba(216,79,69,.08);color:var(--red);border:1px solid rgba(216,79,69,.2)} +.watch-layout{ + display:grid;grid-template-columns:minmax(200px,240px) minmax(0,1fr);gap:14px;align-items:start; +} +.watch-groups{ + background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:12px; + box-shadow:var(--shadow);min-width:0;position:sticky;top:72px; +} +.watch-groups-head{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:10px} +.watch-groups-head b{font-size:.88rem} +.watch-group-list{list-style:none;display:grid;gap:6px;margin:0;padding:0} +.watch-group-list li{display:flex;align-items:stretch;gap:4px;min-width:0} +.watch-group-item{ + flex:1;min-width:0;display:flex;justify-content:space-between;align-items:center;gap:8px; + padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:#f9faf7; + cursor:pointer;font-family:inherit;text-align:left;color:var(--text);transition:.15s; +} +.watch-group-item:hover{border-color:rgba(35,103,199,.35)} +.watch-group-item.active{background:rgba(35,103,199,.08);border-color:rgba(35,103,199,.45);box-shadow:0 2px 8px rgba(35,103,199,.1)} +.watch-group-name{font-size:.82rem;font-weight:800;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.watch-group-count{font-size:.7rem;color:var(--text2);background:#fff;border:1px solid var(--border);border-radius:999px;padding:2px 8px;flex-shrink:0} +.watch-group-del{ + width:32px;border:1px solid var(--border);background:#fff;border-radius:10px;color:var(--text2); + cursor:pointer;font-size:1rem;line-height:1;flex-shrink:0; +} +.watch-group-del:hover{border-color:var(--red);color:var(--red)} +.watch-main{ + background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:16px; + box-shadow:var(--shadow);min-width:0; +} +.watch-main-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:12px;flex-wrap:wrap} +.watch-main-title{font-size:1.05rem;font-weight:820;margin:0;line-height:1.25} +.watch-main-sub{display:block;font-size:.72rem;color:var(--text2);margin-top:4px} +.watch-add-form{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px} +.watch-add-form input{ + flex:1;min-width:140px;padding:10px 12px;border:1px solid var(--border);border-radius:10px; + font-size:.86rem;font-family:inherit; +} +.watch-symbol-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,220px),1fr));gap:10px} +.watch-empty-panel{ + grid-column:1/-1;padding:28px 16px;text-align:center;font-size:.82rem;color:var(--text2); + background:#f9faf7;border:1px dashed var(--border);border-radius:12px;line-height:1.55; +} +.watch-sym-card{ + border:1px solid var(--border);border-radius:12px;background:#f9faf7;overflow:hidden; + display:flex;flex-direction:column;min-width:0; +} +.watch-sym-open{ + flex:1;width:100%;padding:12px 14px;border:none;background:transparent;cursor:pointer; + text-align:left;font-family:inherit;color:var(--text);display:grid;gap:4px;min-width:0; +} +.watch-sym-open:hover{background:rgba(35,103,199,.05)} +.watch-sym-ticker{font-size:1rem;font-weight:900;color:var(--blue);letter-spacing:.02em} +.watch-sym-name{font-size:.68rem;color:var(--text2);line-height:1.35;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-height:1em} +.watch-sym-price{font-size:1.12rem;font-weight:800;margin-top:4px} +.watch-sym-chg{font-size:.78rem;font-weight:700} +.watch-sym-actions{ + display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:8px 10px;border-top:1px solid var(--border); + background:#fff;font-size:.7rem; +} +.watch-sym-move-wrap{display:flex;align-items:center;gap:4px;color:var(--text2)} +.watch-sym-move{ + border:1px solid var(--border);border-radius:8px;padding:4px 8px;font-size:.7rem;font-family:inherit; + max-width:120px; +} +.watch-sym-rm{ + margin-left:auto;border:none;background:transparent;color:var(--text2);cursor:pointer; + font-size:.72rem;font-weight:700;font-family:inherit;padding:4px 6px;border-radius:6px; +} +.watch-sym-rm:hover{color:var(--red);background:rgba(216,79,69,.08)} +@media(max-width:820px){ + .watch-layout{grid-template-columns:1fr} + .watch-groups{position:static} +} .calendar-msg{font-size:.74rem;margin-top:8px;padding:8px 10px;border-radius:8px;line-height:1.45} .calendar-msg.good{background:rgba(31,157,102,.08);color:#1a6b45;border:1px solid rgba(31,157,102,.18)} .calendar-msg.warn{background:rgba(200,138,29,.08);color:#8a5a12;border:1px solid rgba(200,138,29,.18)} @@ -736,7 +822,13 @@ body.cal-modal-open{overflow:hidden} .event-impact.medium{background:var(--orange)} .event-impact.low{background:var(--text2)} .event-symbol{font-size:.68rem;color:var(--blue);font-weight:850;background:rgba(35,103,199,.08);border-radius:999px;padding:3px 7px} -.stock-detail-layout{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(280px,.8fr);gap:14px;align-items:start} +#pane-price{min-width:0;overflow-x:clip} +.stock-detail-layout{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(260px,.85fr);gap:14px;align-items:start} +.stock-detail-layout > *{min-width:0} +#priceChart{width:100%;max-width:100%;min-width:0;overflow:hidden} +#priceChart .chart-root,#priceChart .chart-stage,#priceChart .chart-wrap,#priceChart .chart-area{width:100%;max-width:100%;min-width:0;box-sizing:border-box} +#priceChart .chart-wrap svg{width:100%;max-width:100%;height:auto;display:block} +#priceChart .chart-legend{max-width:100%} .company-profile{ background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:16px;box-shadow:var(--shadow); } @@ -754,25 +846,119 @@ body.cal-modal-open{overflow:hidden} .profile-events{display:grid;gap:6px;margin-top:12px;background:#f9faf7;border:1px solid var(--border);border-radius:10px;padding:10px} .profile-events b{font-size:.74rem} .profile-events span{font-size:.72rem;color:var(--text2);line-height:1.4} -.company-intel{display:grid;gap:14px;margin-top:14px} +.company-intel{display:grid;gap:14px;margin-top:14px;width:100%;max-width:100%;min-width:0;box-sizing:border-box} +.company-intel .intel-section{min-width:0} +.sec-archive-body{display:grid;gap:12px} +.sec-archive-block h4{margin:0 0 8px;font-size:.92rem;color:var(--text)} +.sec-archive-actions{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:10px} +.sec-filing-list,.sec-earn-list{display:grid;gap:8px} +.sec-filing-row,.sec-earn-row{display:grid;gap:8px;padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:var(--card)} +@media(min-width:720px){.sec-filing-row{grid-template-columns:1fr auto;align-items:start}} +.sec-filing-row--earn{border-color:rgba(59,130,246,.35)} +.sec-filing-main b{display:block;font-size:.95rem} +.sec-filing-form{font-size:.75rem;color:var(--muted);margin-left:6px} +.sec-filing-main small{display:block;color:var(--muted);margin-top:4px} +.sec-filing-main p,.sec-earn-row p{margin:6px 0 0;font-size:.82rem;color:var(--muted);line-height:1.45} +.sec-filing-excerpt{font-size:.78rem!important;opacity:.9} +.sec-filing-links{display:flex;flex-wrap:wrap;gap:8px;align-items:center} +.sec-filing-links a{font-size:.8rem} +.sec-missing{font-size:.75rem;color:var(--muted)} +.sec-earn-row small{display:block;color:var(--muted);margin-top:4px} .intel-section{ background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:16px;box-shadow:var(--shadow); } -.chain-map{display:grid;grid-template-columns:1fr .8fr 1fr;gap:10px;align-items:stretch} -.chain-map div{background:#f9faf7;border:1px solid var(--border);border-radius:12px;padding:12px;display:grid;gap:7px} +.intel-sync-bar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px;margin-bottom:4px;padding:10px 12px;background:#f4f6f2;border:1px solid var(--border);border-radius:12px} +.intel-sync-bar span{font-size:.72rem;color:var(--text2);line-height:1.4;min-width:0;word-break:break-word} +.intel-health-notes{margin:0 0 8px;padding:8px 12px 8px 28px;font-size:.72rem;color:var(--orange);background:rgba(200,138,29,.08);border:1px solid rgba(200,138,29,.2);border-radius:10px;line-height:1.5} +.intel-profile-text{margin:0;font-size:.82rem;line-height:1.6;color:var(--text);word-break:break-word} +.chain-map{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;align-items:start;width:100%;max-width:100%} +.chain-map--2{grid-template-columns:repeat(2,minmax(0,1fr))} +.chain-col--down{border-color:rgba(52,199,89,.22);background:#f6fbf7} +.chain-col--up{border-color:rgba(0,113,227,.18)} +.chain-excerpt{font-size:.78rem;color:var(--text2);line-height:1.55;margin:10px 0 0;word-break:break-word} +.intel-resource-links{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px} +.intel-resource-links a{ + font-size:.78rem;padding:6px 12px;border-radius:999px;border:1px solid var(--border); + background:var(--surface);color:var(--blue);text-decoration:none;max-width:100%; +} +.intel-resource-links a:hover{border-color:rgba(0,113,227,.35)} +.chain-map .chain-col{ + background:#f9faf7;border:1px solid var(--border);border-radius:12px;padding:12px;display:grid;gap:7px; + min-width:0;max-width:100%;overflow-x:hidden;overflow-y:auto;max-height:min(420px,70vh);align-content:start; +} +.chain-group--peers em{color:var(--blue)} +.chain-col--mid{border-color:rgba(35,103,199,.28);background:#f6f9ff} .chain-map b{font-size:.82rem} -.chain-map span{font-size:.72rem;color:var(--text2);line-height:1.35} +.chain-map span{font-size:.72rem;color:var(--text2);line-height:1.35;word-break:break-word} +.chain-mid-role{display:block;font-weight:700;color:var(--blue);margin-top:4px} +.chain-chips{display:flex;flex-wrap:wrap;gap:6px;width:100%;max-width:100%;min-width:0} +.chain-chips span,.chain-chip-static{ + font-size:.7rem;padding:4px 8px;border-radius:999px;background:#fff;border:1px solid var(--border); + line-height:1.25;max-width:100%;word-break:break-word;overflow-wrap:anywhere;display:inline-block; + box-sizing:border-box;vertical-align:top; +} +.chain-chip-btn{ + font-size:.7rem;padding:4px 10px;border-radius:999px;background:#fff;border:1px solid var(--border); + line-height:1.25;max-width:100%;word-break:break-word;overflow-wrap:anywhere;color:var(--blue);font-weight:800; + cursor:pointer;font-family:inherit;transition:border-color .15s,background .15s; + box-sizing:border-box;flex:0 1 auto;min-width:0;vertical-align:top; +} +.chain-chip-btn:hover{border-color:rgba(0,113,227,.4);background:rgba(0,113,227,.06)} +.peer-chips{width:100%;max-width:100%;min-width:0} +.peer-chips button{max-width:100%;overflow-wrap:anywhere;word-break:break-word;flex:0 1 auto;min-width:0} +.chain-group{display:grid;gap:5px;margin-bottom:8px} +.chain-group em{font-style:normal;font-size:.68rem;color:var(--muted);font-weight:700} +.chain-group small{font-size:.65rem;color:var(--text2)} +.intel-section--news{min-width:0;overflow:hidden} +.intel-section--news .metric-section-head{align-items:flex-start;flex-direction:column;gap:4px;margin-bottom:10px} +.intel-section--news .metric-section-head span{flex:none;width:100%} +.news-tabs{display:flex;gap:6px;margin-bottom:12px;flex-wrap:wrap} +.news-tab{ + border:1px solid var(--border);background:#fff;border-radius:999px;padding:6px 14px; + font-size:.74rem;font-weight:800;cursor:pointer;color:var(--text2);font-family:inherit; + flex:0 1 auto;white-space:nowrap; +} +.news-tab.active{background:var(--blue);border-color:var(--blue);color:#fff} +.news-panel{min-width:0;width:100%} +.news-panel.hidden{display:none} +.news-list{display:flex;flex-direction:column;gap:10px;width:100%;min-width:0} +.news-empty{ + padding:20px 14px;text-align:center;font-size:.8rem;color:var(--text2); + background:#f9faf7;border:1px dashed var(--border);border-radius:12px; +} +.mgmt-brief-list{display:grid;gap:8px} +.mgmt-brief-row{padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:#f9faf7} +.mgmt-brief-row.good{border-left:4px solid var(--green)} +.mgmt-brief-row.bad{border-left:4px solid var(--red)} +.mgmt-brief-row.warn{border-left:4px solid var(--orange)} +.mgmt-brief-row b{display:block;font-size:.82rem;line-height:1.35;word-break:break-word} +.mgmt-brief-row small{display:block;color:var(--text2);margin-top:4px;font-size:.68rem} +.mgmt-brief-row p{margin:6px 0 0;font-size:.76rem;line-height:1.45;color:var(--text2);word-break:break-word} +.mgmt-brief-row a{font-size:.72rem} .chain-links{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px} -.chain-links a,.peer-chips button{ +.chain-links a,.peer-chips button,.chain-chip-btn{ border:1px solid var(--border);background:#fbfcfa;color:var(--blue);border-radius:999px; padding:6px 10px;font-size:.72rem;font-weight:800;cursor:pointer; } .peer-chips{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px} -.officer-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px} +.officer-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,200px),1fr));gap:10px} .officer-card{background:#f9faf7;border:1px solid var(--border);border-radius:12px;padding:12px} .officer-card b{display:block;font-size:.82rem} .officer-card span{display:block;font-size:.72rem;color:var(--text2);line-height:1.4;margin-top:5px} .officer-card small{display:block;font-size:.68rem;color:var(--text2);margin-top:8px} +.profile-desc-note{margin:4px 0 0;font-size:.68rem;color:var(--text2);line-height:1.4} +.intel-notes{margin:0 0 10px;font-size:.8rem;line-height:1.55;color:var(--text)} +.intel-section--custom{background:#fafbf9} +.intel-custom-hint{margin:0 0 8px;font-size:.74rem;color:var(--text2);line-height:1.5} +.intel-custom-hint code{font-size:.7rem;background:rgba(0,0,0,.05);padding:1px 5px;border-radius:4px} +.intel-custom-json{ + width:100%;box-sizing:border-box;font-family:ui-monospace,monospace;font-size:.72rem; + line-height:1.45;padding:10px 12px;border:1px solid var(--border);border-radius:10px; + background:#fff;resize:vertical;min-height:140px; +} +.intel-custom-actions{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:10px} +.intel-custom-status{font-size:.72rem;color:var(--text2)} +.news-card-en{display:block;font-size:.68rem;color:var(--text2);margin-top:2px;line-height:1.35;word-break:break-word} .insider-list{display:grid;gap:8px} .insider-summary{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:10px} .insider-summary div{background:#f9faf7;border:1px solid var(--border);border-radius:12px;padding:12px} @@ -789,12 +975,29 @@ body.cal-modal-open{overflow:hidden} .insider-row.warn{border-left:5px solid var(--orange)} .insider-row b{display:block;font-size:.8rem} .insider-row span{display:block;font-size:.68rem;color:var(--text2);margin-top:3px;line-height:1.35} -.news-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px} -.news-card{background:#f9faf7;border:1px solid var(--border);border-radius:12px;padding:12px;color:var(--text)} -.news-card:hover{border-color:rgba(35,103,199,.28)} -.news-card b{display:block;font-size:.82rem;line-height:1.35} -.news-card span{display:block;font-size:.68rem;color:var(--text2);margin-top:7px} -.news-card p{font-size:.72rem;color:var(--text2);line-height:1.5;margin-top:8px} +.news-card{ + display:flex;flex-direction:column;gap:6px;min-width:0;width:100%;box-sizing:border-box; + background:#f9faf7;border:1px solid var(--border);border-radius:12px;padding:12px 14px; + color:var(--text);text-decoration:none;transition:border-color .15s,box-shadow .15s; +} +.news-card:hover{border-color:rgba(35,103,199,.35);box-shadow:0 2px 10px rgba(35,103,199,.08)} +.news-card-title{ + font-size:.84rem;font-weight:800;line-height:1.4;color:var(--text); + word-break:break-word;overflow-wrap:anywhere; + display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden; +} +.news-card-meta{display:block;font-size:.68rem;color:var(--text2);line-height:1.35} +.news-card-summary{ + margin:0;font-size:.72rem;color:var(--text2);line-height:1.5; + word-break:break-word;overflow-wrap:anywhere; + display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden; +} +@media(max-width:960px){ + .stock-detail-layout{grid-template-columns:1fr} +} +@media(max-width:820px){ + .chain-map,.chain-map--2{grid-template-columns:1fr} +} @media(max-width:520px){ .metric-grid{grid-template-columns:1fr 1fr} .metric-card{min-height:116px} @@ -810,25 +1013,27 @@ body.cal-modal-open{overflow:hidden} .cal-detail-row{grid-template-columns:1fr} .cal-detail-meta{text-align:left;margin-top:6px} .stock-detail-layout{grid-template-columns:1fr} - .chain-map{grid-template-columns:1fr} .insider-row{grid-template-columns:1fr} .slb-steps{grid-template-columns:1fr 1fr} } +.chart-root{display:flex;flex-direction:column;gap:8px;min-width:0;width:100%} +.chart-stage{position:relative;min-width:0;width:100%} .chart-wrap{ position:relative;width:100%;background:var(--surface); border:1px solid var(--border);border-radius:var(--radius);padding:12px;box-shadow:var(--shadow); } .chart-wrap svg{display:block;width:100%;height:auto} .chart-empty{padding:48px 0;text-align:center;color:var(--text2)} -.chart-legend{display:flex;gap:16px;font-size:.78rem;color:var(--text2);margin-bottom:8px} -.chart-legend i{display:inline-block;width:12px;height:12px;border-radius:4px;margin-right:6px} -.chart-hover{font-size:.8rem;color:var(--text2);margin-top:8px;min-height:1.2em} -.range-btns{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px} +.chart-legend{display:flex;flex-wrap:wrap;gap:8px 16px;font-size:.78rem;color:var(--text2);margin-bottom:4px} +.chart-legend i{display:inline-block;width:12px;height:12px;border-radius:4px;margin-right:6px;vertical-align:middle} +.chart-hover{font-size:.8rem;color:var(--text2);margin-top:8px;min-height:1.2em;line-height:1.45} +.range-btns{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px;max-width:100%;min-width:0} .range-btns button{ background:var(--surface);border:1px solid var(--border);color:var(--text2); - border-radius:10px;padding:8px 16px;font-size:.82rem;font-weight:600; + border-radius:10px;padding:8px 14px;font-size:.82rem;font-weight:600; cursor:pointer;font-family:inherit;box-shadow:var(--shadow);transition:.15s; + flex:0 1 auto;max-width:100%;min-width:0; } .range-btns button:hover{border-color:var(--blue)} .range-btns button.active{background:var(--blue);border-color:var(--blue);color:#fff} @@ -1050,20 +1255,57 @@ body.cal-modal-open{overflow:hidden} @media(max-width:600px){ .form-grid{grid-template-columns:1fr} } /* AI Provider / Page Assistant */ -.ai-settings-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:14px} +.ai-settings-grid{display:grid;grid-template-columns:1fr;gap:14px;min-width:0} +@media(min-width:720px){.ai-settings-grid{grid-template-columns:repeat(2,minmax(0,1fr))}} .ai-provider-card{ background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:18px; - box-shadow:var(--shadow); + box-shadow:var(--shadow);min-width:0;max-width:100%; +} +.ai-provider-head{ + display:flex;flex-wrap:wrap;justify-content:space-between;gap:10px 14px; + align-items:flex-start;margin-bottom:14px; +} +.ai-provider-head>div:first-child{flex:1 1 12rem;min-width:0;max-width:100%} +.ai-provider-head b{display:block;font-size:1rem} +.ai-provider-head span{display:block;font-size:.76rem;color:var(--text2);line-height:1.55;margin-top:5px;overflow-wrap:anywhere} +.ai-default{ + font-size:.78rem;color:var(--text2);display:flex;gap:6px;align-items:center; + flex:0 0 auto;padding:6px 10px;border-radius:10px;background:#f9faf7;border:1px solid var(--border); +} +.ai-default input{accent-color:var(--blue);flex-shrink:0} +.ai-model-row{display:flex;flex-wrap:wrap;gap:8px;align-items:stretch;width:100%;min-width:0} +.ai-model-row input{min-width:0;flex:1 1 12rem;width:100%;max-width:100%} +.ai-model-row button{flex:0 0 auto;align-self:stretch} +.ai-provider-foot{ + display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px; + margin-top:12px;font-size:.74rem;color:var(--text2); +} +.ai-provider-foot>span{flex:1 1 100%;min-width:0;overflow-wrap:anywhere;line-height:1.5} +@media(min-width:520px){.ai-provider-foot>span{flex:1 1 auto}} +.ai-settings-msg{ + font-size:.82rem;color:var(--text2);margin-top:12px;line-height:1.55; + max-width:100%;overflow-wrap:anywhere;word-break:break-word; +} +.ai-settings-msg .md,.ai-settings-msg pre{max-width:100%;overflow-x:auto} +.ai-settings-msg .md table{display:block;overflow-x:auto} + +/* 設定頁 */ +.settings-page{max-width:1040px;min-width:0} +.settings-page .page-sub{max-width:100%} +.settings-env-path{ + margin-top:10px;padding:10px 12px;font-size:.78rem;line-height:1.5;color:var(--text2); + background:#f9faf7;border:1px solid var(--border);border-radius:10px; + overflow-wrap:anywhere;word-break:break-all;max-width:100%; +} +.settings-page .form-grid{grid-template-columns:1fr} +.settings-page .field input,.settings-page .field textarea{max-width:100%} +.settings-page .env-provider-card{margin-bottom:14px} +.settings-page .form-actions{flex-wrap:wrap} +@media(max-width:479px){ + .settings-page .form-actions .btn{width:100%} + .ai-model-row .btn{width:100%} + .ai-provider-foot .btn{width:100%} } -.ai-provider-head{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;margin-bottom:14px} -.ai-provider-head b{display:block;font-size:1rem}.ai-provider-head span{display:block;font-size:.76rem;color:var(--text2);line-height:1.55;margin-top:5px} -.ai-default{font-size:.78rem;color:var(--text2);display:flex;gap:6px;align-items:center;white-space:nowrap} -.ai-default input{accent-color:var(--blue)} -.ai-model-row{display:flex;gap:8px;align-items:center} -.ai-model-row input{min-width:0;flex:1} -.ai-model-row button{flex-shrink:0} -.ai-provider-foot{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:12px;font-size:.74rem;color:var(--text2)} -.ai-settings-msg{font-size:.82rem;color:var(--text2);margin-top:12px;line-height:1.55} .ai-dock{position:fixed;right:22px;bottom:22px;z-index:380} .ai-fab{ width:54px;height:54px;border-radius:50%;border:none;background:#202421;color:#fff; @@ -1078,11 +1320,21 @@ body.cal-modal-open{overflow:hidden} .ai-panel-head{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;border-bottom:1px solid var(--border);padding:14px 14px 10px;background:rgba(249,250,247,.92)} .ai-panel-head b{display:block;font-size:.95rem}.ai-panel-head span{display:block;font-size:.72rem;color:var(--text2);line-height:1.45;margin-top:3px} .ai-panel-head button{border:1px solid var(--border);background:#f9faf7;color:var(--text2);border-radius:8px;width:30px;height:30px;cursor:pointer;font-size:1rem} -.ai-provider-row{display:flex;gap:8px;align-items:center;padding:10px 14px;border-bottom:1px solid var(--border);background:var(--surface)} -.ai-provider-row select{ - flex:1;border:1px solid var(--border);border-radius:10px;background:#f9faf7;color:var(--text); - padding:9px 11px;font:inherit;font-size:.82rem; +.ai-toolbar{ + display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr) auto;gap:8px 10px; + align-items:end;padding:10px 12px;border-bottom:1px solid var(--border);background:var(--surface); } +.ai-field{display:flex;flex-direction:column;gap:4px;min-width:0} +.ai-field-label{font-size:.66rem;font-weight:600;letter-spacing:.02em;color:var(--text2);text-transform:uppercase} +.ai-field select,.ai-toolbar select{ + width:100%;min-width:0;max-width:100%;border:1px solid var(--border);border-radius:10px; + background:#f9faf7;color:var(--text);padding:8px 10px;font:inherit;font-size:.8rem; + line-height:1.35;appearance:none; + background-image:linear-gradient(45deg,transparent 50%,var(--text2) 50%),linear-gradient(135deg,var(--text2) 50%,transparent 50%); + background-position:calc(100% - 14px) calc(50% - 2px),calc(100% - 9px) calc(50% - 2px); + background-size:5px 5px,5px 5px;background-repeat:no-repeat;padding-right:28px; +} +.ai-toolbar-settings{align-self:end;white-space:nowrap} .ai-chat{ flex:1;overflow:auto;padding:14px;background: linear-gradient(180deg,rgba(35,103,199,.04),rgba(32,40,33,.02)); @@ -1094,8 +1346,12 @@ body.cal-modal-open{overflow:hidden} .ai-bubble{ border:1px solid var(--border);background:#fff;color:var(--text); border-radius:16px;padding:10px 12px;font-size:.86rem;line-height:1.6;box-shadow:0 1px 2px rgba(32,40,33,.05); - overflow-wrap:anywhere; + overflow-wrap:anywhere;word-break:break-word;max-width:100%; } +.ai-bubble pre,.ai-bubble code{max-width:100%;overflow-x:auto;white-space:pre-wrap;word-break:break-all} +.ai-bubble .md{max-width:100%;overflow-x:auto} +.ai-bubble .md table{display:block;max-width:100%;overflow-x:auto} +.ai-bubble .mermaid-wrap{max-width:100%;overflow-x:auto} .ai-msg-user .ai-bubble{background:#2367c7;color:#fff;border-color:#2367c7;border-bottom-right-radius:5px} .ai-msg-bot .ai-bubble{border-bottom-left-radius:5px} .ai-msg-meta{font-size:.66rem;color:var(--text2);margin:4px 6px 0} @@ -1116,4 +1372,349 @@ body.cal-modal-open{overflow:hidden} .ai-typing i:nth-child(2){animation-delay:.15s}.ai-typing i:nth-child(3){animation-delay:.3s} @keyframes aiTyping{0%,80%,100%{transform:translateY(0);opacity:.35}40%{transform:translateY(-3px);opacity:.8}} .ai-error{color:var(--red);background:rgba(216,79,69,.08);border:1px solid rgba(216,79,69,.16);border-radius:10px;padding:10px 12px} -@media(max-width:520px){.ai-dock{right:16px;bottom:16px}.ai-panel{height:min(82vh,680px)}.ai-provider-row,.ai-model-row{align-items:stretch;flex-direction:column}} +@media(max-width:520px){ + .ai-dock{right:16px;bottom:16px} + .ai-panel{width:calc(100vw - 24px);height:min(82vh,680px)} + .ai-toolbar{grid-template-columns:1fr 1fr;grid-template-rows:auto auto} + .ai-toolbar-settings{grid-column:1/-1;justify-self:end} + .ai-settings-grid{grid-template-columns:1fr} +} + +/* ── 技術圖表頁(個股 · 技術圖表分頁)── */ +.ta-page{display:flex;flex-direction:column;gap:16px;min-width:0;max-width:100%} +.ta-hero{ + display:flex;flex-wrap:wrap;justify-content:space-between;align-items:flex-end;gap:16px 24px; + padding:18px 20px;background:var(--surface);border:1px solid var(--border);border-radius:16px; + box-shadow:var(--soft-shadow); +} +.ta-hero-title{margin:0;font-size:1.15rem;font-weight:800;line-height:1.3} +.ta-hero-sym{font-size:.82rem;font-weight:700;color:var(--text2);margin-left:6px} +.ta-hero-price{margin:6px 0 0;font-size:1.45rem;font-weight:900;font-variant-numeric:tabular-nums;color:var(--blue)} +.ta-hero-price small{font-size:.78rem;font-weight:600;color:var(--text2);margin-left:8px} +.ta-hero-kpis{display:flex;flex-wrap:wrap;gap:10px} +.ta-hero-kpis div{ + min-width:88px;padding:10px 14px;background:#f9faf7;border:1px solid var(--border);border-radius:12px; +} +.ta-hero-kpis span{display:block;font-size:.68rem;color:var(--text2);font-weight:700;margin-bottom:4px} +.ta-hero-kpis b{font-size:1rem;font-weight:800;font-variant-numeric:tabular-nums} +.ta-hero-kpis small{display:block;font-size:.68rem;color:var(--text2);font-weight:600;margin-top:3px} +.ta-controls{ + display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1fr) minmax(0,1.2fr) auto;gap:12px 16px;align-items:end; + padding:14px 16px;background:var(--surface);border:1px solid var(--border);border-radius:14px; +} +.ta-controls--panels{ + grid-template-columns:1fr;align-items:stretch;margin-top:-4px; +} +.ta-panels-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px} +.ta-panels-row .chip-row{flex:1;min-width:0} +.ta-preset-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center} +.ta-preset-label{font-size:.68rem;font-weight:700;color:var(--text2);white-space:nowrap} + +.ta-control-group{display:flex;flex-direction:column;gap:8px;min-width:0} +.ta-control-group--layers{grid-column:span 1} +.ta-label{font-size:.75rem;font-weight:800;color:var(--text);letter-spacing:.02em} +.ta-control-hint{margin:0;font-size:.72rem;color:var(--text2);line-height:1.45} +.ta-refresh{align-self:end;white-space:nowrap} +.ta-section-title{margin:0 0 10px;font-size:.82rem;font-weight:800;color:var(--text2);letter-spacing:.04em} +.ta-chart-card{ + background:var(--surface);border:1px solid var(--border);border-radius:16px; + box-shadow:var(--soft-shadow);min-width:0;overflow:visible; +} +.ta-chart-top{padding:14px 16px 10px;border-bottom:1px solid var(--border);background:#fafbf9} +.ta-meta-chips{display:flex;flex-wrap:wrap;gap:6px} +.ta-chip{ + font-size:.7rem;font-weight:700;color:var(--text2);background:#fff;border:1px solid var(--border); + border-radius:999px;padding:4px 10px;white-space:nowrap; +} +.ta-chip--ok{color:var(--green);border-color:rgba(52,168,83,.35);background:rgba(52,168,83,.08)} +.ta-db-range{margin:8px 0 0;font-size:.72rem;color:var(--text2);line-height:1.4;word-break:break-all} +.ta-legend{ + display:flex;flex-wrap:wrap;gap:8px 14px;padding:10px 16px;border-bottom:1px solid var(--border); + background:#fff;min-height:40px;align-items:center; +} +.ta-leg-item{display:inline-flex;align-items:center;gap:6px;font-size:.76rem;color:var(--text2);font-weight:600;white-space:nowrap} +.ta-leg-item i{width:14px;height:3px;border-radius:2px;flex-shrink:0} +.ta-workflow-hint{ + margin:0;padding:8px 16px;font-size:.72rem;color:var(--text2);line-height:1.5; + border-bottom:1px solid var(--border);background:#fafbf9; +} +/* TradingView 式圖表:Y 軸固定欄 + 可捲動圖區 + 固定時間軸 */ +.tv-chart{border-top:1px solid var(--border);background:#fff;overflow:hidden} +.tv-chart-body{display:flex;position:relative;min-width:0;background:#fafbfc;overflow:hidden} +.tv-y-col{ + flex:0 0 64px;width:64px;min-width:64px;display:flex;flex-direction:column; + border-right:1px solid var(--border);background:#fff;z-index:8; + position:sticky;left:0;align-self:stretch; +} +.tv-y-slot{ + position:relative;flex-shrink:0;border-bottom:1px solid var(--border); + overflow:visible;background:#fff; +} +.tv-y-slot:last-child{border-bottom:none} +.tv-y-slot-label{ + position:absolute;top:0;left:0;right:0;z-index:2; + font-size:9px;font-weight:800;color:var(--text2);text-align:center; + padding:3px 2px;background:#f5f7f4;border-bottom:1px solid var(--border); + pointer-events:none; +} +.tv-y-slot--sub{padding-top:20px;box-sizing:border-box} +.chart-gutter-y__ticks,.tv-scale-tags{ + position:absolute;inset:0;pointer-events:none; +} +.tv-y-slot--sub .chart-gutter-y__ticks,.tv-y-slot--sub .tv-scale-tags{top:20px} +.tv-scale-tags{z-index:4} +.tv-scale-tags--stack{ + display:flex;align-items:flex-start;justify-content:flex-start; + padding:4px 3px 6px;box-sizing:border-box;overflow-y:auto;overflow-x:hidden; + max-height:100%; +} +.tv-scale-stack{ + display:flex;flex-direction:column;gap:5px;width:100%;max-width:100%; +} +.tv-scale-tag--stacked{ + position:relative;transform:none;top:auto;right:auto;left:auto; + display:flex;align-items:center;justify-content:space-between;gap:5px; + width:100%;box-sizing:border-box;padding:4px 6px;min-height:26px; +} +.tv-scale-tag{ + position:absolute;right:3px;transform:translateY(-50%); + display:inline-flex;align-items:center;gap:4px;max-width:calc(100% - 6px); + padding:4px 6px;border-radius:5px;background:var(--tag-bg,#2367c7);color:#fff; + font-size:9px;font-weight:700;line-height:1.3;white-space:nowrap; + box-shadow:0 1px 6px rgba(0,0,0,.18);pointer-events:none; +} +.tv-scale-tags--vol-hover{ + position:absolute;left:0;right:0;bottom:4px;top:auto;height:auto; + z-index:5;pointer-events:none;padding:0 3px; +} +.tv-scale-vol-pin{ + display:flex;flex-direction:column;gap:3px;width:100%; +} +.tv-scale-vol-sub{ + display:block;font-size:8px;font-weight:700;color:var(--text2); + text-align:center;line-height:1.2;padding:0 2px 2px; +} +.chart-gutter-y__ticks:empty{display:none} +.tv-scale-leader{ + position:absolute;right:100%;top:50%; + width:1px;height:var(--drift,8px); + margin-right:2px; + background:rgba(0,0,0,.25); + transform:translateY(calc(-50% + var(--dir,1) * var(--drift,8px) / 2 * -1)); + pointer-events:none; +} +.tv-scale-tag em{font-style:normal;opacity:.92;font-size:8px;flex-shrink:0} +.tv-scale-tag b{font-weight:800;font-variant-numeric:tabular-nums;flex-shrink:0} +.chart-gutter-y__tick{right:2px;z-index:1} +.tv-scroll{ + flex:1;min-width:0;overflow-x:auto;overflow-y:hidden; + cursor:grab;-webkit-overflow-scrolling:touch; +} +.tv-scroll.is-dragging{cursor:grabbing;user-select:none} +.tv-stack{display:flex;flex-direction:column;min-width:max-content} +.tv-pane--main{background:linear-gradient(180deg,#fff 0%,#f8faf9 100%);border-bottom:1px solid var(--border)} +.tv-pane--solo{border-bottom:none} +.tv-sub-panel{position:relative;border-bottom:1px solid var(--border);background:#fff} +.tv-sub-plot{background:#fff} +.ta-sub-close--float{ + position:absolute;top:4px;right:6px;z-index:6; + width:22px;height:22px;border-radius:6px;border:1px solid var(--border); + background:rgba(255,255,255,.92);font-size:1rem;line-height:1; + cursor:pointer;color:var(--text2);font-weight:700; +} +.ta-sub-close--float:hover{background:#fff;color:var(--text)} +.chart-gutter-y{position:relative;width:100%;height:100%} +.chart-gutter-y__tick{ + position:absolute;right:4px;transform:translateY(-50%); + font-size:10px;font-weight:600;color:#5c6562; + font-variant-numeric:tabular-nums;white-space:nowrap;pointer-events:none; +} +.tv-plot,.chart-plot-area{flex-shrink:0;background:#fff} +.tv-plot .chart-wrap,.tv-plot svg,.chart-plot-area svg{display:block;width:100%;height:100%} +.chart-root--tv .chart-stage,.chart-root--tv .chart-wrap{margin:0;padding:0;border:none;box-shadow:none;background:transparent} +.tv-cursor-y{ + position:absolute;left:2px;z-index:8;transform:translateY(-50%); + padding:2px 6px;border-radius:4px;background:var(--blue);color:#fff; + font-size:10px;font-weight:700;font-variant-numeric:tabular-nums; + pointer-events:none;white-space:nowrap;box-shadow:0 2px 8px rgba(35,103,199,.35); +} +.tv-x-wrap{ + display:flex;position:relative;height:28px; + border-top:1px solid var(--border);background:#fff; +} +.tv-x-pad{flex:0 0 64px;width:64px;min-width:64px;border-right:1px solid var(--border);background:#fafbf9} +.tv-chart{display:block} +.tv-chart-view{display:flex;flex-direction:column;min-width:0} +.tv-chart-foot{ + flex-shrink:0;width:100%;background:#fff; + border-top:1px solid var(--border); + position:sticky;bottom:0;z-index:12; + box-shadow:0 -4px 12px rgba(0,0,0,.06); +} +.ta-readout-wrap--pinned{ + display:block!important;min-height:52px;width:100%;box-sizing:border-box; +} +.tv-y-slot .tv-scale-tags, +.tv-y-slot .tv-scale-tags--vol-hover, +.tv-y-slot .tv-scale-tag{display:none!important} +.tv-x-track{flex:1;position:relative;min-width:0;overflow:hidden} +.tv-x-track .ta-x-axis__tick{top:8px} +.ta-x-axis__tick{ + position:absolute;transform:translateX(-50%); + font-size:10px;font-weight:600;color:#5c6562;white-space:nowrap; + pointer-events:none;font-variant-numeric:tabular-nums; +} +.tv-cursor-x{ + position:absolute;top:3px;z-index:8;transform:translateX(-50%); + padding:2px 8px;border-radius:4px;background:#202421;color:#f5f7f4; + font-size:10px;font-weight:700;pointer-events:none;white-space:nowrap; + box-shadow:0 2px 8px rgba(0,0,0,.2); +} +.tv-readout{min-height:0} +.ta-readout-wrap{ + padding:10px 14px 12px; + background:linear-gradient(180deg,#f6f8fc 0%,#eef2f8 100%); + border-bottom:1px solid var(--border); +} +.ta-readout__chips{ + display:flex;flex-wrap:wrap;gap:8px;align-items:stretch; +} +.ta-readout-chip{ + display:inline-flex;flex-direction:column;gap:3px;min-width:52px;max-width:100%; + padding:7px 11px;background:#fff;border:1px solid rgba(0,0,0,.08); + border-radius:10px;box-shadow:0 1px 2px rgba(0,0,0,.04); + flex:0 1 auto; +} +.ta-readout-chip em{ + font-size:.64rem;font-weight:800;color:var(--text2); + letter-spacing:.04em;font-style:normal;line-height:1.2; +} +.ta-readout-chip b{ + font-size:.84rem;font-weight:800;color:var(--text); + font-variant-numeric:tabular-nums;line-height:1.25;word-break:break-word; +} +.ta-readout-chip small{ + font-size:.64rem;color:var(--text2);line-height:1.3;margin-top:-1px; +} +.ta-readout-chip--date{min-width:108px} +.ta-readout-chip--date b{font-size:.78rem;font-weight:700} +.ta-readout-chip--close b{color:var(--blue)} +.ta-readout-chip--vol-today{border-color:rgba(35,103,199,.25);background:#f5f9ff} +.ta-readout-chip--vol-up{border-color:rgba(200,138,29,.35);background:#fffbf0} +.ta-readout-chip--vol-up b{color:#9a6b12} +.ta-readout-chip--vol-spike{border-color:rgba(216,79,69,.35);background:#fff6f5} +.ta-readout-chip--vol-spike b{color:#b91c1c} +.ta-readout-chip__badge{ + align-self:flex-start;margin-top:2px;padding:2px 7px;border-radius:5px; + font-size:.62rem;font-weight:800;line-height:1.2;letter-spacing:.02em; +} +.ta-readout-chip__badge--elevated{background:#fff6e0;color:#9a6b12} +.ta-readout-chip__badge--spike{background:#fde8e6;color:#b91c1c} +.ta-chip--vol-spike{background:#fde8e6;color:#b91c1c;border:1px solid #f0b4ae;font-weight:800} +.ta-chip--vol-elevated{background:#fff6e0;color:#9a6b12;border:1px solid #ecd9a8;font-weight:800} + +.vol-bar--active{stroke:#202421;stroke-width:1.2} +.vol-bar--spike.vol-bar--active{stroke:#b91c1c} +.ta-glossary-bar{ + display:grid;grid-template-columns:auto 1fr;gap:8px 12px;align-items:center; + padding:10px 14px 12px;border-top:1px solid var(--border);background:#fafbf9; +} +.ta-glossary-title{ + font-size:.72rem;font-weight:800;color:var(--text2);white-space:nowrap; + padding-top:2px; +} +.ta-glossary-chips{ + display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;min-width:0; +} +.ta-glossary-chips .info-btn{margin:0;flex-shrink:0} +.ta-stat-label{ + display:inline-flex;align-items:center;gap:4px; + font-size:.7rem;color:var(--text2);font-weight:700;line-height:1.35; +} +.ta-stat-label .info-btn{margin:0;vertical-align:baseline} +.ta-hero-kpis .ta-stat-label{display:inline-flex} +.chart-row-sticky{display:flex;flex-direction:row;align-items:stretch;width:max-content} +.chart-plot-area{flex-shrink:0} +.ta-sub-panel{border-bottom:1px solid var(--border);background:#fff} +.ta-sub-panel[hidden]{display:none} +.ta-sub-panel-head{ + display:flex;align-items:center;justify-content:space-between;gap:8px; + padding:6px 12px;background:#f5f7f4;border-bottom:1px solid var(--border); +} +.ta-sub-panel-head span{font-size:.72rem;font-weight:800;color:var(--text2);letter-spacing:.02em} +.ta-sub-close{ + border:none;background:transparent;color:var(--text2);font-size:1.1rem;line-height:1; + cursor:pointer;padding:2px 8px;border-radius:6px;font-weight:700; +} +.ta-sub-close:hover{background:rgba(0,0,0,.06);color:var(--text)} +.ta-subchart-body{min-height:88px;overflow:visible} +.ta-subchart-body .chart-row-sticky{border-bottom:none} +.ta-subchart-body svg{display:block;width:100%;height:auto} +.ta-panels-empty{ + padding:20px 16px;text-align:center;font-size:.78rem;color:var(--text2); + border-bottom:1px solid var(--border);background:#fafbfc; +} +.ta-panels-empty[hidden]{display:none} + +.ta-stats-wrap{min-width:0} +.ta-stat-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px} +.ta-stat{ + background:var(--surface);border:1px solid var(--border);border-radius:12px; + padding:12px 14px;min-width:0;box-shadow:var(--shadow); +} +.ta-stat span{display:block;font-size:.7rem;color:var(--text2);font-weight:700;line-height:1.35} +.ta-stat b{display:block;font-size:1.02rem;font-weight:800;margin-top:6px;font-variant-numeric:tabular-nums;word-break:break-word} +.ta-stat small{display:block;font-size:.68rem;color:var(--text2);margin-top:4px;line-height:1.35} +.ta-ai-card{ + background:var(--surface);border:1px solid rgba(35,103,199,.2);border-radius:16px; + padding:18px 20px;min-width:0;box-shadow:var(--soft-shadow); +} +.ta-ai-desc{margin:0 0 12px;font-size:.8rem;color:var(--text2);line-height:1.55} +.ta-ai-actions{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px} +.ta-ai-out{ + font-size:.86rem;line-height:1.65;color:var(--text); + background:#f9faf7;border:1px solid var(--border);border-radius:12px; + padding:14px 16px;min-height:80px;max-height:min(40vh,360px); + overflow-y:auto;overflow-x:hidden;min-width:0; +} +.ta-ai-out .ta-ai-md{min-width:0;overflow-wrap:anywhere;word-break:break-word} +.ta-ai-out .md{font-size:.86rem;line-height:1.65;max-width:100%} +.ta-ai-out .md>:first-child{margin-top:0} +.ta-ai-out .md>:last-child{margin-bottom:0} +.ta-ai-out .md h1{font-size:1.05rem;margin:.6em 0 .35em;padding:0;border:none} +.ta-ai-out .md h2{font-size:.98rem;margin:.75em 0 .35em;padding-bottom:.25em} +.ta-ai-out .md h3{font-size:.9rem;margin:.6em 0 .3em} +.ta-ai-out .md h4{font-size:.86rem;margin:.5em 0 .25em} +.ta-ai-out .md p{margin:.45em 0} +.ta-ai-out .md ul,.ta-ai-out .md ol{margin:.4em 0 .5em;padding-left:1.25em} +.ta-ai-out .md li{margin:.25em 0} +.ta-ai-out .md blockquote{ + margin:.5em 0;padding:8px 12px;border-left:3px solid var(--blue); + background:rgba(35,103,199,.06);border-radius:0 8px 8px 0; +} +.ta-ai-out .md pre,.ta-ai-out .md table{display:block;max-width:100%;overflow-x:auto} +.ta-ai-out .md table{margin:.6em 0;font-size:.8rem} +.ta-ai-out .md hr{margin:.8em 0} +.ta-ai-out .ai-error,.ta-ai-out .ai-typing{word-break:break-word} +.chart-root--ta{width:100%;height:100%} +.chart-stage--ta{width:100%;height:100%} +@media(max-width:960px){ + .ta-controls:not(.ta-controls--panels){grid-template-columns:1fr 1fr} + .ta-control-group--layers{grid-column:1/-1} + .ta-controls .ta-control-group:first-child{grid-column:1/-1} + .ta-refresh{grid-column:1/-1;justify-self:start} + .ta-stat-grid{grid-template-columns:repeat(2,minmax(0,1fr))} +} +@media(max-width:560px){ + .ta-controls{grid-template-columns:1fr} + .ta-hero-kpis{width:100%} + .ta-hero-kpis div{flex:1} + .ta-stat-grid{grid-template-columns:1fr} + .ta-glossary-bar{grid-template-columns:1fr;gap:6px} + .ta-readout-chip{min-width:46px;padding:6px 9px} + .ta-readout-chip--date{min-width:0;flex:1 1 100%} + .tv-y-col{flex:0 0 72px;width:72px;min-width:72px} + .tv-x-pad{flex:0 0 72px;width:72px;min-width:72px} + .tv-scale-tag{font-size:8px;padding:2px 4px} + .sub-tabs{width:100%;max-width:100%} +} diff --git a/app.js b/app.js index 54f2825..3e6bdd2 100644 --- a/app.js +++ b/app.js @@ -186,7 +186,7 @@ function bindWlinks(container) { // ═══════════════════════════════════════════════════════════ // 主視圖路由 // ═══════════════════════════════════════════════════════════ -const VIEW_IDS = ['macro', 'calendar', 'learn', 'stock', 'journal', 'settings']; +const VIEW_IDS = ['macro', 'calendar', 'watchlist', 'learn', 'stock', 'journal', 'settings']; const inited = {}; function parseHash() { const m = location.hash.match(/^#\/(\w+)/); const v = m ? m[1] : 'macro'; return VIEW_IDS.includes(v) ? v : 'macro'; } function setAIFocus(focus) { @@ -202,11 +202,13 @@ function setView(view) { VIEW_IDS.forEach(v => { const e = $('#view-' + v); if (e) e.hidden = v !== view; }); $$('#viewTabs a').forEach(a => a.classList.toggle('active', a.dataset.view === view)); if (view === 'calendar' && !inited.calendar) { inited.calendar = true; initCalendar(); } + if (view === 'watchlist' && !inited.watchlist) { inited.watchlist = true; initWatchlist(); } if (view === 'learn' && !inited.learn) { inited.learn = true; initLearn(); } if (view === 'stock' && !inited.stock) { inited.stock = true; initStock(); } if (view === 'journal' && !inited.journal) { inited.journal = true; initJournal(); } if (view === 'settings' && !inited.settings) { inited.settings = true; initSettings(); } updateAIContextLabel(); + if (!$('#aiPanel')?.hidden) refreshAIContextLabel(); if (view !== 'macro') window.scrollTo({ top: 0 }); } $$('#viewTabs a').forEach(a => a.addEventListener('click', () => { @@ -282,10 +284,11 @@ async function initSettings() { const envSettings = await loadEnvSettings(); const settings = readAISettings(); view.innerHTML = ` -
.env:${escapeHtml(envSettings.envPath || '.env')}。金鑰欄位留空代表保留原值;模型與預設 provider 會直接更新。.env(路徑見下方)。金鑰欄位留空代表保留原值;模型與預設 provider 會直接更新。${fmtNum(ind.close, 2)} ${escapeHtml(snap?.asOf || '')}
+每根 K 棒代表多久(日/周/月),會分開存進資料庫
+ +主圖要顯示多長的走勢;可左右拖曳/捲動看更早的 K
+ +疊在價格上的均線與布林(依 K 根數計算)
+ +券商/TradingView 常一次只看 1~2 個動量副圖;點選開啟,面板右上角可關閉
+${escapeHtml(hist.researchNote || '')}${hist.volumeNote ? ` · ${escapeHtml(hist.volumeNote)}` : ''}${hist.firstDate ? ` · ${escapeHtml(hist.firstDate)} → ${escapeHtml(hist.researchThrough || hist.lastDate || '')}` : ''}
+圖可左右捲動;下方讀數列固定不動。滑過 K 線可查看該根 OHLC、成交量與指標。
+未開啟副圖 — 在上方 ④ 點選指標
+附上圖表區間的指標摘要與報酬,白話分析趨勢與風險(非投資建議)。
+earningsTrend(卡片 ? 內有公式與 endpoint);DCF為 MacroScope 本機模型(? 內列出當次 FCF、成長率、折現率)。無資料時顯示「尚無共識/無法估算」,不填假數字。免費報價可能延遲,交易前請對照券商與官方財報。${escapeHtml(profile.description)}
` : ''} + ${desc ? `${escapeHtml(desc)}
${escapeHtml(descNote)}
` : '尚無公司簡介,進入本頁會自動抓取。
'} - ${notif.length ? `${escapeHtml((e.note || '').slice(0, 200))}
+ +法說逐字稿多由公司 IR 網站發布,免費來源不一定有全文;此處會存財報日、8-K 財報公告與本機副本連結。
+${escapeHtml(newsSummaryText(n))}
` : ''} + `).join(''); +} + +function newsPanelHtml(list) { + return list.length + ? `${escapeHtml(profileDesc)}
+10-K 摘要 ${escapeHtml(chain.tenKExcerpt)}${chain.tenKExcerpt.length >= 400 ? '…' : ''}
` : ''} + ${intelResourceLinksHtml(intel.resources)}${escapeHtml(m.summary || '')}
+ ${m.url ? `原文` : ''} +${escapeHtml(n.description || '')}
- `).join('') : '首次進入本頁會從 Yahoo/SEC 10-K 自動取得名單與職稱(中文對照);下次財報公開日前不會重複抓取。
+${p.reason||''}
+${exp.howToRead||''}
+ ${topChips?`這裡(ETF 持股):看 XLK、SPY 等基金「裡面裝什麼」,反映被動指數與板塊 ETF 的結構性配置,更新約每月。
+13F:美國管理超過 1 億美元機構每季申報的「股票部位」清單(含主動基金),約延遲 45 天,可在 SEC EDGAR 查 Berkshire、Bridgewater 等個別持倉。
+近期流向:上方「資金流向偏強/偏弱」是價量推估,不是逐筆買賣;要追單一股票可再到「個股工具」看量能與新聞。
+${exp.disclaimer||''}
+以 SPDR 11 大行業 ETF 對照 ${sd.benchmark||'SPY'}:熱力圖看漲跌、輪動看相對強度、流向看價量、下方可看 ETF 實際持股(機構多透過 ETF 間接持有)。${cached}
+| 板塊 | ${instHead}
|---|
${inst.disclaimer||''}${inst.totalAumB!=null?` 合計約 $${inst.totalAumB.toFixed(0)}B。`:''}
+ ${holdingsBlock} +${msg}
+o.title !== 'Director' || uniq.size <= 4).slice(0, 12)); +} + +const stripHtml = (s) => String(s || '').replace(/<[^>]+>/g, ' ').replace(/ /g, ' ').replace(/\s+/g, ' ').trim(); + +async function fetchOfficersFromSec10k(symbol) { + const hit = await tickerToCik(symbol); + if (!hit) return []; + const sub = await json(`https://data.sec.gov/submissions/CIK${hit.cik}.json`, { 'User-Agent': SEC_UA }); + const f = sub.filings?.recent || {}; + let accn = null; + let primary = null; + for (let i = 0; i < (f.form || []).length; i++) { + if (f.form[i] === '10-K') { + accn = f.accessionNumber[i]; + primary = f.primaryDocument?.[i]; + break; + } + } + if (!accn || !primary) return []; + const accNo = accn.replace(/-/g, ''); + const url = `https://www.sec.gov/Archives/edgar/data/${Number(hit.cik)}/${accNo}/${primary}`; + const html = await text(url, { 'User-Agent': SEC_UA }, 25000); + const item10 = html.search(/Item\s*10[\s\S]{0,120}(Executive Officers|Directors)/i); + const slice = item10 >= 0 ? html.slice(item10, item10 + 120000) : html.slice(0, 120000); + const rows = [...slice.matchAll(/
${_esc(t.formula)}