diff --git a/_freeze/site_libs/revealjs/dist/theme/quarto-2740581606dc327e82f18f789b69d8a0.css b/_freeze/site_libs/revealjs/dist/theme/quarto-2740581606dc327e82f18f789b69d8a0.css new file mode 100644 index 0000000..d150035 --- /dev/null +++ b/_freeze/site_libs/revealjs/dist/theme/quarto-2740581606dc327e82f18f789b69d8a0.css @@ -0,0 +1,8 @@ +@import"./fonts/source-sans-pro/source-sans-pro.css";:root{--r-background-color: #fff;--r-main-font: Source Sans Pro, Helvetica, sans-serif;--r-main-font-size: 40px;--r-main-color: #222;--r-block-margin: 12px;--r-heading-margin: 0 0 12px 0;--r-heading-font: Source Sans Pro, Helvetica, sans-serif;--r-heading-color: #222;--r-heading-line-height: 1.2;--r-heading-letter-spacing: normal;--r-heading-text-transform: none;--r-heading-text-shadow: none;--r-heading-font-weight: 600;--r-heading1-text-shadow: none;--r-heading1-size: 2.5em;--r-heading2-size: 1.6em;--r-heading3-size: 1.3em;--r-heading4-size: 1em;--r-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-link-color: #2a76dd;--r-link-color-dark: rgb(25.6720647773, 83.0566801619, 160.8279352227);--r-link-color-hover: rgb(85.979757085, 146.2874493927, 228.020242915);--r-selection-background-color: rgb(151.9493927126, 188.7186234818, 238.5506072874);--r-selection-color: #fff;--r-overlay-element-bg-color: 240, 240, 240;--r-overlay-element-fg-color: 0, 0, 0}.reveal-viewport{background:#fff;background-color:var(--r-background-color)}.reveal{font-family:var(--r-main-font);font-size:var(--r-main-font-size);font-weight:normal;color:var(--r-main-color)}.reveal ::selection{color:var(--r-selection-color);background:var(--r-selection-background-color);text-shadow:none}.reveal ::-moz-selection{color:var(--r-selection-color);background:var(--r-selection-background-color);text-shadow:none}.reveal .slides section,.reveal .slides section>section{line-height:1.3;font-weight:inherit}.reveal h1,.reveal h2,.reveal h3,.reveal h4,.reveal h5,.reveal h6{margin:var(--r-heading-margin);color:var(--r-heading-color);font-family:var(--r-heading-font);font-weight:var(--r-heading-font-weight);line-height:var(--r-heading-line-height);letter-spacing:var(--r-heading-letter-spacing);text-transform:var(--r-heading-text-transform);text-shadow:var(--r-heading-text-shadow);word-wrap:break-word}.reveal h1{font-size:var(--r-heading1-size)}.reveal h2{font-size:var(--r-heading2-size)}.reveal h3{font-size:var(--r-heading3-size)}.reveal h4{font-size:var(--r-heading4-size)}.reveal h1{text-shadow:var(--r-heading1-text-shadow)}.reveal p{margin:var(--r-block-margin) 0;line-height:1.3}.reveal h1:last-child,.reveal h2:last-child,.reveal h3:last-child,.reveal h4:last-child,.reveal h5:last-child,.reveal h6:last-child{margin-bottom:0}.reveal img,.reveal video,.reveal iframe{max-width:95%;max-height:95%}.reveal strong,.reveal b{font-weight:bold}.reveal em{font-style:italic}.reveal ol,.reveal dl,.reveal ul{display:inline-block;text-align:left;margin:0 0 0 1em}.reveal ol{list-style-type:decimal}.reveal ul{list-style-type:disc}.reveal ul ul{list-style-type:square}.reveal ul ul ul{list-style-type:circle}.reveal ul ul,.reveal ul ol,.reveal ol ol,.reveal ol ul{display:block;margin-left:40px}.reveal dt{font-weight:bold}.reveal dd{margin-left:40px}.reveal blockquote{display:block;position:relative;width:70%;margin:var(--r-block-margin) auto;padding:5px;font-style:italic;background:hsla(0,0%,100%,.05);box-shadow:0px 0px 2px rgba(0,0,0,.2)}.reveal blockquote p:first-child,.reveal blockquote p:last-child{display:inline-block}.reveal q{font-style:italic}.reveal pre{display:block;position:relative;width:90%;margin:var(--r-block-margin) auto;text-align:left;font-size:.55em;font-family:var(--r-code-font);line-height:1.2em;word-wrap:break-word;box-shadow:0px 5px 15px rgba(0,0,0,.15)}.reveal code{font-family:var(--r-code-font);text-transform:none;tab-size:2}.reveal pre code{display:block;padding:5px;overflow:auto;max-height:400px;word-wrap:normal}.reveal .code-wrapper{white-space:normal}.reveal .code-wrapper code{white-space:pre}.reveal table{margin:auto;border-collapse:collapse;border-spacing:0}.reveal table th{font-weight:bold}.reveal table th,.reveal table td{text-align:left;padding:.2em .5em .2em .5em;border-bottom:1px solid}.reveal table th[align=center],.reveal table td[align=center]{text-align:center}.reveal table th[align=right],.reveal table td[align=right]{text-align:right}.reveal table tbody tr:last-child th,.reveal table tbody tr:last-child td{border-bottom:none}.reveal sup{vertical-align:super;font-size:smaller}.reveal sub{vertical-align:sub;font-size:smaller}.reveal small{display:inline-block;font-size:.6em;line-height:1.2em;vertical-align:top}.reveal small *{vertical-align:top}.reveal img{margin:var(--r-block-margin) 0}.reveal a{color:var(--r-link-color);text-decoration:none;transition:color .15s ease}.reveal a:hover{color:var(--r-link-color-hover);text-shadow:none;border:none}.reveal .roll span:after{color:#fff;background:var(--r-link-color-dark)}.reveal .r-frame{border:4px solid var(--r-main-color);box-shadow:0 0 10px rgba(0,0,0,.15)}.reveal a .r-frame{transition:all .15s linear}.reveal a:hover .r-frame{border-color:var(--r-link-color);box-shadow:0 0 20px rgba(0,0,0,.55)}.reveal .controls{color:var(--r-link-color)}.reveal .progress{background:rgba(0,0,0,.2);color:var(--r-link-color)}@media print{.backgrounds{background-color:var(--r-background-color)}}.top-right{position:absolute;top:1em;right:1em}.visually-hidden{border:0;clip:rect(0 0 0 0);height:auto;margin:0;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap}.hidden{display:none !important}.zindex-bottom{z-index:-1 !important}figure.figure{display:block}.quarto-layout-panel{margin-bottom:1em}.quarto-layout-panel>figure{width:100%}.quarto-layout-panel>figure>figcaption,.quarto-layout-panel>.panel-caption{margin-top:10pt}.quarto-layout-panel>.table-caption{margin-top:0px}.table-caption p{margin-bottom:.5em}.quarto-layout-row{display:flex;flex-direction:row;align-items:flex-start}.quarto-layout-valign-top{align-items:flex-start}.quarto-layout-valign-bottom{align-items:flex-end}.quarto-layout-valign-center{align-items:center}.quarto-layout-cell{position:relative;margin-right:20px}.quarto-layout-cell:last-child{margin-right:0}.quarto-layout-cell figure,.quarto-layout-cell>p{margin:.2em}.quarto-layout-cell img{max-width:100%}.quarto-layout-cell .html-widget{width:100% !important}.quarto-layout-cell div figure p{margin:0}.quarto-layout-cell figure{display:block;margin-inline-start:0;margin-inline-end:0}.quarto-layout-cell table{display:inline-table}.quarto-layout-cell-subref figcaption,figure .quarto-layout-row figure figcaption{text-align:center;font-style:italic}.quarto-figure{position:relative;margin-bottom:1em}.quarto-figure>figure{width:100%;margin-bottom:0}.quarto-figure-left>figure>p,.quarto-figure-left>figure>div{text-align:left}.quarto-figure-center>figure>p,.quarto-figure-center>figure>div{text-align:center}.quarto-figure-right>figure>p,.quarto-figure-right>figure>div{text-align:right}.quarto-figure>figure>div.cell-annotation,.quarto-figure>figure>div code{text-align:left}figure>p:empty{display:none}figure>p:first-child{margin-top:0;margin-bottom:0}figure>figcaption.quarto-float-caption-bottom{margin-bottom:.5em}figure>figcaption.quarto-float-caption-top{margin-top:.5em}div[id^=tbl-]{position:relative}.quarto-figure>.anchorjs-link{position:absolute;top:.6em;right:.5em}div[id^=tbl-]>.anchorjs-link{position:absolute;top:.7em;right:.3em}.quarto-figure:hover>.anchorjs-link,div[id^=tbl-]:hover>.anchorjs-link,h2:hover>.anchorjs-link,h3:hover>.anchorjs-link,h4:hover>.anchorjs-link,h5:hover>.anchorjs-link,h6:hover>.anchorjs-link,.reveal-anchorjs-link>.anchorjs-link{opacity:1}#title-block-header{margin-block-end:1rem;position:relative;margin-top:-1px}#title-block-header .abstract{margin-block-start:1rem}#title-block-header .abstract .abstract-title{font-weight:600}#title-block-header a{text-decoration:none}#title-block-header .author,#title-block-header .date,#title-block-header .doi{margin-block-end:.2rem}#title-block-header .quarto-title-block>div{display:flex}#title-block-header .quarto-title-block>div>h1{flex-grow:1}#title-block-header .quarto-title-block>div>button{flex-shrink:0;height:2.25rem;margin-top:0}tr.header>th>p:last-of-type{margin-bottom:0px}table,table.table{margin-top:.5rem;margin-bottom:.5rem}caption,.table-caption{padding-top:.5rem;padding-bottom:.5rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-top{margin-top:.5rem;margin-bottom:.25rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-bottom{padding-top:.25rem;margin-bottom:.5rem;text-align:center}.utterances{max-width:none;margin-left:-8px}iframe{margin-bottom:1em}details{margin-bottom:1em}details[show]{margin-bottom:0}details>summary{color:rgb(110.5,110.5,110.5)}details>summary>p:only-child{display:inline}div.code-copy-outer-scaffold{position:relative}dd code:not(.sourceCode),p code:not(.sourceCode){white-space:pre-wrap}code{white-space:pre}@media print{code{white-space:pre-wrap}}pre>code{display:block}pre>code.sourceCode{white-space:pre}pre>code.sourceCode>span>a:first-child::before{text-decoration:none}pre.code-overflow-wrap>code.sourceCode{white-space:pre-wrap}pre.code-overflow-scroll>code.sourceCode{white-space:pre}code a:any-link{color:inherit;text-decoration:none}code a:hover{color:inherit;text-decoration:underline}ul.task-list{padding-left:1em}[data-tippy-root]{display:inline-block}.tippy-content .footnote-back{display:none}.footnote-back{margin-left:.2em}.tippy-content{overflow-x:auto}.quarto-embedded-source-code{display:none}.quarto-unresolved-ref{font-weight:600}.quarto-cover-image{max-width:35%;float:right;margin-left:30px}.cell-output-display .widget-subarea{margin-bottom:1em}.cell-output-display:not(.no-overflow-x),.knitsql-table:not(.no-overflow-x){overflow-x:auto}.panel-input{margin-bottom:1em}.panel-input>div,.panel-input>div>div{display:inline-block;vertical-align:top;padding-right:12px}.panel-input>p:last-child{margin-bottom:0}.layout-sidebar{margin-bottom:1em}.layout-sidebar .tab-content{border:none}.tab-content>.page-columns.active{display:grid}div.sourceCode>iframe{width:100%;height:300px;margin-bottom:-0.5em}a{text-underline-offset:3px}.callout pre.sourceCode{padding-left:0}div.ansi-escaped-output{font-family:monospace;display:block}/*! +* +* ansi colors from IPython notebook's +* +* we also add `bright-[color]-` synonyms for the `-[color]-intense` classes since +* that seems to be what ansi_up emits +* +*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-black,.ansi-bright-black-fg{color:#282c36}.ansi-black-intense-black,.ansi-bright-black-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-red,.ansi-bright-red-fg{color:#b22b31}.ansi-red-intense-red,.ansi-bright-red-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-green,.ansi-bright-green-fg{color:#007427}.ansi-green-intense-green,.ansi-bright-green-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-yellow,.ansi-bright-yellow-fg{color:#b27d12}.ansi-yellow-intense-yellow,.ansi-bright-yellow-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-blue,.ansi-bright-blue-fg{color:#0065ca}.ansi-blue-intense-blue,.ansi-bright-blue-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-magenta,.ansi-bright-magenta-fg{color:#a03196}.ansi-magenta-intense-magenta,.ansi-bright-magenta-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-cyan,.ansi-bright-cyan-fg{color:#258f8f}.ansi-cyan-intense-cyan,.ansi-bright-cyan-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-white,.ansi-bright-white-fg{color:#a1a6b2}.ansi-white-intense-white,.ansi-bright-white-bg{background-color:#a1a6b2}.ansi-default-inverse-fg{color:#fff}.ansi-default-inverse-bg{background-color:#000}.ansi-bold{font-weight:bold}.ansi-underline{text-decoration:underline}:root{--quarto-body-bg: #fff;--quarto-body-color: #222;--quarto-text-muted: rgb(110.5, 110.5, 110.5);--quarto-border-color: #bbbbbb;--quarto-border-width: 1px;--quarto-border-radius: 4px}table.gt_table{color:var(--quarto-body-color);font-size:1em;width:100%;background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_column_spanner_outer{color:var(--quarto-body-color);background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_col_heading{color:var(--quarto-body-color);font-weight:bold;background-color:rgba(0,0,0,0)}table.gt_table thead.gt_col_headings{border-bottom:1px solid currentColor;border-top-width:inherit;border-top-color:var(--quarto-border-color)}table.gt_table thead.gt_col_headings:not(:first-child){border-top-width:1px;border-top-color:var(--quarto-border-color)}table.gt_table td.gt_row{border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-width:0px}table.gt_table tbody.gt_table_body{border-top-width:1px;border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-color:currentColor}div.columns{display:initial;gap:initial}div.column{display:inline-block;overflow-x:initial;vertical-align:top;width:50%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:relative;float:right;background-color:rgba(0,0,0,0)}input[type=checkbox]{margin-right:.5ch}:root{--mermaid-bg-color: #fff;--mermaid-edge-color: #999;--mermaid-node-fg-color: #222;--mermaid-fg-color: #222;--mermaid-fg-color--lighter: rgb(59.5, 59.5, 59.5);--mermaid-fg-color--lightest: #555555;--mermaid-font-family: Source Sans Pro, Helvetica, sans-serif;--mermaid-label-bg-color: #fff;--mermaid-label-fg-color: #2a76dd;--mermaid-node-bg-color: rgba(42, 118, 221, 0.1);--mermaid-node-fg-color: #222}@media print{:root{font-size:11pt}#quarto-sidebar,#TOC,.nav-page{display:none}.page-columns .content{grid-column-start:page-start}.fixed-top{position:relative}.panel-caption,.figure-caption,figcaption{color:#666}}body.quarto-light .dark-content{display:none !important}body.quarto-dark .light-content{display:none !important}.code-copy-button{position:absolute;top:0;right:0;border:0;margin-top:5px;margin-right:5px;background-color:rgba(0,0,0,0);z-index:3}.code-copy-button-tooltip{font-size:.75em}div.code-copy-outer-scaffold:hover>.code-copy-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}div.code-copy-outer-scaffold:hover>.code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}div.code-copy-outer-scaffold:hover>.code-copy-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}div.code-copy-outer-scaffold:hover>.code-copy-button-checked:hover>.bi::before{background-image:url('data:image/svg+xml,')}.panel-tabset [role=tablist]{border-bottom:1px solid #bbb;list-style:none;margin:0;padding:0;width:100%}.panel-tabset [role=tablist] *{-webkit-box-sizing:border-box;box-sizing:border-box}@media(min-width: 30em){.panel-tabset [role=tablist] li{display:inline-block}}.panel-tabset [role=tab]{border:1px solid rgba(0,0,0,0);border-top-color:#bbb;display:block;padding:.5em 1em;text-decoration:none}@media(min-width: 30em){.panel-tabset [role=tab]{border-top-color:rgba(0,0,0,0);display:inline-block;margin-bottom:-1px}}.panel-tabset [role=tab][aria-selected=true]{background-color:#bbb}@media(min-width: 30em){.panel-tabset [role=tab][aria-selected=true]{background-color:rgba(0,0,0,0);border:1px solid #bbb;border-bottom-color:#fff}}@media(min-width: 30em){.panel-tabset [role=tab]:hover:not([aria-selected=true]){border:1px solid #bbb}}.code-with-filename .code-with-filename-file{margin-bottom:0;padding-bottom:2px;padding-top:2px;padding-left:.7em;border:var(--quarto-border-width) solid var(--quarto-border-color);border-radius:var(--quarto-border-radius);border-bottom:0;border-bottom-left-radius:0%;border-bottom-right-radius:0%}.code-with-filename div.sourceCode,.reveal .code-with-filename div.sourceCode{margin-top:0;border-top-left-radius:0%;border-top-right-radius:0%}.code-with-filename .code-with-filename-file pre{margin-bottom:0}.code-with-filename .code-with-filename-file{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}.reveal.center .slide aside,.reveal.center .slide div.aside{position:initial}section.has-light-background,section.has-light-background h1,section.has-light-background h2,section.has-light-background h3,section.has-light-background h4,section.has-light-background h5,section.has-light-background h6{color:#222}section.has-light-background a,section.has-light-background a:hover{color:#2a76dd}section.has-light-background code{color:#4758ab}section.has-dark-background,section.has-dark-background h1,section.has-dark-background h2,section.has-dark-background h3,section.has-dark-background h4,section.has-dark-background h5,section.has-dark-background h6{color:#fff}section.has-dark-background a,section.has-dark-background a:hover{color:#42affa}section.has-dark-background code{color:#ffa07a}#title-slide,div.reveal div.slides section.quarto-title-block{text-align:center}#title-slide .subtitle,div.reveal div.slides section.quarto-title-block .subtitle{margin-bottom:2.5rem}.reveal .slides{text-align:left}.reveal .title-slide h1{font-size:1.6em}.reveal[data-navigation-mode=linear] .title-slide h1{font-size:2.5em}.reveal div.sourceCode{border:1px solid #bbb;border-radius:4px}.reveal pre{width:100%;box-shadow:none;background-color:#fff;border:none;margin:0;font-size:.55em;line-height:1.3;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.reveal pre code{background-color:#fff;font-size:inherit;color:#222;font-family:inherit}.reveal pre.sourceCode code{color:#222;font-size:inherit;background-color:inherit;white-space:pre;font-family:inherit;padding:6px 9px;max-height:500px}.reveal .code-with-filename .code-with-filename-file pre{background-color:unset}.reveal code{color:var(--quarto-hl-fu-color);font-size:.875em;background-color:rgba(0,0,0,0);white-space:pre-wrap;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.reveal .column-output-location{display:flex;align-items:stretch}.reveal .column-output-location .column:first-of-type div.sourceCode{height:100%;background-color:#fff}.reveal blockquote{display:block;position:relative;color:rgb(110.5,110.5,110.5);width:unset;margin:var(--r-block-margin) auto;padding:.625rem 1.75rem;border-left:.25rem solid rgb(110.5,110.5,110.5);font-style:normal;background:none;box-shadow:none}.reveal blockquote p:first-child,.reveal blockquote p:last-child{display:block}.reveal .slide aside,.reveal .slide div.aside{position:absolute;bottom:20px;font-size:0.7em;color:rgb(110.5,110.5,110.5)}.reveal .slide sup{font-size:0.7em}.reveal .slide.scrollable aside,.reveal .slide.scrollable div.aside{position:relative;margin-top:1em}.reveal .slide aside .aside-footnotes{margin-bottom:0}.reveal .slide aside .aside-footnotes li:first-of-type{margin-top:0}.reveal .layout-sidebar{display:flex;width:100%;margin-top:.8em}.reveal .layout-sidebar .panel-sidebar{width:270px}.reveal .layout-sidebar-left .panel-sidebar{margin-right:calc(0.5em*2)}.reveal .layout-sidebar-right .panel-sidebar{margin-left:calc(0.5em*2)}.reveal .layout-sidebar .panel-fill,.reveal .layout-sidebar .panel-center,.reveal .layout-sidebar .panel-tabset{flex:1}.reveal .panel-input,.reveal .panel-sidebar{font-size:.5em;padding:.5em;border-style:solid;border-color:#bbb;border-width:1px;border-radius:4px;background-color:#f8f9fa}.reveal .panel-sidebar :first-child,.reveal .panel-fill :first-child{margin-top:0}.reveal .panel-sidebar :last-child,.reveal .panel-fill :last-child{margin-bottom:0}.panel-input>div,.panel-input>div>div{vertical-align:middle;padding-right:1em}.reveal p,.reveal .slides section,.reveal .slides section>section{line-height:1.3}.reveal.smaller .slides section{font-size:0.7em}.reveal.smaller .slides section section{font-size:inherit}.reveal.smaller .slides h1{font-size:calc(2.5em/0.7)}.reveal.smaller .slides h2{font-size:calc(1.6em/0.7)}.reveal.smaller .slides h3{font-size:calc(1.3em/0.7)}.reveal .slides section.smaller{font-size:0.7em}.reveal .slides section.smaller h1{font-size:calc(2.5em/0.7)}.reveal .slides section.smaller h2{font-size:calc(1.6em/0.7)}.reveal .slides section.smaller h3{font-size:calc(1.3em/0.7)}.reveal .slides section div.callout{font-size:0.7em}.reveal .slides section div.callout h1{font-size:calc(2.5em/0.7)}.reveal .slides section div.callout h2{font-size:calc(1.6em/0.7)}.reveal .slides section div.callout h3{font-size:calc(1.3em/0.7)}.reveal .columns>.column>:not(ul,ol){margin-left:.25rem;margin-right:.25rem}.reveal .columns>.column:first-child>:not(ul,ol){margin-right:.5rem;margin-left:0}.reveal .columns>.column:last-child>:not(ul,ol){margin-right:0;margin-left:.5rem}.reveal .slide-number{color:rgb(85.979757085,146.2874493927,228.020242915);background-color:#fff}.reveal .footer{color:rgb(110.5,110.5,110.5)}.reveal .footer a{color:#2a76dd}.reveal .footer.has-dark-background{color:#fff}.reveal .footer.has-dark-background a{color:rgb(122.7,198.1114130435,250)}.reveal .footer.has-light-background{color:hsl(0,0%,31.2169312169%)}.reveal .footer.has-light-background a{color:rgb(105.9,154.769273743,221)}.reveal .slide-number{color:rgb(110.5,110.5,110.5)}.reveal .slide-number.has-dark-background{color:#fff}.reveal .slide-number.has-light-background{color:hsl(0,0%,31.2169312169%)}.reveal .slide figure>figcaption,.reveal .slide img.stretch+p.caption,.reveal .slide img.r-stretch+p.caption{font-size:0.7em}@media screen and (min-width: 500px){.reveal .controls[data-controls-layout=edges] .navigate-left{left:.2em}.reveal .controls[data-controls-layout=edges] .navigate-right{right:.2em}.reveal .controls[data-controls-layout=edges] .navigate-up{top:.4em}.reveal .controls[data-controls-layout=edges] .navigate-down{bottom:2.3em}}.tippy-box[data-theme~=light-border]{background-color:#fff;color:#222;border-radius:4px;border:solid 1px rgb(110.5,110.5,110.5);font-size:.6em}.tippy-box[data-theme~=light-border] .tippy-arrow{color:rgb(110.5,110.5,110.5)}.tippy-box[data-placement^=bottom]>.tippy-content{padding:7px 10px;z-index:1}.reveal .panel-tabset [role=tab]{padding:.25em .7em}.reveal .slide-menu-button .fa-bars::before{background-image:url('data:image/svg+xml,')}.reveal .slide-chalkboard-buttons .fa-easel2::before{background-image:url('data:image/svg+xml,')}.reveal .slide-chalkboard-buttons .fa-brush::before{background-image:url('data:image/svg+xml,')}/*! light */.reveal ol[type=a]{list-style-type:lower-alpha}.reveal ol[type=a s]{list-style-type:lower-alpha}.reveal ol[type=A s]{list-style-type:upper-alpha}.reveal ol[type=i]{list-style-type:lower-roman}.reveal ol[type=i s]{list-style-type:lower-roman}.reveal ol[type=I s]{list-style-type:upper-roman}.reveal ol[type="1"]{list-style-type:decimal}.reveal ul.task-list{list-style:none}.reveal ul.task-list li input[type=checkbox]{width:2em;height:2em;margin:0 1em .5em -1.6em;vertical-align:middle}div.cell-output-display div.pagedtable-wrapper table.table{font-size:.6em}.reveal .code-annotation-container-hidden{display:none}.reveal code.sourceCode button.code-annotation-anchor,.reveal code.sourceCode .code-annotation-anchor{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;font-size:.7em;line-height:1.2em;margin-top:2px;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none}.reveal code.sourceCode button.code-annotation-anchor{cursor:pointer}.reveal code.sourceCode a.code-annotation-anchor{text-align:center;vertical-align:middle;text-decoration:none;cursor:default;height:1.2em;width:1.2em}.reveal code.sourceCode.fragment a.code-annotation-anchor{left:auto}.reveal #code-annotation-line-highlight-gutter{width:100%;border-top:solid var(--quarto-hl-co-color) 1px;border-bottom:solid var(--quarto-hl-co-color) 1px;z-index:2}.reveal #code-annotation-line-highlight{margin-left:-8em;width:calc(100% + 4em);border-top:solid var(--quarto-hl-co-color) 1px;border-bottom:solid var(--quarto-hl-co-color) 1px;z-index:2;margin-bottom:-2px}.reveal code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#fff;font-weight:bolder}.reveal pre.code-annotation-code{padding-top:0;padding-bottom:0}.reveal pre.code-annotation-code code{z-index:3;padding-left:0px}.reveal dl.code-annotation-container-grid{margin-left:.1em}.reveal dl.code-annotation-container-grid dt{margin-top:.65rem;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;border:solid #222 1px;border-radius:50%;height:1.3em;width:1.3em;line-height:1.3em;font-size:.5em;text-align:center;vertical-align:middle;text-decoration:none}.reveal dl.code-annotation-container-grid dd{margin-left:.25em}.reveal .scrollable ol li:first-child:nth-last-child(n+10),.reveal .scrollable ol li:first-child:nth-last-child(n+10)~li{margin-left:1em}kbd{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:40px;color:#222;background-color:#f8f9fa;border:1px solid;border-color:#bbb;border-radius:5px;padding:.4rem .4rem}:root{--r-inline-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-block-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-inline-code-font-size: 0.875em;--r-block-code-font-size: 0.55em}.reveal a{font-weight:400;background-color:rgba(0,0,0,0);text-decoration:inherit}.reveal div.callout{margin-top:1rem;margin-bottom:1rem;border-radius:4px;overflow-wrap:break-word}.reveal div.callout.callout-style-simple,.reveal div.callout.callout-style-default{border-left:.3rem solid #acacac;border-right:solid 1px #bbb;border-top:solid 1px #bbb;border-bottom:solid 1px #bbb}.reveal div.callout.callout-style-simple div.callout-body,.reveal div.callout.callout-style-simple div.callout-title,.reveal div.callout.callout-style-default div.callout-body,.reveal div.callout.callout-style-default div.callout-title{font-size:inherit;border-bottom:none;font-weight:600}.reveal div.callout.callout-style-simple div.callout-title,.reveal div.callout.callout-style-default div.callout-title{display:flex;align-items:center}.reveal div.callout.callout-style-simple div.callout-title p,.reveal div.callout.callout-style-default div.callout-title p{margin-top:.5em;margin-bottom:.5em;color:var(--r-main-color)}.reveal div.callout.callout-style-simple .callout-icon::before,.reveal div.callout.callout-style-default .callout-icon::before{height:1.25em;width:1.25em;background-size:1.25em 1.25em}.reveal div.callout.callout-style-simple.callout-titled .callout-body>.callout-content>:last-child,.reveal div.callout.callout-style-default.callout-titled .callout-body>.callout-content>:last-child{margin-bottom:var(--r-block-margin)}.reveal div.callout.callout-style-simple.callout-titled .callout-body>.callout-content>:last-child:not(div.sourceCode),.reveal div.callout.callout-style-default.callout-titled .callout-body>.callout-content>:last-child:not(div.sourceCode){padding-bottom:.5rem;margin-bottom:0}.reveal div.callout.callout-style-simple.callout-titled .callout-icon::before,.reveal div.callout.callout-style-default.callout-titled .callout-icon::before{margin-top:.25em;padding-right:.25em}.reveal div.callout.callout-style-simple.no-icon::before,.reveal div.callout.callout-style-default.no-icon::before{display:none !important}.reveal div.callout.callout-style-simple{padding:0em .5em;display:flex}.reveal div.callout.callout-style-simple.callout-titled .callout-body{margin-top:.2em}.reveal div.callout.callout-style-simple.callout-titled:not(.no-icon) .callout-content{padding-left:1.6em}.reveal div.callout.callout-style-simple.callout-titled .callout-content p{margin-top:0}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body{display:flex}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-icon::before{margin-top:var(--r-block-margin);padding-right:.5em}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body>.callout-content>div.sourceCode:last-child{margin-bottom:1rem}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body>.callout-content>:first-child{margin-top:var(--r-block-margin)}.reveal div.callout.callout-style-simple .callout-icon::before{display:inline-block;content:"";background-repeat:no-repeat}.reveal div.callout.callout-style-simple div.callout-title{opacity:75%}.reveal div.callout.callout-style-simple div.callout-body{font-weight:400}.reveal div.callout.callout-style-default.callout-titled .callout-content p{margin-top:.7em}.reveal div.callout.callout-style-default .callout-icon::before{display:inline-block;content:"";background-repeat:no-repeat}.reveal div.callout.callout-style-default div.callout-body{font-weight:400}.reveal div.callout.callout-style-default div.callout-title{opacity:85%;padding-left:.5em;padding-right:.5em}.reveal div.callout.callout-style-default div.callout-content{padding-left:.5em;padding-right:.5em}.reveal div.callout .callout-body-container{flex-grow:1}.reveal div.callout.callout-note{border-left-color:#0d6efd}.reveal div.callout.callout-note.callout-style-default .callout-title{background-color:rgb(230.8,240.5,254.8)}.reveal div.callout.callout-note .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-tip{border-left-color:#198754}.reveal div.callout.callout-tip.callout-style-default .callout-title{background-color:rgb(232,243,237.9)}.reveal div.callout.callout-tip .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-warning{border-left-color:#ffc107}.reveal div.callout.callout-warning.callout-style-default .callout-title{background-color:rgb(255,248.8,230.2)}.reveal div.callout.callout-warning .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-caution{border-left-color:#fd7e14}.reveal div.callout.callout-caution.callout-style-default .callout-title{background-color:rgb(254.8,242.1,231.5)}.reveal div.callout.callout-caution .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-important{border-left-color:#dc3545}.reveal div.callout.callout-important.callout-style-default .callout-title{background-color:rgb(251.5,234.8,236.4)}.reveal div.callout.callout-important .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal .quarto-title-block .quarto-title-authors{display:flex;justify-content:center}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author{padding-left:.5em;padding-right:.5em}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:hover,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:visited,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:active{color:inherit;text-decoration:none}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-name{margin-bottom:.1rem}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-email{margin-top:0px;margin-bottom:.4em;font-size:.6em}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-orcid img{margin-bottom:4px}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-affiliation{font-size:.7em;margin-top:0px;margin-bottom:8px}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-affiliation:first{margin-top:12px}:root{--quarto-scss-export-link-color-bg: transparent;--quarto-scss-export-body-bg: #fff;--quarto-scss-export-body-color: #222;--quarto-scss-export-text-muted: rgb(110.5, 110.5, 110.5);--quarto-scss-export-gray-200: #e9ecef;--quarto-scss-export-gray-100: #f8f9fa;--quarto-scss-export-gray-900: #212529;--quarto-scss-export-primary: #2a76dd;--quarto-scss-export-link-color: #2a76dd;--quarto-scss-export-link-color-hover: rgb(85.979757085, 146.2874493927, 228.020242915);--quarto-scss-export-selection-bg: rgb(151.9493927126, 188.7186234818, 238.5506072874);--quarto-scss-export-selection-color: #fff;--quarto-scss-export-border-color: rgb(110.5, 110.5, 110.5);--quarto-scss-export-presentation-heading-color: #222;--quarto-scss-export-presentation-list-bullet-color: #222;--quarto-scss-export-code-block-bg: #fff;--quarto-scss-export-code-block-border-color: #bbbbbb;--quarto-scss-export-code-block-color: #222;--quarto-scss-export-code-bg: transparent;--quarto-scss-export-tabset-border-color: #bbbbbb;--quarto-scss-export-table-border-color: #bbbbbb;--quarto-scss-export-input-panel-border-color: #bbbbbb;--quarto-scss-export-input-panel-bg: rgb(248, 249, 250);--quarto-scss-export-callout-color-note: #0d6efd;--quarto-scss-export-callout-color-tip: #198754;--quarto-scss-export-callout-color-important: #dc3545;--quarto-scss-export-callout-color-caution: #fd7e14;--quarto-scss-export-callout-color-warning: #ffc107;--quarto-scss-export-light-bg-text-color: #222;--quarto-scss-export-dark-bg-text-color: #fff;--quarto-scss-export-light-bg-link-color: #2a76dd;--quarto-scss-export-dark-bg-link-color: #42affa;--quarto-scss-export-light-bg-code-color: #4758ab;--quarto-scss-export-dark-bg-code-color: #ffa07a;--quarto-scss-export-kbd-color: #222;--quarto-scss-export-kbd-bg: #f8f9fa;--quarto-scss-export-revealjs-heading-color: #222;--quarto-scss-export-revealjs-list-bullet-color: #222;--quarto-scss-export-backgroundColor: #fff;--quarto-scss-export-mainColor: #222;--quarto-scss-export-headingColor: #222;--quarto-scss-export-linkColor: #2a76dd;--quarto-scss-export-linkColorHover: rgb(85.979757085, 146.2874493927, 228.020242915);--quarto-scss-export-selectionBackgroundColor: rgb(151.9493927126, 188.7186234818, 238.5506072874);--quarto-scss-export-selectionColor: #fff;--quarto-scss-export-btn-code-copy-color: rgb(110.5, 110.5, 110.5);--quarto-scss-export-btn-code-copy-color-active: #2a76dd;--quarto-scss-export-secondary: #999;--quarto-scss-export-mermaid-bg-color: #fff;--quarto-scss-export-mermaid-edge-color: #999;--quarto-scss-export-mermaid-node-fg-color: #222;--quarto-scss-export-mermaid-fg-color: #222;--quarto-scss-export-mermaid-fg-color--lighter: rgb(59.5, 59.5, 59.5);--quarto-scss-export-mermaid-fg-color--lightest: #555555;--quarto-scss-export-mermaid-label-bg-color: #fff;--quarto-scss-export-mermaid-label-fg-color: #2a76dd;--quarto-scss-export-mermaid-node-bg-color: rgba(42, 118, 221, 0.1)} \ No newline at end of file diff --git a/_freeze/site_libs/revealjs/dist/theme/quarto-f563837468303362081e247dddd440d0.css b/_freeze/site_libs/revealjs/dist/theme/quarto-f563837468303362081e247dddd440d0.css new file mode 100644 index 0000000..7c88e48 --- /dev/null +++ b/_freeze/site_libs/revealjs/dist/theme/quarto-f563837468303362081e247dddd440d0.css @@ -0,0 +1,8 @@ +@import"./fonts/source-sans-pro/source-sans-pro.css";:root{--r-background-color: #fff;--r-main-font: Source Sans Pro, Helvetica, sans-serif;--r-main-font-size: 40px;--r-main-color: #222;--r-block-margin: 12px;--r-heading-margin: 0 0 12px 0;--r-heading-font: Source Sans Pro, Helvetica, sans-serif;--r-heading-color: #222;--r-heading-line-height: 1.2;--r-heading-letter-spacing: normal;--r-heading-text-transform: none;--r-heading-text-shadow: none;--r-heading-font-weight: 600;--r-heading1-text-shadow: none;--r-heading1-size: 2.5em;--r-heading2-size: 1.6em;--r-heading3-size: 1.3em;--r-heading4-size: 1em;--r-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-link-color: #2a76dd;--r-link-color-dark: rgb(25.6720647773, 83.0566801619, 160.8279352227);--r-link-color-hover: rgb(85.979757085, 146.2874493927, 228.020242915);--r-selection-background-color: rgb(151.9493927126, 188.7186234818, 238.5506072874);--r-selection-color: #fff;--r-overlay-element-bg-color: 240, 240, 240;--r-overlay-element-fg-color: 0, 0, 0}.reveal-viewport{background:#fff;background-color:var(--r-background-color)}.reveal{font-family:var(--r-main-font);font-size:var(--r-main-font-size);font-weight:normal;color:var(--r-main-color)}.reveal ::selection{color:var(--r-selection-color);background:var(--r-selection-background-color);text-shadow:none}.reveal ::-moz-selection{color:var(--r-selection-color);background:var(--r-selection-background-color);text-shadow:none}.reveal .slides section,.reveal .slides section>section{line-height:1.3;font-weight:inherit}.reveal h1,.reveal h2,.reveal h3,.reveal h4,.reveal h5,.reveal h6{margin:var(--r-heading-margin);color:var(--r-heading-color);font-family:var(--r-heading-font);font-weight:var(--r-heading-font-weight);line-height:var(--r-heading-line-height);letter-spacing:var(--r-heading-letter-spacing);text-transform:var(--r-heading-text-transform);text-shadow:var(--r-heading-text-shadow);word-wrap:break-word}.reveal h1{font-size:var(--r-heading1-size)}.reveal h2{font-size:var(--r-heading2-size)}.reveal h3{font-size:var(--r-heading3-size)}.reveal h4{font-size:var(--r-heading4-size)}.reveal h1{text-shadow:var(--r-heading1-text-shadow)}.reveal p{margin:var(--r-block-margin) 0;line-height:1.3}.reveal h1:last-child,.reveal h2:last-child,.reveal h3:last-child,.reveal h4:last-child,.reveal h5:last-child,.reveal h6:last-child{margin-bottom:0}.reveal img,.reveal video,.reveal iframe{max-width:95%;max-height:95%}.reveal strong,.reveal b{font-weight:bold}.reveal em{font-style:italic}.reveal ol,.reveal dl,.reveal ul{display:inline-block;text-align:left;margin:0 0 0 1em}.reveal ol{list-style-type:decimal}.reveal ul{list-style-type:disc}.reveal ul ul{list-style-type:square}.reveal ul ul ul{list-style-type:circle}.reveal ul ul,.reveal ul ol,.reveal ol ol,.reveal ol ul{display:block;margin-left:40px}.reveal dt{font-weight:bold}.reveal dd{margin-left:40px}.reveal blockquote{display:block;position:relative;width:70%;margin:var(--r-block-margin) auto;padding:5px;font-style:italic;background:hsla(0,0%,100%,.05);box-shadow:0px 0px 2px rgba(0,0,0,.2)}.reveal blockquote p:first-child,.reveal blockquote p:last-child{display:inline-block}.reveal q{font-style:italic}.reveal pre{display:block;position:relative;width:90%;margin:var(--r-block-margin) auto;text-align:left;font-size:.55em;font-family:var(--r-code-font);line-height:1.2em;word-wrap:break-word;box-shadow:0px 5px 15px rgba(0,0,0,.15)}.reveal code{font-family:var(--r-code-font);text-transform:none;tab-size:2}.reveal pre code{display:block;padding:5px;overflow:auto;max-height:400px;word-wrap:normal}.reveal .code-wrapper{white-space:normal}.reveal .code-wrapper code{white-space:pre}.reveal table{margin:auto;border-collapse:collapse;border-spacing:0}.reveal table th{font-weight:bold}.reveal table th,.reveal table td{text-align:left;padding:.2em .5em .2em .5em;border-bottom:1px solid}.reveal table th[align=center],.reveal table td[align=center]{text-align:center}.reveal table th[align=right],.reveal table td[align=right]{text-align:right}.reveal table tbody tr:last-child th,.reveal table tbody tr:last-child td{border-bottom:none}.reveal sup{vertical-align:super;font-size:smaller}.reveal sub{vertical-align:sub;font-size:smaller}.reveal small{display:inline-block;font-size:.6em;line-height:1.2em;vertical-align:top}.reveal small *{vertical-align:top}.reveal img{margin:var(--r-block-margin) 0}.reveal a{color:var(--r-link-color);text-decoration:none;transition:color .15s ease}.reveal a:hover{color:var(--r-link-color-hover);text-shadow:none;border:none}.reveal .roll span:after{color:#fff;background:var(--r-link-color-dark)}.reveal .r-frame{border:4px solid var(--r-main-color);box-shadow:0 0 10px rgba(0,0,0,.15)}.reveal a .r-frame{transition:all .15s linear}.reveal a:hover .r-frame{border-color:var(--r-link-color);box-shadow:0 0 20px rgba(0,0,0,.55)}.reveal .controls{color:var(--r-link-color)}.reveal .progress{background:rgba(0,0,0,.2);color:var(--r-link-color)}@media print{.backgrounds{background-color:var(--r-background-color)}}.top-right{position:absolute;top:1em;right:1em}.visually-hidden{border:0;clip:rect(0 0 0 0);height:auto;margin:0;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap}.hidden{display:none !important}.zindex-bottom{z-index:-1 !important}figure.figure{display:block}.quarto-layout-panel{margin-bottom:1em}.quarto-layout-panel>figure{width:100%}.quarto-layout-panel>figure>figcaption,.quarto-layout-panel>.panel-caption{margin-top:10pt}.quarto-layout-panel>.table-caption{margin-top:0px}.table-caption p{margin-bottom:.5em}.quarto-layout-row{display:flex;flex-direction:row;align-items:flex-start}.quarto-layout-valign-top{align-items:flex-start}.quarto-layout-valign-bottom{align-items:flex-end}.quarto-layout-valign-center{align-items:center}.quarto-layout-cell{position:relative;margin-right:20px}.quarto-layout-cell:last-child{margin-right:0}.quarto-layout-cell figure,.quarto-layout-cell>p{margin:.2em}.quarto-layout-cell img{max-width:100%}.quarto-layout-cell .html-widget{width:100% !important}.quarto-layout-cell div figure p{margin:0}.quarto-layout-cell figure{display:block;margin-inline-start:0;margin-inline-end:0}.quarto-layout-cell table{display:inline-table}.quarto-layout-cell-subref figcaption,figure .quarto-layout-row figure figcaption{text-align:center;font-style:italic}.quarto-figure{position:relative;margin-bottom:1em}.quarto-figure>figure{width:100%;margin-bottom:0}.quarto-figure-left>figure>p,.quarto-figure-left>figure>div{text-align:left}.quarto-figure-center>figure>p,.quarto-figure-center>figure>div{text-align:center}.quarto-figure-right>figure>p,.quarto-figure-right>figure>div{text-align:right}.quarto-figure>figure>div.cell-annotation,.quarto-figure>figure>div code{text-align:left}figure>p:empty{display:none}figure>p:first-child{margin-top:0;margin-bottom:0}figure>figcaption.quarto-float-caption-bottom{margin-bottom:.5em}figure>figcaption.quarto-float-caption-top{margin-top:.5em}div[id^=tbl-]{position:relative}.quarto-figure>.anchorjs-link{position:absolute;top:.6em;right:.5em}div[id^=tbl-]>.anchorjs-link{position:absolute;top:.7em;right:.3em}.quarto-figure:hover>.anchorjs-link,div[id^=tbl-]:hover>.anchorjs-link,h2:hover>.anchorjs-link,h3:hover>.anchorjs-link,h4:hover>.anchorjs-link,h5:hover>.anchorjs-link,h6:hover>.anchorjs-link,.reveal-anchorjs-link>.anchorjs-link{opacity:1}#title-block-header{margin-block-end:1rem;position:relative;margin-top:-1px}#title-block-header .abstract{margin-block-start:1rem}#title-block-header .abstract .abstract-title{font-weight:600}#title-block-header a{text-decoration:none}#title-block-header .author,#title-block-header .date,#title-block-header .doi{margin-block-end:.2rem}#title-block-header .quarto-title-block>div{display:flex}#title-block-header .quarto-title-block>div>h1{flex-grow:1}#title-block-header .quarto-title-block>div>button{flex-shrink:0;height:2.25rem;margin-top:0}tr.header>th>p:last-of-type{margin-bottom:0px}table,table.table{margin-top:.5rem;margin-bottom:.5rem}caption,.table-caption{padding-top:.5rem;padding-bottom:.5rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-top{margin-top:.5rem;margin-bottom:.25rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-bottom{padding-top:.25rem;margin-bottom:.5rem;text-align:center}.utterances{max-width:none;margin-left:-8px}iframe{margin-bottom:1em}details{margin-bottom:1em}details[show]{margin-bottom:0}details>summary{color:rgb(110.5,110.5,110.5)}details>summary>p:only-child{display:inline}pre.sourceCode,code.sourceCode{position:relative}dd code:not(.sourceCode),p code:not(.sourceCode){white-space:pre-wrap}code{white-space:pre}@media print{code{white-space:pre-wrap}}pre>code{display:block}pre>code.sourceCode{white-space:pre}pre>code.sourceCode>span>a:first-child::before{text-decoration:none}pre.code-overflow-wrap>code.sourceCode{white-space:pre-wrap}pre.code-overflow-scroll>code.sourceCode{white-space:pre}code a:any-link{color:inherit;text-decoration:none}code a:hover{color:inherit;text-decoration:underline}ul.task-list{padding-left:1em}[data-tippy-root]{display:inline-block}.tippy-content .footnote-back{display:none}.footnote-back{margin-left:.2em}.tippy-content{overflow-x:auto}.quarto-embedded-source-code{display:none}.quarto-unresolved-ref{font-weight:600}.quarto-cover-image{max-width:35%;float:right;margin-left:30px}.cell-output-display .widget-subarea{margin-bottom:1em}.cell-output-display:not(.no-overflow-x),.knitsql-table:not(.no-overflow-x){overflow-x:auto}.panel-input{margin-bottom:1em}.panel-input>div,.panel-input>div>div{display:inline-block;vertical-align:top;padding-right:12px}.panel-input>p:last-child{margin-bottom:0}.layout-sidebar{margin-bottom:1em}.layout-sidebar .tab-content{border:none}.tab-content>.page-columns.active{display:grid}div.sourceCode>iframe{width:100%;height:300px;margin-bottom:-0.5em}a{text-underline-offset:3px}.callout pre.sourceCode{padding-left:0}div.ansi-escaped-output{font-family:monospace;display:block}/*! +* +* ansi colors from IPython notebook's +* +* we also add `bright-[color]-` synonyms for the `-[color]-intense` classes since +* that seems to be what ansi_up emits +* +*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-black,.ansi-bright-black-fg{color:#282c36}.ansi-black-intense-black,.ansi-bright-black-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-red,.ansi-bright-red-fg{color:#b22b31}.ansi-red-intense-red,.ansi-bright-red-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-green,.ansi-bright-green-fg{color:#007427}.ansi-green-intense-green,.ansi-bright-green-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-yellow,.ansi-bright-yellow-fg{color:#b27d12}.ansi-yellow-intense-yellow,.ansi-bright-yellow-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-blue,.ansi-bright-blue-fg{color:#0065ca}.ansi-blue-intense-blue,.ansi-bright-blue-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-magenta,.ansi-bright-magenta-fg{color:#a03196}.ansi-magenta-intense-magenta,.ansi-bright-magenta-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-cyan,.ansi-bright-cyan-fg{color:#258f8f}.ansi-cyan-intense-cyan,.ansi-bright-cyan-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-white,.ansi-bright-white-fg{color:#a1a6b2}.ansi-white-intense-white,.ansi-bright-white-bg{background-color:#a1a6b2}.ansi-default-inverse-fg{color:#fff}.ansi-default-inverse-bg{background-color:#000}.ansi-bold{font-weight:bold}.ansi-underline{text-decoration:underline}:root{--quarto-body-bg: #fff;--quarto-body-color: #222;--quarto-text-muted: rgb(110.5, 110.5, 110.5);--quarto-border-color: #bbbbbb;--quarto-border-width: 1px;--quarto-border-radius: 4px}table.gt_table{color:var(--quarto-body-color);font-size:1em;width:100%;background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_column_spanner_outer{color:var(--quarto-body-color);background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_col_heading{color:var(--quarto-body-color);font-weight:bold;background-color:rgba(0,0,0,0)}table.gt_table thead.gt_col_headings{border-bottom:1px solid currentColor;border-top-width:inherit;border-top-color:var(--quarto-border-color)}table.gt_table thead.gt_col_headings:not(:first-child){border-top-width:1px;border-top-color:var(--quarto-border-color)}table.gt_table td.gt_row{border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-width:0px}table.gt_table tbody.gt_table_body{border-top-width:1px;border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-color:currentColor}div.columns{display:initial;gap:initial}div.column{display:inline-block;overflow-x:initial;vertical-align:top;width:50%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:relative;float:right;background-color:rgba(0,0,0,0)}input[type=checkbox]{margin-right:.5ch}:root{--mermaid-bg-color: #fff;--mermaid-edge-color: #999;--mermaid-node-fg-color: #222;--mermaid-fg-color: #222;--mermaid-fg-color--lighter: rgb(59.5, 59.5, 59.5);--mermaid-fg-color--lightest: #555555;--mermaid-font-family: Source Sans Pro, Helvetica, sans-serif;--mermaid-label-bg-color: #fff;--mermaid-label-fg-color: #2a76dd;--mermaid-node-bg-color: rgba(42, 118, 221, 0.1);--mermaid-node-fg-color: #222}@media print{:root{font-size:11pt}#quarto-sidebar,#TOC,.nav-page{display:none}.page-columns .content{grid-column-start:page-start}.fixed-top{position:relative}.panel-caption,.figure-caption,figcaption{color:#666}}.code-copy-button{position:absolute;top:0;right:0;border:0;margin-top:5px;margin-right:5px;background-color:rgba(0,0,0,0);z-index:3}.code-copy-button-tooltip{font-size:.75em}pre.sourceCode:hover>.code-copy-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}pre.sourceCode:hover>.code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}pre.sourceCode:hover>.code-copy-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}pre.sourceCode:hover>.code-copy-button-checked:hover>.bi::before{background-image:url('data:image/svg+xml,')}.panel-tabset [role=tablist]{border-bottom:1px solid #bbb;list-style:none;margin:0;padding:0;width:100%}.panel-tabset [role=tablist] *{-webkit-box-sizing:border-box;box-sizing:border-box}@media(min-width: 30em){.panel-tabset [role=tablist] li{display:inline-block}}.panel-tabset [role=tab]{border:1px solid rgba(0,0,0,0);border-top-color:#bbb;display:block;padding:.5em 1em;text-decoration:none}@media(min-width: 30em){.panel-tabset [role=tab]{border-top-color:rgba(0,0,0,0);display:inline-block;margin-bottom:-1px}}.panel-tabset [role=tab][aria-selected=true]{background-color:#bbb}@media(min-width: 30em){.panel-tabset [role=tab][aria-selected=true]{background-color:rgba(0,0,0,0);border:1px solid #bbb;border-bottom-color:#fff}}@media(min-width: 30em){.panel-tabset [role=tab]:hover:not([aria-selected=true]){border:1px solid #bbb}}.code-with-filename .code-with-filename-file{margin-bottom:0;padding-bottom:2px;padding-top:2px;padding-left:.7em;border:var(--quarto-border-width) solid var(--quarto-border-color);border-radius:var(--quarto-border-radius);border-bottom:0;border-bottom-left-radius:0%;border-bottom-right-radius:0%}.code-with-filename div.sourceCode,.reveal .code-with-filename div.sourceCode{margin-top:0;border-top-left-radius:0%;border-top-right-radius:0%}.code-with-filename .code-with-filename-file pre{margin-bottom:0}.code-with-filename .code-with-filename-file{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}.reveal.center .slide aside,.reveal.center .slide div.aside{position:initial}section.has-light-background,section.has-light-background h1,section.has-light-background h2,section.has-light-background h3,section.has-light-background h4,section.has-light-background h5,section.has-light-background h6{color:#222}section.has-light-background a,section.has-light-background a:hover{color:#2a76dd}section.has-light-background code{color:#4758ab}section.has-dark-background,section.has-dark-background h1,section.has-dark-background h2,section.has-dark-background h3,section.has-dark-background h4,section.has-dark-background h5,section.has-dark-background h6{color:#fff}section.has-dark-background a,section.has-dark-background a:hover{color:#42affa}section.has-dark-background code{color:#ffa07a}#title-slide,div.reveal div.slides section.quarto-title-block{text-align:center}#title-slide .subtitle,div.reveal div.slides section.quarto-title-block .subtitle{margin-bottom:2.5rem}.reveal .slides{text-align:left}.reveal .title-slide h1{font-size:1.6em}.reveal[data-navigation-mode=linear] .title-slide h1{font-size:2.5em}.reveal div.sourceCode{border:1px solid #bbb;border-radius:4px}.reveal pre{width:100%;box-shadow:none;background-color:#fff;border:none;margin:0;font-size:.55em;line-height:1.3;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.reveal pre code{background-color:#fff;font-size:inherit;color:#222;font-family:inherit}.reveal pre.sourceCode code{color:#222;font-size:inherit;background-color:inherit;white-space:pre;font-family:inherit;padding:6px 9px;max-height:500px}.reveal .code-with-filename .code-with-filename-file pre{background-color:unset}.reveal code{color:var(--quarto-hl-fu-color);font-size:.875em;background-color:rgba(0,0,0,0);white-space:pre-wrap;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.reveal .column-output-location{display:flex;align-items:stretch}.reveal .column-output-location .column:first-of-type div.sourceCode{height:100%;background-color:#fff}.reveal blockquote{display:block;position:relative;color:rgb(110.5,110.5,110.5);width:unset;margin:var(--r-block-margin) auto;padding:.625rem 1.75rem;border-left:.25rem solid rgb(110.5,110.5,110.5);font-style:normal;background:none;box-shadow:none}.reveal blockquote p:first-child,.reveal blockquote p:last-child{display:block}.reveal .slide aside,.reveal .slide div.aside{position:absolute;bottom:20px;font-size:0.7em;color:rgb(110.5,110.5,110.5)}.reveal .slide sup{font-size:0.7em}.reveal .slide.scrollable aside,.reveal .slide.scrollable div.aside{position:relative;margin-top:1em}.reveal .slide aside .aside-footnotes{margin-bottom:0}.reveal .slide aside .aside-footnotes li:first-of-type{margin-top:0}.reveal .layout-sidebar{display:flex;width:100%;margin-top:.8em}.reveal .layout-sidebar .panel-sidebar{width:270px}.reveal .layout-sidebar-left .panel-sidebar{margin-right:calc(0.5em*2)}.reveal .layout-sidebar-right .panel-sidebar{margin-left:calc(0.5em*2)}.reveal .layout-sidebar .panel-fill,.reveal .layout-sidebar .panel-center,.reveal .layout-sidebar .panel-tabset{flex:1}.reveal .panel-input,.reveal .panel-sidebar{font-size:.5em;padding:.5em;border-style:solid;border-color:#bbb;border-width:1px;border-radius:4px;background-color:#f8f9fa}.reveal .panel-sidebar :first-child,.reveal .panel-fill :first-child{margin-top:0}.reveal .panel-sidebar :last-child,.reveal .panel-fill :last-child{margin-bottom:0}.panel-input>div,.panel-input>div>div{vertical-align:middle;padding-right:1em}.reveal p,.reveal .slides section,.reveal .slides section>section{line-height:1.3}.reveal.smaller .slides section{font-size:0.7em}.reveal.smaller .slides section section{font-size:inherit}.reveal.smaller .slides h1{font-size:calc(2.5em/0.7)}.reveal.smaller .slides h2{font-size:calc(1.6em/0.7)}.reveal.smaller .slides h3{font-size:calc(1.3em/0.7)}.reveal .slides section.smaller{font-size:0.7em}.reveal .slides section.smaller h1{font-size:calc(2.5em/0.7)}.reveal .slides section.smaller h2{font-size:calc(1.6em/0.7)}.reveal .slides section.smaller h3{font-size:calc(1.3em/0.7)}.reveal .slides section div.callout{font-size:0.7em}.reveal .slides section div.callout h1{font-size:calc(2.5em/0.7)}.reveal .slides section div.callout h2{font-size:calc(1.6em/0.7)}.reveal .slides section div.callout h3{font-size:calc(1.3em/0.7)}.reveal .columns>.column>:not(ul,ol){margin-left:.25rem;margin-right:.25rem}.reveal .columns>.column:first-child>:not(ul,ol){margin-right:.5rem;margin-left:0}.reveal .columns>.column:last-child>:not(ul,ol){margin-right:0;margin-left:.5rem}.reveal .slide-number{color:rgb(85.979757085,146.2874493927,228.020242915);background-color:#fff}.reveal .footer{color:rgb(110.5,110.5,110.5)}.reveal .footer a{color:#2a76dd}.reveal .footer.has-dark-background{color:#fff}.reveal .footer.has-dark-background a{color:rgb(122.7,198.1114130435,250)}.reveal .footer.has-light-background{color:hsl(0,0%,31.2169312169%)}.reveal .footer.has-light-background a{color:rgb(105.9,154.769273743,221)}.reveal .slide-number{color:rgb(110.5,110.5,110.5)}.reveal .slide-number.has-dark-background{color:#fff}.reveal .slide-number.has-light-background{color:hsl(0,0%,31.2169312169%)}.reveal .slide figure>figcaption,.reveal .slide img.stretch+p.caption,.reveal .slide img.r-stretch+p.caption{font-size:0.7em}@media screen and (min-width: 500px){.reveal .controls[data-controls-layout=edges] .navigate-left{left:.2em}.reveal .controls[data-controls-layout=edges] .navigate-right{right:.2em}.reveal .controls[data-controls-layout=edges] .navigate-up{top:.4em}.reveal .controls[data-controls-layout=edges] .navigate-down{bottom:2.3em}}.tippy-box[data-theme~=light-border]{background-color:#fff;color:#222;border-radius:4px;border:solid 1px rgb(110.5,110.5,110.5);font-size:.6em}.tippy-box[data-theme~=light-border] .tippy-arrow{color:rgb(110.5,110.5,110.5)}.tippy-box[data-placement^=bottom]>.tippy-content{padding:7px 10px;z-index:1}.reveal .panel-tabset [role=tab]{padding:.25em .7em}.reveal .slide-menu-button .fa-bars::before{background-image:url('data:image/svg+xml,')}.reveal .slide-chalkboard-buttons .fa-easel2::before{background-image:url('data:image/svg+xml,')}.reveal .slide-chalkboard-buttons .fa-brush::before{background-image:url('data:image/svg+xml,')}/*! light */.reveal ol[type=a]{list-style-type:lower-alpha}.reveal ol[type=a s]{list-style-type:lower-alpha}.reveal ol[type=A s]{list-style-type:upper-alpha}.reveal ol[type=i]{list-style-type:lower-roman}.reveal ol[type=i s]{list-style-type:lower-roman}.reveal ol[type=I s]{list-style-type:upper-roman}.reveal ol[type="1"]{list-style-type:decimal}.reveal ul.task-list{list-style:none}.reveal ul.task-list li input[type=checkbox]{width:2em;height:2em;margin:0 1em .5em -1.6em;vertical-align:middle}div.cell-output-display div.pagedtable-wrapper table.table{font-size:.6em}.reveal .code-annotation-container-hidden{display:none}.reveal code.sourceCode button.code-annotation-anchor,.reveal code.sourceCode .code-annotation-anchor{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;font-size:.7em;line-height:1.2em;margin-top:2px;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none}.reveal code.sourceCode button.code-annotation-anchor{cursor:pointer}.reveal code.sourceCode a.code-annotation-anchor{text-align:center;vertical-align:middle;text-decoration:none;cursor:default;height:1.2em;width:1.2em}.reveal code.sourceCode.fragment a.code-annotation-anchor{left:auto}.reveal #code-annotation-line-highlight-gutter{width:100%;border-top:solid var(--quarto-hl-co-color) 1px;border-bottom:solid var(--quarto-hl-co-color) 1px;z-index:2}.reveal #code-annotation-line-highlight{margin-left:-8em;width:calc(100% + 4em);border-top:solid var(--quarto-hl-co-color) 1px;border-bottom:solid var(--quarto-hl-co-color) 1px;z-index:2;margin-bottom:-2px}.reveal code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#fff;font-weight:bolder}.reveal pre.code-annotation-code{padding-top:0;padding-bottom:0}.reveal pre.code-annotation-code code{z-index:3;padding-left:0px}.reveal dl.code-annotation-container-grid{margin-left:.1em}.reveal dl.code-annotation-container-grid dt{margin-top:.65rem;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;border:solid #222 1px;border-radius:50%;height:1.3em;width:1.3em;line-height:1.3em;font-size:.5em;text-align:center;vertical-align:middle;text-decoration:none}.reveal dl.code-annotation-container-grid dd{margin-left:.25em}.reveal .scrollable ol li:first-child:nth-last-child(n+10),.reveal .scrollable ol li:first-child:nth-last-child(n+10)~li{margin-left:1em}kbd{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:40px;color:#222;background-color:#f8f9fa;border:1px solid;border-color:#bbb;border-radius:5px;padding:.4rem .4rem}:root{--r-inline-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-block-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-inline-code-font-size: 0.875em;--r-block-code-font-size: 0.55em}.reveal a{font-weight:400;background-color:rgba(0,0,0,0);text-decoration:inherit}.reveal div.callout{margin-top:1rem;margin-bottom:1rem;border-radius:4px;overflow-wrap:break-word}.reveal div.callout.callout-style-simple,.reveal div.callout.callout-style-default{border-left:.3rem solid #acacac;border-right:solid 1px #bbb;border-top:solid 1px #bbb;border-bottom:solid 1px #bbb}.reveal div.callout.callout-style-simple div.callout-body,.reveal div.callout.callout-style-simple div.callout-title,.reveal div.callout.callout-style-default div.callout-body,.reveal div.callout.callout-style-default div.callout-title{font-size:inherit;border-bottom:none;font-weight:600}.reveal div.callout.callout-style-simple div.callout-title,.reveal div.callout.callout-style-default div.callout-title{display:flex;align-items:center}.reveal div.callout.callout-style-simple div.callout-title p,.reveal div.callout.callout-style-default div.callout-title p{margin-top:.5em;margin-bottom:.5em;color:var(--r-main-color)}.reveal div.callout.callout-style-simple .callout-icon::before,.reveal div.callout.callout-style-default .callout-icon::before{height:1.25em;width:1.25em;background-size:1.25em 1.25em}.reveal div.callout.callout-style-simple.callout-titled .callout-body>.callout-content>:last-child,.reveal div.callout.callout-style-default.callout-titled .callout-body>.callout-content>:last-child{margin-bottom:var(--r-block-margin)}.reveal div.callout.callout-style-simple.callout-titled .callout-body>.callout-content>:last-child:not(div.sourceCode),.reveal div.callout.callout-style-default.callout-titled .callout-body>.callout-content>:last-child:not(div.sourceCode){padding-bottom:.5rem;margin-bottom:0}.reveal div.callout.callout-style-simple.callout-titled .callout-icon::before,.reveal div.callout.callout-style-default.callout-titled .callout-icon::before{margin-top:.25em;padding-right:.25em}.reveal div.callout.callout-style-simple.no-icon::before,.reveal div.callout.callout-style-default.no-icon::before{display:none !important}.reveal div.callout.callout-style-simple{padding:0em .5em;display:flex}.reveal div.callout.callout-style-simple.callout-titled .callout-body{margin-top:.2em}.reveal div.callout.callout-style-simple.callout-titled:not(.no-icon) .callout-content{padding-left:1.6em}.reveal div.callout.callout-style-simple.callout-titled .callout-content p{margin-top:0}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body{display:flex}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-icon::before{margin-top:var(--r-block-margin);padding-right:.5em}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body>.callout-content>div.sourceCode:last-child{margin-bottom:1rem}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body>.callout-content>:first-child{margin-top:var(--r-block-margin)}.reveal div.callout.callout-style-simple .callout-icon::before{display:inline-block;content:"";background-repeat:no-repeat}.reveal div.callout.callout-style-simple div.callout-title{opacity:75%}.reveal div.callout.callout-style-simple div.callout-body{font-weight:400}.reveal div.callout.callout-style-default.callout-titled .callout-content p{margin-top:.7em}.reveal div.callout.callout-style-default .callout-icon::before{display:inline-block;content:"";background-repeat:no-repeat}.reveal div.callout.callout-style-default div.callout-body{font-weight:400}.reveal div.callout.callout-style-default div.callout-title{opacity:85%;padding-left:.5em;padding-right:.5em}.reveal div.callout.callout-style-default div.callout-content{padding-left:.5em;padding-right:.5em}.reveal div.callout .callout-body-container{flex-grow:1}.reveal div.callout.callout-note{border-left-color:#0d6efd}.reveal div.callout.callout-note.callout-style-default .callout-title{background-color:rgb(230.8,240.5,254.8)}.reveal div.callout.callout-note .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-tip{border-left-color:#198754}.reveal div.callout.callout-tip.callout-style-default .callout-title{background-color:rgb(232,243,237.9)}.reveal div.callout.callout-tip .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-warning{border-left-color:#ffc107}.reveal div.callout.callout-warning.callout-style-default .callout-title{background-color:rgb(255,248.8,230.2)}.reveal div.callout.callout-warning .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-caution{border-left-color:#fd7e14}.reveal div.callout.callout-caution.callout-style-default .callout-title{background-color:rgb(254.8,242.1,231.5)}.reveal div.callout.callout-caution .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-important{border-left-color:#dc3545}.reveal div.callout.callout-important.callout-style-default .callout-title{background-color:rgb(251.5,234.8,236.4)}.reveal div.callout.callout-important .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal .quarto-title-block .quarto-title-authors{display:flex;justify-content:center}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author{padding-left:.5em;padding-right:.5em}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:hover,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:visited,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:active{color:inherit;text-decoration:none}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-name{margin-bottom:.1rem}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-email{margin-top:0px;margin-bottom:.4em;font-size:.6em}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-orcid img{margin-bottom:4px}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-affiliation{font-size:.7em;margin-top:0px;margin-bottom:8px}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-affiliation:first{margin-top:12px}:root{--quarto-scss-export-link-color-bg: transparent;--quarto-scss-export-body-bg: #fff;--quarto-scss-export-body-color: #222;--quarto-scss-export-text-muted: rgb(110.5, 110.5, 110.5);--quarto-scss-export-gray-200: #e9ecef;--quarto-scss-export-gray-100: #f8f9fa;--quarto-scss-export-gray-900: #212529;--quarto-scss-export-primary: #2a76dd;--quarto-scss-export-link-color: #2a76dd;--quarto-scss-export-link-color-hover: rgb(85.979757085, 146.2874493927, 228.020242915);--quarto-scss-export-selection-bg: rgb(151.9493927126, 188.7186234818, 238.5506072874);--quarto-scss-export-selection-color: #fff;--quarto-scss-export-border-color: rgb(110.5, 110.5, 110.5);--quarto-scss-export-presentation-heading-color: #222;--quarto-scss-export-presentation-list-bullet-color: #222;--quarto-scss-export-code-block-bg: #fff;--quarto-scss-export-code-block-border-color: #bbbbbb;--quarto-scss-export-code-block-color: #222;--quarto-scss-export-code-bg: transparent;--quarto-scss-export-tabset-border-color: #bbbbbb;--quarto-scss-export-table-border-color: #bbbbbb;--quarto-scss-export-input-panel-border-color: #bbbbbb;--quarto-scss-export-input-panel-bg: rgb(248, 249, 250);--quarto-scss-export-callout-color-note: #0d6efd;--quarto-scss-export-callout-color-tip: #198754;--quarto-scss-export-callout-color-important: #dc3545;--quarto-scss-export-callout-color-caution: #fd7e14;--quarto-scss-export-callout-color-warning: #ffc107;--quarto-scss-export-light-bg-text-color: #222;--quarto-scss-export-dark-bg-text-color: #fff;--quarto-scss-export-light-bg-link-color: #2a76dd;--quarto-scss-export-dark-bg-link-color: #42affa;--quarto-scss-export-light-bg-code-color: #4758ab;--quarto-scss-export-dark-bg-code-color: #ffa07a;--quarto-scss-export-kbd-color: #222;--quarto-scss-export-kbd-bg: #f8f9fa;--quarto-scss-export-revealjs-heading-color: #222;--quarto-scss-export-revealjs-list-bullet-color: #222;--quarto-scss-export-backgroundColor: #fff;--quarto-scss-export-mainColor: #222;--quarto-scss-export-headingColor: #222;--quarto-scss-export-linkColor: #2a76dd;--quarto-scss-export-linkColorHover: rgb(85.979757085, 146.2874493927, 228.020242915);--quarto-scss-export-selectionBackgroundColor: rgb(151.9493927126, 188.7186234818, 238.5506072874);--quarto-scss-export-selectionColor: #fff;--quarto-scss-export-btn-code-copy-color: rgb(110.5, 110.5, 110.5);--quarto-scss-export-btn-code-copy-color-active: #2a76dd;--quarto-scss-export-secondary: #999;--quarto-scss-export-mermaid-bg-color: #fff;--quarto-scss-export-mermaid-edge-color: #999;--quarto-scss-export-mermaid-node-fg-color: #222;--quarto-scss-export-mermaid-fg-color: #222;--quarto-scss-export-mermaid-fg-color--lighter: rgb(59.5, 59.5, 59.5);--quarto-scss-export-mermaid-fg-color--lightest: #555555;--quarto-scss-export-mermaid-label-bg-color: #fff;--quarto-scss-export-mermaid-label-fg-color: #2a76dd;--quarto-scss-export-mermaid-node-bg-color: rgba(42, 118, 221, 0.1)} \ No newline at end of file diff --git a/_quarto.yml b/_quarto.yml index 0ecf893..6d8141e 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -79,15 +79,8 @@ website: - section: "Semana 10" contents: - pages/Bloque3/ReactIV.qmd -<<<<<<< HEAD - - pages/Bloque3/t10.qmd -======= - - - - - text: "Próximamente..." ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + - pages/Bloque3/t10.qmd - section: "Bloque IV Comunicación" contents: - section: "Semana 11" @@ -95,6 +88,7 @@ website: - pages/Bloque4/protocolos.qmd - pages/rubric.qmd + - pages/final.qmd footer: "![](https://i.imgur.com/jbGUd7N.png)


© Javier Ribal del Río
2025 - Hyperloop UPV" repo-url: https://github.com/HyperloopUPV-H8/Web-TC-SW diff --git a/docs/about.pdf b/docs/about.pdf index be621c3..9b10f8e 100644 Binary files a/docs/about.pdf and b/docs/about.pdf differ diff --git a/docs/index.html b/docs/index.html index e72da4a..78adaee 100644 --- a/docs/index.html +++ b/docs/index.html @@ -515,17 +515,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + diff --git a/docs/index.pdf b/docs/index.pdf index 454e185..66ab82e 100644 Binary files a/docs/index.pdf and b/docs/index.pdf differ diff --git a/docs/pages/Bloque1/MAcss.html b/docs/pages/Bloque1/MAcss.html index 0cf50ef..b4cf16e 100644 --- a/docs/pages/Bloque1/MAcss.html +++ b/docs/pages/Bloque1/MAcss.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -617,7 +612,7 @@

MA: Selectores y Pseudoselectores

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque1/MAcss.pdf b/docs/pages/Bloque1/MAcss.pdf index 03a2218..c29af35 100644 Binary files a/docs/pages/Bloque1/MAcss.pdf and b/docs/pages/Bloque1/MAcss.pdf differ diff --git a/docs/pages/Bloque1/MAhtml2.html b/docs/pages/Bloque1/MAhtml2.html index 30b7eb1..ea6a567 100644 --- a/docs/pages/Bloque1/MAhtml2.html +++ b/docs/pages/Bloque1/MAhtml2.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -617,7 +612,7 @@

MA: Mas HTML

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque1/MAhtml2.pdf b/docs/pages/Bloque1/MAhtml2.pdf index 96343fa..43663eb 100644 Binary files a/docs/pages/Bloque1/MAhtml2.pdf and b/docs/pages/Bloque1/MAhtml2.pdf differ diff --git a/docs/pages/Bloque1/bloque1.html b/docs/pages/Bloque1/bloque1.html index 51708c2..1d0aac2 100644 --- a/docs/pages/Bloque1/bloque1.html +++ b/docs/pages/Bloque1/bloque1.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -624,7 +619,7 @@

Bloque I Introducción

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque1/bloque1.pdf b/docs/pages/Bloque1/bloque1.pdf index 3a4b89b..df2f8c0 100644 Binary files a/docs/pages/Bloque1/bloque1.pdf and b/docs/pages/Bloque1/bloque1.pdf differ diff --git a/docs/pages/Bloque1/css.html b/docs/pages/Bloque1/css.html index ee9e156..bd14c1e 100644 --- a/docs/pages/Bloque1/css.html +++ b/docs/pages/Bloque1/css.html @@ -551,17 +551,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -680,7 +675,7 @@

CSS

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque1/css.pdf b/docs/pages/Bloque1/css.pdf index 6235781..45a5190 100644 Binary files a/docs/pages/Bloque1/css.pdf and b/docs/pages/Bloque1/css.pdf differ diff --git a/docs/pages/Bloque1/entorno.html b/docs/pages/Bloque1/entorno.html index e85df9a..59d318a 100644 --- a/docs/pages/Bloque1/entorno.html +++ b/docs/pages/Bloque1/entorno.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -654,7 +649,7 @@

Preparación del entorno de trabajo

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque1/entorno.pdf b/docs/pages/Bloque1/entorno.pdf index 2eb83cd..b626cd7 100644 Binary files a/docs/pages/Bloque1/entorno.pdf and b/docs/pages/Bloque1/entorno.pdf differ diff --git a/docs/pages/Bloque1/html.html b/docs/pages/Bloque1/html.html index 42ec4ce..f9aae7f 100644 --- a/docs/pages/Bloque1/html.html +++ b/docs/pages/Bloque1/html.html @@ -551,17 +551,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -686,7 +681,7 @@

HTML

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque1/html.pdf b/docs/pages/Bloque1/html.pdf index 10e81a4..a217409 100644 Binary files a/docs/pages/Bloque1/html.pdf and b/docs/pages/Bloque1/html.pdf differ diff --git a/docs/pages/Bloque1/t1.html b/docs/pages/Bloque1/t1.html index 272d170..42e0355 100644 --- a/docs/pages/Bloque1/t1.html +++ b/docs/pages/Bloque1/t1.html @@ -551,17 +551,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -652,7 +647,7 @@

T1: Web de HL I

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque1/t1.pdf b/docs/pages/Bloque1/t1.pdf index 492f3e6..40eb2ae 100644 Binary files a/docs/pages/Bloque1/t1.pdf and b/docs/pages/Bloque1/t1.pdf differ diff --git a/docs/pages/Bloque1/t1a.html b/docs/pages/Bloque1/t1a.html index 6395798..71902f6 100644 --- a/docs/pages/Bloque1/t1a.html +++ b/docs/pages/Bloque1/t1a.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -617,7 +612,7 @@

TA1: Mi CV I

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque1/t1a.pdf b/docs/pages/Bloque1/t1a.pdf index e5ccad5..979246c 100644 Binary files a/docs/pages/Bloque1/t1a.pdf and b/docs/pages/Bloque1/t1a.pdf differ diff --git a/docs/pages/Bloque1/t2.html b/docs/pages/Bloque1/t2.html index 05c03da..8242407 100644 --- a/docs/pages/Bloque1/t2.html +++ b/docs/pages/Bloque1/t2.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -617,7 +612,7 @@

T2: Web de HL II

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque1/t2.pdf b/docs/pages/Bloque1/t2.pdf index b7f185c..9487afb 100644 Binary files a/docs/pages/Bloque1/t2.pdf and b/docs/pages/Bloque1/t2.pdf differ diff --git a/docs/pages/Bloque1/t2a.html b/docs/pages/Bloque1/t2a.html index 59d6b13..da8c678 100644 --- a/docs/pages/Bloque1/t2a.html +++ b/docs/pages/Bloque1/t2a.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -617,7 +612,7 @@

TA2: Mi CV II

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque1/t2a.pdf b/docs/pages/Bloque1/t2a.pdf index 061d79e..0d27b06 100644 Binary files a/docs/pages/Bloque1/t2a.pdf and b/docs/pages/Bloque1/t2a.pdf differ diff --git a/docs/pages/Bloque2/MAclases.html b/docs/pages/Bloque2/MAclases.html index f7d4d55..abd15e7 100644 --- a/docs/pages/Bloque2/MAclases.html +++ b/docs/pages/Bloque2/MAclases.html @@ -551,17 +551,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -674,7 +669,7 @@

MA: Clases ES6

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque2/MAclases.pdf b/docs/pages/Bloque2/MAclases.pdf index 5764ad6..a9fbd78 100644 Binary files a/docs/pages/Bloque2/MAclases.pdf and b/docs/pages/Bloque2/MAclases.pdf differ diff --git a/docs/pages/Bloque2/intensivo.html b/docs/pages/Bloque2/intensivo.html index c0b4768..30119bf 100644 --- a/docs/pages/Bloque2/intensivo.html +++ b/docs/pages/Bloque2/intensivo.html @@ -551,17 +551,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -725,7 +720,7 @@

Repaso Intensivo JS

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque2/intensivo.pdf b/docs/pages/Bloque2/intensivo.pdf index 0c9f5a3..1e17b26 100644 Binary files a/docs/pages/Bloque2/intensivo.pdf and b/docs/pages/Bloque2/intensivo.pdf differ diff --git a/docs/pages/Bloque2/jsI.html b/docs/pages/Bloque2/jsI.html index 787ea0c..447563e 100644 --- a/docs/pages/Bloque2/jsI.html +++ b/docs/pages/Bloque2/jsI.html @@ -306,7 +306,7 @@

Introducción a JS

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque2/jsI.pdf b/docs/pages/Bloque2/jsI.pdf index 0c2894c..dccc250 100644 Binary files a/docs/pages/Bloque2/jsI.pdf and b/docs/pages/Bloque2/jsI.pdf differ diff --git a/docs/pages/Bloque2/ti.html b/docs/pages/Bloque2/ti.html index 58e6572..e05cdcb 100644 --- a/docs/pages/Bloque2/ti.html +++ b/docs/pages/Bloque2/ti.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -633,7 +628,7 @@

TI: Ejercicios JS

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque2/ti.pdf b/docs/pages/Bloque2/ti.pdf index b869e42..aca5a48 100644 Binary files a/docs/pages/Bloque2/ti.pdf and b/docs/pages/Bloque2/ti.pdf differ diff --git a/docs/pages/Bloque2/tia.html b/docs/pages/Bloque2/tia.html index c5368ab..645137a 100644 --- a/docs/pages/Bloque2/tia.html +++ b/docs/pages/Bloque2/tia.html @@ -551,17 +551,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -676,7 +671,7 @@

TAI: La objetización de las personas

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque2/tia.pdf b/docs/pages/Bloque2/tia.pdf index 14c4513..feb4ee5 100644 Binary files a/docs/pages/Bloque2/tia.pdf and b/docs/pages/Bloque2/tia.pdf differ diff --git a/docs/pages/Bloque3/DOM.html b/docs/pages/Bloque3/DOM.html index 41cc9b0..cabccf6 100644 --- a/docs/pages/Bloque3/DOM.html +++ b/docs/pages/Bloque3/DOM.html @@ -551,17 +551,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -675,7 +670,7 @@

DOM

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque3/DOM.pdf b/docs/pages/Bloque3/DOM.pdf index 0435f8d..2a073ee 100644 Binary files a/docs/pages/Bloque3/DOM.pdf and b/docs/pages/Bloque3/DOM.pdf differ diff --git a/docs/pages/Bloque3/Intermedio.html b/docs/pages/Bloque3/Intermedio.html index 1414aaa..84ceecc 100644 --- a/docs/pages/Bloque3/Intermedio.html +++ b/docs/pages/Bloque3/Intermedio.html @@ -551,17 +551,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -697,7 +692,7 @@

React III

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque3/Intermedio.pdf b/docs/pages/Bloque3/Intermedio.pdf index c2ddcde..4e4d21a 100644 Binary files a/docs/pages/Bloque3/Intermedio.pdf and b/docs/pages/Bloque3/Intermedio.pdf differ diff --git a/docs/pages/Bloque3/MARef.html b/docs/pages/Bloque3/MARef.html index c5368ef..93e336e 100644 --- a/docs/pages/Bloque3/MARef.html +++ b/docs/pages/Bloque3/MARef.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -617,7 +612,7 @@

MA: Uncontrolled fields y useRef

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque3/MARef.pdf b/docs/pages/Bloque3/MARef.pdf index 62d505d..6a95eec 100644 Binary files a/docs/pages/Bloque3/MARef.pdf and b/docs/pages/Bloque3/MARef.pdf differ diff --git a/docs/pages/Bloque3/MAcss2.html b/docs/pages/Bloque3/MAcss2.html index cb60c86..36d69d3 100644 --- a/docs/pages/Bloque3/MAcss2.html +++ b/docs/pages/Bloque3/MAcss2.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -617,7 +612,7 @@

MA: CSS Flex y CSS

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque3/MAcss2.pdf b/docs/pages/Bloque3/MAcss2.pdf index d8b53c8..4016f00 100644 Binary files a/docs/pages/Bloque3/MAcss2.pdf and b/docs/pages/Bloque3/MAcss2.pdf differ diff --git a/docs/pages/Bloque3/MAentorno.html b/docs/pages/Bloque3/MAentorno.html index 51788cb..5af1cae 100644 --- a/docs/pages/Bloque3/MAentorno.html +++ b/docs/pages/Bloque3/MAentorno.html @@ -280,7 +280,7 @@

MA: Entorno

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque3/MAentorno.pdf b/docs/pages/Bloque3/MAentorno.pdf index a485f0d..8b977d7 100644 Binary files a/docs/pages/Bloque3/MAentorno.pdf and b/docs/pages/Bloque3/MAentorno.pdf differ diff --git a/docs/pages/Bloque3/ReactI.html b/docs/pages/Bloque3/ReactI.html index 1d5137b..5230d0f 100644 --- a/docs/pages/Bloque3/ReactI.html +++ b/docs/pages/Bloque3/ReactI.html @@ -551,17 +551,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -699,7 +694,7 @@

React I

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque3/ReactI.pdf b/docs/pages/Bloque3/ReactI.pdf index 6337856..b5188b6 100644 Binary files a/docs/pages/Bloque3/ReactI.pdf and b/docs/pages/Bloque3/ReactI.pdf differ diff --git a/docs/pages/Bloque3/ReactIV.html b/docs/pages/Bloque3/ReactIV.html index b661f8f..fea6ceb 100644 --- a/docs/pages/Bloque3/ReactIV.html +++ b/docs/pages/Bloque3/ReactIV.html @@ -1,176 +1,87 @@ - + - + + - - + - + - - - React IV – Training Center SW - +React IV – Training Center SW + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + - - -
+
-
+ +
+ + - - -
- - -
- + + + + + + + + + + + +
+ +
+ - -
- + +
-
- -
-

React IV

-

Ecosistema npm, React Router, useContext

-
+
+
+

React IV

+

Ecosistema npm, React Router, useContext

+
-
-
-
Autor/a
-
-

Javier Ribal del Río

-
-
- -
-
Fecha de publicación
-
-

6 de marzo de 2026

-
-
+
-
-
Fecha de última modificación
-
- <<<<<<< HEAD

7 de marzo de 2026

- ======= -

4 de marzo de 2026

- >>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) -
+
+
Autor/a
+
+

Javier Ribal del Río

- -
- +
+ +
+
Fecha de publicación
+
+

6 de marzo de 2026

+
+
+ +
+
Fecha de última modificación
+
+

27 de marzo de 2026

+
+
+ +
+ -
+
-

Contenido

-
    -
  • Ecosistema React y paquetes npm
  • -
  • Librerías externas
  • -
  • React Router
  • -
  • Parámetros de URL
  • -
  • useContext
  • -
-
-

Ecosistema Node

-

Hasta ahora hemos trabajado únicamente con React y JavaScript.

-

Sin embargo, en el desarrollo real de aplicaciones es muy común utilizar librerías externas, - para ello podemos recurrir Node.JS.

-

React está diseñado para funcionar dentro de un ecosistema de paquetes.

-

Ejemplos habituales:

-
    -
  • React Router → navegación
  • -
  • Axios → peticiones HTTP
  • -
  • Chart.js → gráficas
  • -
  • Zustand / Redux → gestión de estado
  • -
  • Librerías de UI: Tailwind, Bootstrap, Booswatch
  • -
-
-
-

npm

-

npm (Node Package Manager) es el sistema que permite instalar y gestionar librerías de - JavaScript. Como su nombre indica, está desarrollado por Node.JS

-

Cada proyecto tiene un archivo:

-
package.json
-

En este archivo se registran las dependencias del proyecto.

-

Ejemplo:

-
-
-
{
+

Contenido

+
    +
  • Ecosistema React y paquetes npm
  • +
  • Librerías externas
  • +
  • React Router
  • +
  • Parámetros de URL
  • +
  • useContext
  • +
+
+

Ecosistema Node

+

Hasta ahora hemos trabajado únicamente con React y JavaScript.

+

Sin embargo, en el desarrollo real de aplicaciones es muy común utilizar librerías externas, para ello podemos recurrir Node.JS.

+

React está diseñado para funcionar dentro de un ecosistema de paquetes.

+

Ejemplos habituales:

+
    +
  • React Router → navegación
  • +
  • Axios → peticiones HTTP
  • +
  • Chart.js → gráficas
  • +
  • Zustand / Redux → gestión de estado
  • +
  • Librerías de UI: Tailwind, Bootstrap, Booswatch
  • +
+
+
+

npm

+

npm (Node Package Manager) es el sistema que permite instalar y gestionar librerías de JavaScript. Como su nombre indica, está desarrollado por Node.JS

+

Cada proyecto tiene un archivo:

+
package.json
+

En este archivo se registran las dependencias del proyecto.

+

Ejemplo:

+
{
   "dependencies": {
     "react": "^18.0.0",
     "react-router-dom": "^6.0.0"
   }
-}
-
-
-
-

Instalación de paquetes

-

Las librerías se instalan desde la terminal.

-
-
-
npm install react-router-dom
-
-
-

Esto hace tres cosas:

-
    -
  1. Descarga el paquete
  2. -
  3. Lo guarda en node_modules (capreta donde se guardan las librerías externas)
  4. -
  5. Añade la dependencia en package.json
  6. -
-
-
-
-

React Router

-

En una aplicación web tradicional cada enlace carga una página nueva.

-

En React normalmente trabajamos con Single Page Applications (SPA).

-

En una SPA:

-
    -
  • el navegador no recarga la página
  • -
  • React cambia los componentes que se muestran
  • -
-

Para gestionar esto utilizamos React Router.

-

Realmente es la misma página, solo que el usuario lo percibe como distintas páginas

-

El usuario percibirá que el sitio web está divido en diferentes subpáginas

-
    -
  • /
  • -
  • pokemons
  • -
  • pokemons/pikachu
  • -
-
-

Componentes principales de React - Router

-

Los elementos fundamentales son:

-
    -
  • BrowserRouter
  • -
  • Routes
  • -
  • Route
  • -
  • Link
  • -
-

Ejemplo básico:

-
-
-
import { BrowserRouter, Routes, Route } from "react-router-dom";
+}
+
+

Instalación de paquetes

+

Las librerías se instalan desde la terminal.

+
npm install react-router-dom
+

Esto hace tres cosas:

+
    +
  1. Descarga el paquete
  2. +
  3. Lo guarda en node_modules (capreta donde se guardan las librerías externas)
  4. +
  5. Añade la dependencia en package.json
  6. +
+
+
+
+

React Router

+

En una aplicación web tradicional cada enlace carga una página nueva.

+

En React normalmente trabajamos con Single Page Applications (SPA).

+

En una SPA:

+
    +
  • el navegador no recarga la página
  • +
  • React cambia los componentes que se muestran
  • +
+

Para gestionar esto utilizamos React Router.

+

Realmente es la misma página, solo que el usuario lo percibe como distintas páginas

+

El usuario percibirá que el sitio web está divido en diferentes subpáginas

+
    +
  • /
  • +
  • pokemons
  • +
  • pokemons/pikachu
  • +
+
+

Componentes principales de React Router

+

Los elementos fundamentales son:

+
    +
  • BrowserRouter
  • +
  • Routes
  • +
  • Route
  • +
  • Link
  • +
+

Ejemplo básico:

+
import { BrowserRouter, Routes, Route } from "react-router-dom";
 
 function App() {
   return (
@@ -976,70 +778,54 @@ 

Co </BrowserRouter> ); -}

-
-
-

Elementos del código

-
    -
  • BrowserRouter
    - Es el componente que activa el sistema de navegación de React Router. Utiliza la API de historial del - navegador para cambiar la URL sin recargar la página.
  • -
  • Routes
    - Es el contenedor donde se definen todas las rutas de la aplicación. React Router examina las rutas dentro - de este componente para decidir qué componente mostrar.
  • -
  • Route
    - Define una ruta concreta de la aplicación.
  • -
-
    -
  • path
    - Indica la URL que activa la ruta.
    - Ejemplo: / corresponde a la página principal.
  • -
  • element
    - Es el componente de React que se renderiza cuando la URL coincide con el path.
  • -
  • <Home />
    - Componente que se muestra cuando el usuario accede a la ruta /.
  • -
  • <PokemonList />
    - Componente que se muestra cuando el usuario accede a la ruta /pokemons.
  • -
-
- + -
-

Parámetros dinámicos

-

Podemos crear rutas dinámicas.

-

Ejemplo:

-
/pokemon/25
+<Link to="/pokemon">Pokemon</Link>
+

A diferencia de <a>:

+
    +
  • no recarga la página
  • +
  • React cambia el componente visible
  • +
+
+
+

Parámetros dinámicos

+

Podemos crear rutas dinámicas.

+

Ejemplo:

+
/pokemon/25
 /pokemon/7
-

Definición de la ruta:

-
-
-
<Route path="/pokemon/:id" element={<PokemonDetail />} />
-
-
-
-

useParams

-

Para acceder al parámetro utilizamos el hook useParams de React Router.

-
-
-
import { useParams } from "react-router-dom";
+

Definición de la ruta:

+
<Route path="/pokemon/:id" element={<PokemonDetail />} />
+
+

useParams

+

Para acceder al parámetro utilizamos el hook useParams de React Router.

+
import { useParams } from "react-router-dom";
 
 function PokemonDetail() {
 
@@ -1047,88 +833,68 @@ 

useParams

return <p>Pokemon {id}</p>; -}
-
-
-

Este parámetro puede utilizarse para realizar peticiones a una API.

-
-
-
-
-

Context API

-

En aplicaciones grandes aparece un problema frecuente.

-

Muchos componentes necesitan acceder a la misma información.

-

Por ejemplo:

-
    -
  • usuario
  • -
  • tema visual
  • -
  • idioma
  • -
  • configuración
  • -
-

Si pasamos la información mediante props, los datos deben atravesar muchos componentes.

-

Representación conceptual:

-
App
+}
+

Este parámetro puede utilizarse para realizar peticiones a una API.

+ + + +
+

Context API

+

En aplicaciones grandes aparece un problema frecuente.

+

Muchos componentes necesitan acceder a la misma información.

+

Por ejemplo:

+
    +
  • usuario
  • +
  • tema visual
  • +
  • idioma
  • +
  • configuración
  • +
+

Si pasamos la información mediante props, los datos deben atravesar muchos componentes.

+

Representación conceptual:

+
App
  └ Layout
     └ Page
        └ Component
-

Si todos necesitan user, debemos pasar la prop continuamente.

-

Este problema se conoce como prop drilling.

-
-

Context

-

React proporciona una solución llamada Context.

-

Context permite compartir información entre múltiples componentes sin pasar props - manualmente.

-

El proceso tiene tres pasos:

-
    -
  1. Crear el contexto
  2. -
  3. Proveer el contexto
  4. -
  5. Consumir el contexto
  6. -
-
-

Crear un contexto

-
-
-
import { createContext } from "react";
+

Si todos necesitan user, debemos pasar la prop continuamente.

+

Este problema se conoce como prop drilling.

+
+

Context

+

React proporciona una solución llamada Context.

+

Context permite compartir información entre múltiples componentes sin pasar props manualmente.

+

El proceso tiene tres pasos:

+
    +
  1. Crear el contexto
  2. +
  3. Proveer el contexto
  4. +
  5. Consumir el contexto
  6. +
+
+

Crear un contexto

+
import { createContext } from "react";
 
-const UserContext = createContext();
-
-
-

Esto crea un contenedor que puede almacenar información compartida.

-
-
-

Provider

-

El Provider permite que los componentes hijos accedan al contexto.

-
-
-
<UserContext.Provider value={user}>
+const UserContext = createContext();
+

Esto crea un contenedor que puede almacenar información compartida.

+
+
+

Provider

+

El Provider permite que los componentes hijos accedan al contexto.

+
<UserContext.Provider value={user}>
 
   <App />
 
-</UserContext.Provider>
-
-
-

Todos los componentes dentro del Provider pueden acceder al valor.

-
-
-

Consumir el contexto

-

Para acceder al contexto utilizamos el hook useContext.

-
-
-
import { useContext } from "react";
+</UserContext.Provider>
+

Todos los componentes dentro del Provider pueden acceder al valor.

+
+
+

Consumir el contexto

+

Para acceder al contexto utilizamos el hook useContext.

+
import { useContext } from "react";
 
-const user = useContext(UserContext);
-
-
-

El componente obtiene directamente el valor almacenado en el contexto.

-
-
-

Ejemplo completo

-
-
-
import { createContext, useContext } from "react";
+const user = useContext(UserContext);
+

El componente obtiene directamente el valor almacenado en el contexto.

+
+
+

Ejemplo completo

+
import { createContext, useContext } from "react";
 
 const UserContext = createContext();
 
@@ -1153,283 +919,279 @@ 

Ejemplo completo

return <h1>{user.name}</h1>; -}
-
-
+}
-
-
-
+ + + - - - - + } else { + return undefined; + } + }; + var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]'); + for (var i=0; i + - - \ No newline at end of file + \ No newline at end of file diff --git a/docs/pages/Bloque3/ReactIV.pdf b/docs/pages/Bloque3/ReactIV.pdf index bbe3f73..763f756 100644 Binary files a/docs/pages/Bloque3/ReactIV.pdf and b/docs/pages/Bloque3/ReactIV.pdf differ diff --git a/docs/pages/Bloque3/ps.html b/docs/pages/Bloque3/ps.html index 0677f46..1eeebd5 100644 --- a/docs/pages/Bloque3/ps.html +++ b/docs/pages/Bloque3/ps.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -628,7 +623,7 @@

Proyecto Subsistema

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque3/ps.pdf b/docs/pages/Bloque3/ps.pdf index 11610f8..60cd4a5 100644 Binary files a/docs/pages/Bloque3/ps.pdf and b/docs/pages/Bloque3/ps.pdf differ diff --git a/docs/pages/Bloque3/t10.html b/docs/pages/Bloque3/t10.html index 26b436b..81618af 100644 --- a/docs/pages/Bloque3/t10.html +++ b/docs/pages/Bloque3/t10.html @@ -600,6 +600,12 @@ Rúbrica de evaluación + + @@ -661,7 +667,7 @@

T10: Quiz app!

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque3/t10.pdf b/docs/pages/Bloque3/t10.pdf index 386fe53..00b728e 100644 Binary files a/docs/pages/Bloque3/t10.pdf and b/docs/pages/Bloque3/t10.pdf differ diff --git a/docs/pages/Bloque3/t6.html b/docs/pages/Bloque3/t6.html index 4159e59..2cd4369 100644 --- a/docs/pages/Bloque3/t6.html +++ b/docs/pages/Bloque3/t6.html @@ -551,17 +551,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -685,7 +680,7 @@

T6: Romancero Gitano I

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque3/t6.pdf b/docs/pages/Bloque3/t6.pdf index afe987e..0e54a1b 100644 Binary files a/docs/pages/Bloque3/t6.pdf and b/docs/pages/Bloque3/t6.pdf differ diff --git a/docs/pages/Bloque3/t9.html b/docs/pages/Bloque3/t9.html index e6fa209..7e39eb7 100644 --- a/docs/pages/Bloque3/t9.html +++ b/docs/pages/Bloque3/t9.html @@ -348,7 +348,7 @@

T6: Romancero Gitano I

Fecha de última modificación
-

4 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque3/t9.pdf b/docs/pages/Bloque3/t9.pdf index f62e76b..09527bd 100644 Binary files a/docs/pages/Bloque3/t9.pdf and b/docs/pages/Bloque3/t9.pdf differ diff --git a/docs/pages/Bloque4/protocolos.html b/docs/pages/Bloque4/protocolos.html index c7f338a..83fabd6 100644 --- a/docs/pages/Bloque4/protocolos.html +++ b/docs/pages/Bloque4/protocolos.html @@ -600,6 +600,12 @@ Rúbrica de evaluación + + @@ -687,7 +693,7 @@

Protocolos de Comunicación

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/pages/Bloque4/protocolos.pdf b/docs/pages/Bloque4/protocolos.pdf index e6e20ae..5cca353 100644 Binary files a/docs/pages/Bloque4/protocolos.pdf and b/docs/pages/Bloque4/protocolos.pdf differ diff --git a/docs/pages/backend-linux.zip b/docs/pages/backend-linux.zip new file mode 100644 index 0000000..783b53e Binary files /dev/null and b/docs/pages/backend-linux.zip differ diff --git a/docs/pages/backend-windows.zip b/docs/pages/backend-windows.zip new file mode 100644 index 0000000..c55c4ce Binary files /dev/null and b/docs/pages/backend-windows.zip differ diff --git a/docs/pages/final.html b/docs/pages/final.html new file mode 100644 index 0000000..e26f7f8 --- /dev/null +++ b/docs/pages/final.html @@ -0,0 +1,1775 @@ + + + + + + + + + + +Proyecto Final TC SW – Training Center SW + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+ + +
+ + + +
+ + +
+
+

Proyecto Final TC SW

+
+ + + +
+ +
+
Autor/a
+
+

Javier Ribal del Río

+
+
+ +
+
Fecha de publicación
+
+

27 de marzo de 2026

+
+
+ +
+
Fecha de última modificación
+
+

27 de marzo de 2026

+
+
+ +
+ + + +
+ + +
+
+

Contextualización de la tarea

+

El Hyperloop es una tecnología que se basa en la eficiencia energética; para ello se introduce lo que a día de hoy conocemos como booster, un módulo de infraestructura que confiere energía al pod para su propulsión. Al externalizar la potencia de la cápsula al carril, se logra un sistema más verde y sostenible, reduciendo el peso del vehículo y optimizando el consumo eléctrico mediante una gestión inteligente de la energía.

+

A lo largo de los años en Hyperloop-UPV hemos tratado de desarrollar nuestro propio booster, uno de los problemas que identificamos a la hora de desarrollarlo es que para poder hacer pruebas era necesario disponer del POD completo. Por ello, este año hemos diseñado una bancada de pruebas1 que nos permite validar el funcionamiento del booster de forma sencilla.

+
+

Fundamentos físicos

+
+

Componentes

+

Podemos dividir la bancada de pruebas en 4 componentes:

+
    +
  • Carro booster (o Carro): módulo de tracción móvil compuesto por un bastidor de cuatro ruedas y un EMS2 que es propulsado por la sección
  • +
  • Track: vía de 50 metros por la que circula el Carro booster +
      +
    • Sección booster: sección del track por la que el al pasar Carro booster lo impulsa.
    • +
  • +
  • Armario booster: lugar donde se almacenan los supercondensadores y las resistencias que generan el campo magnético que impulsa al Carro booster en la Sección booster
  • +
+
+
+

Variables

+

Trabajaremos como todas la variables asumiendo que son escalares.

+
+
Carro Booster
+
    +
  • Posición: ubicación física del Carro en el Track. \(s\) medida en \(\mathrm{m}\) y \(s \in[0,50]\). +
      +
    • Asumiremos que la Sección booster se encuentra ubicada entre \(s=2\) y \(s =4\)
    • +
  • +
  • Velocidad: cambio de la posición del Carro respecto al tiempo. \(v\) medida en \(\mathrm{km/h}\)
  • +
  • Aceleración: cambio de la velocidad del Carro respecto al tiempo. \(a\) medida en \(\mathrm{m/s^2}\)
  • +
  • Masa: masa total del Carro booster. \(m\) medida en \(\mathrm{kg}\)
  • +
  • Fuerza: fuerza neta aplicada sobre el Carro, resultado del empuje del booster y de las fuerzas de frenado u oposición al movimiento. \(F\) medida en \(\mathrm{N}\). Por convenio, \(F>0\) si actúa en el sentido de avance (dirección creciente de \(s\)) y \(F<0\) en caso contrario.
  • +
+
+
+
Armario Booster
+
    +
  • Tensión: diferencia de potencial eléctrico suministrada por el sistema de almacenamiento de energía. \(V\) medida en \(\mathrm{V}\)
  • +
  • Intensidad: corriente eléctrica que circula por el sistema para generar el campo magnético del booster. \(I\) medida en \(\mathrm{A}\)
  • +
+
+
+
Otras consideraciones
+

Asumiremos que:

+
    +
  • El movimiento únicamente es horizontal \[ +\begin{aligned} +g &= 9.81\,\mathrm{m/s^2} \\ +F &= m \cdot a \\ +|\vec{N}| &= |\vec{P}| = m g \\ +F_\text{rozamiento} &= \mu|\vec{N}| \\ +\mu &= +\begin{cases} +0, & \text{si el Carro no está frenando} \\ +0.5, & \text{si el Carro está frenando} +\end{cases} +\end{aligned} +\]
  • +
+
+
+
+

Funcionamiento

+
    +
  • Partiremos de un estado inicial de espera IDLE donde \(s=0; a = 0; v = 0; V= 0; I = 0\);
  • +
  • Se dará la orden de precarga PRECHARGE donde todas las variables se mantendrán a 0 menos \(V\) que empezará a aumentar hasta alcanzar \(V =400\)
  • +
  • Cuando \(V=400\) entramos al estado de READY, donde la bancada está lista para operar
  • +
  • Cuando se inicie la prueba, el carro adquirirá una \(v_\text{base} = 4\) que mantendrá en todo momento hasta que frene. RUNNING \(a = 0; V= 400\)
  • +
  • Al entrar en la Sección Booster la velocidad y aceleración del Carro se verán incrementadas. BOOSTING. Durante el funcionamiento del Booster la Intensidad se modificará
  • +
  • Una vez haya salido de la Sección Booster (\(s > 4\)) mantendrá una velocidad constante de \(v = 25\) hasta que el Carro llegue al final del track (\(s=50\)) o bien sea frenado. RUNNING
  • +
  • Frenado: al lanzar la orden de Frenado BRAKE el Carro comenzará a disminuir su velocidad hasta \(v = 0\) o llegar al final del track. +
      +
    • Si el Carro llega a detenerse antes de alcanzar el final del track (\(s < 50\)), se informará mediante un mensaje
    • +
    • En caso de que el Carro llegue al final del track con \(v > 0\), será detenido por el mechanical stopper3, también se informará con un mensaje.
    • +
  • +
  • Una vez el Carro reste detenido STOPPED todas las variables de mantendran a 0 salvo la posición.
  • +
+
+
+
+
+
+

Objetivos de aprendizaje

+

El objetivo principal de la tarea es poner en práctica el contenido aprendido durante el training center de Software, el diseño de UI dinámicas para el control de telemetría del vehículo. Asimismo, el proyecto se realizará en colaboración con otros subsistemas, para potenciar el trabajo en equipo y la comunicación entre los subsistemas, simulando así un miniequipo de Hyperloop.

+
+
+

Descripción general de la tarea

+

El objetivo del proyecto final consiste en desarrollar una bancada de pruebas para el booster (de ahora en adelante bancada booster). Los alumnos de los diferentes subsistemas del Training Center tienen asignadas diferentes tareas interrelacionadas entre sí, donde será necesario que todos cooperen de manera coordinada para cumplir con los requisitos técnicos y alcanzar el objetivo final de la tarea.

+
+

Descripción de la tarea de Software

+

Se deberá implementar un emulador de la bancada booster, el emulador estará compuesto por dos partes:

+
    +
  • Frontend: se conecta al backend y muestra datos sobre la bancada
  • +
  • Backend: emulación de la bancada booster. (Se permite hacer uso del backend de ejemplo)
  • +
+
+
+

Evaluación de la tarea

+

Los requesitos mínimos para evaluar la tarea es realizar un frontend incluyendo las 5 funcionalidades detalladas en el apartado anterior.

+

Aquellos que deseen aspirar a nota extra podrán entregar también su propio backend que cumpla con las especificaciones de la tarea.

+
+
+
+
+

Frontend

+

La Graphical User Interface (GUI) o frontend, debe de conectarse al backend utilizando los protocolos http y websockets.

+
+

Especificacciones de la GUI

+
    +
  1. Una sección con gráficas que nos permita visualizar las siguientes varibles
  2. +
+
    +
  • \(V\)

  • +
  • \(a\)

  • +
  • \(v\)

  • +
  • \(F\) (Habrá que calcularla en el Frontend. Usar 2ª Ley de Newton)

  • +
  • \(I\)

  • +
  • Además, de una cronograma con los estados establecidos.

  • +
+

IDLE, PRECHARGE, READY, RUNNING, BOOSTING, BRAKING y STOPPED

+
    +
  1. Una sección que nos permita mediante botones enviar ordenes al simulador
  2. +
+
    +
  • PRECHARGE: inicia la precarga
  • +
  • START: inicia el movimiento del Carro, se debe de especificar la masa
  • +
  • BRAKE: frena el Carro
  • +
  • RESET: reinicia el simulador
  • +
+
    +
  1. Una sección que nos permita ver los mensajes que envía el simulador al Front, los mensajes harán referencia al estado del simulador, por ejemplo : V = 400V precharge completed sucessfully

  2. +
  3. Una sección que nos permita calcular la distancia óptima para presionar el freno mediante una llamada a API

  4. +
  5. Inclusión de un modelo 3D del Carro Booster

  6. +
+
+
+

Implementación

+
+

Notas Generales

+
    +
  • Se debe desarollar una aplicación Web utilizando React
  • +
  • Se debe utilizar preferiblemente Typescript o en su defecto JavaScript
  • +
  • Se recomienda el uso de librerías de estilo como tailwind o bootsrap, así mismo se recomienda el uso de librerías de componentes, en especial shadcn
  • +
  • El frontend deberá validar los inputs antes de enviarlos
  • +
+
+
+

Gráficas (1) y Mensajes (3)

+

El Backend expondrá en el puerto 5001 un protocolo websockets con ruta backend/stream que se corresponderá con el flujo de datos envidados del cliente a servidor; es decir utilizaremos websockets únicamente para leer datos. De la información transmitida por ws obtendremos tanto los datos que habrá que mostrar en la gráfica, como los mensajes que se debén de mostrar en el área de mensajes.

+

WebSocket endpoint: ws://localhost:5001/backend/stream

+

La comunicación por websockets estará, codificada como JSON, del cual distinguiremos 2 formatos, uno para recepción de los datos y otro para los mensajes del servidor.

+

Formato datos:

+
{
+  "topic": "data",
+  "payload": {
+    "timestamp": "2026-03-27T10:15:32.125Z",
+    "state": "RUNNING",
+    "position_m": 3.42,
+    "velocity_kmh": 18.7,
+    "acceleration_ms2": 2.15,
+    "mass_kg": 40,
+    "voltage_v": 400,
+    "current_a": 125.4
+  }
+}
+

Formato mensajes:

+
{
+  "topic": "message",
+  "payload": {
+    "type": "info", 
+    "content": "Precharge started"
+  }
+}
+

Los tipos de mensajes pueden ser:info, critical, error y success

+

En el caso de formato de datos los paquetes llegarán con una frecuencia de 4 Hz, debido a la gran afluencia de paquetes se recomienda mantener un histórico limitado (por ejemplo, los últimos 5–10 segundos de datos) para la representación gráfica, eliminando los valores más antiguos para evitar problemas de rendimiento.

+
+
+

Botones (2)

+

Al ser presionados mandarán una orden utilizando una petición HTTP-POST al backend utilizando el formato especificado. El botón de START debe de estar acompañado de un input numérico que permita especificar la massa del carro.

+

La orden se dirigirá al siguiente enpoint /api/command la cual estará en el puerto 8001. El servidor tras recibir la orden devolvera el código 200 indicando que todo ha sido correcto y se enviará un mensaje via websockets.

+

El servidor responderá con:

+
    +
  • 200 OK si el comando se ha aceptado correctamente
  • +
  • 400 Bad Request si el comando es inválido
  • +
+

El resultado de la acción se notificará mediante un mensaje vía websockets.

+

Formato petición HTTP:

+
POST /api/command
+Content-Type: application/json
+
+{
+  "command": "PRECHARGE"
+}
+

(en el caso de la orden START)

+
POST /api/command
+Content-Type: application/json
+
+{
+  "command": "START",
+  "payload": {
+    "mass": 40
+  }
+}
+

En caso de error (400 Bad Request), el frontend deberá mostrar un mensaje informativo al usuario indicando que la operación no se ha podido realizar.

+
+
+

Cálculo de la posición óptima para presionar el freno (4)

+

Sección con un formulario que permitirá indicar el peso del vehículo y a cuánta distancia se desea acabar del fin de la vía. Al presionar el botón de submit el frontend realizará una petición tipo GET - HTTP a /api/calculate con los query parameters m y d, que se corresponden con la masa del Carro y la distancia deseada, respectivamente.

+

El servidor devolverá una respuesta en formato JSON con la posición óptima en la que se debe iniciar el frenado. El frontend deberá procesar dicha respuesta y mostrar el resultado de forma clara al usuario.

+

Formato de respuesta esperado:

+
{
+  "braking_position_m": 37.5
+}
+
+
+

Inclusión del modelo 3D (5)

+

Se debe incluir un modelo 3D del Carro booster diseñado por el subsistema de Mechanics, debe de ser interáctivo, no debe de tratarse de una fotografía. 4

+
+

Nota sobre la velocidad del backend de ejemplo

+

El backend de ejemplo no corre en tiempo real: la simulación transcurre 5× más lenta que el tiempo real (SIM_SPEED = 0.2). La frecuencia de emisión por WebSocket se mantiene en 4 Hz, pero los valores de posición, velocidad, etc. avanzan más despacio de lo que cabría esperar físicamente. Esto es intencionado para facilitar las pruebas interactivas — el frontend no necesita hacer ningún ajuste al respecto.

+
+
+
+
+

Recomendaciones

+
    +
  • El objetivo de la tarea es diseñar un panel de control, por lo que sería interesante que haya una única vista, donde no se pueda hacer scroll que lo concentre todo
  • +
  • Se recomienda hacer uso de los estilos como herramienta para resaltar los eventos que suceden en el simulador, por ejemplo si el Carro, se estrella con el mechanical stopper, podría ponerse el fondo en rojo para indicar que ha colisionado.
  • +
+
+
+
+
+

Backend (VOLUNTARIA REALIZACIÓN)

+

El backend será el encargado de emular el comportamiento de la bancada booster, gestionando tanto la lógica de estados como la generación de datos de telemetría y la comunicación con el frontend.

+

El backend deberá implementarse preferiblemente en Rust o Go, o en su defecto en Java. Queda a criterio del alumno la elección del lenguaje dentro de estas opciones.

+

El backend deberá exponer dos tipos de comunicación:

+
    +
  • WebSockets: envío continuo de datos y mensajes al frontend
  • +
  • HTTP: recepción de órdenes y cálculo de la posición óptima de frenado
  • +
+
+

Especificaciones del Backend

+
+

Arquitectura general

+

El backend deberá estar compuesto por:

+
    +
  • Un servidor WebSocket en el puerto 5001 para el envío de datos en tiempo real
  • +
  • Un servidor HTTP en el puerto 8001 para la recepción de comandos y cálculo
  • +
+

El sistema deberá funcionar como una máquina de estados, donde las transiciones estarán controladas por las órdenes recibidas.

+
+
+
+
+

Máquina de estados

+

El backend deberá implementar los siguientes estados:

+

IDLE, PRECHARGE, READY, RUNNING, BOOSTING, BRAKING y STOPPED

+
+

Transiciones

+
    +
  • IDLE → PRECHARGE mediante comando PRECHARGE
  • +
  • PRECHARGE → READY cuando \(V = 400\)
  • +
  • READY → RUNNING mediante comando START
  • +
  • RUNNING → BOOSTING cuando \(2 \le s \le 4\)
  • +
  • BOOSTING → RUNNING cuando \(s > 4\)
  • +
  • RUNNING → BRAKING mediante comando BRAKE
  • +
  • BOOSTING → BRAKING mediante comando BRAKE
  • +
  • BRAKING → STOPPED cuando \(v = 0\)
  • +
  • Cualquier estado → IDLE mediante comando RESET: todas las variables vuelven a su valor inicial (\(s=0\), \(v=0\), \(a=0\), \(V=0\), \(I=0\), \(m=0\))
  • +
+

Los comandos únicamente podrán ser enviados por el frontend

+

No se permitirán transiciones no definidas.

+
+
+
+
+

Generación de datos

+

El backend deberá generar y enviar datos de forma continua mediante WebSockets.

+
    +
  • Frecuencia: 4 Hz
  • +
  • Cada mensaje representará el estado instantáneo del sistema
  • +
+
+

Variables a generar

+
    +
  • position_m
  • +
  • velocity_kmh
  • +
  • acceleration_ms2
  • +
  • mass_kg
  • +
  • voltage_v
  • +
  • current_a
  • +
  • state
  • +
  • timestamp
  • +
+

El campo timestamp deberá estar en formato ISO 8601 (UTC).

+
+
+
+
+

Lógica física simplificada

+

Se deberá respetar el siguiente comportamiento:

+
    +
  • En PRECHARGE: incremento progresivo de \(V\) hasta 400 (Incremento de \(25\,\mathrm{V}\) por tick, es decir, cada \(250\,\mathrm{ms}\); la precarga completa dura 16 ticks = 4 segundos)
  • +
  • En RUNNING: velocidad constante \(v = 4\,\mathrm{km/h}\) (antes del booster)
  • +
  • En BOOSTING: la aceleración se recalcula en cada tick para garantizar que el Carro alcance exactamente \(v_f = 25\,\mathrm{km/h}\) al llegar a \(s = 4\,\mathrm{m}\): \[ +a = \frac{v_f^2 - v^2}{2\,(4 - s)} +\] donde \(v\) y \(s\) son la velocidad (en \(\mathrm{m/s}\)) y posición actuales. La fuerza es \(F = m \cdot a\). Cuando \(s \ge 4\), no calcular \(a\) — fijar directamente \(v = 25\,\mathrm{km/h}\) y transicionar a RUNNING para evitar división por cero. La intensidad sigue el perfil: \[ +I(s) = I_{\max} \cdot \sin\!\left(\frac{\pi\,(s - 2)}{2}\right), \quad I_{\max} = 200\,\mathrm{A} +\]
  • +
  • En RUNNING (post-booster): velocidad constante \(v = 25\,\mathrm{km/h}\), \(a = 0\), \(I = 0\)
  • +
  • En BRAKING: \(F_{\text{brake}} = 196\,\mathrm{N}\), \(a = -F_{\text{brake}}/m\). En cada tick (\(\Delta t = 0.25\,\mathrm{s}\)) se actualiza: \[ +v \leftarrow v + a\,\Delta t \qquad s \leftarrow s + v\,\Delta t +\] Si \(v \le 0\) → transición a STOPPED. Si \(s \ge 50\)mechanical stopper.
  • +
  • En STOPPED: todas las variables a 0 salvo posición
  • +
+

No es necesario implementar un modelo físico complejo, pero sí coherente con las reglas anteriores.

+
+

Velocidad de simulación

+

El backend no corre en tiempo real. El paso de tiempo físico se escala con un factor fijo SIM_SPEED = 0.2, de modo que la simulación transcurre 5× más lenta que el tiempo real. La frecuencia de emisión por WebSocket se mantiene en 4 Hz.

+

\[ +\Delta t_{\text{físico}} = \Delta t \times 0{,}2 = 0{,}05\,\mathrm{s/tick} +\]

+

Esto proporciona aproximadamente 33 segundos de margen en el tramo post-booster para enviar la orden de frenado, facilitando las pruebas interactivas.

+
+
+
+
+

Comunicación WebSocket

+

El servidor deberá emitir mensajes en formato JSON siguiendo los dos tipos definidos:

+
+

Datos

+
{
+  "topic": "data",
+  "payload": {
+    "timestamp": "2026-03-27T10:15:32.125Z",
+    "state": "RUNNING",
+    "position_m": 3.42,
+    "velocity_kmh": 18.7,
+    "acceleration_ms2": 2.15,
+    "mass_kg": 40,
+    "voltage_v": 400,
+    "current_a": 125.4
+  }
+}
+
+
+

Mensajes

+
{
+  "topic": "message",
+  "payload": {
+    "type": "info",
+    "content": "Precharge started"
+  }
+}
+

Los tipos posibles son info, success, error y critical. El backend deberá emitir un mensaje en cada transición de estado:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Eventotypecontent (ejemplo)
Comando PRECHARGE recibidoinfo"Precharge started"
\(V = 400\) alcanzadosuccess"V = 400V precharge completed successfully"
Comando START recibidoinfo"Booster test started. Mass: 40 kg"
Carro entra en Sección Boosterinfo"Booster section entered"
Carro sale de Sección Boostersuccess"Boost completed. Velocity: 25 km/h"
Comando BRAKE recibidoinfo"Braking initiated"
Carro detenido (\(v = 0\), \(s < 50\))success"Cart stopped at s = 12.3 m"
Carro llega al mechanical stoppercritical"Cart reached mechanical stopper at s = 50 m"
Comando no válido para el estado actualerror"Command BRAKE not allowed in state IDLE"
Comando RESET recibidoinfo"System reset"
+
+
+
+
+

Comunicación HTTP

+

El servidor HTTP escuchará en el puerto 8001 y expondrá dos endpoints.

+
+

POST /api/command

+

Recibe un comando del frontend. Si el comando no es válido para el estado actual, responde 400 Bad Request. En caso contrario responde 200 OK y emite el mensaje correspondiente por WebSocket.

+

Comandos aceptados:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComandoEstado requeridoPayload adicional
PRECHARGEIDLE
STARTREADY{ "mass": <número> }
BRAKERUNNING o BOOSTING
RESETCualquiera
+
+
+

GET /api/calculate

+

Calcula la posición óptima para iniciar el frenado. Recibe dos query parameters:

+
    +
  • m: masa del Carro en \(\mathrm{kg}\)
  • +
  • d: distancia deseada al final del track en \(\mathrm{m}\)
  • +
+

\[ +d_{\text{brake}} = \frac{v_0^2 \cdot m}{2\,F_{\text{brake}}} \qquad s_{\text{brake}} = (50 - d) - d_{\text{brake}} +\]

+

con \(v_0 = 25\,\mathrm{km/h}\) (convertido a m/s) y \(F_{\text{brake}} = 196\,\mathrm{N}\).

+

Respuesta:

+
{
+  "braking_position_m": 37.5
+}
+

Si los parámetros son inválidos (negativos, ausentes, o \(s_{\text{brake}} < 0\)), responder 400 Bad Request.

+
+
+
+
+
+

Uso del backend de ejemplo

+

Para los alumnos que realicen únicamente el frontend, se proporciona un backend de ejemplo ya compilado y listo para ejecutar, sin necesidad de instalar Python ni ninguna dependencia.

+
+

Descarga

+ + + + + + + + + + + + + + + + + +
Sistema operativoEnlace
WindowsDescargar backend.exe
LinuxDescargar backend
+
+
+

Ejecución

+
+ +
+
+

Haz doble clic sobre backend.exe o ejecútalo desde una terminal:

+
backend.exe
+
+
+

Otorga permisos de ejecución y lánzalo:

+
chmod +x backend
+./backend
+
+
+
+
+
+

Puertos

+

Una vez en marcha, el backend expone dos servicios de forma simultánea:

+ + + + + + + + + + + + + + + + + +
ServicioURL
WebSocket (telemetría)ws://localhost:5001/backend/stream
HTTP (comandos y cálculo)http://localhost:8001
+

El frontend debe estar lanzado después de que el backend esté corriendo. Asegúrate de que los puertos 5001 y 8001 no están ocupados por otro proceso.

+
+
+
+
+
+

Entrega

+

La entrega del proyecto se realizará a través de un repositorio de GitHub que deberá cumplir los siguientes requisitos:

+
    +
  • El repositorio debe contener únicamente el proyecto — sin archivos innecesarios, sin el binario del backend de ejemplo descargado, y sin carpetas de dependencias (node_modules, entornos virtuales, etc.). Se recomienda incluir un .gitignore adecuado.
  • +
  • El repositorio debe incluir un README.md con instrucciones claras para instalar y ejecutar el proyecto.
  • +
  • Se enviará el enlace al repositorio al responsable del subsistema de Software.
  • +
+
+

Plazos

+ + + + + + + + + + + + + + + + + +
HitoFecha
Plazo para añadir cambios al repositorio12 de mayo de 2026
Exposición del proyecto14 de mayo de 2026
+
+

A partir del 12 de mayo no se tendrán en cuenta los cambios realizados en el repositorio para la evaluación. La exposición consistirá en una demostración en vivo del proyecto y una breve explicación de las decisiones de implementación tomadas.

+
+ + +
+
+ + +

Notas

+ +
    +
  1. Sistema estructural diseñado para el montaje rígido de prototipos o componentes, equipado con sensores para la medición de parámetros operativos y la simulación de condiciones de carga controladas.↩︎

  2. +
  3. Un imán.↩︎

  4. +
  5. Plancha de metal que asegura que el Carro booster no salga del track.↩︎

  6. +
  7. No se incluye una descripción más extensa porque el desafio de este apartado es ver, vuestras habilidades para seleccionar la forma óptima.↩︎

  8. +
+
+ +
+ + + + + \ No newline at end of file diff --git a/docs/pages/final.pdf b/docs/pages/final.pdf new file mode 100644 index 0000000..68827bb Binary files /dev/null and b/docs/pages/final.pdf differ diff --git a/docs/pages/rubric.html b/docs/pages/rubric.html index 5223bf4..6c45cb6 100644 --- a/docs/pages/rubric.html +++ b/docs/pages/rubric.html @@ -516,17 +516,6 @@ - ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) + @@ -617,7 +612,7 @@

Rúbrica de evaluación

Fecha de última modificación
-

13 de marzo de 2026

+

27 de marzo de 2026

diff --git a/docs/rubriuca.pdf b/docs/rubriuca.pdf index 4e2f4cf..fe65f4b 100644 Binary files a/docs/rubriuca.pdf and b/docs/rubriuca.pdf differ diff --git a/docs/search.json b/docs/search.json index 8badb3b..d4c7a32 100644 --- a/docs/search.json +++ b/docs/search.json @@ -42,80 +42,186 @@ "text": "Evaluación Notas Generales\nEvaluación previa realizada por el profesor antes de la presentación, la podeis leer para valorar vosotros los fallos y calificarlos según vuestro criterio\n\nIvan Martínez Enguix\nPor algún motivo tiene los tamaños de todo muy distorsionado, habrá que preguntarle durante la presentación, seguramente mientras acababa de programar modificaría el zoom de la preview\n\nDiseño Estructural\nEstéticamente está bien, gestiona el espacio de una manera correcta pero mejorable, podría haber puesto un overflow pero en general bastante bien distribuido. Destacar el uso del <footer>.\nEn relación al código en general está bastante bien aunque tiene algunos fallos importantes. Siendo estos algunos de los más destacables.\n\n<div id=\"titulo\"><h1><strong>Lista de tareas</strong></h1></div>\n\nMal uso del <form> y de la etiqueta <label>, (personalmente me sorprende que la aplicación funcione con esto tal y como está)\n <!--Div con los dos campos que rellenar-->\n <div class=\"elflex\">\n\n <!--DOS FORMS???!!!!-->\n <form class=\"losforms\">\n <label for=\"first-name\">Nombre:</label>\n <input type=\"text\" name=\"first-name\" class=\"losforms\" id=\"nomtarea\"/>\n </form>\n\n <!--Desplegable y boton-->\n <form class=\"losforms\">\n <!-- LABEL QUE APUNTA AL BOTÓN DE SUBMTI-->\n <label for=\"elinput\">Responsable: </label>\n <select name=\"Responsable:\" id=\"desplegable\" class=\"losforms\">\n <option value=\"Software\">Software</option>\n <option value=\"Hardware\">Hardware</option>\n <option value=\"Partners\">Partners</option>\n <option value=\"Otros\">Otros</option>\n </select>\n <input type=\"submit\" value=\"Añadir\" id=\"elinput\" class=\"losforms\" />\n </form>\n </div>\nPuntuación: 6.8, aunque el HTML tiene varios errores garrafales la estructura es correcta y el resto del HTML está bastante bien\n\n\nDiseño Formato\nUso muy bueno de las propiedades y de los selectores/pseudoselectores de CSS. Domina funciones avanzadas como el flex, el degradado en el fondo, así como configurar correctamente las propiedades de la foto del pie de sitio.\nUtiliza una buena paleta de colores que incluye el AZUL y el VERDE, colores complementarios siguiendo la teoría de color.\nAbusa del uso de los píxeles como unidades de medida, (aunque en el TC tampoco hemos visto más). Hubiera faltado formatear mejor los botones.\nPor último en relación a la hoja de estilos podría haber agrupados varias propiedades en una única clase y aplicarla indistintamente o haber definido una variable de CSS\nPuntuación: 7.5, muy buen diseño y uso de los selectores cosas mejorables en la hoja de estilos pero en general bien. Obiando lo del zoom está todo correcto\n\n\nFuncionalidad\nIncorpora todas las funcionalidades básicas, sin embargo presenta algunos errores, y bugs críticos:\n\nAñadir dos veces la misma tarea\nAñadir tareas sin especificar texto\nAl presionar el botón de intro en el <input> de la tarea se recarga el sitio web (esto por la mala configuración del formulario)\n\nNo mucho más que añadir.\nPuntuación 5.5: cumple con lo mínimo y presenta muchos bugs\n\n\nProgramación\nCódigo JS correcto, no maneja el DOM de la forma más eficiente, aun así parece que no acaba de comprender el paso de variables de las funiones de ES6.\nAunque el manejo del DOM no es el del todo correcto, lo ha solucionado con de una forma muy creativa jugando con selectores CSS. Aun así presenta código innecesario\n const btn = document.getElementById(\"elinput\");\nconst subsistema = document.getElementById(\"desplegable\");\nconst lista = document.getElementById(\"listado\");\nconst tarea = document.getElementById(\"nomtarea\");\n\n//Recoge la información del campo escrito y desplegable y los enseña por pantalla en la lista de tareas\nbtn.addEventListener(\"click\",(event1)=>{\n\n event1.preventDefault();\n const li = document.createElement(\"li\");\n li.textContent = tarea.value + \" (\" + subsistema.value + \")\";\n // AÑADIR NEGRITA AQUÍ\n lista.appendChild(li);\n tarea.value = null\n});\n\n//Detecta sobre que elemento de la lista estamos haciendo click y lo elimina\nlista.addEventListener(\"click\", function(event2) {\n\n if (event2.target.tagName === \"LI\") {\n event2.target.remove();\n }\n});\n\n// TODO ESTO SOBRA SE PODRÍA AÑADIR NEGRITA DIRECTAMENTE AL LI\n//Cuando se pasa por encima de una tarea \nlista.addEventListener(\"mouseover\", function(event3) {\n\n if (event3.target.tagName === \"LI\") {\n event3.target.classList.add(\"negrita\");\n }\n});\nPuntuación: 7.5: En general bien sería interesante que almacenará las tareas, aunque tal y como lo ha planteado no le hacía falta\n\n\nDocumentación\nHay comentarios en el HTML y en JS, en el CSS no. Tampoco hay documntación, pero ha usado git aunque sus mensajes de log no son los más correctos.\nPuntuación: 7\n\n\nObservaciones\nPor algún motivo hay 3 fotos en la misma carpeta que los ficheros de código (dos paisajes y una fotografía de Shrek [Sí, el ogro verde de dreamworks])\n\n\nPreguntas técnicas\n\nPreguntarle lo que es un flex\nPreguntarle a que se corresponde la variable listado de su código\n\n\n\n\nPau Minguet Micó\nHa hecho dos versiones una con DOM y otra con React, así en general evaluaremos la de React pero hay que destacar que el código JS del DOM está correcto. (Es más correcta la solución de DOM a nivel de programación pero valoaremos la de React más positivamente)\n\nDiseño Estructural\nNo se distingue lo que es HTML de lo que no, lo cual no es malo, distribuye el espacio de forma mediocre aunque no mal del todo.\nEn general está bien no utiliza la etiqueta <header/> para la cabecera y utiliza la propiedad style para introducir estilos con la sintaxis JSX, lo cual según mi criterio no es del todo correcto, sin embargo aquí lo hace dos veces por lo que podría haber introducido una clase.\n <span className=\"item-text\" style={{ textDecoration: checked ? \"line-through\" : \"none\" }}>{item.text}</span>\n <span className=\"item-responsible\" style={{ textDecoration: checked ? \"line-through\" : \"none\" }}>{item.responsible}</span>\nFaltaría añadir un label para el checkbox para que si haces click en el texto se marque la casilla Puntuación:8\n\n\nDiseño Formato\nImpresionante, luce el CSS, aunque hay cosas que no acaban de agradar, el alumno maneja correctamente las propiedades CSS juega perfectamente con los flex y trabaja los efectos y animaciones de forma sencilla, la web es casi responsive. Lo único que no me gusta es que con la imagen de fondo no podemos evaluar su totalment paleta de colores, aun así los colores mostrados son correctos.\nAdemás incorpora iconos y letras importadas de CSS, así como checkbox muy customizadas.\nMe llama especialmente la atención el uso de las propiedades CSS, le preguntaremos mucho sobre esto, hay un par de propiedades muy especificas.\nPuntuación: 9\n\n\nFuncionalidad\nIncorpora todas las funcionalidades básicas, sin embargo presenta algunos errores\n\nAñadir dos veces la misma tarea\n\nIncorpora la posibilidad de marcar una tarea sin eliminarla, así como la posibilidad de editar la tarea, lo cual no es el del todo fácil.\nPuntuación 8: incluye lo mínimo y funcionalidades\n\n\nProgramación\nUtiliza React. Buen diseño de componentes generalmente utiliza correctamente las props y el state (aun no lo hemos visto en el TC) aun así presenta algunos errores de metodología (los cuales serán trabajados en el TC posteriormente).\nEl error más destable es utilizar el DOM, pero bueno lo hace de forma correcta y en susitución de los controlled-inputs (que tampoco los hemos visto en el TC).\nimport ListItem from \"./ListItem\";\nimport EditItem from \"./EditItem\";\nimport { useState } from \"react\";\nimport \"../styles/TaskList.css\";\n\nconst TaskList = ({ items, updateList }) => {\n\n // Stores the index of the task that is being edited\n const [editIndex, setEditIndex] = useState(-1);\n\n // When the user clicks on a task, it will be removed from the list\n const handleClick = item => {\n updateList(items.filter(n => !(n.text === item.text && n.responsible === item.responsible)));\n };\n\n // When the user clicks on the save button in the EditItem component, this function will be called to update the task in the list\n const handleSave = () => {\n const text = document.querySelector(\".edit-text\").value;\n const responsible = document.querySelector(\".edit-select\").value;\n if (text !== \"\") {\n const newItems = [...items];\n newItems[editIndex] = { text, responsible };\n updateList(newItems);\n setEditIndex(-1);\n }\n }\n\n // MUY FEO ESTO\n return (\n <div className=\"task-list\">\n {items.map((item, i) => (\n // Just one of the items will be in edit mode\n editIndex === i ? (\n <EditItem key={i} item={item} onSave={handleSave} onCancel={() => setEditIndex(-1)} />\n ) : (\n <ListItem \n key={i} \n onClick={() => handleClick(item)} \n item={item} \n setEditIndex={setEditIndex} \n pos={i} \n />\n )\n ))}\n </div>\n );\n};\n\nexport default TaskList;\nPuntuación: 9\n\n\nDocumentación\nHay comentarios en JSX pero no en CSS. No hay documentación uso correcto de Git y Git Hub se ha hecho una PR a si mismo, trabaja con ramas y utiliza convenctional commits. No presenta documentación.\nPuntuación 8:\n\n\nEsfuerzo\nMe ha ido enseñando distintas versiones de la web me ha ido preguntando por mi opinión y ha ido haciendo casos de mis sugerencias\n\n\nPreguntas técnicas\n\nPreguntarle lo que es un flex y sus propiedades\nPreguntarle el diagrama de componentes de su app\n\n\n\n\nDaniel Zanón Barney\nTengo dudas respecto a este alumno sus tares han sido bastante malas y en clase me transmite la sensación de que no se ha enterado de mucho durante el TC y tengo mis sospechas de que el trabajo está hecho en una gran parte con IA, su tarea presenta un error se lo indicaremos y le diremos que lo corrija en directo.\nEl error es que no llama bien al método get de texto, utiliza una mayúscula en vez de una minúscula." }, { - "objectID": "pages/Bloque3/ReactI.html", - "href": "pages/Bloque3/ReactI.html", - "title": "React I", + "objectID": "pages/Bloque4/protocolos.html", + "href": "pages/Bloque4/protocolos.html", + "title": "Protocolos de Comunicación", "section": "", "text": "Contenido", "crumbs": [ - "Bloque III Interacción", - "Semana 7", - "React I" + "Bloque IV Comunicación", + "Semana 11", + "Protocolos de Comunicación" ] }, { - "objectID": "pages/Bloque3/ReactI.html#sintaxis-jsx", - "href": "pages/Bloque3/ReactI.html#sintaxis-jsx", - "title": "React I", - "section": "Sintaxis JSX", - "text": "Sintaxis JSX\n\n¿Qué es la sintaxis JSX?\n\nJSX (JavaScript XML) es una extensión de JavaScript utilizada en React.\n\nPermite escribir estructuras similares a HTML directamente dentro del código JavaScript.\n\nFacilita la creación y lectura de interfaces de usuario, integrando lógica y presentación.\n\nAunque su apariencia es HTML, JSX se transpila a JavaScript puro antes de ejecutarse en el navegador.\nForma cómoda de almacenar etiquetas html como variables de JS\n\n\n\n¿Qué nos ofrece la sintaxis JSX?\n\nAlmacenar una etiqueta como una variable\nconst title = <h1>Web del TC de SW</h1>;\n\nconst driveIndication = (drives)? <p>Puede conducir</p> : <p>No puede conducir</p>;\nDe esta forma la variable title se corresponde con una etiqueta que contiene el título. Sin embargo, como podemos podemos utilizar está etiqueta una vez la hemos definido como una variable.\n\n\nRenderizar etiquetas dentro de etiquetas\nconst title = <h1>Web del TC de SW</h1>;\n\nconst page = <div>{title}</div>\nDe esta forma estámos encapsulando la etiqueta h1 dentro del div.\n\n\nRenderizar con el map\nconst profeAsi= [[\"Rosa\", \"Macro I\"], [\"Gabriela\", \"FSO\"],[\"Yolanda\",\"LTP\"], [\"Ferrán\", \"EM\"], [\"Pepe\", \"TCO\"]];\n\n// Array de li Asignatura: nombre profesor\nconst itemsProfAsi = profeAsi.map(x => <li>{x[1]}: {x[0]}</li>);\n\nconst listaProfesoresAsignatura = <ul>{itemsProfAsi}</ul>;\n\n\nRenderizar con el map\nconst profeAsi= [[\"Rosa\", \"Macro I\"], [\"Gabriela\", \"FSO\"], [\"Yolanda\",\"LTP\"], [\"Ferrán\", \"EM\"], [\"Pepe\", \"TCO\"]];\n\nconst listaProfesoresAsignatura = <ul>\n{profeAsi.map((x,i) => <li key={i}>{x[1]}: {x[0]}</li>)}\n</ul>;", + "objectID": "pages/Bloque4/protocolos.html#http", + "href": "pages/Bloque4/protocolos.html#http", + "title": "Protocolos de Comunicación", + "section": "HTTP", + "text": "HTTP\n\n¿Qué es HTTP?\nHTTP (HyperText Transfer Protocol) es el protocolo fundamental de la web.\nDefine cómo:\n\nEl cliente (navegador o app)\nSolicita recursos\nA un servidor\n\nModelo conceptual:\nCliente → Solicitud HTTP → Servidor\nCliente ← Respuesta HTTP ← Servidor\nHTTP es sin estado (stateless).\nCada petición es independiente.\n\n\nComponentes de una petición\nUna petición HTTP contiene:\n\nMétodo\nURL\nCabeceras (headers)\nCuerpo (body, opcional)\n\nEjemplo conceptual:\nGET /users HTTP/1.1\nHost: api.example.com\nAuthorization: Bearer token\n\n\nMétodos HTTP principales\n\nGET\n\nSolicita datos\nNo modifica el servidor\nDebe ser idempotente\n\nEjemplo:\nGET /users\nUso típico en React:\nfetch(\"/api/users\")\n\n\n\nPOST\n\nEnvía datos al servidor\nCrea recursos nuevos\nTiene cuerpo\n\nEjemplo:\nfetch(\"/api/users\", {\n method: \"POST\",\n body: JSON.stringify({ name: \"Ana\" }),\n headers: { \"Content-Type\": \"application/json\" }\n});\n\n\nPUT\n\nActualiza completamente un recurso existente\nIdempotente\n\nPUT /users/3\n\n\nDELETE\n\nElimina un recurso\n\nDELETE /users/3\n\n\nHEAD\n\nIgual que GET\nNo devuelve cuerpo\nSolo cabeceras\n\nSe utiliza para:\n\nComprobar existencia\nTamaño\nMetadatos", "crumbs": [ - "Bloque III Interacción", - "Semana 7", - "React I" + "Bloque IV Comunicación", + "Semana 11", + "Protocolos de Comunicación" ] }, { - "objectID": "pages/Bloque3/ReactI.html#react", - "href": "pages/Bloque3/ReactI.html#react", - "title": "React I", - "section": "React", - "text": "React\n\nMotivación\nHasta el momento hemos trabajado únicamente con HTML5, el cual a pesar de ser muy eficaz, presenta el inconveniente de que no implementa ninguna clase de lógica. Es decir, si queremos repetir alguna estructura previamente definida deberemos copiarla y pegarla. Otro problema, de HTML puro es la complejidad que requiere el manejo del DOM, como ya se vio en la semana anterior.\nBajo este pretexto, introducimos el framework React diseñado por Meta.\n\n\nBases de React\nReact es un componente basado en la sintaxis JS y en la programación funcional introducida en ES6.\n\n\nComponente React\nLos componentes de React los definimos como unidades reutilizables y modulares que se implementan de esta forma: una función que devuelve un objeto de JSX\nfunction Saludo() {\n return <h1>Hola, mundo</h1>;\n}\nPodemos renderizar nuestro componente Saludo asumiendo que se comporta como una etiqueta html. Recordemos que no lo es.\n<main>\n <Saludo />\n</main>\nDentro de este componente podemos incluir lógica propia de JavaScript, de forma que al definir componente podemos dotarlo de lógica interna.\nfunction DivNúmeroAleatorio() {\n\n let n = Math.random();\n return <div className=\"n-random\">{n}</div>;\n}\n*Nótese que aunque JS utilice camelCase para el nombre de las funciones, con React utilizaremos PascalCase para definir los componentes puesto que a pesar de que se trata de funciones, trabajaremos con ellas como si fuesen clases.\n\n\nProps en React\nHasta ahora, los componentes definidos siempre devuelven el mismo contenido. Sin embargo, para que un componente sea realmente reutilizable, necesitamos poder pasarle información desde el exterior.\nEn React, esta información se transmite mediante props (properties).\nLas props se definen como los parámetros de entrada de un componente y permiten personalizar su comportamiento o su contenido sin modificar su definición interna.\n\nEjemplo 1: uso de props (forma explícita)\nfunction Saludo(props) {\n return <h1>Hola, {props.nombre}</h1>;\n}\nUso del componente:\n<Saludo nombre=\"Rosa\" />\n<Saludo nombre=\"Gabriela\" />\n\n\nEjemplo 2: props desestructuradas (sintaxis JavaScript)\nfunction Saludo({ nombre }) {\n return <h1>Hola, {nombre}</h1>;\n}\nUso del componente (idéntico al anterior):\n<Saludo nombre=\"Rosa\" />\n<Saludo nombre=\"Gabriela\" />\n\n\n\nEventos en React\nEn React, el manejo de eventos se basa en los eventos del DOM, pero utilizando una sintaxis propia de JSX y siguiendo el estilo de JavaScript.\n\nEjemplo básico de evento\nfunction BotonSaludo() {\n\n function manejarClick() {\n alert(\"Hola desde React\");\n }\n\n return (\n <button onClick={manejarClick}>\n Saludar\n </button>\n );\n}\n\n\nPaso de funciones como eventos\nLa función asociada al evento no se ejecuta al renderizar el componente, sino únicamente cuando ocurre el evento; es decir la pasamos como un ciudadano de primera clase.\n<button onClick={manejarClick}>Saludar</button>\nIncorrecto:\n<button onClick={manejarClick()}>Saludar</button>\n\n\nEventos más habituales en React\nReact soporta la mayoría de los eventos del DOM, adaptados a la sintaxis JSX. A continuación se listan los eventos más utilizados, clasificados por tipo.\n\nEventos de ratón\n\nonClick\nonDoubleClick\nonMouseEnter\nonMouseLeave\nonMouseMove\nonMouseDown\nonMouseUp\n\n\n\nEventos de teclado\n\nonKeyDown\nonKeyUp\nonKeyPress\n\n\n\nEventos de formularios\n\nonChange\nonSubmit\nonFocus\nonBlur\nonInput\nonReset\n\n\n\nEventos de carga y ciclo del elemento\n\nonLoad\nonError\n\n\n\nEventos de selección y portapapeles\n\nonSelect\nonCopy\nonCut\nonPaste\n\n\n\nEventos táctiles (dispositivos móviles)\n\nonTouchStart\nonTouchMove\nonTouchEnd\nonTouchCancel\n\n\n\n\n\nCiclo de vida de los componentes y renderizado en React\nEn React, los componentes no se renderizan directamente sobre el DOM real, sino que siguen un proceso intermedio que permite optimizar las actualizaciones de la interfaz.\nLa idea clave es que la interfaz se organiza como un árbol de componentes, análogo al árbol del DOM, pero a un nivel de abstracción superior.\n\nEl árbol de componentes\nCuando una aplicación React se ejecuta:\n\nCada componente genera otros componentes o elementos JSX.\nReact construye un árbol de componentes, donde:\n\nEl componente raíz suele ser App\nCada componente hijo ocupa una rama del árbol\n\n\nEste árbol de componentes se corresponde conceptualmente con el árbol del DOM, aunque no es el DOM real.\n\n\nProceso de renderizado\nEl proceso general es el siguiente:\n\nReact ejecuta un componente (función).\nEl componente devuelve JSX.\nEl JSX se transforma en una representación interna del árbol.\nReact compara este árbol con el anterior.\nSolo se actualizan en el DOM real los nodos que han cambiado.\n\nDe esta forma, React minimiza el acceso directo al DOM, que es una operación costosa.\n\n\nCiclo de vida (visión simplificada)\nDesde un punto de vista conceptual, un componente pasa por tres fases:\n\nMontaje\n\nEl componente se crea y se inserta en el DOM.\n\nActualización\n\nEl componente se vuelve a renderizar cuando cambian sus datos.\n\nDesmontaje\n\nEl componente se elimina del DOM.\n\n\nEn componentes funcionales modernos, este ciclo se gestiona de forma implícita mediante el renderizado y, más adelante, mediante hooks.\n\n\nRelación con el DOM\n\nEn JavaScript puro:\n\nSe modifica el DOM directamente.\n\nEn React:\n\nSe describe cómo debe ser la interfaz.\nReact decide el cuándo y el cómo modificar el DOM.\n\n\nEl desarrollador no recorre ni manipula el árbol del DOM, sino que trabaja sobre el árbol de componentes.", + "objectID": "pages/Bloque4/protocolos.html#http-vs-comunicación-en-tiempo-real", + "href": "pages/Bloque4/protocolos.html#http-vs-comunicación-en-tiempo-real", + "title": "Protocolos de Comunicación", + "section": "HTTP vs Comunicación en tiempo real", + "text": "HTTP vs Comunicación en tiempo real\nHTTP funciona bajo el modelo:\n\nPetición → Respuesta → Fin\n\nSi queremos datos continuamente:\n\nDebemos repetir peticiones\nIntroduce latencia\nIneficiente\n\nSolución: WebSockets", "crumbs": [ - "Bloque III Interacción", - "Semana 7", - "React I" + "Bloque IV Comunicación", + "Semana 11", + "Protocolos de Comunicación" ] }, { - "objectID": "pages/Bloque3/ReactI.html#creación-de-una-reactjs-app", - "href": "pages/Bloque3/ReactI.html#creación-de-una-reactjs-app", - "title": "React I", - "section": "Creación de una ReactJS app", - "text": "Creación de una ReactJS app\nEl desarrollo de aplicaciones web modernas requiere coordinar múltiples elementos: estructura del proyecto, gestión de dependencias, compilación del código y ejecución en el navegador.\nUn framework de desarrollo proporciona un entorno de trabajo preconfigurado que organiza estos elementos y establece una forma estándar de trabajar.\n\n¿Para qué sirve un framework?\nUn framework permite al desarrollador:\n\nEvitar configuraciones manuales complejas\nSeguir una estructura común de proyecto\nAutomatizar tareas repetitivas\nCentrarse en la lógica de la aplicación y no en la infraestructura\n\nEn lugar de partir de un proyecto vacío, el framework ofrece una base funcional sobre la que desarrollar.\n\n\nFramework y React\nReact se encarga de definir cómo se construye la interfaz, pero no gestiona por sí mismo:\n\nLa estructura de archivos\nEl arranque de la aplicación\nLa compilación del código\nEl servidor de desarrollo\n\nPor ello, React se apoya en un framework que orquesta el entorno de ejecución.\n\n\nCreación de la aplicación\nPara crear una nueva aplicación React utilizando un framework, se ejecuta el siguiente comando en la terminal:\nnpm create vite@latest\nEste comando genera:\n\nUna estructura inicial del proyecto\nLa configuración necesaria para trabajar con React\nUn entorno listo para el desarrollo", + "objectID": "pages/Bloque4/protocolos.html#websockets", + "href": "pages/Bloque4/protocolos.html#websockets", + "title": "Protocolos de Comunicación", + "section": "WebSockets", + "text": "WebSockets\nWebSockets permiten comunicación bidireccional persistente.\nCaracterísticas:\n\nConexión permanente\nCliente y servidor pueden enviar datos\nBaja latencia\nIdeal para tiempo real\n\nEjemplos:\n\nChats\nJuegos online\nTelemetría\nTrading\nSistemas IoT\n\n\nFlujo conceptual\nCliente ↔ Servidor\n conexión abierta\nNo se crea una nueva conexión por mensaje.\n\n\nCreación de un WebSocket en JavaScript\nconst socket = new WebSocket(\"ws://localhost:8080\");\nEventos principales:\n\nonopen\nonmessage\nonclose\nonerror\n\n\n\nRecepción de mensajes\nsocket.onmessage = (event) => {\n console.log(\"Mensaje:\", event.data);\n};\n\n\nEnvío de mensajes\nsocket.send(\"Hola servidor\");", "crumbs": [ - "Bloque III Interacción", - "Semana 7", - "React I" + "Bloque IV Comunicación", + "Semana 11", + "Protocolos de Comunicación" ] }, { - "objectID": "pages/Bloque3/reactI.html#sintaxis-jsx", - "href": "pages/Bloque3/reactI.html#sintaxis-jsx", - "title": "React I", - "section": "Sintaxis JSX", - "text": "Sintaxis JSX\n¿Qué es la sintaxis JSX?\n\nJSX (JavaScript XML) es una extensión de JavaScript utilizada en React.\n\nPermite escribir estructuras similares a HTML directamente dentro del código JavaScript.\n\nFacilita la creación y lectura de interfaces de usuario, integrando lógica y presentación.\n\nAunque su apariencia es HTML, JSX se transpila a JavaScript puro antes de ejecutarse en el navegador.\nForma cómoda de almacenar etiquetas html como variables de JS" + "objectID": "pages/Bloque4/protocolos.html#hook-personalizado", + "href": "pages/Bloque4/protocolos.html#hook-personalizado", + "title": "Protocolos de Comunicación", + "section": "Hook Personalizado", + "text": "Hook Personalizado\nSi usamos WebSockets directamente dentro del componente:\n\nMezclamos lógica de conexión con renderizado\nCódigo difícil de reutilizar\nProblemas de ciclo de vida\n\nSolución: Hook personalizado\n\nHook personalizado\nUn hook personalizado es:\n\nUna función que utiliza hooks internos para encapsular lógica reutilizable\n\nConvención:\nuseNombre()\n\n\nHook useWebSocket\nimport { useEffect, useState } from \"react\";\n\nexport function useWebSocket(url) {\n\n const [data, setData] = useState(null);\n\n useEffect(() => {\n\n const socket = new WebSocket(url);\n\n socket.onmessage = (event) => {\n setData(event.data);\n };\n\n return () => socket.close();\n\n }, [url]);\n\n return data;\n}\n\n\nAnálisis conceptual\n\nSe crea la conexión al montar\nSe actualiza el estado al recibir datos\nSe cierra al desmontar\nEl componente solo consume datos\n\n\n\nUso del hook\nfunction Monitor() {\n\n const data = useWebSocket(\"ws://localhost:8080\");\n\n return (\n <div>\n <h2>Datos en tiempo real</h2>\n <p>{data}</p>\n </div>\n );\n}\nFlujo:\n\nComponente se renderiza\n\nHook abre conexión\n\nLlegan datos\n\nEstado cambia\n\nNuevo render", + "crumbs": [ + "Bloque IV Comunicación", + "Semana 11", + "Protocolos de Comunicación" + ] }, { - "objectID": "pages/Bloque3/reactI.html#react", - "href": "pages/Bloque3/reactI.html#react", - "title": "React I", - "section": "React", - "text": "React\nMotivación\nHasta el momento hemos trabajado únicamente con HTML5, el cual a pesar de ser muy eficaz, presenta el inconveniente de que no implementa ninguna clase de lógica. Es decir, si queremos repetir alguna estructura previamente definida deberemos copiarla y pegarla. Otro problema, de HTML puro es la complejidad que requiere el manejo del DOM, como ya se vio en la semana anterior.\nBajo este pretexto, introducimos el framework React diseñado por Meta." + "objectID": "pages/Bloque4/protocolos.html#visualización-de-datos", + "href": "pages/Bloque4/protocolos.html#visualización-de-datos", + "title": "Protocolos de Comunicación", + "section": "Visualización de datos", + "text": "Visualización de datos\nEn aplicaciones reales no basta con mostrar texto.\nNecesitamos:\n\nGráficas\nPaneles de control\nVisualización clara\n\nUna librería popular para React es Recharts.", + "crumbs": [ + "Bloque IV Comunicación", + "Semana 11", + "Protocolos de Comunicación" + ] }, { - "objectID": "pages/Bloque3/reactI.html#creación-de-una-reactjs-app", - "href": "pages/Bloque3/reactI.html#creación-de-una-reactjs-app", - "title": "React I", - "section": "Creación de una ReactJS app", - "text": "Creación de una ReactJS app\nEl desarrollo de aplicaciones web modernas requiere coordinar múltiples elementos: estructura del proyecto, gestión de dependencias, compilación del código y ejecución en el navegador.\nUn framework de desarrollo proporciona un entorno de trabajo preconfigurado que organiza estos elementos y establece una forma estándar de trabajar." + "objectID": "pages/Bloque4/protocolos.html#recharts", + "href": "pages/Bloque4/protocolos.html#recharts", + "title": "Protocolos de Comunicación", + "section": "Recharts", + "text": "Recharts\nRecharts es una librería basada en:\n\nComponentes React\nSVG\nComposición declarativa\n\nInstalación:\nnpm install recharts\n\nEjemplo básico\nimport {\n LineChart, Line, XAxis, YAxis,\n CartesianGrid, Tooltip\n} from \"recharts\";\n\nconst data = [\n { t: 1, v: 10 },\n { t: 2, v: 15 },\n { t: 3, v: 8 }\n];\n\nfunction Grafica() {\n return (\n <LineChart width={400} height={300} data={data}>\n <CartesianGrid stroke=\"#ccc\" />\n <XAxis dataKey=\"t\" />\n <YAxis />\n <Tooltip />\n <Line type=\"monotone\" dataKey=\"v\" stroke=\"#8884d8\" />\n </LineChart>\n );\n}\n\n\nInterpretación\nCada objeto representa un punto.\n{ t: 1, v: 10 }\n\nt → eje X\nv → eje Y", + "crumbs": [ + "Bloque IV Comunicación", + "Semana 11", + "Protocolos de Comunicación" + ] }, { - "objectID": "pages/Bloque3/DOM.html", - "href": "pages/Bloque3/DOM.html", - "title": "DOM", + "objectID": "pages/Bloque4/protocolos.html#integración-completa", + "href": "pages/Bloque4/protocolos.html#integración-completa", + "title": "Protocolos de Comunicación", + "section": "Integración completa", + "text": "Integración completa\nWebSocket + Hook + Recharts\nObjetivo:\n\nVisualizar datos en tiempo real\n\n\nEjemplo conceptual\nfunction Dashboard() {\n\n const data = useWebSocket(\"ws://localhost:8080\");\n\n const chartData = data\n ? JSON.parse(data)\n : [];\n\n return (\n <LineChart width={500} height={300} data={chartData}>\n <XAxis dataKey=\"time\" />\n <YAxis />\n <Tooltip />\n <Line dataKey=\"value\" stroke=\"#82ca9d\" />\n </LineChart>\n );\n}\n\n\nFlujo completo de una app moderna\nServidor → WebSocket → Hook → Estado → React → Gráfica\nArquitectura típica de sistemas en tiempo real.", + "crumbs": [ + "Bloque IV Comunicación", + "Semana 11", + "Protocolos de Comunicación" + ] + }, + { + "objectID": "pages/Bloque4/protocolos.html#material", + "href": "pages/Bloque4/protocolos.html#material", + "title": "Protocolos de Comunicación", + "section": "Material", + "text": "Material\nServidor de testing de Python: Enlace.\nInstalar websockets con pip install websockets\nTester de prueba: Enlace (click derecho guardar enlace como)\nhttps://recharts.github.io/", + "crumbs": [ + "Bloque IV Comunicación", + "Semana 11", + "Protocolos de Comunicación" + ] + }, + { + "objectID": "pages/Bloque4/Protocolos.html#http", + "href": "pages/Bloque4/Protocolos.html#http", + "title": "Protocolos de Comunicación", + "section": "HTTP", + "text": "HTTP\n¿Qué es HTTP?\nHTTP (HyperText Transfer Protocol) es el protocolo fundamental de la web.\nDefine cómo:\n\nEl cliente (navegador o app)\nSolicita recursos\nA un servidor" + }, + { + "objectID": "pages/Bloque4/Protocolos.html#http-vs-comunicación-en-tiempo-real", + "href": "pages/Bloque4/Protocolos.html#http-vs-comunicación-en-tiempo-real", + "title": "Protocolos de Comunicación", + "section": "HTTP vs Comunicación en tiempo real", + "text": "HTTP vs Comunicación en tiempo real\nHTTP funciona bajo el modelo:\n\nPetición → Respuesta → Fin\n\nSi queremos datos continuamente:\n\nDebemos repetir peticiones\nIntroduce latencia\nIneficiente\n\nSolución: WebSockets" + }, + { + "objectID": "pages/Bloque4/Protocolos.html#websockets", + "href": "pages/Bloque4/Protocolos.html#websockets", + "title": "Protocolos de Comunicación", + "section": "WebSockets", + "text": "WebSockets\nWebSockets permiten comunicación bidireccional persistente.\nCaracterísticas:\n\nConexión permanente\nCliente y servidor pueden enviar datos\nBaja latencia\nIdeal para tiempo real" + }, + { + "objectID": "pages/Bloque4/Protocolos.html#hook-personalizado", + "href": "pages/Bloque4/Protocolos.html#hook-personalizado", + "title": "Protocolos de Comunicación", + "section": "Hook Personalizado", + "text": "Hook Personalizado\nSi usamos WebSockets directamente dentro del componente:\n\nMezclamos lógica de conexión con renderizado\nCódigo difícil de reutilizar\nProblemas de ciclo de vida\n\nSolución: Hook personalizado" + }, + { + "objectID": "pages/Bloque4/Protocolos.html#visualización-de-datos", + "href": "pages/Bloque4/Protocolos.html#visualización-de-datos", + "title": "Protocolos de Comunicación", + "section": "Visualización de datos", + "text": "Visualización de datos\nEn aplicaciones reales no basta con mostrar texto.\nNecesitamos:\n\nGráficas\nPaneles de control\nVisualización clara\n\nUna librería popular para React es Recharts." + }, + { + "objectID": "pages/Bloque4/Protocolos.html#recharts", + "href": "pages/Bloque4/Protocolos.html#recharts", + "title": "Protocolos de Comunicación", + "section": "Recharts", + "text": "Recharts\nRecharts es una librería basada en:\n\nComponentes React\nSVG\nComposición declarativa\n\nInstalación:\nnpm install recharts" + }, + { + "objectID": "pages/Bloque4/Protocolos.html#integración-completa", + "href": "pages/Bloque4/Protocolos.html#integración-completa", + "title": "Protocolos de Comunicación", + "section": "Integración completa", + "text": "Integración completa\nWebSocket + Hook + Recharts\nObjetivo:\n\nVisualizar datos en tiempo real" + }, + { + "objectID": "pages/Bloque4/Protocolos.html#material", + "href": "pages/Bloque4/Protocolos.html#material", + "title": "Protocolos de Comunicación", + "section": "Material", + "text": "Material\nServidor de testing de Python: Enlace.\nInstalar websockets con pip install websockets\nTester de prueba: Enlace (click derecho guardar enlace como)\nhttps://recharts.github.io/" + }, + { + "objectID": "pages/Bloque3/t10.html", + "href": "pages/Bloque3/t10.html", + "title": "T10: Quiz app!", + "section": "", + "text": "Desarrollar una aplicación React que permita al usuario realizar un quiz de cultura general utilizando preguntas obtenidas desde una API pública.\nhttps://astonishing-douhua-44213e.netlify.app/\nLa aplicación debe mostrar preguntas, permitir seleccionar respuestas y calcular la puntuación final.", + "crumbs": [ + "Bloque III Interacción", + "Semana 10", + "T10: Quiz app!" + ] + }, + { + "objectID": "pages/Bloque3/t10.html#objetivo", + "href": "pages/Bloque3/t10.html#objetivo", + "title": "T10: Quiz app!", "section": "", -<<<<<<< HEAD "text": "Desarrollar una aplicación React que permita al usuario realizar un quiz de cultura general utilizando preguntas obtenidas desde una API pública.\nhttps://astonishing-douhua-44213e.netlify.app/\nLa aplicación debe mostrar preguntas, permitir seleccionar respuestas y calcular la puntuación final.", "crumbs": [ "Bloque III Interacción", @@ -208,231 +314,60 @@ ] }, { -<<<<<<< HEAD -======= - "objectID": "pages/Bloque3/dom.html#qué-es-el-dom", - "href": "pages/Bloque3/dom.html#qué-es-el-dom", - "title": "DOM", - "section": "¿Qué es el DOM?", - "text": "¿Qué es el DOM?\nEl DOM (Document Object Model) es la representación en memoria que el navegador crea a partir del HTML.\n\nEl HTML es estático\nEl DOM es dinámico\nJavaScript interactúa con el DOM, no con el HTML original\n\n\nEl DOM es la página web viva dentro del navegador." + "objectID": "pages/Bloque3/t9.html", + "href": "pages/Bloque3/t9.html", + "title": "T6: Romancero Gitano I", + "section": "", + "text": "El objetivo de esta tarea es aplicar los conceptos básicos de HTML, CSS y JavaScript (DOM) para crear una página web interactiva que muestre poemas del Romancero gitano de Federico García Lorca y modifique su apariencia visual en función de las diferentes metáforas.\nResultado esperado" }, { - "objectID": "pages/Bloque3/dom.html#el-dom-como-árbol", - "href": "pages/Bloque3/dom.html#el-dom-como-árbol", - "title": "DOM", - "section": "El DOM como árbol", - "text": "El DOM como árbol\nEl DOM tiene estructura de árbol (grafo):\n\nCada etiqueta HTML es un nodo\nLos nodos tienen relaciones (padre, hijos, hermanos)\n\nEjemplo:\n<body>\n <h1>Título</h1>\n <button>Click</button>\n</body>" + "objectID": "pages/Bloque3/t9.html#objetivo-de-la-tarea", + "href": "pages/Bloque3/t9.html#objetivo-de-la-tarea", + "title": "T6: Romancero Gitano I", + "section": "", + "text": "El objetivo de esta tarea es aplicar los conceptos básicos de HTML, CSS y JavaScript (DOM) para crear una página web interactiva que muestre poemas del Romancero gitano de Federico García Lorca y modifique su apariencia visual en función de las diferentes metáforas.\nResultado esperado" }, { - "objectID": "pages/Bloque3/dom.html#acceso-al-dom-desde-javascript", - "href": "pages/Bloque3/dom.html#acceso-al-dom-desde-javascript", - "title": "DOM", - "section": "Acceso al DOM desde JavaScript", - "text": "Acceso al DOM desde JavaScript\nEl navegador nos proporciona el objeto global document, que permite:\n\nBuscar elementos\nLeer su contenido\nModificar la interfaz\n\ndocument\ndocument.body" + "objectID": "pages/Bloque3/t9.html#estructura-html", + "href": "pages/Bloque3/t9.html#estructura-html", + "title": "T6: Romancero Gitano I", + "section": "Estructura HTML", + "text": "Estructura HTML\nEl documento HTML debe:\n\nEstar correctamente estructurado (<!DOCTYPE>, <html>, <head>, <body>).\nContener un contenedor principal centrado horizontalmente.\nIncluir, en este orden:\n\nUn elemento para mostrar información métrica del poema (número de estrofas y versos).\nUn elemento para el título del poema.\nUn elemento para el texto del poema, respetando los saltos de verso utilizando la etiqueta <pre>.\nUn botón que permita cambiar de poema.\n\n\nNo debe utilizarse ningún framework ni librería externa." }, { - "objectID": "pages/Bloque3/dom.html#selectores", - "href": "pages/Bloque3/dom.html#selectores", - "title": "DOM", - "section": "Selectores", - "text": "Selectores\nPor ID\ndocument.getElementById(\"count\")\ndocument.querySelector(\"#count\")\nPor etiqueta\ndocument.querySelector(\"button\")" + "objectID": "pages/Bloque3/t9.html#javascript", + "href": "pages/Bloque3/t9.html#javascript", + "title": "T6: Romancero Gitano I", + "section": "JavaScript", + "text": "JavaScript\n\nDatos\nEn JavaScript se debe definir un array de objetos, donde cada objeto represente un poema y tenga al menos:\n\nUn título.\nEl texto del poema como una cadena de texto, usando saltos de línea para los versos.\n\nEl cambio de poema debe hacerse recorriendo este array de forma cíclica (cola circular) mediante el botón.\nDescargar poemas (click derecho guardar enlace como)\n\n\nManipulación del DOM\nEl comportamiento de la página debe implementarse exclusivamente con DOM nativo, cumpliendo lo siguiente:\n\nTodas las funciones deben declararse como arrow functions.\nAl pulsar el botón:\n\nSe actualiza el título.\nSe actualiza el texto del poema.\nSe modifica el formato del fondo.\nSe recalculan y muestran:\n\nNúmero de versos.\nNúmero de estrofas (bloques separados por líneas en blanco).\n\n\n\nEl cálculo debe realizarse a partir del texto del poema, no con valores predefinidos." }, { - "objectID": "pages/Bloque3/dom.html#leer-y-modificar-elementos", - "href": "pages/Bloque3/dom.html#leer-y-modificar-elementos", - "title": "DOM", - "section": "Leer y modificar elementos", - "text": "Leer y modificar elementos\nelement.textContent\nelement.textContent = \"Nuevo texto\"\nelement.classList.add(\"active\")\nelement.classList.remove(\"active\")" + "objectID": "pages/Bloque3/t9.html#css", + "href": "pages/Bloque3/t9.html#css", + "title": "T6: Romancero Gitano I", + "section": "CSS", + "text": "CSS\nLorca utiliza las metáforas y la alegoría como elemento principal sobre el que construye sus poemas, al analizar profundamente descubrimos que siempre recurre a las mismas metáforas.\nEl color no se utiliza aquí solo como elemento decorativo, sino como un recurso semántico: cada color representa un símbolo literario presente en el poema.\nDesde el punto de vista técnico: - Los colores de fondo y de texto deben definirse en clases CSS. - JavaScript no decide colores, únicamente decide qué clase aplicar según el contenido del poema.\n\nPalabras clave y colores asociados\nSe debe detectar el texto del poema las siguientes palabras clave y aplicar el color de fondo correspondiente:\n\nguardia civil → fondo verde militar\nsangre → fondo granate oscuro\ncaballo → fondo color tierra\ncuchillo / navaja → fondo plateado\n\nEl color del texto debe elegirse siempre de forma que exista contraste suficiente con el fondo y se garantice la legibilidad.\nSi el poema contiene varias palabras clave, solo debe aplicarse un único estilo, siguiendo un orden de prioridad definido previamente.\nSi el poema no contiene ninguna de estas palabras, se aplicará un estilo por defecto con colores aleatorios." }, { - "objectID": "pages/Bloque3/dom.html#eventos", - "href": "pages/Bloque3/dom.html#eventos", - "title": "DOM", - "section": "Eventos", - "text": "Eventos\nEl DOM permite reaccionar a acciones del usuario mediante eventos.\nbutton.addEventListener(\"click\", () => {\n // código a ejecutar\n});\nFlujo típico:\nUsuario → Evento → JavaScript → DOM actualizado" - }, - { - "objectID": "pages/Bloque3/dom.html#ejemplo-contador-con-botón", - "href": "pages/Bloque3/dom.html#ejemplo-contador-con-botón", - "title": "DOM", - "section": "Ejemplo: contador con botón", - "text": "Ejemplo: contador con botón\nHTML\n<h1 id=\"count\">0</h1>\n<button id=\"btn\">Sumar 1</button>\nJavaScript\nlet count = 0;\n\nconst countEl = document.querySelector(\"#count\");\nconst btn = document.querySelector(\"#btn\");\n\nbtn.addEventListener(\"click\", () => {\n count++;\n countEl.textContent = count;\n});" - }, - { - "objectID": "pages/Bloque3/dom.html#más-ejemplos", - "href": "pages/Bloque3/dom.html#más-ejemplos", - "title": "DOM", - "section": "Más ejemplos", - "text": "Más ejemplos\nEj1: Show input\nEj2: Par e Impar\nEj3: Toggle\nEj4: Creación dinámica\nEj5: Validación básica" - }, - { ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) - "objectID": "pages/Bloque3/MAcss2.html", - "href": "pages/Bloque3/MAcss2.html", - "title": "MA: CSS Flex y CSS", - "section": "", - "text": "Además de las propiedades estudiadas de CSS también hay algunas críticas que debido a la falta de tiempo no se han podido explicar como es el caso de display a continuación estos dos vídeos profundizan en los modos flex y grid formas diferentes de diseñar la estructura de la página", -<<<<<<< HEAD -======= - "text": "El DOM (Document Object Model) es la representación en memoria que el navegador crea a partir del HTML.\n\nEl HTML es estático\nEl DOM es dinámico\nJavaScript interactúa con el DOM, no con el HTML original\n\n\nEl DOM es la página web viva dentro del navegador.", ->>>>>>> e3c3469 (feat: varios) - "crumbs": [ - "Bloque III Interacción", - "Semana 6", - "DOM" - ] - }, - { - "objectID": "pages/Bloque3/DOM.html#qué-es-el-dom", - "href": "pages/Bloque3/DOM.html#qué-es-el-dom", - "title": "DOM", - "section": "", - "text": "El DOM (Document Object Model) es la representación en memoria que el navegador crea a partir del HTML.\n\nEl HTML es estático\nEl DOM es dinámico\nJavaScript interactúa con el DOM, no con el HTML original\n\n\nEl DOM es la página web viva dentro del navegador.", - "crumbs": [ - "Bloque III Interacción", - "Semana 6", - "DOM" - ] - }, - { - "objectID": "pages/Bloque3/DOM.html#el-dom-como-árbol", - "href": "pages/Bloque3/DOM.html#el-dom-como-árbol", - "title": "DOM", - "section": "El DOM como árbol", - "text": "El DOM como árbol\nEl DOM tiene estructura de árbol (grafo):\n\nCada etiqueta HTML es un nodo\nLos nodos tienen relaciones (padre, hijos, hermanos)\n\nEjemplo:\n<body>\n <h1>Título</h1>\n <button>Click</button>\n</body>\nRepresentación conceptual:\ndocument\n └─ body\n ├─ h1\n └─ button", - "crumbs": [ - "Bloque III Interacción", - "Semana 6", - "DOM" - ] - }, - { - "objectID": "pages/Bloque3/DOM.html#acceso-al-dom-desde-javascript", - "href": "pages/Bloque3/DOM.html#acceso-al-dom-desde-javascript", - "title": "DOM", - "section": "Acceso al DOM desde JavaScript", - "text": "Acceso al DOM desde JavaScript\nEl navegador nos proporciona el objeto global document, que permite:\n\nBuscar elementos\nLeer su contenido\nModificar la interfaz\n\ndocument\ndocument.body", - "crumbs": [ - "Bloque III Interacción", - "Semana 6", - "DOM" - ] - }, - { - "objectID": "pages/Bloque3/DOM.html#selectores", - "href": "pages/Bloque3/DOM.html#selectores", - "title": "DOM", - "section": "Selectores", - "text": "Selectores\n\nPor ID\ndocument.getElementById(\"count\")\ndocument.querySelector(\"#count\")\n\n\nPor etiqueta\ndocument.querySelector(\"button\")\n\n\nPor clase\ndocument.querySelector(\".item\")\ndocument.querySelectorAll(\".item\")\nNotas: - querySelector devuelve el primer elemento - querySelectorAll devuelve una lista - Usan la misma sintaxis que CSS", - "crumbs": [ - "Bloque III Interacción", - "Semana 6", - "DOM" - ] - }, - { - "objectID": "pages/Bloque3/DOM.html#leer-y-modificar-elementos", - "href": "pages/Bloque3/DOM.html#leer-y-modificar-elementos", - "title": "DOM", - "section": "Leer y modificar elementos", - "text": "Leer y modificar elementos\nelement.textContent\nelement.textContent = \"Nuevo texto\"\nelement.classList.add(\"active\")\nelement.classList.remove(\"active\")", - "crumbs": [ - "Bloque III Interacción", - "Semana 6", - "DOM" - ] - }, - { - "objectID": "pages/Bloque3/DOM.html#eventos", - "href": "pages/Bloque3/DOM.html#eventos", - "title": "DOM", - "section": "Eventos", - "text": "Eventos\nEl DOM permite reaccionar a acciones del usuario mediante eventos.\nbutton.addEventListener(\"click\", () => {\n // código a ejecutar\n});\nFlujo típico:\nUsuario → Evento → JavaScript → DOM actualizado", - "crumbs": [ - "Bloque III Interacción", - "Semana 6", - "DOM" - ] - }, - { - "objectID": "pages/Bloque3/DOM.html#ejemplo-contador-con-botón", - "href": "pages/Bloque3/DOM.html#ejemplo-contador-con-botón", - "title": "DOM", - "section": "Ejemplo: contador con botón", - "text": "Ejemplo: contador con botón\n\nHTML\n<h1 id=\"count\">0</h1>\n<button id=\"btn\">Sumar 1</button>\n\n\nJavaScript\nlet count = 0;\n\nconst countEl = document.querySelector(\"#count\");\nconst btn = document.querySelector(\"#btn\");\n\nbtn.addEventListener(\"click\", () => {\n count++;\n countEl.textContent = count;\n});", - "crumbs": [ - "Bloque III Interacción", - "Semana 6", - "DOM" - ] - }, - { - "objectID": "pages/Bloque3/DOM.html#más-ejemplos", - "href": "pages/Bloque3/DOM.html#más-ejemplos", - "title": "DOM", - "section": "Más ejemplos", - "text": "Más ejemplos\nEj1: Show input\nEj2: Par e Impar\nEj3: Toggle\nEj4: Creación dinámica\nEj5: Validación básica", - "crumbs": [ - "Bloque III Interacción", - "Semana 6", - "DOM" - ] - }, - { - "objectID": "pages/Bloque3/dom.html#qué-es-el-dom", - "href": "pages/Bloque3/dom.html#qué-es-el-dom", - "title": "DOM", - "section": "¿Qué es el DOM?", - "text": "¿Qué es el DOM?\nEl DOM (Document Object Model) es la representación en memoria que el navegador crea a partir del HTML.\n\nEl HTML es estático\nEl DOM es dinámico\nJavaScript interactúa con el DOM, no con el HTML original\n\n\nEl DOM es la página web viva dentro del navegador." - }, - { - "objectID": "pages/Bloque3/dom.html#el-dom-como-árbol", - "href": "pages/Bloque3/dom.html#el-dom-como-árbol", - "title": "DOM", - "section": "El DOM como árbol", - "text": "El DOM como árbol\nEl DOM tiene estructura de árbol (grafo):\n\nCada etiqueta HTML es un nodo\nLos nodos tienen relaciones (padre, hijos, hermanos)\n\nEjemplo:\n<body>\n <h1>Título</h1>\n <button>Click</button>\n</body>" - }, - { - "objectID": "pages/Bloque3/dom.html#acceso-al-dom-desde-javascript", - "href": "pages/Bloque3/dom.html#acceso-al-dom-desde-javascript", - "title": "DOM", - "section": "Acceso al DOM desde JavaScript", - "text": "Acceso al DOM desde JavaScript\nEl navegador nos proporciona el objeto global document, que permite:\n\nBuscar elementos\nLeer su contenido\nModificar la interfaz\n\ndocument\ndocument.body" - }, - { - "objectID": "pages/Bloque3/dom.html#selectores", - "href": "pages/Bloque3/dom.html#selectores", - "title": "DOM", - "section": "Selectores", - "text": "Selectores\nPor ID\ndocument.getElementById(\"count\")\ndocument.querySelector(\"#count\")\nPor etiqueta\ndocument.querySelector(\"button\")" - }, - { - "objectID": "pages/Bloque3/dom.html#leer-y-modificar-elementos", - "href": "pages/Bloque3/dom.html#leer-y-modificar-elementos", - "title": "DOM", - "section": "Leer y modificar elementos", - "text": "Leer y modificar elementos\nelement.textContent\nelement.textContent = \"Nuevo texto\"\nelement.classList.add(\"active\")\nelement.classList.remove(\"active\")" - }, - { - "objectID": "pages/Bloque3/dom.html#eventos", - "href": "pages/Bloque3/dom.html#eventos", - "title": "DOM", - "section": "Eventos", - "text": "Eventos\nEl DOM permite reaccionar a acciones del usuario mediante eventos.\nbutton.addEventListener(\"click\", () => {\n // código a ejecutar\n});\nFlujo típico:\nUsuario → Evento → JavaScript → DOM actualizado" + "objectID": "pages/Bloque3/t9.html#extra-opcional-símbolo-aurora", + "href": "pages/Bloque3/t9.html#extra-opcional-símbolo-aurora", + "title": "T6: Romancero Gitano I", + "section": "Extra opcional: símbolo aurora 🌈", + "text": "Extra opcional: símbolo aurora 🌈\nDe forma voluntaria, se puede añadir el símbolo aurora, que tendrá prioridad absoluta sobre todos los demás.\nCuando el poema contenga la palabra aurora, el fondo debe mostrar un degradado animado.\n\nCSS necesario para el extra\n.aurora {\n background: linear-gradient(120deg,\n #ff005d,\n #ff9f1c,\n #ffee32,\n #3cff00,\n #00e5ff,\n #7a00ff,\n #ff00c8\n );\n background-size: 600% 600%;\n animation: aurora 10s linear infinite;\n color: black;\n}\n\n@keyframes aurora {\n 0% { background-position: 0% 50%; }\n 50% { background-position: 100% 50%; }\n 100% { background-position: 0% 50%; }\n}" }, { - "objectID": "pages/Bloque3/dom.html#ejemplo-contador-con-botón", - "href": "pages/Bloque3/dom.html#ejemplo-contador-con-botón", - "title": "DOM", - "section": "Ejemplo: contador con botón", - "text": "Ejemplo: contador con botón\nHTML\n<h1 id=\"count\">0</h1>\n<button id=\"btn\">Sumar 1</button>\nJavaScript\nlet count = 0;\n\nconst countEl = document.querySelector(\"#count\");\nconst btn = document.querySelector(\"#btn\");\n\nbtn.addEventListener(\"click\", () => {\n count++;\n countEl.textContent = count;\n});" + "objectID": "pages/Bloque3/t9.html#consejos", + "href": "pages/Bloque3/t9.html#consejos", + "title": "T6: Romancero Gitano I", + "section": "Consejos", + "text": "Consejos\n\nCentrado de la página (layout clásico)\nToda la página debe estar centrada horizontalmente.\nEl centrado se consigue mediante:\n\nUn contenedor con un ancho máximo definido.\nMárgenes automáticos a izquierda y derecha.\n\nDesde el punto de vista conceptual:\n\nmargin: 0 auto no centra texto, centra bloques.\nEl navegador reparte automáticamente el espacio sobrante a ambos lados del contenedor.\n\n\n\nAnimación del cambio de color\nPara evitar que los cambios de color sean bruscos, se utiliza la propiedad transition en CSS.\nEsta propiedad indica al navegador que los cambios en determinadas propiedades visuales deben hacerse de forma progresiva.\nEjemplo conceptual:\nbody {\n transition: background-color 0.6s, color 0.6s;\n}\nGracias a esta transición: - Cuando JavaScript cambia una clase o un color, - el navegador interpola automáticamente entre el color anterior y el nuevo.\nNo es necesario programar animaciones en JavaScript: CSS se encarga de todo el efecto visual.\n\n\nBúsqueda de una palabra\nPara buscar si determinada palabra determinada se debe utilizar la función includes de la clase string\n\ntexto = \"hoy nieva\";\n\ntexto.includes(\"nieva\"); //TRUE\ntexto.includes(\"llueve\"); //FALSE\n\n\nGeneración del color aleatorio\nPara la generacióndel color aleatorio de puede utilizar la siguiente función\nconst randomColor = () => '#' + Math.floor(Math.random() * 16777215).toString(16);\n\n\nConteo versos y estrofas\nEl recuento de versos y estrofas debe hacerse a partir del texto del poema, no con valores escritos a mano. A continuación se dan algunas pistas técnicas para abordar este problema.\n\nConteo de versos\nUn verso puede considerarse, a efectos prácticos en esta tarea, como una línea de texto no vacía.\nUna estrategia habitual consiste en: - Separar el texto por saltos de línea. - Eliminar las líneas vacías o formadas solo por espacios. - Contar cuántas líneas válidas quedan.\nEjemplo orientativo:\nconst versos = texto\n .split('\\n')\n .filter(linea => linea.trim() !== '')\n .length;\nEste enfoque es suficiente para la mayoría de los poemas del Romancero gitano y evita errores frecuentes.\n\n\nConteo de estrofas\nUna estrofa puede entenderse como un bloque de versos separado de otros bloques por una línea en blanco.\nUna posible estrategia es: - Separar el texto usando dos (o más) saltos de línea consecutivos. - Eliminar bloques vacíos. - Contar los bloques resultantes.\nEjemplo orientativo:\nconst estrofas = texto\n .split(/\\n\\s*\\n/)\n .filter(bloque => bloque.trim() !== '')\n .length;\nEntrega: un único archivo HTML funcional." }, { - "objectID": "pages/Bloque3/dom.html#más-ejemplos", - "href": "pages/Bloque3/dom.html#más-ejemplos", - "title": "DOM", - "section": "Más ejemplos", - "text": "Más ejemplos\nEj1: Show input\nEj2: Par e Impar\nEj3: Toggle\nEj4: Creación dinámica\nEj5: Validación básica" + "objectID": "pages/Bloque3/t9.html#recomendaciones-para-afrontar-la-tarea", + "href": "pages/Bloque3/t9.html#recomendaciones-para-afrontar-la-tarea", + "title": "T6: Romancero Gitano I", + "section": "Recomendaciones para afrontar la tarea", + "text": "Recomendaciones para afrontar la tarea\nAl enfrentarnos a un proyecto de cierta complejitud, el como afrontarlo resulta de cricial para el desarollo del mismo por ello a a continuación dejo un esquema de implementación modular. Sería mejor no utilizarlo, pero puede ser un buen punto de partida si no se sabe como empezar la app, no es el único proceso de implementación válido ni es más correcto que cualquier otro\n\nEstructura de HTML básica contadores de versos y estrofas placeholders de poema y título y botón\nFormateo CSS parte estática: centrado, tamaño (todo lo que no sea color)\nDefinir clases con colores predeterminados\nLógica interna con JS\n\n\nBuscar palabra\nContar estrofas\nConteo versos\nSelección de clase\nGenerar color aleatorio si procede\n\n\nIntegración con DOM" }, { "objectID": "pages/Bloque3/MARef.html", @@ -543,169 +478,35 @@ ] }, { -<<<<<<< HEAD - "objectID": "pages/Bloque3/intermedio.html#useeffect", - "href": "pages/Bloque3/intermedio.html#useeffect", - "title": "React III", - "section": "useEffect", - "text": "useEffect\n\nuseEffect es un Hook que permite ejecutar efectos secundarios dentro de un componente funcional.\nUn efecto secundario es cualquier operación que no sea simplemente devolver JSX (por ejemplo: peticiones a un servidor, temporizadores, suscripciones).\nSe ejecuta después de que el componente se renderiza en el DOM." - }, - { - "objectID": "pages/Bloque3/intermedio.html#fetch", - "href": "pages/Bloque3/intermedio.html#fetch", - "title": "React III", - "section": "fetch()", - "text": "fetch()\nHasta ahora nuestros componentes eran completamente locales.\nSin embargo, en aplicaciones reales necesitamos:\n\nDescargar datos\n\nConectarnos a servidores\n\nTrabajar con APIs externas\n\nPara ello utilizamos fetch()." - }, - { - "objectID": "pages/Bloque3/intermedio.html#async-y-await", - "href": "pages/Bloque3/intermedio.html#async-y-await", - "title": "React III", - "section": "async y await", - "text": "async y await\nHasta ahora hemos utilizado fetch() con encadenamiento.\nExiste una forma más clara y estructurada de escribir código asíncrono:\n\nasync\nawait\n\nSu objetivo es hacer que el código sea más legible." - }, - { - "objectID": "pages/Bloque3/intermedio.html#ejemplo-con-la-pokeapi", - "href": "pages/Bloque3/intermedio.html#ejemplo-con-la-pokeapi", - "title": "React III", - "section": "Ejemplo con la Pokeapi", - "text": "Ejemplo con la Pokeapi\nPoke-API\nDownload Code" - }, - { - "objectID": "pages/Bloque2/intensivo.html", - "href": "pages/Bloque2/intensivo.html", - "title": "Repaso Intensivo JS", -======= + "objectID": "pages/Bloque3/ps.html", + "href": "pages/Bloque3/ps.html", + "title": "Proyecto Subsistema", + "section": "", + "text": "Desarrollar una pequeña aplicación web para gestionar una lista de tareas utilizando HTML, CSS y JavaScript, trabajando directamente con el DOM.\nbase", "crumbs": [ "Bloque III Interacción", - "Semana 6", - "MA: CSS Flex y CSS" + "Semana 7", + "Proyecto Subsistema" ] }, { - "objectID": "pages/Bloque3/ReactIV.html", - "href": "pages/Bloque3/ReactIV.html", - "title": "React IV", + "objectID": "pages/Bloque3/ps.html#objetivo", + "href": "pages/Bloque3/ps.html#objetivo", + "title": "Proyecto Subsistema", "section": "", - "text": "Contenido", + "text": "Desarrollar una pequeña aplicación web para gestionar una lista de tareas utilizando HTML, CSS y JavaScript, trabajando directamente con el DOM.\nbase", "crumbs": [ "Bloque III Interacción", - "Semana 10", - "React IV" + "Semana 7", + "Proyecto Subsistema" ] }, { - "objectID": "pages/Bloque3/ReactIV.html#ecosistema-node", - "href": "pages/Bloque3/ReactIV.html#ecosistema-node", - "title": "React IV", - "section": "Ecosistema Node", - "text": "Ecosistema Node\nHasta ahora hemos trabajado únicamente con React y JavaScript.\nSin embargo, en el desarrollo real de aplicaciones es muy común utilizar librerías externas, para ello podemos recurrir Node.JS.\nReact está diseñado para funcionar dentro de un ecosistema de paquetes.\nEjemplos habituales:\n\nReact Router → navegación\nAxios → peticiones HTTP\nChart.js → gráficas\nZustand / Redux → gestión de estado\nLibrerías de UI: Tailwind, Bootstrap, Booswatch", - "crumbs": [ - "Bloque III Interacción", - "Semana 10", - "React IV" - ] - }, - { - "objectID": "pages/Bloque3/ReactIV.html#npm", - "href": "pages/Bloque3/ReactIV.html#npm", - "title": "React IV", - "section": "npm", - "text": "npm\nnpm (Node Package Manager) es el sistema que permite instalar y gestionar librerías de JavaScript. Como su nombre indica, está desarrollado por Node.JS\nCada proyecto tiene un archivo:\npackage.json\nEn este archivo se registran las dependencias del proyecto.\nEjemplo:\n{\n \"dependencies\": {\n \"react\": \"^18.0.0\",\n \"react-router-dom\": \"^6.0.0\"\n }\n}\n\nInstalación de paquetes\nLas librerías se instalan desde la terminal.\nnpm install react-router-dom\nEsto hace tres cosas:\n\nDescarga el paquete\nLo guarda en node_modules (capreta donde se guardan las librerías externas)\nAñade la dependencia en package.json", - "crumbs": [ - "Bloque III Interacción", - "Semana 10", - "React IV" - ] - }, - { - "objectID": "pages/Bloque3/ReactIV.html#react-router", - "href": "pages/Bloque3/ReactIV.html#react-router", - "title": "React IV", - "section": "React Router", - "text": "React Router\nEn una aplicación web tradicional cada enlace carga una página nueva.\nEn React normalmente trabajamos con Single Page Applications (SPA).\nEn una SPA:\n\nel navegador no recarga la página\nReact cambia los componentes que se muestran\n\nPara gestionar esto utilizamos React Router.\nRealmente es la misma página, solo que el usuario lo percibe como distintas páginas\nEl usuario percibirá que el sitio web está divido en diferentes subpáginas\n\n/\npokemons\npokemons/pikachu\n\n\nComponentes principales de React Router\nLos elementos fundamentales son:\n\nBrowserRouter\nRoutes\nRoute\nLink\n\nEjemplo básico:\nimport { BrowserRouter, Routes, Route } from \"react-router-dom\";\n\nfunction App() {\n return (\n <BrowserRouter>\n\n <Routes>\n\n <Route path=\"/\" element={<Home />} />\n\n <Route path=\"/pokemons\" element={<PokemonList />} />\n\n </Routes>\n\n </BrowserRouter>\n );\n}\nElementos del código\n\nBrowserRouter\nEs el componente que activa el sistema de navegación de React Router. Utiliza la API de historial del navegador para cambiar la URL sin recargar la página.\nRoutes\nEs el contenedor donde se definen todas las rutas de la aplicación. React Router examina las rutas dentro de este componente para decidir qué componente mostrar.\nRoute\nDefine una ruta concreta de la aplicación.\n\n\npath\nIndica la URL que activa la ruta.\nEjemplo: / corresponde a la página principal.\nelement\nEs el componente de React que se renderiza cuando la URL coincide con el path.\n<Home />\nComponente que se muestra cuando el usuario accede a la ruta /.\n<PokemonList />\nComponente que se muestra cuando el usuario accede a la ruta /pokemons.\n\n\n\nNavegación\nPara navegar entre páginas utilizamos el componente Link.\nimport { Link } from \"react-router-dom\";\n\n<Link to=\"/\">Inicio</Link>\n\n<Link to=\"/pokemon\">Pokemon</Link>\nA diferencia de <a>:\n\nno recarga la página\nReact cambia el componente visible\n\n\n\nParámetros dinámicos\nPodemos crear rutas dinámicas.\nEjemplo:\n/pokemon/25\n/pokemon/7\nDefinición de la ruta:\n<Route path=\"/pokemon/:id\" element={<PokemonDetail />} />\n\nuseParams\nPara acceder al parámetro utilizamos el hook useParams de React Router.\nimport { useParams } from \"react-router-dom\";\n\nfunction PokemonDetail() {\n\n const { id } = useParams();\n\n return <p>Pokemon {id}</p>;\n\n}\nEste parámetro puede utilizarse para realizar peticiones a una API.", - "crumbs": [ - "Bloque III Interacción", - "Semana 10", - "React IV" - ] - }, - { - "objectID": "pages/Bloque3/ReactIV.html#context-api", - "href": "pages/Bloque3/ReactIV.html#context-api", - "title": "React IV", - "section": "Context API", - "text": "Context API\nEn aplicaciones grandes aparece un problema frecuente.\nMuchos componentes necesitan acceder a la misma información.\nPor ejemplo:\n\nusuario\ntema visual\nidioma\nconfiguración\n\nSi pasamos la información mediante props, los datos deben atravesar muchos componentes.\nRepresentación conceptual:\nApp\n └ Layout\n └ Page\n └ Component\nSi todos necesitan user, debemos pasar la prop continuamente.\nEste problema se conoce como prop drilling.\n\nContext\nReact proporciona una solución llamada Context.\nContext permite compartir información entre múltiples componentes sin pasar props manualmente.\nEl proceso tiene tres pasos:\n\nCrear el contexto\nProveer el contexto\nConsumir el contexto\n\n\nCrear un contexto\nimport { createContext } from \"react\";\n\nconst UserContext = createContext();\nEsto crea un contenedor que puede almacenar información compartida.\n\n\nProvider\nEl Provider permite que los componentes hijos accedan al contexto.\n<UserContext.Provider value={user}>\n\n <App />\n\n</UserContext.Provider>\nTodos los componentes dentro del Provider pueden acceder al valor.\n\n\nConsumir el contexto\nPara acceder al contexto utilizamos el hook useContext.\nimport { useContext } from \"react\";\n\nconst user = useContext(UserContext);\nEl componente obtiene directamente el valor almacenado en el contexto.\n\n\nEjemplo completo\nimport { createContext, useContext } from \"react\";\n\nconst UserContext = createContext();\n\nfunction App() {\n\n const user = { name: \"Javier\" };\n\n return (\n\n <UserContext.Provider value={user}>\n\n <Profile />\n\n </UserContext.Provider>\n\n );\n}\n\nfunction Profile() {\n\n const user = useContext(UserContext);\n\n return <h1>{user.name}</h1>;\n\n}", - "crumbs": [ - "Bloque III Interacción", - "Semana 10", - "React IV" - ] - }, - { - "objectID": "pages/Bloque3/presentacion-reactIV.html#ecosistema-node", - "href": "pages/Bloque3/presentacion-reactIV.html#ecosistema-node", - "title": "React IV", - "section": "Ecosistema Node", - "text": "Ecosistema Node\nHasta ahora hemos trabajado únicamente con React y JavaScript.\nSin embargo, en el desarrollo real de aplicaciones es muy común utilizar librerías externas, para ello podemos recurrir Node.JS.\nReact está diseñado para funcionar dentro de un ecosistema de paquetes." - }, - { - "objectID": "pages/Bloque3/presentacion-reactIV.html#npm", - "href": "pages/Bloque3/presentacion-reactIV.html#npm", - "title": "React IV", - "section": "npm", - "text": "npm\nnpm (Node Package Manager) es el sistema que permite instalar y gestionar librerías de JavaScript. Como su nombre indica, está desarrollado por Node.JS\nCada proyecto tiene un archivo:\npackage.json\nEn este archivo se registran las dependencias del proyecto." - }, - { - "objectID": "pages/Bloque3/presentacion-reactIV.html#react-router", - "href": "pages/Bloque3/presentacion-reactIV.html#react-router", - "title": "React IV", - "section": "React Router", - "text": "React Router\nEn una aplicación web tradicional cada enlace carga una página nueva.\nEn React normalmente trabajamos con Single Page Applications (SPA).\nEn una SPA:\n\nel navegador no recarga la página\nReact cambia los componentes que se muestran\n\nPara gestionar esto utilizamos React Router.\nRealmente es la misma página, solo que el usuario lo percibe como distintas páginas" - }, - { - "objectID": "pages/Bloque3/presentacion-reactIV.html#context-api", - "href": "pages/Bloque3/presentacion-reactIV.html#context-api", - "title": "React IV", - "section": "Context API", - "text": "Context API\nEn aplicaciones grandes aparece un problema frecuente.\nMuchos componentes necesitan acceder a la misma información.\nPor ejemplo:\n\nusuario\ntema visual\nidioma\nconfiguración\n\nSi pasamos la información mediante props, los datos deben atravesar muchos componentes." - }, - { - "objectID": "pages/Bloque3/ps.html", - "href": "pages/Bloque3/ps.html", - "title": "Proyecto Subsistema", ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) -======= - "objectID": "pages/Bloque3/ps.html", - "href": "pages/Bloque3/ps.html", - "title": "Proyecto Subsistema", ->>>>>>> e3c3469 (feat: varios) - "section": "", - "text": "Desarrollar una pequeña aplicación web para gestionar una lista de tareas utilizando HTML, CSS y JavaScript, trabajando directamente con el DOM.\nbase", - "crumbs": [ - "Bloque III Interacción", - "Semana 7", - "Proyecto Subsistema" - ] - }, - { - "objectID": "pages/Bloque3/ps.html#objetivo", - "href": "pages/Bloque3/ps.html#objetivo", - "title": "Proyecto Subsistema", - "section": "", - "text": "Desarrollar una pequeña aplicación web para gestionar una lista de tareas utilizando HTML, CSS y JavaScript, trabajando directamente con el DOM.\nbase", - "crumbs": [ - "Bloque III Interacción", - "Semana 7", - "Proyecto Subsistema" - ] - }, - { - "objectID": "pages/Bloque3/ps.html#qué-se-quiere-hacer", - "href": "pages/Bloque3/ps.html#qué-se-quiere-hacer", - "title": "Proyecto Subsistema", - "section": "Qué se quiere hacer", - "text": "Qué se quiere hacer\n\nCrear una página web que permita añadir tareas.\nCada tarea debe tener:\n\nUn texto descriptivo.\nUn responsable (Software, Hardware, Partners, Otros).\n\nLas tareas deben mostrarse en una lista.\nAl hacer click sobre una tarea, esta debe eliminarse.\nLa página no debe recargarse al añadir o eliminar tareas.", + "objectID": "pages/Bloque3/ps.html#qué-se-quiere-hacer", + "href": "pages/Bloque3/ps.html#qué-se-quiere-hacer", + "title": "Proyecto Subsistema", + "section": "Qué se quiere hacer", + "text": "Qué se quiere hacer\n\nCrear una página web que permita añadir tareas.\nCada tarea debe tener:\n\nUn texto descriptivo.\nUn responsable (Software, Hardware, Partners, Otros).\n\nLas tareas deben mostrarse en una lista.\nAl hacer click sobre una tarea, esta debe eliminarse.\nLa página no debe recargarse al añadir o eliminar tareas.", "crumbs": [ "Bloque III Interacción", "Semana 7", @@ -1052,12 +853,7 @@ "href": "pages/Bloque1/css.html", "title": "CSS", "section": "", -<<<<<<< HEAD - "text": "Por el momento hemos visto dos hooks useState y useEffect, pero recordemos que en React existen más tipos de hooks que por ser menos útiles y debido a la falta de tiempo no son explicados, no obstante si hay alguno digno de mención es useRef o las referencias.\nUna ref es una referencia mutable que permite acceder directamente a un elemento del DOM o almacenar un valor persistente sin provocar un re-render del componente. El siguiente video ilustra su uso. Es un poco largo pero sirve para repasar los otros hooks.\n\n\nCon las ref podemos introducir los uncontrolled fields que nos permiten trabajar de otra forma con los formularios.\n\nSe prefiere la segunda implementación (la que no utiliza referencias) sobre la primera.", -<<<<<<< HEAD -======= "text": "Contenido", ->>>>>>> e3c3469 (feat: varios) "crumbs": [ "Bloque I Introducción", "Semana 2", @@ -1258,81 +1054,6 @@ "section": "HTML5", "text": "HTML5\n\n¿Qué es HTML?\nHTML es un lenguaje de marcas que permite definir la estructura y el contenido de un documento para la web. Mediante un conjunto de etiquetas, el desarrollador describe elementos como textos, imágenes, enlaces o formularios, indicando su función dentro de la página. Es la base sobre la que posteriormente se aplican estilos (CSS) y comportamientos dinámicos (JavaScript).\n\n\nEstructura básica\n<!DOCTYPE html>\n<html>\n<head>\n\n</head>\n\n<body>\n\n <p>Clase 1 del TC</p>\n \n</body>\n</html>\nEn el código aparecen varias etiquetas HTML, y cada una tiene una etiqueta de apertura y una etiqueta de cierre (por ejemplo, <p> y </p>).\nEstas etiquetas delimitan y organizan el contenido del documento:\n- <head> agrupa la información del documento,\n- <body> contiene lo que verá el usuario,\n- <p> define un párrafo.\nLa función de las etiquetas es indicar al navegador qué es cada parte del documento y cómo debe interpretarla.\n\n\nHead\nSe corresponde con la cabecera de la pestaña, principalmente solo se modifica el logotipo y el contenido de esta.\nTambién se utiliza para almacenar los metadatos de la web, así como scripts de JavaScript y hojas de estilo de CSS.\n<!DOCTYPE html>\n<html>\n<head>\n\n <title> Web del TC </title>\n\n</head>\n\n<body>\n\n <p>Clase 1 del TC</p>\n \n</body>\n</html>\n\n\nBody\nSe corresponde con todo el contenido de la página dentro podemos utilizar multitud de etiquetas, acontinuación expondremos las más importantes.\nHasta el momento hemos trabajado únicamente con etiquietas invividuales, pero pronto veremos como podemos juntar varias etiquetas simples para obtener estructuras más complejas como tablas o formularios.\nTodo el código mostrado a partir de ahora se asume que esta correctamente ubicado dentro de una etiqueta body\n\n<p> Paragraph\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n<p>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n</p>\n\n\n<br> Break Line\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation\nullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n<p>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation\n <br></br>\n ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n</p>\n\n\n<ul> Unordered list\n\nFrancisco de Quevedo\nLope de Vega\nGóngora\n\n<ul>\n <li>Francisco de Quevedo</li>\n <li>Lope de Vega</li>\n <li>Góngora</li>\n</ul>\n\n\n<ol> Ordered list\n\nRecepción\nDiscurso del Rector\nDespedida\n\n<ol>\n <li>Recepción </li>\n <li>Discruso del Rector</li>\n <li>Despedida</li>\n</ol>\n\n\n<h#> Headings\n<h1>Título 1</h1>\n<h2>Título 2</h2>\n<h3>Título 3</h3>\n<h4>Título 4</h4>\n<h5>Título 5</h5>\n<h6>Título 6</h6>\n\n\n<div> Divisor y <span> Span\nEtiquetas contenedores, utilizadas para agrupar otras etiquetas, no presentan ningún efecto visible intrínsico, salvo que tras utilizar <div> se produce un salto de línea. Por lo que utilizaremos <div> para agrupar grandes grupos de etiquetas y <span> para fragementos más pequeños\n\n<div>\n<div>\n <h2>Capítulo 2 </h2>\n <p>\n Mio Cid Ruy Díaz por Burgos entró,<br></br>\n en su conpaña sessaenta pendones.<br></br>\n Exiénlo ver mugieres e varones,<br></br>\n burgeses e burgesas por las finiestras son,<br></br>\n plorando de los ojos, tanto avién el dolor,<br></br>\n de las sus bocas todos dizían una razón:<br></br>\n — ¡Dios, qué buen vassallo, si oviesse buen señor!—\n </p>\n</div>\n\n\n\n<strong> Strong, <i> Italic\nNegrita Itálica Negrita e Itálica\n<strong>Negrita</strong>\n<i>Itálica</i>\n<strong><i> Negrita e Itálica</i></strong>\n\n\nComentario\nEtiqueta invisible\n<p>Etiqueta invisible</p>\n\n<!-- Soy un comenario -->\n\n\nTablas\n\n\n\n\nProducto\n\n\nCantidad\n\n\nPrecio (€)\n\n\n\n\n\n\nManzanas\n\n\n10\n\n\n3,50\n\n\n\n\nNaranjas\n\n\n8\n\n\n4,00\n\n\n\n\nPlátanos\n\n\n6\n\n\n2,80\n\n\n\n\n\n\nTotal\n\n\n24\n\n\n10,30\n\n\n\n\n<table>\n <thead>\n <tr>\n <th>Producto</th>\n <th>Cantidad</th>\n <th>Precio (€)</th>\n </tr>\n </thead>\n\n <tbody>\n <tr>\n <td>Manzanas</td>\n <td>10</td>\n <td>3,50</td>\n </tr>\n <tr>\n <td>Naranjas</td>\n <td>8</td>\n <td>4,00</td>\n </tr>\n <tr>\n <td>Plátanos</td>\n <td>6</td>\n <td>2,80</td>\n </tr>\n </tbody>\n\n <tfoot>\n <tr>\n <td>Total</td>\n <td>24</td>\n <td>10,30</td>\n </tr>\n </tfoot>\n</table>\n\n\n<a> Link\nHyperloopUPV\n<!-- ¿Qué pasa con la URL -->\n<a>Hyperloop UPV</a>\n\nPropiedades HTML\nLas etiquetas o elementos html disponen de atributos que nos permiten modificar su comportamiento interno.\nEn el caso de las etiquetas <a> debemos de especificar en los atributos la referncia en cuestión\n <a href=\"https://hyperloopupv.com/\"> HyperloopUPV </a>\n\n\n\n<img> Image\n\n\n\n“Plaza de la Reina”\n\n\n<img src=\"../pr.jpg\" alt=\"Plaza con una fuente en el centro alrdedor casetas y fincas\">\n\n\n<form> Forms\nAmpliamente utilizados en HTML, trabajan sobre la etiqueta <input> la cual tiene muchos tipos.\n\n<input type=\"text\">\n\nNombre: \n\n<form>\n\n <label for=\"first-name\">Nombre:</label>\n\n <input type=\"text\" name=\"first-name\"/>\n</form>\n\n\n<input type=\"number\">\n\nEdad: \n\n<form>\n\n <label for=\"age\">Edad:</label>\n\n <input type=\"number\" name=\"age\" />\n</form>\n\n\n<input type=\"radio\">\n\nSubsistema: Software Firmware \n\n<form>\n\n <label for=\"sub\">Subsistema: </label><br />\n <input type=\"radio\" name=\"sub\" value=\"software\" /> Software <br />\n <input type=\"radio\" name=\"sub\" value=\"firmware\" /> Firmware <br />\n\n</form>\n\n\n<input type=\"submit\">\n\nNombre: \n\n\n<form>\n\n <label for=\"first-name\">Nombre:</label>\n <input type=\"text\" name=\"first-name\"/>\n\n <br/>\n\n <input type=\"submit\"/>\n</form>\n\n\n\n<button> Button\nPor ahora no los utilizaremos, pero es importante saber que existen\n\nSuscribirse\n\n<button>Suscribirse</button>", "crumbs": [ -<<<<<<< HEAD - "Bloque III Interacción", - "Semana 6", - "DOM" -======= - "crumbs": [ - "Bloque III Interacción", - "Semana 9", - "MA: *Uncontrolled fields* y `useRef`" ->>>>>>> 8e10774 (feat: explicacion semana 10 (#4)) - ] - }, - { - "objectID": "pages/Bloque3/t9.html", - "href": "pages/Bloque3/t9.html", - "title": "T6: Romancero Gitano I", - "section": "", - "text": "El objetivo de esta tarea es aplicar los conceptos básicos de HTML, CSS y JavaScript (DOM) para crear una página web interactiva que muestre poemas del Romancero gitano de Federico García Lorca y modifique su apariencia visual en función de las diferentes metáforas.\nResultado esperado" - }, - { - "objectID": "pages/Bloque3/t9.html#objetivo-de-la-tarea", - "href": "pages/Bloque3/t9.html#objetivo-de-la-tarea", - "title": "T6: Romancero Gitano I", - "section": "", - "text": "El objetivo de esta tarea es aplicar los conceptos básicos de HTML, CSS y JavaScript (DOM) para crear una página web interactiva que muestre poemas del Romancero gitano de Federico García Lorca y modifique su apariencia visual en función de las diferentes metáforas.\nResultado esperado" - }, - { - "objectID": "pages/Bloque3/t9.html#estructura-html", - "href": "pages/Bloque3/t9.html#estructura-html", - "title": "T6: Romancero Gitano I", - "section": "Estructura HTML", - "text": "Estructura HTML\nEl documento HTML debe:\n\nEstar correctamente estructurado (<!DOCTYPE>, <html>, <head>, <body>).\nContener un contenedor principal centrado horizontalmente.\nIncluir, en este orden:\n\nUn elemento para mostrar información métrica del poema (número de estrofas y versos).\nUn elemento para el título del poema.\nUn elemento para el texto del poema, respetando los saltos de verso utilizando la etiqueta <pre>.\nUn botón que permita cambiar de poema.\n\n\nNo debe utilizarse ningún framework ni librería externa." - }, - { - "objectID": "pages/Bloque3/t9.html#javascript", - "href": "pages/Bloque3/t9.html#javascript", - "title": "T6: Romancero Gitano I", - "section": "JavaScript", - "text": "JavaScript\n\nDatos\nEn JavaScript se debe definir un array de objetos, donde cada objeto represente un poema y tenga al menos:\n\nUn título.\nEl texto del poema como una cadena de texto, usando saltos de línea para los versos.\n\nEl cambio de poema debe hacerse recorriendo este array de forma cíclica (cola circular) mediante el botón.\nDescargar poemas (click derecho guardar enlace como)\n\n\nManipulación del DOM\nEl comportamiento de la página debe implementarse exclusivamente con DOM nativo, cumpliendo lo siguiente:\n\nTodas las funciones deben declararse como arrow functions.\nAl pulsar el botón:\n\nSe actualiza el título.\nSe actualiza el texto del poema.\nSe modifica el formato del fondo.\nSe recalculan y muestran:\n\nNúmero de versos.\nNúmero de estrofas (bloques separados por líneas en blanco).\n\n\n\nEl cálculo debe realizarse a partir del texto del poema, no con valores predefinidos." - }, - { - "objectID": "pages/Bloque3/t9.html#css", - "href": "pages/Bloque3/t9.html#css", - "title": "T6: Romancero Gitano I", - "section": "CSS", - "text": "CSS\nLorca utiliza las metáforas y la alegoría como elemento principal sobre el que construye sus poemas, al analizar profundamente descubrimos que siempre recurre a las mismas metáforas.\nEl color no se utiliza aquí solo como elemento decorativo, sino como un recurso semántico: cada color representa un símbolo literario presente en el poema.\nDesde el punto de vista técnico: - Los colores de fondo y de texto deben definirse en clases CSS. - JavaScript no decide colores, únicamente decide qué clase aplicar según el contenido del poema.\n\nPalabras clave y colores asociados\nSe debe detectar el texto del poema las siguientes palabras clave y aplicar el color de fondo correspondiente:\n\nguardia civil → fondo verde militar\nsangre → fondo granate oscuro\ncaballo → fondo color tierra\ncuchillo / navaja → fondo plateado\n\nEl color del texto debe elegirse siempre de forma que exista contraste suficiente con el fondo y se garantice la legibilidad.\nSi el poema contiene varias palabras clave, solo debe aplicarse un único estilo, siguiendo un orden de prioridad definido previamente.\nSi el poema no contiene ninguna de estas palabras, se aplicará un estilo por defecto con colores aleatorios." - }, - { - "objectID": "pages/Bloque3/t9.html#extra-opcional-símbolo-aurora", - "href": "pages/Bloque3/t9.html#extra-opcional-símbolo-aurora", - "title": "T6: Romancero Gitano I", - "section": "Extra opcional: símbolo aurora 🌈", - "text": "Extra opcional: símbolo aurora 🌈\nDe forma voluntaria, se puede añadir el símbolo aurora, que tendrá prioridad absoluta sobre todos los demás.\nCuando el poema contenga la palabra aurora, el fondo debe mostrar un degradado animado.\n\nCSS necesario para el extra\n.aurora {\n background: linear-gradient(120deg,\n #ff005d,\n #ff9f1c,\n #ffee32,\n #3cff00,\n #00e5ff,\n #7a00ff,\n #ff00c8\n );\n background-size: 600% 600%;\n animation: aurora 10s linear infinite;\n color: black;\n}\n\n@keyframes aurora {\n 0% { background-position: 0% 50%; }\n 50% { background-position: 100% 50%; }\n 100% { background-position: 0% 50%; }\n}" - }, - { - "objectID": "pages/Bloque3/t9.html#consejos", - "href": "pages/Bloque3/t9.html#consejos", - "title": "T6: Romancero Gitano I", - "section": "Consejos", - "text": "Consejos\n\nCentrado de la página (layout clásico)\nToda la página debe estar centrada horizontalmente.\nEl centrado se consigue mediante:\n\nUn contenedor con un ancho máximo definido.\nMárgenes automáticos a izquierda y derecha.\n\nDesde el punto de vista conceptual:\n\nmargin: 0 auto no centra texto, centra bloques.\nEl navegador reparte automáticamente el espacio sobrante a ambos lados del contenedor.\n\n\n\nAnimación del cambio de color\nPara evitar que los cambios de color sean bruscos, se utiliza la propiedad transition en CSS.\nEsta propiedad indica al navegador que los cambios en determinadas propiedades visuales deben hacerse de forma progresiva.\nEjemplo conceptual:\nbody {\n transition: background-color 0.6s, color 0.6s;\n}\nGracias a esta transición: - Cuando JavaScript cambia una clase o un color, - el navegador interpola automáticamente entre el color anterior y el nuevo.\nNo es necesario programar animaciones en JavaScript: CSS se encarga de todo el efecto visual.\n\n\nBúsqueda de una palabra\nPara buscar si determinada palabra determinada se debe utilizar la función includes de la clase string\n\ntexto = \"hoy nieva\";\n\ntexto.includes(\"nieva\"); //TRUE\ntexto.includes(\"llueve\"); //FALSE\n\n\nGeneración del color aleatorio\nPara la generacióndel color aleatorio de puede utilizar la siguiente función\nconst randomColor = () => '#' + Math.floor(Math.random() * 16777215).toString(16);\n\n\nConteo versos y estrofas\nEl recuento de versos y estrofas debe hacerse a partir del texto del poema, no con valores escritos a mano. A continuación se dan algunas pistas técnicas para abordar este problema.\n\nConteo de versos\nUn verso puede considerarse, a efectos prácticos en esta tarea, como una línea de texto no vacía.\nUna estrategia habitual consiste en: - Separar el texto por saltos de línea. - Eliminar las líneas vacías o formadas solo por espacios. - Contar cuántas líneas válidas quedan.\nEjemplo orientativo:\nconst versos = texto\n .split('\\n')\n .filter(linea => linea.trim() !== '')\n .length;\nEste enfoque es suficiente para la mayoría de los poemas del Romancero gitano y evita errores frecuentes.\n\n\nConteo de estrofas\nUna estrofa puede entenderse como un bloque de versos separado de otros bloques por una línea en blanco.\nUna posible estrategia es: - Separar el texto usando dos (o más) saltos de línea consecutivos. - Eliminar bloques vacíos. - Contar los bloques resultantes.\nEjemplo orientativo:\nconst estrofas = texto\n .split(/\\n\\s*\\n/)\n .filter(bloque => bloque.trim() !== '')\n .length;\nEntrega: un único archivo HTML funcional." - }, - { - "objectID": "pages/Bloque3/t9.html#recomendaciones-para-afrontar-la-tarea", - "href": "pages/Bloque3/t9.html#recomendaciones-para-afrontar-la-tarea", - "title": "T6: Romancero Gitano I", - "section": "Recomendaciones para afrontar la tarea", - "text": "Recomendaciones para afrontar la tarea\nAl enfrentarnos a un proyecto de cierta complejitud, el como afrontarlo resulta de cricial para el desarollo del mismo por ello a a continuación dejo un esquema de implementación modular. Sería mejor no utilizarlo, pero puede ser un buen punto de partida si no se sabe como empezar la app, no es el único proceso de implementación válido ni es más correcto que cualquier otro\n\nEstructura de HTML básica contadores de versos y estrofas placeholders de poema y título y botón\nFormateo CSS parte estática: centrado, tamaño (todo lo que no sea color)\nDefinir clases con colores predeterminados\nLógica interna con JS\n\n\nBuscar palabra\nContar estrofas\nConteo versos\nSelección de clase\nGenerar color aleatorio si procede\n\n\nIntegración con DOM" - }, - { - "objectID": "pages/Bloque3/reactI.html#sintaxis-jsx", - "href": "pages/Bloque3/reactI.html#sintaxis-jsx", - "title": "React I", - "section": "Sintaxis JSX", - "text": "Sintaxis JSX\n¿Qué es la sintaxis JSX?\n\nJSX (JavaScript XML) es una extensión de JavaScript utilizada en React.\n\nPermite escribir estructuras similares a HTML directamente dentro del código JavaScript.\n\nFacilita la creación y lectura de interfaces de usuario, integrando lógica y presentación.\n\nAunque su apariencia es HTML, JSX se transpila a JavaScript puro antes de ejecutarse en el navegador.\nForma cómoda de almacenar etiquetas html como variables de JS" -======= "Bloque I Introducción", "Semana 1", "HTML" @@ -1349,7 +1070,6 @@ "Semana 1", "HTML" ] ->>>>>>> e3c3469 (feat: varios) }, { "objectID": "pages/Bloque1/t1.html", @@ -1928,275 +1648,316 @@ ] }, { - "objectID": "pages/Bloque3/t10.html", - "href": "pages/Bloque3/t10.html", - "title": "T10: Quiz app!", + "objectID": "pages/Bloque3/dom.html#qué-es-el-dom", + "href": "pages/Bloque3/dom.html#qué-es-el-dom", + "title": "DOM", + "section": "¿Qué es el DOM?", + "text": "¿Qué es el DOM?\nEl DOM (Document Object Model) es la representación en memoria que el navegador crea a partir del HTML.\n\nEl HTML es estático\nEl DOM es dinámico\nJavaScript interactúa con el DOM, no con el HTML original\n\n\nEl DOM es la página web viva dentro del navegador." + }, + { + "objectID": "pages/Bloque3/dom.html#el-dom-como-árbol", + "href": "pages/Bloque3/dom.html#el-dom-como-árbol", + "title": "DOM", + "section": "El DOM como árbol", + "text": "El DOM como árbol\nEl DOM tiene estructura de árbol (grafo):\n\nCada etiqueta HTML es un nodo\nLos nodos tienen relaciones (padre, hijos, hermanos)\n\nEjemplo:\n<body>\n <h1>Título</h1>\n <button>Click</button>\n</body>" + }, + { + "objectID": "pages/Bloque3/dom.html#acceso-al-dom-desde-javascript", + "href": "pages/Bloque3/dom.html#acceso-al-dom-desde-javascript", + "title": "DOM", + "section": "Acceso al DOM desde JavaScript", + "text": "Acceso al DOM desde JavaScript\nEl navegador nos proporciona el objeto global document, que permite:\n\nBuscar elementos\nLeer su contenido\nModificar la interfaz\n\ndocument\ndocument.body" + }, + { + "objectID": "pages/Bloque3/dom.html#selectores", + "href": "pages/Bloque3/dom.html#selectores", + "title": "DOM", + "section": "Selectores", + "text": "Selectores\nPor ID\ndocument.getElementById(\"count\")\ndocument.querySelector(\"#count\")\nPor etiqueta\ndocument.querySelector(\"button\")" + }, + { + "objectID": "pages/Bloque3/dom.html#leer-y-modificar-elementos", + "href": "pages/Bloque3/dom.html#leer-y-modificar-elementos", + "title": "DOM", + "section": "Leer y modificar elementos", + "text": "Leer y modificar elementos\nelement.textContent\nelement.textContent = \"Nuevo texto\"\nelement.classList.add(\"active\")\nelement.classList.remove(\"active\")" + }, + { + "objectID": "pages/Bloque3/dom.html#eventos", + "href": "pages/Bloque3/dom.html#eventos", + "title": "DOM", + "section": "Eventos", + "text": "Eventos\nEl DOM permite reaccionar a acciones del usuario mediante eventos.\nbutton.addEventListener(\"click\", () => {\n // código a ejecutar\n});\nFlujo típico:\nUsuario → Evento → JavaScript → DOM actualizado" + }, + { + "objectID": "pages/Bloque3/dom.html#ejemplo-contador-con-botón", + "href": "pages/Bloque3/dom.html#ejemplo-contador-con-botón", + "title": "DOM", + "section": "Ejemplo: contador con botón", + "text": "Ejemplo: contador con botón\nHTML\n<h1 id=\"count\">0</h1>\n<button id=\"btn\">Sumar 1</button>\nJavaScript\nlet count = 0;\n\nconst countEl = document.querySelector(\"#count\");\nconst btn = document.querySelector(\"#btn\");\n\nbtn.addEventListener(\"click\", () => {\n count++;\n countEl.textContent = count;\n});" + }, + { + "objectID": "pages/Bloque3/dom.html#más-ejemplos", + "href": "pages/Bloque3/dom.html#más-ejemplos", + "title": "DOM", + "section": "Más ejemplos", + "text": "Más ejemplos\nEj1: Show input\nEj2: Par e Impar\nEj3: Toggle\nEj4: Creación dinámica\nEj5: Validación básica" + }, + { + "objectID": "pages/Bloque3/DOM.html", + "href": "pages/Bloque3/DOM.html", + "title": "DOM", "section": "", - "text": "Desarrollar una aplicación React que permita al usuario realizar un quiz de cultura general utilizando preguntas obtenidas desde una API pública.\nhttps://astonishing-douhua-44213e.netlify.app/\nLa aplicación debe mostrar preguntas, permitir seleccionar respuestas y calcular la puntuación final.", + "text": "El DOM (Document Object Model) es la representación en memoria que el navegador crea a partir del HTML.\n\nEl HTML es estático\nEl DOM es dinámico\nJavaScript interactúa con el DOM, no con el HTML original\n\n\nEl DOM es la página web viva dentro del navegador.", "crumbs": [ "Bloque III Interacción", - "Semana 10", - "T10: Quiz app!" + "Semana 6", + "DOM" ] }, { - "objectID": "pages/Bloque3/t10.html#objetivo", - "href": "pages/Bloque3/t10.html#objetivo", - "title": "T10: Quiz app!", + "objectID": "pages/Bloque3/DOM.html#qué-es-el-dom", + "href": "pages/Bloque3/DOM.html#qué-es-el-dom", + "title": "DOM", "section": "", - "text": "Desarrollar una aplicación React que permita al usuario realizar un quiz de cultura general utilizando preguntas obtenidas desde una API pública.\nhttps://astonishing-douhua-44213e.netlify.app/\nLa aplicación debe mostrar preguntas, permitir seleccionar respuestas y calcular la puntuación final.", + "text": "El DOM (Document Object Model) es la representación en memoria que el navegador crea a partir del HTML.\n\nEl HTML es estático\nEl DOM es dinámico\nJavaScript interactúa con el DOM, no con el HTML original\n\n\nEl DOM es la página web viva dentro del navegador.", "crumbs": [ "Bloque III Interacción", - "Semana 10", - "T10: Quiz app!" + "Semana 6", + "DOM" ] }, { - "objectID": "pages/Bloque3/t10.html#cargar-preguntas-desde-la-api", - "href": "pages/Bloque3/t10.html#cargar-preguntas-desde-la-api", - "title": "T10: Quiz app!", - "section": "1. Cargar preguntas desde la API", - "text": "1. Cargar preguntas desde la API\nCuando la aplicación se inicia debe obtener 10 preguntas desde la API.\nPara ello se debe usar:\n\nfetch\nuseEffect\n\nMientras se cargan los datos se debe mostrar:\nLoading...", + "objectID": "pages/Bloque3/DOM.html#el-dom-como-árbol", + "href": "pages/Bloque3/DOM.html#el-dom-como-árbol", + "title": "DOM", + "section": "El DOM como árbol", + "text": "El DOM como árbol\nEl DOM tiene estructura de árbol (grafo):\n\nCada etiqueta HTML es un nodo\nLos nodos tienen relaciones (padre, hijos, hermanos)\n\nEjemplo:\n<body>\n <h1>Título</h1>\n <button>Click</button>\n</body>\nRepresentación conceptual:\ndocument\n └─ body\n ├─ h1\n └─ button", "crumbs": [ "Bloque III Interacción", - "Semana 10", - "T10: Quiz app!" + "Semana 6", + "DOM" ] }, { - "objectID": "pages/Bloque3/t10.html#mostrar-la-pregunta-actual", - "href": "pages/Bloque3/t10.html#mostrar-la-pregunta-actual", - "title": "T10: Quiz app!", - "section": "2. Mostrar la pregunta actual", - "text": "2. Mostrar la pregunta actual\nLa aplicación debe mostrar:\n\nnúmero de pregunta\ntexto de la pregunta\n\nEjemplo:\nQuestion 3\nWhat is the capital of France?", + "objectID": "pages/Bloque3/DOM.html#acceso-al-dom-desde-javascript", + "href": "pages/Bloque3/DOM.html#acceso-al-dom-desde-javascript", + "title": "DOM", + "section": "Acceso al DOM desde JavaScript", + "text": "Acceso al DOM desde JavaScript\nEl navegador nos proporciona el objeto global document, que permite:\n\nBuscar elementos\nLeer su contenido\nModificar la interfaz\n\ndocument\ndocument.body", "crumbs": [ "Bloque III Interacción", - "Semana 10", - "T10: Quiz app!" + "Semana 6", + "DOM" ] }, { - "objectID": "pages/Bloque3/t10.html#mostrar-las-posibles-respuestas", - "href": "pages/Bloque3/t10.html#mostrar-las-posibles-respuestas", - "title": "T10: Quiz app!", - "section": "3. Mostrar las posibles respuestas", - "text": "3. Mostrar las posibles respuestas\nCada pregunta tiene:\n\n1 respuesta correcta\n3 respuestas incorrectas\n\nSe deben mostrar las 4 respuestas como botones.\nEjemplo:\nParis\nRome\nMadrid\nBerlin", + "objectID": "pages/Bloque3/DOM.html#selectores", + "href": "pages/Bloque3/DOM.html#selectores", + "title": "DOM", + "section": "Selectores", + "text": "Selectores\n\nPor ID\ndocument.getElementById(\"count\")\ndocument.querySelector(\"#count\")\n\n\nPor etiqueta\ndocument.querySelector(\"button\")\n\n\nPor clase\ndocument.querySelector(\".item\")\ndocument.querySelectorAll(\".item\")\nNotas: - querySelector devuelve el primer elemento - querySelectorAll devuelve una lista - Usan la misma sintaxis que CSS", "crumbs": [ "Bloque III Interacción", - "Semana 10", - "T10: Quiz app!" + "Semana 6", + "DOM" ] }, { - "objectID": "pages/Bloque3/t10.html#seleccionar-una-respuesta", - "href": "pages/Bloque3/t10.html#seleccionar-una-respuesta", - "title": "T10: Quiz app!", - "section": "4. Seleccionar una respuesta", - "text": "4. Seleccionar una respuesta\nCuando el usuario hace clic en una respuesta:\n\nla opción debe quedar marcada visualmente\nla selección debe guardarse en el estado del componente", + "objectID": "pages/Bloque3/DOM.html#leer-y-modificar-elementos", + "href": "pages/Bloque3/DOM.html#leer-y-modificar-elementos", + "title": "DOM", + "section": "Leer y modificar elementos", + "text": "Leer y modificar elementos\nelement.textContent\nelement.textContent = \"Nuevo texto\"\nelement.classList.add(\"active\")\nelement.classList.remove(\"active\")", "crumbs": [ "Bloque III Interacción", - "Semana 10", - "T10: Quiz app!" + "Semana 6", + "DOM" ] }, { - "objectID": "pages/Bloque3/t10.html#pasar-a-la-siguiente-pregunta", - "href": "pages/Bloque3/t10.html#pasar-a-la-siguiente-pregunta", - "title": "T10: Quiz app!", - "section": "5. Pasar a la siguiente pregunta", - "text": "5. Pasar a la siguiente pregunta\nDebe existir un botón:\nNext\nEste botón:\n\nsolo se puede pulsar cuando el usuario ha seleccionado una respuesta\npasa a la siguiente pregunta", + "objectID": "pages/Bloque3/DOM.html#eventos", + "href": "pages/Bloque3/DOM.html#eventos", + "title": "DOM", + "section": "Eventos", + "text": "Eventos\nEl DOM permite reaccionar a acciones del usuario mediante eventos.\nbutton.addEventListener(\"click\", () => {\n // código a ejecutar\n});\nFlujo típico:\nUsuario → Evento → JavaScript → DOM actualizado", "crumbs": [ "Bloque III Interacción", - "Semana 10", - "T10: Quiz app!" + "Semana 6", + "DOM" ] }, { - "objectID": "pages/Bloque3/t10.html#calcular-la-puntuación", - "href": "pages/Bloque3/t10.html#calcular-la-puntuación", - "title": "T10: Quiz app!", - "section": "6. Calcular la puntuación", - "text": "6. Calcular la puntuación\nCuando el usuario responde una pregunta:\n\nsi la respuesta es correcta → sumar 1 punto", + "objectID": "pages/Bloque3/DOM.html#ejemplo-contador-con-botón", + "href": "pages/Bloque3/DOM.html#ejemplo-contador-con-botón", + "title": "DOM", + "section": "Ejemplo: contador con botón", + "text": "Ejemplo: contador con botón\n\nHTML\n<h1 id=\"count\">0</h1>\n<button id=\"btn\">Sumar 1</button>\n\n\nJavaScript\nlet count = 0;\n\nconst countEl = document.querySelector(\"#count\");\nconst btn = document.querySelector(\"#btn\");\n\nbtn.addEventListener(\"click\", () => {\n count++;\n countEl.textContent = count;\n});", "crumbs": [ "Bloque III Interacción", - "Semana 10", - "T10: Quiz app!" + "Semana 6", + "DOM" ] }, { - "objectID": "pages/Bloque3/t10.html#mostrar-el-resultado-final", - "href": "pages/Bloque3/t10.html#mostrar-el-resultado-final", - "title": "T10: Quiz app!", - "section": "7. Mostrar el resultado final", - "text": "7. Mostrar el resultado final\nAl terminar el quiz debe mostrarse:\nQuiz finished!\nYour score: X / 10", + "objectID": "pages/Bloque3/DOM.html#más-ejemplos", + "href": "pages/Bloque3/DOM.html#más-ejemplos", + "title": "DOM", + "section": "Más ejemplos", + "text": "Más ejemplos\nEj1: Show input\nEj2: Par e Impar\nEj3: Toggle\nEj4: Creación dinámica\nEj5: Validación básica", "crumbs": [ "Bloque III Interacción", - "Semana 10", - "T10: Quiz app!" + "Semana 6", + "DOM" ] }, { - "objectID": "pages/Bloque4/Protocolos.html#http", - "href": "pages/Bloque4/Protocolos.html#http", - "title": "Protocolos de Comunicación", - "section": "HTTP", - "text": "HTTP\n¿Qué es HTTP?\nHTTP (HyperText Transfer Protocol) es el protocolo fundamental de la web.\nDefine cómo:\n\nEl cliente (navegador o app)\nSolicita recursos\nA un servidor" - }, - { - "objectID": "pages/Bloque4/Protocolos.html#http-vs-comunicación-en-tiempo-real", - "href": "pages/Bloque4/Protocolos.html#http-vs-comunicación-en-tiempo-real", - "title": "Protocolos de Comunicación", - "section": "HTTP vs Comunicación en tiempo real", - "text": "HTTP vs Comunicación en tiempo real\nHTTP funciona bajo el modelo:\n\nPetición → Respuesta → Fin\n\nSi queremos datos continuamente:\n\nDebemos repetir peticiones\nIntroduce latencia\nIneficiente\n\nSolución: WebSockets" - }, - { - "objectID": "pages/Bloque4/Protocolos.html#websockets", - "href": "pages/Bloque4/Protocolos.html#websockets", - "title": "Protocolos de Comunicación", - "section": "WebSockets", - "text": "WebSockets\nWebSockets permiten comunicación bidireccional persistente.\nCaracterísticas:\n\nConexión permanente\nCliente y servidor pueden enviar datos\nBaja latencia\nIdeal para tiempo real" + "objectID": "pages/Bloque3/reactI.html#sintaxis-jsx", + "href": "pages/Bloque3/reactI.html#sintaxis-jsx", + "title": "React I", + "section": "Sintaxis JSX", + "text": "Sintaxis JSX\n¿Qué es la sintaxis JSX?\n\nJSX (JavaScript XML) es una extensión de JavaScript utilizada en React.\n\nPermite escribir estructuras similares a HTML directamente dentro del código JavaScript.\n\nFacilita la creación y lectura de interfaces de usuario, integrando lógica y presentación.\n\nAunque su apariencia es HTML, JSX se transpila a JavaScript puro antes de ejecutarse en el navegador.\nForma cómoda de almacenar etiquetas html como variables de JS" }, { - "objectID": "pages/Bloque4/Protocolos.html#hook-personalizado", - "href": "pages/Bloque4/Protocolos.html#hook-personalizado", - "title": "Protocolos de Comunicación", - "section": "Hook Personalizado", - "text": "Hook Personalizado\nSi usamos WebSockets directamente dentro del componente:\n\nMezclamos lógica de conexión con renderizado\nCódigo difícil de reutilizar\nProblemas de ciclo de vida\n\nSolución: Hook personalizado" + "objectID": "pages/Bloque3/reactI.html#react", + "href": "pages/Bloque3/reactI.html#react", + "title": "React I", + "section": "React", + "text": "React\nMotivación\nHasta el momento hemos trabajado únicamente con HTML5, el cual a pesar de ser muy eficaz, presenta el inconveniente de que no implementa ninguna clase de lógica. Es decir, si queremos repetir alguna estructura previamente definida deberemos copiarla y pegarla. Otro problema, de HTML puro es la complejidad que requiere el manejo del DOM, como ya se vio en la semana anterior.\nBajo este pretexto, introducimos el framework React diseñado por Meta." }, { - "objectID": "pages/Bloque4/Protocolos.html#visualización-de-datos", - "href": "pages/Bloque4/Protocolos.html#visualización-de-datos", - "title": "Protocolos de Comunicación", - "section": "Visualización de datos", - "text": "Visualización de datos\nEn aplicaciones reales no basta con mostrar texto.\nNecesitamos:\n\nGráficas\nPaneles de control\nVisualización clara\n\nUna librería popular para React es Recharts." + "objectID": "pages/Bloque3/reactI.html#creación-de-una-reactjs-app", + "href": "pages/Bloque3/reactI.html#creación-de-una-reactjs-app", + "title": "React I", + "section": "Creación de una ReactJS app", + "text": "Creación de una ReactJS app\nEl desarrollo de aplicaciones web modernas requiere coordinar múltiples elementos: estructura del proyecto, gestión de dependencias, compilación del código y ejecución en el navegador.\nUn framework de desarrollo proporciona un entorno de trabajo preconfigurado que organiza estos elementos y establece una forma estándar de trabajar." }, { - "objectID": "pages/Bloque4/Protocolos.html#recharts", - "href": "pages/Bloque4/Protocolos.html#recharts", - "title": "Protocolos de Comunicación", - "section": "Recharts", - "text": "Recharts\nRecharts es una librería basada en:\n\nComponentes React\nSVG\nComposición declarativa\n\nInstalación:\nnpm install recharts" + "objectID": "pages/Bloque3/ReactI.html", + "href": "pages/Bloque3/ReactI.html", + "title": "React I", + "section": "", + "text": "Contenido", + "crumbs": [ + "Bloque III Interacción", + "Semana 7", + "React I" + ] }, { - "objectID": "pages/Bloque4/Protocolos.html#integración-completa", - "href": "pages/Bloque4/Protocolos.html#integración-completa", - "title": "Protocolos de Comunicación", - "section": "Integración completa", - "text": "Integración completa\nWebSocket + Hook + Recharts\nObjetivo:\n\nVisualizar datos en tiempo real" + "objectID": "pages/Bloque3/ReactI.html#sintaxis-jsx", + "href": "pages/Bloque3/ReactI.html#sintaxis-jsx", + "title": "React I", + "section": "Sintaxis JSX", + "text": "Sintaxis JSX\n\n¿Qué es la sintaxis JSX?\n\nJSX (JavaScript XML) es una extensión de JavaScript utilizada en React.\n\nPermite escribir estructuras similares a HTML directamente dentro del código JavaScript.\n\nFacilita la creación y lectura de interfaces de usuario, integrando lógica y presentación.\n\nAunque su apariencia es HTML, JSX se transpila a JavaScript puro antes de ejecutarse en el navegador.\nForma cómoda de almacenar etiquetas html como variables de JS\n\n\n\n¿Qué nos ofrece la sintaxis JSX?\n\nAlmacenar una etiqueta como una variable\nconst title = <h1>Web del TC de SW</h1>;\n\nconst driveIndication = (drives)? <p>Puede conducir</p> : <p>No puede conducir</p>;\nDe esta forma la variable title se corresponde con una etiqueta que contiene el título. Sin embargo, como podemos podemos utilizar está etiqueta una vez la hemos definido como una variable.\n\n\nRenderizar etiquetas dentro de etiquetas\nconst title = <h1>Web del TC de SW</h1>;\n\nconst page = <div>{title}</div>\nDe esta forma estámos encapsulando la etiqueta h1 dentro del div.\n\n\nRenderizar con el map\nconst profeAsi= [[\"Rosa\", \"Macro I\"], [\"Gabriela\", \"FSO\"],[\"Yolanda\",\"LTP\"], [\"Ferrán\", \"EM\"], [\"Pepe\", \"TCO\"]];\n\n// Array de li Asignatura: nombre profesor\nconst itemsProfAsi = profeAsi.map(x => <li>{x[1]}: {x[0]}</li>);\n\nconst listaProfesoresAsignatura = <ul>{itemsProfAsi}</ul>;\n\n\nRenderizar con el map\nconst profeAsi= [[\"Rosa\", \"Macro I\"], [\"Gabriela\", \"FSO\"], [\"Yolanda\",\"LTP\"], [\"Ferrán\", \"EM\"], [\"Pepe\", \"TCO\"]];\n\nconst listaProfesoresAsignatura = <ul>\n{profeAsi.map((x,i) => <li key={i}>{x[1]}: {x[0]}</li>)}\n</ul>;", + "crumbs": [ + "Bloque III Interacción", + "Semana 7", + "React I" + ] }, { - "objectID": "pages/Bloque4/Protocolos.html#material", - "href": "pages/Bloque4/Protocolos.html#material", - "title": "Protocolos de Comunicación", - "section": "Material", - "text": "Material\nServidor de testing de Python: Enlace.\nInstalar websockets con pip install websockets\nTester de prueba: Enlace (click derecho guardar enlace como)\nhttps://recharts.github.io/" + "objectID": "pages/Bloque3/ReactI.html#react", + "href": "pages/Bloque3/ReactI.html#react", + "title": "React I", + "section": "React", + "text": "React\n\nMotivación\nHasta el momento hemos trabajado únicamente con HTML5, el cual a pesar de ser muy eficaz, presenta el inconveniente de que no implementa ninguna clase de lógica. Es decir, si queremos repetir alguna estructura previamente definida deberemos copiarla y pegarla. Otro problema, de HTML puro es la complejidad que requiere el manejo del DOM, como ya se vio en la semana anterior.\nBajo este pretexto, introducimos el framework React diseñado por Meta.\n\n\nBases de React\nReact es un componente basado en la sintaxis JS y en la programación funcional introducida en ES6.\n\n\nComponente React\nLos componentes de React los definimos como unidades reutilizables y modulares que se implementan de esta forma: una función que devuelve un objeto de JSX\nfunction Saludo() {\n return <h1>Hola, mundo</h1>;\n}\nPodemos renderizar nuestro componente Saludo asumiendo que se comporta como una etiqueta html. Recordemos que no lo es.\n<main>\n <Saludo />\n</main>\nDentro de este componente podemos incluir lógica propia de JavaScript, de forma que al definir componente podemos dotarlo de lógica interna.\nfunction DivNúmeroAleatorio() {\n\n let n = Math.random();\n return <div className=\"n-random\">{n}</div>;\n}\n*Nótese que aunque JS utilice camelCase para el nombre de las funciones, con React utilizaremos PascalCase para definir los componentes puesto que a pesar de que se trata de funciones, trabajaremos con ellas como si fuesen clases.\n\n\nProps en React\nHasta ahora, los componentes definidos siempre devuelven el mismo contenido. Sin embargo, para que un componente sea realmente reutilizable, necesitamos poder pasarle información desde el exterior.\nEn React, esta información se transmite mediante props (properties).\nLas props se definen como los parámetros de entrada de un componente y permiten personalizar su comportamiento o su contenido sin modificar su definición interna.\n\nEjemplo 1: uso de props (forma explícita)\nfunction Saludo(props) {\n return <h1>Hola, {props.nombre}</h1>;\n}\nUso del componente:\n<Saludo nombre=\"Rosa\" />\n<Saludo nombre=\"Gabriela\" />\n\n\nEjemplo 2: props desestructuradas (sintaxis JavaScript)\nfunction Saludo({ nombre }) {\n return <h1>Hola, {nombre}</h1>;\n}\nUso del componente (idéntico al anterior):\n<Saludo nombre=\"Rosa\" />\n<Saludo nombre=\"Gabriela\" />\n\n\n\nEventos en React\nEn React, el manejo de eventos se basa en los eventos del DOM, pero utilizando una sintaxis propia de JSX y siguiendo el estilo de JavaScript.\n\nEjemplo básico de evento\nfunction BotonSaludo() {\n\n function manejarClick() {\n alert(\"Hola desde React\");\n }\n\n return (\n <button onClick={manejarClick}>\n Saludar\n </button>\n );\n}\n\n\nPaso de funciones como eventos\nLa función asociada al evento no se ejecuta al renderizar el componente, sino únicamente cuando ocurre el evento; es decir la pasamos como un ciudadano de primera clase.\n<button onClick={manejarClick}>Saludar</button>\nIncorrecto:\n<button onClick={manejarClick()}>Saludar</button>\n\n\nEventos más habituales en React\nReact soporta la mayoría de los eventos del DOM, adaptados a la sintaxis JSX. A continuación se listan los eventos más utilizados, clasificados por tipo.\n\nEventos de ratón\n\nonClick\nonDoubleClick\nonMouseEnter\nonMouseLeave\nonMouseMove\nonMouseDown\nonMouseUp\n\n\n\nEventos de teclado\n\nonKeyDown\nonKeyUp\nonKeyPress\n\n\n\nEventos de formularios\n\nonChange\nonSubmit\nonFocus\nonBlur\nonInput\nonReset\n\n\n\nEventos de carga y ciclo del elemento\n\nonLoad\nonError\n\n\n\nEventos de selección y portapapeles\n\nonSelect\nonCopy\nonCut\nonPaste\n\n\n\nEventos táctiles (dispositivos móviles)\n\nonTouchStart\nonTouchMove\nonTouchEnd\nonTouchCancel\n\n\n\n\n\nCiclo de vida de los componentes y renderizado en React\nEn React, los componentes no se renderizan directamente sobre el DOM real, sino que siguen un proceso intermedio que permite optimizar las actualizaciones de la interfaz.\nLa idea clave es que la interfaz se organiza como un árbol de componentes, análogo al árbol del DOM, pero a un nivel de abstracción superior.\n\nEl árbol de componentes\nCuando una aplicación React se ejecuta:\n\nCada componente genera otros componentes o elementos JSX.\nReact construye un árbol de componentes, donde:\n\nEl componente raíz suele ser App\nCada componente hijo ocupa una rama del árbol\n\n\nEste árbol de componentes se corresponde conceptualmente con el árbol del DOM, aunque no es el DOM real.\n\n\nProceso de renderizado\nEl proceso general es el siguiente:\n\nReact ejecuta un componente (función).\nEl componente devuelve JSX.\nEl JSX se transforma en una representación interna del árbol.\nReact compara este árbol con el anterior.\nSolo se actualizan en el DOM real los nodos que han cambiado.\n\nDe esta forma, React minimiza el acceso directo al DOM, que es una operación costosa.\n\n\nCiclo de vida (visión simplificada)\nDesde un punto de vista conceptual, un componente pasa por tres fases:\n\nMontaje\n\nEl componente se crea y se inserta en el DOM.\n\nActualización\n\nEl componente se vuelve a renderizar cuando cambian sus datos.\n\nDesmontaje\n\nEl componente se elimina del DOM.\n\n\nEn componentes funcionales modernos, este ciclo se gestiona de forma implícita mediante el renderizado y, más adelante, mediante hooks.\n\n\nRelación con el DOM\n\nEn JavaScript puro:\n\nSe modifica el DOM directamente.\n\nEn React:\n\nSe describe cómo debe ser la interfaz.\nReact decide el cuándo y el cómo modificar el DOM.\n\n\nEl desarrollador no recorre ni manipula el árbol del DOM, sino que trabaja sobre el árbol de componentes.", + "crumbs": [ + "Bloque III Interacción", + "Semana 7", + "React I" + ] }, { - "objectID": "pages/Bloque4/protocolos.html", - "href": "pages/Bloque4/protocolos.html", - "title": "Protocolos de Comunicación", - "section": "", - "text": "Contenido", + "objectID": "pages/Bloque3/ReactI.html#creación-de-una-reactjs-app", + "href": "pages/Bloque3/ReactI.html#creación-de-una-reactjs-app", + "title": "React I", + "section": "Creación de una ReactJS app", + "text": "Creación de una ReactJS app\nEl desarrollo de aplicaciones web modernas requiere coordinar múltiples elementos: estructura del proyecto, gestión de dependencias, compilación del código y ejecución en el navegador.\nUn framework de desarrollo proporciona un entorno de trabajo preconfigurado que organiza estos elementos y establece una forma estándar de trabajar.\n\n¿Para qué sirve un framework?\nUn framework permite al desarrollador:\n\nEvitar configuraciones manuales complejas\nSeguir una estructura común de proyecto\nAutomatizar tareas repetitivas\nCentrarse en la lógica de la aplicación y no en la infraestructura\n\nEn lugar de partir de un proyecto vacío, el framework ofrece una base funcional sobre la que desarrollar.\n\n\nFramework y React\nReact se encarga de definir cómo se construye la interfaz, pero no gestiona por sí mismo:\n\nLa estructura de archivos\nEl arranque de la aplicación\nLa compilación del código\nEl servidor de desarrollo\n\nPor ello, React se apoya en un framework que orquesta el entorno de ejecución.\n\n\nCreación de la aplicación\nPara crear una nueva aplicación React utilizando un framework, se ejecuta el siguiente comando en la terminal:\nnpm create vite@latest\nEste comando genera:\n\nUna estructura inicial del proyecto\nLa configuración necesaria para trabajar con React\nUn entorno listo para el desarrollo", "crumbs": [ - "Bloque IV Comunicación", - "Semana 11", - "Protocolos de Comunicación" + "Bloque III Interacción", + "Semana 7", + "React I" ] }, { - "objectID": "pages/Bloque4/protocolos.html#http", - "href": "pages/Bloque4/protocolos.html#http", - "title": "Protocolos de Comunicación", - "section": "HTTP", - "text": "HTTP\n\n¿Qué es HTTP?\nHTTP (HyperText Transfer Protocol) es el protocolo fundamental de la web.\nDefine cómo:\n\nEl cliente (navegador o app)\nSolicita recursos\nA un servidor\n\nModelo conceptual:\nCliente → Solicitud HTTP → Servidor\nCliente ← Respuesta HTTP ← Servidor\nHTTP es sin estado (stateless).\nCada petición es independiente.\n\n\nComponentes de una petición\nUna petición HTTP contiene:\n\nMétodo\nURL\nCabeceras (headers)\nCuerpo (body, opcional)\n\nEjemplo conceptual:\nGET /users HTTP/1.1\nHost: api.example.com\nAuthorization: Bearer token\n\n\nMétodos HTTP principales\n\nGET\n\nSolicita datos\nNo modifica el servidor\nDebe ser idempotente\n\nEjemplo:\nGET /users\nUso típico en React:\nfetch(\"/api/users\")\n\n\n\nPOST\n\nEnvía datos al servidor\nCrea recursos nuevos\nTiene cuerpo\n\nEjemplo:\nfetch(\"/api/users\", {\n method: \"POST\",\n body: JSON.stringify({ name: \"Ana\" }),\n headers: { \"Content-Type\": \"application/json\" }\n});\n\n\nPUT\n\nActualiza completamente un recurso existente\nIdempotente\n\nPUT /users/3\n\n\nDELETE\n\nElimina un recurso\n\nDELETE /users/3\n\n\nHEAD\n\nIgual que GET\nNo devuelve cuerpo\nSolo cabeceras\n\nSe utiliza para:\n\nComprobar existencia\nTamaño\nMetadatos", + "objectID": "pages/final.html#contextualización-de-la-tarea", + "href": "pages/final.html#contextualización-de-la-tarea", + "title": "Proyecto Final TC SW", + "section": "Contextualización de la tarea", + "text": "Contextualización de la tarea\nEl Hyperloop es una tecnología que se basa en la eficiencia energética; para ello se introduce lo que a día de hoy conocemos como booster, un módulo de infraestructura que confiere energía al pod para su propulsión. Al externalizar la potencia de la cápsula al carril, se logra un sistema más verde y sostenible, reduciendo el peso del vehículo y optimizando el consumo eléctrico mediante una gestión inteligente de la energía.\nA lo largo de los años en Hyperloop-UPV hemos tratado de desarrollar nuestro propio booster, uno de los problemas que identificamos a la hora de desarrollarlo es que para poder hacer pruebas era necesario disponer del POD completo. Por ello, este año hemos diseñado una bancada de pruebas1 que nos permite validar el funcionamiento del booster de forma sencilla.\n\nFundamentos físicos\n\nComponentes\nPodemos dividir la bancada de pruebas en 4 componentes:\n\nCarro booster (o Carro): módulo de tracción móvil compuesto por un bastidor de cuatro ruedas y un EMS2 que es propulsado por la sección\nTrack: vía de 50 metros por la que circula el Carro booster\n\nSección booster: sección del track por la que el al pasar Carro booster lo impulsa.\n\nArmario booster: lugar donde se almacenan los supercondensadores y las resistencias que generan el campo magnético que impulsa al Carro booster en la Sección booster\n\n\n\nVariables\nTrabajaremos como todas la variables asumiendo que son escalares.\n\nCarro Booster\n\nPosición: ubicación física del Carro en el Track. \\(s\\) medida en \\(\\mathrm{m}\\) y \\(s \\in[0,50]\\).\n\nAsumiremos que la Sección booster se encuentra ubicada entre \\(s=2\\) y \\(s =4\\)\n\nVelocidad: cambio de la posición del Carro respecto al tiempo. \\(v\\) medida en \\(\\mathrm{km/h}\\)\nAceleración: cambio de la velocidad del Carro respecto al tiempo. \\(a\\) medida en \\(\\mathrm{m/s^2}\\)\nMasa: masa total del Carro booster. \\(m\\) medida en \\(\\mathrm{kg}\\)\nFuerza: fuerza neta aplicada sobre el Carro, resultado del empuje del booster y de las fuerzas de frenado u oposición al movimiento. \\(F\\) medida en \\(\\mathrm{N}\\). Por convenio, \\(F>0\\) si actúa en el sentido de avance (dirección creciente de \\(s\\)) y \\(F<0\\) en caso contrario.\n\n\n\nArmario Booster\n\nTensión: diferencia de potencial eléctrico suministrada por el sistema de almacenamiento de energía. \\(V\\) medida en \\(\\mathrm{V}\\)\nIntensidad: corriente eléctrica que circula por el sistema para generar el campo magnético del booster. \\(I\\) medida en \\(\\mathrm{A}\\)\n\n\n\nOtras consideraciones\nAsumiremos que:\n\nEl movimiento únicamente es horizontal \\[\n\\begin{aligned}\ng &= 9.81\\,\\mathrm{m/s^2} \\\\\nF &= m \\cdot a \\\\\n|\\vec{N}| &= |\\vec{P}| = m g \\\\\nF_\\text{rozamiento} &= \\mu|\\vec{N}| \\\\\n\\mu &=\n\\begin{cases}\n0, & \\text{si el Carro no está frenando} \\\\\n0.5, & \\text{si el Carro está frenando}\n\\end{cases}\n\\end{aligned}\n\\]\n\n\n\n\nFuncionamiento\n\nPartiremos de un estado inicial de espera IDLE donde \\(s=0; a = 0; v = 0; V= 0; I = 0\\);\nSe dará la orden de precarga PRECHARGE donde todas las variables se mantendrán a 0 menos \\(V\\) que empezará a aumentar hasta alcanzar \\(V =400\\)\nCuando \\(V=400\\) entramos al estado de READY, donde la bancada está lista para operar\nCuando se inicie la prueba, el carro adquirirá una \\(v_\\text{base} = 4\\) que mantendrá en todo momento hasta que frene. RUNNING \\(a = 0; V= 400\\)\nAl entrar en la Sección Booster la velocidad y aceleración del Carro se verán incrementadas. BOOSTING. Durante el funcionamiento del Booster la Intensidad se modificará\nUna vez haya salido de la Sección Booster (\\(s > 4\\)) mantendrá una velocidad constante de \\(v = 25\\) hasta que el Carro llegue al final del track (\\(s=50\\)) o bien sea frenado. RUNNING\nFrenado: al lanzar la orden de Frenado BRAKE el Carro comenzará a disminuir su velocidad hasta \\(v = 0\\) o llegar al final del track.\n\nSi el Carro llega a detenerse antes de alcanzar el final del track (\\(s < 50\\)), se informará mediante un mensaje\nEn caso de que el Carro llegue al final del track con \\(v > 0\\), será detenido por el mechanical stopper3, también se informará con un mensaje.\n\nUna vez el Carro reste detenido STOPPED todas las variables de mantendran a 0 salvo la posición.", "crumbs": [ - "Bloque IV Comunicación", - "Semana 11", - "Protocolos de Comunicación" + "Proyecto Final TC SW" ] }, { - "objectID": "pages/Bloque4/protocolos.html#http-vs-comunicación-en-tiempo-real", - "href": "pages/Bloque4/protocolos.html#http-vs-comunicación-en-tiempo-real", - "title": "Protocolos de Comunicación", - "section": "HTTP vs Comunicación en tiempo real", - "text": "HTTP vs Comunicación en tiempo real\nHTTP funciona bajo el modelo:\n\nPetición → Respuesta → Fin\n\nSi queremos datos continuamente:\n\nDebemos repetir peticiones\nIntroduce latencia\nIneficiente\n\nSolución: WebSockets", + "objectID": "pages/final.html#objetivos-de-aprendizaje", + "href": "pages/final.html#objetivos-de-aprendizaje", + "title": "Proyecto Final TC SW", + "section": "Objetivos de aprendizaje", + "text": "Objetivos de aprendizaje\nEl objetivo principal de la tarea es poner en práctica el contenido aprendido durante el training center de Software, el diseño de UI dinámicas para el control de telemetría del vehículo. Asimismo, el proyecto se realizará en colaboración con otros subsistemas, para potenciar el trabajo en equipo y la comunicación entre los subsistemas, simulando así un miniequipo de Hyperloop.", "crumbs": [ - "Bloque IV Comunicación", - "Semana 11", - "Protocolos de Comunicación" + "Proyecto Final TC SW" ] }, { - "objectID": "pages/Bloque4/protocolos.html#websockets", - "href": "pages/Bloque4/protocolos.html#websockets", - "title": "Protocolos de Comunicación", - "section": "WebSockets", - "text": "WebSockets\nWebSockets permiten comunicación bidireccional persistente.\nCaracterísticas:\n\nConexión permanente\nCliente y servidor pueden enviar datos\nBaja latencia\nIdeal para tiempo real\n\nEjemplos:\n\nChats\nJuegos online\nTelemetría\nTrading\nSistemas IoT\n\n\nFlujo conceptual\nCliente ↔ Servidor\n conexión abierta\nNo se crea una nueva conexión por mensaje.\n\n\nCreación de un WebSocket en JavaScript\nconst socket = new WebSocket(\"ws://localhost:8080\");\nEventos principales:\n\nonopen\nonmessage\nonclose\nonerror\n\n\n\nRecepción de mensajes\nsocket.onmessage = (event) => {\n console.log(\"Mensaje:\", event.data);\n};\n\n\nEnvío de mensajes\nsocket.send(\"Hola servidor\");", + "objectID": "pages/final.html#descripción-general-de-la-tarea", + "href": "pages/final.html#descripción-general-de-la-tarea", + "title": "Proyecto Final TC SW", + "section": "Descripción general de la tarea", + "text": "Descripción general de la tarea\nEl objetivo del proyecto final consiste en desarrollar una bancada de pruebas para el booster (de ahora en adelante bancada booster). Los alumnos de los diferentes subsistemas del Training Center tienen asignadas diferentes tareas interrelacionadas entre sí, donde será necesario que todos cooperen de manera coordinada para cumplir con los requisitos técnicos y alcanzar el objetivo final de la tarea.\n\nDescripción de la tarea de Software\nSe deberá implementar un emulador de la bancada booster, el emulador estará compuesto por dos partes:\n\nFrontend: se conecta al backend y muestra datos sobre la bancada\nBackend: emulación de la bancada booster. (Se permite hacer uso del backend de ejemplo)\n\n\n\nEvaluación de la tarea\nLos requesitos mínimos para evaluar la tarea es realizar un frontend incluyendo las 5 funcionalidades detalladas en el apartado anterior.\nAquellos que deseen aspirar a nota extra podrán entregar también su propio backend que cumpla con las especificaciones de la tarea.", "crumbs": [ - "Bloque IV Comunicación", - "Semana 11", - "Protocolos de Comunicación" + "Proyecto Final TC SW" ] }, { - "objectID": "pages/Bloque4/protocolos.html#hook-personalizado", - "href": "pages/Bloque4/protocolos.html#hook-personalizado", - "title": "Protocolos de Comunicación", - "section": "Hook Personalizado", - "text": "Hook Personalizado\nSi usamos WebSockets directamente dentro del componente:\n\nMezclamos lógica de conexión con renderizado\nCódigo difícil de reutilizar\nProblemas de ciclo de vida\n\nSolución: Hook personalizado\n\nHook personalizado\nUn hook personalizado es:\n\nUna función que utiliza hooks internos para encapsular lógica reutilizable\n\nConvención:\nuseNombre()\n\n\nHook useWebSocket\nimport { useEffect, useState } from \"react\";\n\nexport function useWebSocket(url) {\n\n const [data, setData] = useState(null);\n\n useEffect(() => {\n\n const socket = new WebSocket(url);\n\n socket.onmessage = (event) => {\n setData(event.data);\n };\n\n return () => socket.close();\n\n }, [url]);\n\n return data;\n}\n\n\nAnálisis conceptual\n\nSe crea la conexión al montar\nSe actualiza el estado al recibir datos\nSe cierra al desmontar\nEl componente solo consume datos\n\n\n\nUso del hook\nfunction Monitor() {\n\n const data = useWebSocket(\"ws://localhost:8080\");\n\n return (\n <div>\n <h2>Datos en tiempo real</h2>\n <p>{data}</p>\n </div>\n );\n}\nFlujo:\n\nComponente se renderiza\n\nHook abre conexión\n\nLlegan datos\n\nEstado cambia\n\nNuevo render", + "objectID": "pages/final.html#frontend", + "href": "pages/final.html#frontend", + "title": "Proyecto Final TC SW", + "section": "Frontend", + "text": "Frontend\nLa Graphical User Interface (GUI) o frontend, debe de conectarse al backend utilizando los protocolos http y websockets.\n\nEspecificacciones de la GUI\n\nUna sección con gráficas que nos permita visualizar las siguientes varibles\n\n\n\\(V\\)\n\\(a\\)\n\\(v\\)\n\\(F\\) (Habrá que calcularla en el Frontend. Usar 2ª Ley de Newton)\n\\(I\\)\nAdemás, de una cronograma con los estados establecidos.\n\nIDLE, PRECHARGE, READY, RUNNING, BOOSTING, BRAKING y STOPPED\n\nUna sección que nos permita mediante botones enviar ordenes al simulador\n\n\nPRECHARGE: inicia la precarga\nSTART: inicia el movimiento del Carro, se debe de especificar la masa\nBRAKE: frena el Carro\nRESET: reinicia el simulador\n\n\nUna sección que nos permita ver los mensajes que envía el simulador al Front, los mensajes harán referencia al estado del simulador, por ejemplo : V = 400V precharge completed sucessfully\nUna sección que nos permita calcular la distancia óptima para presionar el freno mediante una llamada a API\nInclusión de un modelo 3D del Carro Booster\n\n\n\nImplementación\n\nNotas Generales\n\nSe debe desarollar una aplicación Web utilizando React\nSe debe utilizar preferiblemente Typescript o en su defecto JavaScript\nSe recomienda el uso de librerías de estilo como tailwind o bootsrap, así mismo se recomienda el uso de librerías de componentes, en especial shadcn\nEl frontend deberá validar los inputs antes de enviarlos\n\n\n\nGráficas (1) y Mensajes (3)\nEl Backend expondrá en el puerto 5001 un protocolo websockets con ruta backend/stream que se corresponderá con el flujo de datos envidados del cliente a servidor; es decir utilizaremos websockets únicamente para leer datos. De la información transmitida por ws obtendremos tanto los datos que habrá que mostrar en la gráfica, como los mensajes que se debén de mostrar en el área de mensajes.\nWebSocket endpoint: ws://localhost:5001/backend/stream\nLa comunicación por websockets estará, codificada como JSON, del cual distinguiremos 2 formatos, uno para recepción de los datos y otro para los mensajes del servidor.\nFormato datos:\n{\n \"topic\": \"data\",\n \"payload\": {\n \"timestamp\": \"2026-03-27T10:15:32.125Z\",\n \"state\": \"RUNNING\",\n \"position_m\": 3.42,\n \"velocity_kmh\": 18.7,\n \"acceleration_ms2\": 2.15,\n \"mass_kg\": 40,\n \"voltage_v\": 400,\n \"current_a\": 125.4\n }\n}\nFormato mensajes:\n{\n \"topic\": \"message\",\n \"payload\": {\n \"type\": \"info\", \n \"content\": \"Precharge started\"\n }\n}\nLos tipos de mensajes pueden ser:info, critical, error y success\nEn el caso de formato de datos los paquetes llegarán con una frecuencia de 4 Hz, debido a la gran afluencia de paquetes se recomienda mantener un histórico limitado (por ejemplo, los últimos 5–10 segundos de datos) para la representación gráfica, eliminando los valores más antiguos para evitar problemas de rendimiento.\n\n\nBotones (2)\nAl ser presionados mandarán una orden utilizando una petición HTTP-POST al backend utilizando el formato especificado. El botón de START debe de estar acompañado de un input numérico que permita especificar la massa del carro.\nLa orden se dirigirá al siguiente enpoint /api/command la cual estará en el puerto 8001. El servidor tras recibir la orden devolvera el código 200 indicando que todo ha sido correcto y se enviará un mensaje via websockets.\nEl servidor responderá con:\n\n200 OK si el comando se ha aceptado correctamente\n400 Bad Request si el comando es inválido\n\nEl resultado de la acción se notificará mediante un mensaje vía websockets.\nFormato petición HTTP:\nPOST /api/command\nContent-Type: application/json\n\n{\n \"command\": \"PRECHARGE\"\n}\n(en el caso de la orden START)\nPOST /api/command\nContent-Type: application/json\n\n{\n \"command\": \"START\",\n \"payload\": {\n \"mass\": 40\n }\n}\nEn caso de error (400 Bad Request), el frontend deberá mostrar un mensaje informativo al usuario indicando que la operación no se ha podido realizar.\n\n\nCálculo de la posición óptima para presionar el freno (4)\nSección con un formulario que permitirá indicar el peso del vehículo y a cuánta distancia se desea acabar del fin de la vía. Al presionar el botón de submit el frontend realizará una petición tipo GET - HTTP a /api/calculate con los query parameters m y d, que se corresponden con la masa del Carro y la distancia deseada, respectivamente.\nEl servidor devolverá una respuesta en formato JSON con la posición óptima en la que se debe iniciar el frenado. El frontend deberá procesar dicha respuesta y mostrar el resultado de forma clara al usuario.\nFormato de respuesta esperado:\n{\n \"braking_position_m\": 37.5\n}\n\n\nInclusión del modelo 3D (5)\nSe debe incluir un modelo 3D del Carro booster diseñado por el subsistema de Mechanics, debe de ser interáctivo, no debe de tratarse de una fotografía. 4\n\nNota sobre la velocidad del backend de ejemplo\nEl backend de ejemplo no corre en tiempo real: la simulación transcurre 5× más lenta que el tiempo real (SIM_SPEED = 0.2). La frecuencia de emisión por WebSocket se mantiene en 4 Hz, pero los valores de posición, velocidad, etc. avanzan más despacio de lo que cabría esperar físicamente. Esto es intencionado para facilitar las pruebas interactivas — el frontend no necesita hacer ningún ajuste al respecto.\n\n\n\n\nRecomendaciones\n\nEl objetivo de la tarea es diseñar un panel de control, por lo que sería interesante que haya una única vista, donde no se pueda hacer scroll que lo concentre todo\nSe recomienda hacer uso de los estilos como herramienta para resaltar los eventos que suceden en el simulador, por ejemplo si el Carro, se estrella con el mechanical stopper, podría ponerse el fondo en rojo para indicar que ha colisionado.", "crumbs": [ - "Bloque IV Comunicación", - "Semana 11", - "Protocolos de Comunicación" + "Proyecto Final TC SW" ] }, { - "objectID": "pages/Bloque4/protocolos.html#visualización-de-datos", - "href": "pages/Bloque4/protocolos.html#visualización-de-datos", - "title": "Protocolos de Comunicación", - "section": "Visualización de datos", - "text": "Visualización de datos\nEn aplicaciones reales no basta con mostrar texto.\nNecesitamos:\n\nGráficas\nPaneles de control\nVisualización clara\n\nUna librería popular para React es Recharts.", + "objectID": "pages/final.html#backend-voluntaria-realización", + "href": "pages/final.html#backend-voluntaria-realización", + "title": "Proyecto Final TC SW", + "section": "Backend (VOLUNTARIA REALIZACIÓN)", + "text": "Backend (VOLUNTARIA REALIZACIÓN)\nEl backend será el encargado de emular el comportamiento de la bancada booster, gestionando tanto la lógica de estados como la generación de datos de telemetría y la comunicación con el frontend.\nEl backend deberá implementarse preferiblemente en Rust o Go, o en su defecto en Java. Queda a criterio del alumno la elección del lenguaje dentro de estas opciones.\nEl backend deberá exponer dos tipos de comunicación:\n\nWebSockets: envío continuo de datos y mensajes al frontend\nHTTP: recepción de órdenes y cálculo de la posición óptima de frenado\n\n\nEspecificaciones del Backend\n\nArquitectura general\nEl backend deberá estar compuesto por:\n\nUn servidor WebSocket en el puerto 5001 para el envío de datos en tiempo real\nUn servidor HTTP en el puerto 8001 para la recepción de comandos y cálculo\n\nEl sistema deberá funcionar como una máquina de estados, donde las transiciones estarán controladas por las órdenes recibidas.\n\n\n\n\nMáquina de estados\nEl backend deberá implementar los siguientes estados:\nIDLE, PRECHARGE, READY, RUNNING, BOOSTING, BRAKING y STOPPED\n\nTransiciones\n\nIDLE → PRECHARGE mediante comando PRECHARGE\nPRECHARGE → READY cuando \\(V = 400\\)\nREADY → RUNNING mediante comando START\nRUNNING → BOOSTING cuando \\(2 \\le s \\le 4\\)\nBOOSTING → RUNNING cuando \\(s > 4\\)\nRUNNING → BRAKING mediante comando BRAKE\nBOOSTING → BRAKING mediante comando BRAKE\nBRAKING → STOPPED cuando \\(v = 0\\)\nCualquier estado → IDLE mediante comando RESET: todas las variables vuelven a su valor inicial (\\(s=0\\), \\(v=0\\), \\(a=0\\), \\(V=0\\), \\(I=0\\), \\(m=0\\))\n\nLos comandos únicamente podrán ser enviados por el frontend\nNo se permitirán transiciones no definidas.\n\n\n\n\nGeneración de datos\nEl backend deberá generar y enviar datos de forma continua mediante WebSockets.\n\nFrecuencia: 4 Hz\nCada mensaje representará el estado instantáneo del sistema\n\n\nVariables a generar\n\nposition_m\nvelocity_kmh\nacceleration_ms2\nmass_kg\nvoltage_v\ncurrent_a\nstate\ntimestamp\n\nEl campo timestamp deberá estar en formato ISO 8601 (UTC).\n\n\n\n\nLógica física simplificada\nSe deberá respetar el siguiente comportamiento:\n\nEn PRECHARGE: incremento progresivo de \\(V\\) hasta 400 (Incremento de \\(25\\,\\mathrm{V}\\) por tick, es decir, cada \\(250\\,\\mathrm{ms}\\); la precarga completa dura 16 ticks = 4 segundos)\nEn RUNNING: velocidad constante \\(v = 4\\,\\mathrm{km/h}\\) (antes del booster)\nEn BOOSTING: la aceleración se recalcula en cada tick para garantizar que el Carro alcance exactamente \\(v_f = 25\\,\\mathrm{km/h}\\) al llegar a \\(s = 4\\,\\mathrm{m}\\): \\[\na = \\frac{v_f^2 - v^2}{2\\,(4 - s)}\n\\] donde \\(v\\) y \\(s\\) son la velocidad (en \\(\\mathrm{m/s}\\)) y posición actuales. La fuerza es \\(F = m \\cdot a\\). Cuando \\(s \\ge 4\\), no calcular \\(a\\) — fijar directamente \\(v = 25\\,\\mathrm{km/h}\\) y transicionar a RUNNING para evitar división por cero. La intensidad sigue el perfil: \\[\nI(s) = I_{\\max} \\cdot \\sin\\!\\left(\\frac{\\pi\\,(s - 2)}{2}\\right), \\quad I_{\\max} = 200\\,\\mathrm{A}\n\\]\nEn RUNNING (post-booster): velocidad constante \\(v = 25\\,\\mathrm{km/h}\\), \\(a = 0\\), \\(I = 0\\)\nEn BRAKING: \\(F_{\\text{brake}} = 196\\,\\mathrm{N}\\), \\(a = -F_{\\text{brake}}/m\\). En cada tick (\\(\\Delta t = 0.25\\,\\mathrm{s}\\)) se actualiza: \\[\nv \\leftarrow v + a\\,\\Delta t \\qquad s \\leftarrow s + v\\,\\Delta t\n\\] Si \\(v \\le 0\\) → transición a STOPPED. Si \\(s \\ge 50\\) → mechanical stopper.\nEn STOPPED: todas las variables a 0 salvo posición\n\nNo es necesario implementar un modelo físico complejo, pero sí coherente con las reglas anteriores.\n\nVelocidad de simulación\nEl backend no corre en tiempo real. El paso de tiempo físico se escala con un factor fijo SIM_SPEED = 0.2, de modo que la simulación transcurre 5× más lenta que el tiempo real. La frecuencia de emisión por WebSocket se mantiene en 4 Hz.\n\\[\n\\Delta t_{\\text{físico}} = \\Delta t \\times 0{,}2 = 0{,}05\\,\\mathrm{s/tick}\n\\]\nEsto proporciona aproximadamente 33 segundos de margen en el tramo post-booster para enviar la orden de frenado, facilitando las pruebas interactivas.\n\n\n\n\nComunicación WebSocket\nEl servidor deberá emitir mensajes en formato JSON siguiendo los dos tipos definidos:\n\nDatos\n{\n \"topic\": \"data\",\n \"payload\": {\n \"timestamp\": \"2026-03-27T10:15:32.125Z\",\n \"state\": \"RUNNING\",\n \"position_m\": 3.42,\n \"velocity_kmh\": 18.7,\n \"acceleration_ms2\": 2.15,\n \"mass_kg\": 40,\n \"voltage_v\": 400,\n \"current_a\": 125.4\n }\n}\n\n\nMensajes\n{\n \"topic\": \"message\",\n \"payload\": {\n \"type\": \"info\",\n \"content\": \"Precharge started\"\n }\n}\nLos tipos posibles son info, success, error y critical. El backend deberá emitir un mensaje en cada transición de estado:\n\n\n\n\n\n\n\n\nEvento\ntype\ncontent (ejemplo)\n\n\n\n\nComando PRECHARGE recibido\ninfo\n\"Precharge started\"\n\n\n\\(V = 400\\) alcanzado\nsuccess\n\"V = 400V precharge completed successfully\"\n\n\nComando START recibido\ninfo\n\"Booster test started. Mass: 40 kg\"\n\n\nCarro entra en Sección Booster\ninfo\n\"Booster section entered\"\n\n\nCarro sale de Sección Booster\nsuccess\n\"Boost completed. Velocity: 25 km/h\"\n\n\nComando BRAKE recibido\ninfo\n\"Braking initiated\"\n\n\nCarro detenido (\\(v = 0\\), \\(s < 50\\))\nsuccess\n\"Cart stopped at s = 12.3 m\"\n\n\nCarro llega al mechanical stopper\ncritical\n\"Cart reached mechanical stopper at s = 50 m\"\n\n\nComando no válido para el estado actual\nerror\n\"Command BRAKE not allowed in state IDLE\"\n\n\nComando RESET recibido\ninfo\n\"System reset\"\n\n\n\n\n\n\n\nComunicación HTTP\nEl servidor HTTP escuchará en el puerto 8001 y expondrá dos endpoints.\n\nPOST /api/command\nRecibe un comando del frontend. Si el comando no es válido para el estado actual, responde 400 Bad Request. En caso contrario responde 200 OK y emite el mensaje correspondiente por WebSocket.\nComandos aceptados:\n\n\n\nComando\nEstado requerido\nPayload adicional\n\n\n\n\nPRECHARGE\nIDLE\n—\n\n\nSTART\nREADY\n{ \"mass\": <número> }\n\n\nBRAKE\nRUNNING o BOOSTING\n—\n\n\nRESET\nCualquiera\n—\n\n\n\n\n\nGET /api/calculate\nCalcula la posición óptima para iniciar el frenado. Recibe dos query parameters:\n\nm: masa del Carro en \\(\\mathrm{kg}\\)\nd: distancia deseada al final del track en \\(\\mathrm{m}\\)\n\n\\[\nd_{\\text{brake}} = \\frac{v_0^2 \\cdot m}{2\\,F_{\\text{brake}}} \\qquad s_{\\text{brake}} = (50 - d) - d_{\\text{brake}}\n\\]\ncon \\(v_0 = 25\\,\\mathrm{km/h}\\) (convertido a m/s) y \\(F_{\\text{brake}} = 196\\,\\mathrm{N}\\).\nRespuesta:\n{\n \"braking_position_m\": 37.5\n}\nSi los parámetros son inválidos (negativos, ausentes, o \\(s_{\\text{brake}} < 0\\)), responder 400 Bad Request.", "crumbs": [ - "Bloque IV Comunicación", - "Semana 11", - "Protocolos de Comunicación" + "Proyecto Final TC SW" ] }, { - "objectID": "pages/Bloque4/protocolos.html#recharts", - "href": "pages/Bloque4/protocolos.html#recharts", - "title": "Protocolos de Comunicación", - "section": "Recharts", - "text": "Recharts\nRecharts es una librería basada en:\n\nComponentes React\nSVG\nComposición declarativa\n\nInstalación:\nnpm install recharts\n\nEjemplo básico\nimport {\n LineChart, Line, XAxis, YAxis,\n CartesianGrid, Tooltip\n} from \"recharts\";\n\nconst data = [\n { t: 1, v: 10 },\n { t: 2, v: 15 },\n { t: 3, v: 8 }\n];\n\nfunction Grafica() {\n return (\n <LineChart width={400} height={300} data={data}>\n <CartesianGrid stroke=\"#ccc\" />\n <XAxis dataKey=\"t\" />\n <YAxis />\n <Tooltip />\n <Line type=\"monotone\" dataKey=\"v\" stroke=\"#8884d8\" />\n </LineChart>\n );\n}\n\n\nInterpretación\nCada objeto representa un punto.\n{ t: 1, v: 10 }\n\nt → eje X\nv → eje Y", + "objectID": "pages/final.html#uso-del-backend-de-ejemplo", + "href": "pages/final.html#uso-del-backend-de-ejemplo", + "title": "Proyecto Final TC SW", + "section": "Uso del backend de ejemplo", + "text": "Uso del backend de ejemplo\nPara los alumnos que realicen únicamente el frontend, se proporciona un backend de ejemplo ya compilado y listo para ejecutar, sin necesidad de instalar Python ni ninguna dependencia.\n\nDescarga\n\n\n\nSistema operativo\nEnlace\n\n\n\n\nWindows\nDescargar backend.exe\n\n\nLinux\nDescargar backend\n\n\n\n\n\nEjecución\n\nWindowsLinux\n\n\nHaz doble clic sobre backend.exe o ejecútalo desde una terminal:\nbackend.exe\n\n\nOtorga permisos de ejecución y lánzalo:\nchmod +x backend\n./backend\n\n\n\n\n\nPuertos\nUna vez en marcha, el backend expone dos servicios de forma simultánea:\n\n\n\nServicio\nURL\n\n\n\n\nWebSocket (telemetría)\nws://localhost:5001/backend/stream\n\n\nHTTP (comandos y cálculo)\nhttp://localhost:8001\n\n\n\nEl frontend debe estar lanzado después de que el backend esté corriendo. Asegúrate de que los puertos 5001 y 8001 no están ocupados por otro proceso.", "crumbs": [ - "Bloque IV Comunicación", - "Semana 11", - "Protocolos de Comunicación" + "Proyecto Final TC SW" ] }, { - "objectID": "pages/Bloque4/protocolos.html#integración-completa", - "href": "pages/Bloque4/protocolos.html#integración-completa", - "title": "Protocolos de Comunicación", - "section": "Integración completa", - "text": "Integración completa\nWebSocket + Hook + Recharts\nObjetivo:\n\nVisualizar datos en tiempo real\n\n\nEjemplo conceptual\nfunction Dashboard() {\n\n const data = useWebSocket(\"ws://localhost:8080\");\n\n const chartData = data\n ? JSON.parse(data)\n : [];\n\n return (\n <LineChart width={500} height={300} data={chartData}>\n <XAxis dataKey=\"time\" />\n <YAxis />\n <Tooltip />\n <Line dataKey=\"value\" stroke=\"#82ca9d\" />\n </LineChart>\n );\n}\n\n\nFlujo completo de una app moderna\nServidor → WebSocket → Hook → Estado → React → Gráfica\nArquitectura típica de sistemas en tiempo real.", + "objectID": "pages/final.html#entrega", + "href": "pages/final.html#entrega", + "title": "Proyecto Final TC SW", + "section": "Entrega", + "text": "Entrega\nLa entrega del proyecto se realizará a través de un repositorio de GitHub que deberá cumplir los siguientes requisitos:\n\nEl repositorio debe contener únicamente el proyecto — sin archivos innecesarios, sin el binario del backend de ejemplo descargado, y sin carpetas de dependencias (node_modules, entornos virtuales, etc.). Se recomienda incluir un .gitignore adecuado.\nEl repositorio debe incluir un README.md con instrucciones claras para instalar y ejecutar el proyecto.\nSe enviará el enlace al repositorio al responsable del subsistema de Software.\n\n\nPlazos\n\n\n\nHito\nFecha\n\n\n\n\nPlazo para añadir cambios al repositorio\n12 de mayo de 2026\n\n\nExposición del proyecto\n14 de mayo de 2026\n\n\n\n\nA partir del 12 de mayo no se tendrán en cuenta los cambios realizados en el repositorio para la evaluación. La exposición consistirá en una demostración en vivo del proyecto y una breve explicación de las decisiones de implementación tomadas.", "crumbs": [ - "Bloque IV Comunicación", - "Semana 11", - "Protocolos de Comunicación" + "Proyecto Final TC SW" ] }, { - "objectID": "pages/Bloque4/protocolos.html#material", - "href": "pages/Bloque4/protocolos.html#material", - "title": "Protocolos de Comunicación", - "section": "Material", - "text": "Material\nServidor de testing de Python: Enlace.\nInstalar websockets con pip install websockets\nTester de prueba: Enlace (click derecho guardar enlace como)\nhttps://recharts.github.io/", + "objectID": "pages/final.html#footnotes", + "href": "pages/final.html#footnotes", + "title": "Proyecto Final TC SW", + "section": "Notas", + "text": "Notas\n\n\nSistema estructural diseñado para el montaje rígido de prototipos o componentes, equipado con sensores para la medición de parámetros operativos y la simulación de condiciones de carga controladas.↩︎\nUn imán.↩︎\nPlancha de metal que asegura que el Carro booster no salga del track.↩︎\nNo se incluye una descripción más extensa porque el desafio de este apartado es ver, vuestras habilidades para seleccionar la forma óptima.↩︎", "crumbs": [ - "Bloque IV Comunicación", - "Semana 11", - "Protocolos de Comunicación" + "Proyecto Final TC SW" ] }, { diff --git a/docs/site_libs/revealjs/dist/theme/quarto-2740581606dc327e82f18f789b69d8a0.css b/docs/site_libs/revealjs/dist/theme/quarto-2740581606dc327e82f18f789b69d8a0.css new file mode 100644 index 0000000..d150035 --- /dev/null +++ b/docs/site_libs/revealjs/dist/theme/quarto-2740581606dc327e82f18f789b69d8a0.css @@ -0,0 +1,8 @@ +@import"./fonts/source-sans-pro/source-sans-pro.css";:root{--r-background-color: #fff;--r-main-font: Source Sans Pro, Helvetica, sans-serif;--r-main-font-size: 40px;--r-main-color: #222;--r-block-margin: 12px;--r-heading-margin: 0 0 12px 0;--r-heading-font: Source Sans Pro, Helvetica, sans-serif;--r-heading-color: #222;--r-heading-line-height: 1.2;--r-heading-letter-spacing: normal;--r-heading-text-transform: none;--r-heading-text-shadow: none;--r-heading-font-weight: 600;--r-heading1-text-shadow: none;--r-heading1-size: 2.5em;--r-heading2-size: 1.6em;--r-heading3-size: 1.3em;--r-heading4-size: 1em;--r-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-link-color: #2a76dd;--r-link-color-dark: rgb(25.6720647773, 83.0566801619, 160.8279352227);--r-link-color-hover: rgb(85.979757085, 146.2874493927, 228.020242915);--r-selection-background-color: rgb(151.9493927126, 188.7186234818, 238.5506072874);--r-selection-color: #fff;--r-overlay-element-bg-color: 240, 240, 240;--r-overlay-element-fg-color: 0, 0, 0}.reveal-viewport{background:#fff;background-color:var(--r-background-color)}.reveal{font-family:var(--r-main-font);font-size:var(--r-main-font-size);font-weight:normal;color:var(--r-main-color)}.reveal ::selection{color:var(--r-selection-color);background:var(--r-selection-background-color);text-shadow:none}.reveal ::-moz-selection{color:var(--r-selection-color);background:var(--r-selection-background-color);text-shadow:none}.reveal .slides section,.reveal .slides section>section{line-height:1.3;font-weight:inherit}.reveal h1,.reveal h2,.reveal h3,.reveal h4,.reveal h5,.reveal h6{margin:var(--r-heading-margin);color:var(--r-heading-color);font-family:var(--r-heading-font);font-weight:var(--r-heading-font-weight);line-height:var(--r-heading-line-height);letter-spacing:var(--r-heading-letter-spacing);text-transform:var(--r-heading-text-transform);text-shadow:var(--r-heading-text-shadow);word-wrap:break-word}.reveal h1{font-size:var(--r-heading1-size)}.reveal h2{font-size:var(--r-heading2-size)}.reveal h3{font-size:var(--r-heading3-size)}.reveal h4{font-size:var(--r-heading4-size)}.reveal h1{text-shadow:var(--r-heading1-text-shadow)}.reveal p{margin:var(--r-block-margin) 0;line-height:1.3}.reveal h1:last-child,.reveal h2:last-child,.reveal h3:last-child,.reveal h4:last-child,.reveal h5:last-child,.reveal h6:last-child{margin-bottom:0}.reveal img,.reveal video,.reveal iframe{max-width:95%;max-height:95%}.reveal strong,.reveal b{font-weight:bold}.reveal em{font-style:italic}.reveal ol,.reveal dl,.reveal ul{display:inline-block;text-align:left;margin:0 0 0 1em}.reveal ol{list-style-type:decimal}.reveal ul{list-style-type:disc}.reveal ul ul{list-style-type:square}.reveal ul ul ul{list-style-type:circle}.reveal ul ul,.reveal ul ol,.reveal ol ol,.reveal ol ul{display:block;margin-left:40px}.reveal dt{font-weight:bold}.reveal dd{margin-left:40px}.reveal blockquote{display:block;position:relative;width:70%;margin:var(--r-block-margin) auto;padding:5px;font-style:italic;background:hsla(0,0%,100%,.05);box-shadow:0px 0px 2px rgba(0,0,0,.2)}.reveal blockquote p:first-child,.reveal blockquote p:last-child{display:inline-block}.reveal q{font-style:italic}.reveal pre{display:block;position:relative;width:90%;margin:var(--r-block-margin) auto;text-align:left;font-size:.55em;font-family:var(--r-code-font);line-height:1.2em;word-wrap:break-word;box-shadow:0px 5px 15px rgba(0,0,0,.15)}.reveal code{font-family:var(--r-code-font);text-transform:none;tab-size:2}.reveal pre code{display:block;padding:5px;overflow:auto;max-height:400px;word-wrap:normal}.reveal .code-wrapper{white-space:normal}.reveal .code-wrapper code{white-space:pre}.reveal table{margin:auto;border-collapse:collapse;border-spacing:0}.reveal table th{font-weight:bold}.reveal table th,.reveal table td{text-align:left;padding:.2em .5em .2em .5em;border-bottom:1px solid}.reveal table th[align=center],.reveal table td[align=center]{text-align:center}.reveal table th[align=right],.reveal table td[align=right]{text-align:right}.reveal table tbody tr:last-child th,.reveal table tbody tr:last-child td{border-bottom:none}.reveal sup{vertical-align:super;font-size:smaller}.reveal sub{vertical-align:sub;font-size:smaller}.reveal small{display:inline-block;font-size:.6em;line-height:1.2em;vertical-align:top}.reveal small *{vertical-align:top}.reveal img{margin:var(--r-block-margin) 0}.reveal a{color:var(--r-link-color);text-decoration:none;transition:color .15s ease}.reveal a:hover{color:var(--r-link-color-hover);text-shadow:none;border:none}.reveal .roll span:after{color:#fff;background:var(--r-link-color-dark)}.reveal .r-frame{border:4px solid var(--r-main-color);box-shadow:0 0 10px rgba(0,0,0,.15)}.reveal a .r-frame{transition:all .15s linear}.reveal a:hover .r-frame{border-color:var(--r-link-color);box-shadow:0 0 20px rgba(0,0,0,.55)}.reveal .controls{color:var(--r-link-color)}.reveal .progress{background:rgba(0,0,0,.2);color:var(--r-link-color)}@media print{.backgrounds{background-color:var(--r-background-color)}}.top-right{position:absolute;top:1em;right:1em}.visually-hidden{border:0;clip:rect(0 0 0 0);height:auto;margin:0;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap}.hidden{display:none !important}.zindex-bottom{z-index:-1 !important}figure.figure{display:block}.quarto-layout-panel{margin-bottom:1em}.quarto-layout-panel>figure{width:100%}.quarto-layout-panel>figure>figcaption,.quarto-layout-panel>.panel-caption{margin-top:10pt}.quarto-layout-panel>.table-caption{margin-top:0px}.table-caption p{margin-bottom:.5em}.quarto-layout-row{display:flex;flex-direction:row;align-items:flex-start}.quarto-layout-valign-top{align-items:flex-start}.quarto-layout-valign-bottom{align-items:flex-end}.quarto-layout-valign-center{align-items:center}.quarto-layout-cell{position:relative;margin-right:20px}.quarto-layout-cell:last-child{margin-right:0}.quarto-layout-cell figure,.quarto-layout-cell>p{margin:.2em}.quarto-layout-cell img{max-width:100%}.quarto-layout-cell .html-widget{width:100% !important}.quarto-layout-cell div figure p{margin:0}.quarto-layout-cell figure{display:block;margin-inline-start:0;margin-inline-end:0}.quarto-layout-cell table{display:inline-table}.quarto-layout-cell-subref figcaption,figure .quarto-layout-row figure figcaption{text-align:center;font-style:italic}.quarto-figure{position:relative;margin-bottom:1em}.quarto-figure>figure{width:100%;margin-bottom:0}.quarto-figure-left>figure>p,.quarto-figure-left>figure>div{text-align:left}.quarto-figure-center>figure>p,.quarto-figure-center>figure>div{text-align:center}.quarto-figure-right>figure>p,.quarto-figure-right>figure>div{text-align:right}.quarto-figure>figure>div.cell-annotation,.quarto-figure>figure>div code{text-align:left}figure>p:empty{display:none}figure>p:first-child{margin-top:0;margin-bottom:0}figure>figcaption.quarto-float-caption-bottom{margin-bottom:.5em}figure>figcaption.quarto-float-caption-top{margin-top:.5em}div[id^=tbl-]{position:relative}.quarto-figure>.anchorjs-link{position:absolute;top:.6em;right:.5em}div[id^=tbl-]>.anchorjs-link{position:absolute;top:.7em;right:.3em}.quarto-figure:hover>.anchorjs-link,div[id^=tbl-]:hover>.anchorjs-link,h2:hover>.anchorjs-link,h3:hover>.anchorjs-link,h4:hover>.anchorjs-link,h5:hover>.anchorjs-link,h6:hover>.anchorjs-link,.reveal-anchorjs-link>.anchorjs-link{opacity:1}#title-block-header{margin-block-end:1rem;position:relative;margin-top:-1px}#title-block-header .abstract{margin-block-start:1rem}#title-block-header .abstract .abstract-title{font-weight:600}#title-block-header a{text-decoration:none}#title-block-header .author,#title-block-header .date,#title-block-header .doi{margin-block-end:.2rem}#title-block-header .quarto-title-block>div{display:flex}#title-block-header .quarto-title-block>div>h1{flex-grow:1}#title-block-header .quarto-title-block>div>button{flex-shrink:0;height:2.25rem;margin-top:0}tr.header>th>p:last-of-type{margin-bottom:0px}table,table.table{margin-top:.5rem;margin-bottom:.5rem}caption,.table-caption{padding-top:.5rem;padding-bottom:.5rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-top{margin-top:.5rem;margin-bottom:.25rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-bottom{padding-top:.25rem;margin-bottom:.5rem;text-align:center}.utterances{max-width:none;margin-left:-8px}iframe{margin-bottom:1em}details{margin-bottom:1em}details[show]{margin-bottom:0}details>summary{color:rgb(110.5,110.5,110.5)}details>summary>p:only-child{display:inline}div.code-copy-outer-scaffold{position:relative}dd code:not(.sourceCode),p code:not(.sourceCode){white-space:pre-wrap}code{white-space:pre}@media print{code{white-space:pre-wrap}}pre>code{display:block}pre>code.sourceCode{white-space:pre}pre>code.sourceCode>span>a:first-child::before{text-decoration:none}pre.code-overflow-wrap>code.sourceCode{white-space:pre-wrap}pre.code-overflow-scroll>code.sourceCode{white-space:pre}code a:any-link{color:inherit;text-decoration:none}code a:hover{color:inherit;text-decoration:underline}ul.task-list{padding-left:1em}[data-tippy-root]{display:inline-block}.tippy-content .footnote-back{display:none}.footnote-back{margin-left:.2em}.tippy-content{overflow-x:auto}.quarto-embedded-source-code{display:none}.quarto-unresolved-ref{font-weight:600}.quarto-cover-image{max-width:35%;float:right;margin-left:30px}.cell-output-display .widget-subarea{margin-bottom:1em}.cell-output-display:not(.no-overflow-x),.knitsql-table:not(.no-overflow-x){overflow-x:auto}.panel-input{margin-bottom:1em}.panel-input>div,.panel-input>div>div{display:inline-block;vertical-align:top;padding-right:12px}.panel-input>p:last-child{margin-bottom:0}.layout-sidebar{margin-bottom:1em}.layout-sidebar .tab-content{border:none}.tab-content>.page-columns.active{display:grid}div.sourceCode>iframe{width:100%;height:300px;margin-bottom:-0.5em}a{text-underline-offset:3px}.callout pre.sourceCode{padding-left:0}div.ansi-escaped-output{font-family:monospace;display:block}/*! +* +* ansi colors from IPython notebook's +* +* we also add `bright-[color]-` synonyms for the `-[color]-intense` classes since +* that seems to be what ansi_up emits +* +*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-black,.ansi-bright-black-fg{color:#282c36}.ansi-black-intense-black,.ansi-bright-black-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-red,.ansi-bright-red-fg{color:#b22b31}.ansi-red-intense-red,.ansi-bright-red-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-green,.ansi-bright-green-fg{color:#007427}.ansi-green-intense-green,.ansi-bright-green-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-yellow,.ansi-bright-yellow-fg{color:#b27d12}.ansi-yellow-intense-yellow,.ansi-bright-yellow-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-blue,.ansi-bright-blue-fg{color:#0065ca}.ansi-blue-intense-blue,.ansi-bright-blue-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-magenta,.ansi-bright-magenta-fg{color:#a03196}.ansi-magenta-intense-magenta,.ansi-bright-magenta-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-cyan,.ansi-bright-cyan-fg{color:#258f8f}.ansi-cyan-intense-cyan,.ansi-bright-cyan-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-white,.ansi-bright-white-fg{color:#a1a6b2}.ansi-white-intense-white,.ansi-bright-white-bg{background-color:#a1a6b2}.ansi-default-inverse-fg{color:#fff}.ansi-default-inverse-bg{background-color:#000}.ansi-bold{font-weight:bold}.ansi-underline{text-decoration:underline}:root{--quarto-body-bg: #fff;--quarto-body-color: #222;--quarto-text-muted: rgb(110.5, 110.5, 110.5);--quarto-border-color: #bbbbbb;--quarto-border-width: 1px;--quarto-border-radius: 4px}table.gt_table{color:var(--quarto-body-color);font-size:1em;width:100%;background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_column_spanner_outer{color:var(--quarto-body-color);background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_col_heading{color:var(--quarto-body-color);font-weight:bold;background-color:rgba(0,0,0,0)}table.gt_table thead.gt_col_headings{border-bottom:1px solid currentColor;border-top-width:inherit;border-top-color:var(--quarto-border-color)}table.gt_table thead.gt_col_headings:not(:first-child){border-top-width:1px;border-top-color:var(--quarto-border-color)}table.gt_table td.gt_row{border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-width:0px}table.gt_table tbody.gt_table_body{border-top-width:1px;border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-color:currentColor}div.columns{display:initial;gap:initial}div.column{display:inline-block;overflow-x:initial;vertical-align:top;width:50%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:relative;float:right;background-color:rgba(0,0,0,0)}input[type=checkbox]{margin-right:.5ch}:root{--mermaid-bg-color: #fff;--mermaid-edge-color: #999;--mermaid-node-fg-color: #222;--mermaid-fg-color: #222;--mermaid-fg-color--lighter: rgb(59.5, 59.5, 59.5);--mermaid-fg-color--lightest: #555555;--mermaid-font-family: Source Sans Pro, Helvetica, sans-serif;--mermaid-label-bg-color: #fff;--mermaid-label-fg-color: #2a76dd;--mermaid-node-bg-color: rgba(42, 118, 221, 0.1);--mermaid-node-fg-color: #222}@media print{:root{font-size:11pt}#quarto-sidebar,#TOC,.nav-page{display:none}.page-columns .content{grid-column-start:page-start}.fixed-top{position:relative}.panel-caption,.figure-caption,figcaption{color:#666}}body.quarto-light .dark-content{display:none !important}body.quarto-dark .light-content{display:none !important}.code-copy-button{position:absolute;top:0;right:0;border:0;margin-top:5px;margin-right:5px;background-color:rgba(0,0,0,0);z-index:3}.code-copy-button-tooltip{font-size:.75em}div.code-copy-outer-scaffold:hover>.code-copy-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}div.code-copy-outer-scaffold:hover>.code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}div.code-copy-outer-scaffold:hover>.code-copy-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}div.code-copy-outer-scaffold:hover>.code-copy-button-checked:hover>.bi::before{background-image:url('data:image/svg+xml,')}.panel-tabset [role=tablist]{border-bottom:1px solid #bbb;list-style:none;margin:0;padding:0;width:100%}.panel-tabset [role=tablist] *{-webkit-box-sizing:border-box;box-sizing:border-box}@media(min-width: 30em){.panel-tabset [role=tablist] li{display:inline-block}}.panel-tabset [role=tab]{border:1px solid rgba(0,0,0,0);border-top-color:#bbb;display:block;padding:.5em 1em;text-decoration:none}@media(min-width: 30em){.panel-tabset [role=tab]{border-top-color:rgba(0,0,0,0);display:inline-block;margin-bottom:-1px}}.panel-tabset [role=tab][aria-selected=true]{background-color:#bbb}@media(min-width: 30em){.panel-tabset [role=tab][aria-selected=true]{background-color:rgba(0,0,0,0);border:1px solid #bbb;border-bottom-color:#fff}}@media(min-width: 30em){.panel-tabset [role=tab]:hover:not([aria-selected=true]){border:1px solid #bbb}}.code-with-filename .code-with-filename-file{margin-bottom:0;padding-bottom:2px;padding-top:2px;padding-left:.7em;border:var(--quarto-border-width) solid var(--quarto-border-color);border-radius:var(--quarto-border-radius);border-bottom:0;border-bottom-left-radius:0%;border-bottom-right-radius:0%}.code-with-filename div.sourceCode,.reveal .code-with-filename div.sourceCode{margin-top:0;border-top-left-radius:0%;border-top-right-radius:0%}.code-with-filename .code-with-filename-file pre{margin-bottom:0}.code-with-filename .code-with-filename-file{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}.reveal.center .slide aside,.reveal.center .slide div.aside{position:initial}section.has-light-background,section.has-light-background h1,section.has-light-background h2,section.has-light-background h3,section.has-light-background h4,section.has-light-background h5,section.has-light-background h6{color:#222}section.has-light-background a,section.has-light-background a:hover{color:#2a76dd}section.has-light-background code{color:#4758ab}section.has-dark-background,section.has-dark-background h1,section.has-dark-background h2,section.has-dark-background h3,section.has-dark-background h4,section.has-dark-background h5,section.has-dark-background h6{color:#fff}section.has-dark-background a,section.has-dark-background a:hover{color:#42affa}section.has-dark-background code{color:#ffa07a}#title-slide,div.reveal div.slides section.quarto-title-block{text-align:center}#title-slide .subtitle,div.reveal div.slides section.quarto-title-block .subtitle{margin-bottom:2.5rem}.reveal .slides{text-align:left}.reveal .title-slide h1{font-size:1.6em}.reveal[data-navigation-mode=linear] .title-slide h1{font-size:2.5em}.reveal div.sourceCode{border:1px solid #bbb;border-radius:4px}.reveal pre{width:100%;box-shadow:none;background-color:#fff;border:none;margin:0;font-size:.55em;line-height:1.3;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.reveal pre code{background-color:#fff;font-size:inherit;color:#222;font-family:inherit}.reveal pre.sourceCode code{color:#222;font-size:inherit;background-color:inherit;white-space:pre;font-family:inherit;padding:6px 9px;max-height:500px}.reveal .code-with-filename .code-with-filename-file pre{background-color:unset}.reveal code{color:var(--quarto-hl-fu-color);font-size:.875em;background-color:rgba(0,0,0,0);white-space:pre-wrap;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.reveal .column-output-location{display:flex;align-items:stretch}.reveal .column-output-location .column:first-of-type div.sourceCode{height:100%;background-color:#fff}.reveal blockquote{display:block;position:relative;color:rgb(110.5,110.5,110.5);width:unset;margin:var(--r-block-margin) auto;padding:.625rem 1.75rem;border-left:.25rem solid rgb(110.5,110.5,110.5);font-style:normal;background:none;box-shadow:none}.reveal blockquote p:first-child,.reveal blockquote p:last-child{display:block}.reveal .slide aside,.reveal .slide div.aside{position:absolute;bottom:20px;font-size:0.7em;color:rgb(110.5,110.5,110.5)}.reveal .slide sup{font-size:0.7em}.reveal .slide.scrollable aside,.reveal .slide.scrollable div.aside{position:relative;margin-top:1em}.reveal .slide aside .aside-footnotes{margin-bottom:0}.reveal .slide aside .aside-footnotes li:first-of-type{margin-top:0}.reveal .layout-sidebar{display:flex;width:100%;margin-top:.8em}.reveal .layout-sidebar .panel-sidebar{width:270px}.reveal .layout-sidebar-left .panel-sidebar{margin-right:calc(0.5em*2)}.reveal .layout-sidebar-right .panel-sidebar{margin-left:calc(0.5em*2)}.reveal .layout-sidebar .panel-fill,.reveal .layout-sidebar .panel-center,.reveal .layout-sidebar .panel-tabset{flex:1}.reveal .panel-input,.reveal .panel-sidebar{font-size:.5em;padding:.5em;border-style:solid;border-color:#bbb;border-width:1px;border-radius:4px;background-color:#f8f9fa}.reveal .panel-sidebar :first-child,.reveal .panel-fill :first-child{margin-top:0}.reveal .panel-sidebar :last-child,.reveal .panel-fill :last-child{margin-bottom:0}.panel-input>div,.panel-input>div>div{vertical-align:middle;padding-right:1em}.reveal p,.reveal .slides section,.reveal .slides section>section{line-height:1.3}.reveal.smaller .slides section{font-size:0.7em}.reveal.smaller .slides section section{font-size:inherit}.reveal.smaller .slides h1{font-size:calc(2.5em/0.7)}.reveal.smaller .slides h2{font-size:calc(1.6em/0.7)}.reveal.smaller .slides h3{font-size:calc(1.3em/0.7)}.reveal .slides section.smaller{font-size:0.7em}.reveal .slides section.smaller h1{font-size:calc(2.5em/0.7)}.reveal .slides section.smaller h2{font-size:calc(1.6em/0.7)}.reveal .slides section.smaller h3{font-size:calc(1.3em/0.7)}.reveal .slides section div.callout{font-size:0.7em}.reveal .slides section div.callout h1{font-size:calc(2.5em/0.7)}.reveal .slides section div.callout h2{font-size:calc(1.6em/0.7)}.reveal .slides section div.callout h3{font-size:calc(1.3em/0.7)}.reveal .columns>.column>:not(ul,ol){margin-left:.25rem;margin-right:.25rem}.reveal .columns>.column:first-child>:not(ul,ol){margin-right:.5rem;margin-left:0}.reveal .columns>.column:last-child>:not(ul,ol){margin-right:0;margin-left:.5rem}.reveal .slide-number{color:rgb(85.979757085,146.2874493927,228.020242915);background-color:#fff}.reveal .footer{color:rgb(110.5,110.5,110.5)}.reveal .footer a{color:#2a76dd}.reveal .footer.has-dark-background{color:#fff}.reveal .footer.has-dark-background a{color:rgb(122.7,198.1114130435,250)}.reveal .footer.has-light-background{color:hsl(0,0%,31.2169312169%)}.reveal .footer.has-light-background a{color:rgb(105.9,154.769273743,221)}.reveal .slide-number{color:rgb(110.5,110.5,110.5)}.reveal .slide-number.has-dark-background{color:#fff}.reveal .slide-number.has-light-background{color:hsl(0,0%,31.2169312169%)}.reveal .slide figure>figcaption,.reveal .slide img.stretch+p.caption,.reveal .slide img.r-stretch+p.caption{font-size:0.7em}@media screen and (min-width: 500px){.reveal .controls[data-controls-layout=edges] .navigate-left{left:.2em}.reveal .controls[data-controls-layout=edges] .navigate-right{right:.2em}.reveal .controls[data-controls-layout=edges] .navigate-up{top:.4em}.reveal .controls[data-controls-layout=edges] .navigate-down{bottom:2.3em}}.tippy-box[data-theme~=light-border]{background-color:#fff;color:#222;border-radius:4px;border:solid 1px rgb(110.5,110.5,110.5);font-size:.6em}.tippy-box[data-theme~=light-border] .tippy-arrow{color:rgb(110.5,110.5,110.5)}.tippy-box[data-placement^=bottom]>.tippy-content{padding:7px 10px;z-index:1}.reveal .panel-tabset [role=tab]{padding:.25em .7em}.reveal .slide-menu-button .fa-bars::before{background-image:url('data:image/svg+xml,')}.reveal .slide-chalkboard-buttons .fa-easel2::before{background-image:url('data:image/svg+xml,')}.reveal .slide-chalkboard-buttons .fa-brush::before{background-image:url('data:image/svg+xml,')}/*! light */.reveal ol[type=a]{list-style-type:lower-alpha}.reveal ol[type=a s]{list-style-type:lower-alpha}.reveal ol[type=A s]{list-style-type:upper-alpha}.reveal ol[type=i]{list-style-type:lower-roman}.reveal ol[type=i s]{list-style-type:lower-roman}.reveal ol[type=I s]{list-style-type:upper-roman}.reveal ol[type="1"]{list-style-type:decimal}.reveal ul.task-list{list-style:none}.reveal ul.task-list li input[type=checkbox]{width:2em;height:2em;margin:0 1em .5em -1.6em;vertical-align:middle}div.cell-output-display div.pagedtable-wrapper table.table{font-size:.6em}.reveal .code-annotation-container-hidden{display:none}.reveal code.sourceCode button.code-annotation-anchor,.reveal code.sourceCode .code-annotation-anchor{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;font-size:.7em;line-height:1.2em;margin-top:2px;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none}.reveal code.sourceCode button.code-annotation-anchor{cursor:pointer}.reveal code.sourceCode a.code-annotation-anchor{text-align:center;vertical-align:middle;text-decoration:none;cursor:default;height:1.2em;width:1.2em}.reveal code.sourceCode.fragment a.code-annotation-anchor{left:auto}.reveal #code-annotation-line-highlight-gutter{width:100%;border-top:solid var(--quarto-hl-co-color) 1px;border-bottom:solid var(--quarto-hl-co-color) 1px;z-index:2}.reveal #code-annotation-line-highlight{margin-left:-8em;width:calc(100% + 4em);border-top:solid var(--quarto-hl-co-color) 1px;border-bottom:solid var(--quarto-hl-co-color) 1px;z-index:2;margin-bottom:-2px}.reveal code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#fff;font-weight:bolder}.reveal pre.code-annotation-code{padding-top:0;padding-bottom:0}.reveal pre.code-annotation-code code{z-index:3;padding-left:0px}.reveal dl.code-annotation-container-grid{margin-left:.1em}.reveal dl.code-annotation-container-grid dt{margin-top:.65rem;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;border:solid #222 1px;border-radius:50%;height:1.3em;width:1.3em;line-height:1.3em;font-size:.5em;text-align:center;vertical-align:middle;text-decoration:none}.reveal dl.code-annotation-container-grid dd{margin-left:.25em}.reveal .scrollable ol li:first-child:nth-last-child(n+10),.reveal .scrollable ol li:first-child:nth-last-child(n+10)~li{margin-left:1em}kbd{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:40px;color:#222;background-color:#f8f9fa;border:1px solid;border-color:#bbb;border-radius:5px;padding:.4rem .4rem}:root{--r-inline-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-block-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-inline-code-font-size: 0.875em;--r-block-code-font-size: 0.55em}.reveal a{font-weight:400;background-color:rgba(0,0,0,0);text-decoration:inherit}.reveal div.callout{margin-top:1rem;margin-bottom:1rem;border-radius:4px;overflow-wrap:break-word}.reveal div.callout.callout-style-simple,.reveal div.callout.callout-style-default{border-left:.3rem solid #acacac;border-right:solid 1px #bbb;border-top:solid 1px #bbb;border-bottom:solid 1px #bbb}.reveal div.callout.callout-style-simple div.callout-body,.reveal div.callout.callout-style-simple div.callout-title,.reveal div.callout.callout-style-default div.callout-body,.reveal div.callout.callout-style-default div.callout-title{font-size:inherit;border-bottom:none;font-weight:600}.reveal div.callout.callout-style-simple div.callout-title,.reveal div.callout.callout-style-default div.callout-title{display:flex;align-items:center}.reveal div.callout.callout-style-simple div.callout-title p,.reveal div.callout.callout-style-default div.callout-title p{margin-top:.5em;margin-bottom:.5em;color:var(--r-main-color)}.reveal div.callout.callout-style-simple .callout-icon::before,.reveal div.callout.callout-style-default .callout-icon::before{height:1.25em;width:1.25em;background-size:1.25em 1.25em}.reveal div.callout.callout-style-simple.callout-titled .callout-body>.callout-content>:last-child,.reveal div.callout.callout-style-default.callout-titled .callout-body>.callout-content>:last-child{margin-bottom:var(--r-block-margin)}.reveal div.callout.callout-style-simple.callout-titled .callout-body>.callout-content>:last-child:not(div.sourceCode),.reveal div.callout.callout-style-default.callout-titled .callout-body>.callout-content>:last-child:not(div.sourceCode){padding-bottom:.5rem;margin-bottom:0}.reveal div.callout.callout-style-simple.callout-titled .callout-icon::before,.reveal div.callout.callout-style-default.callout-titled .callout-icon::before{margin-top:.25em;padding-right:.25em}.reveal div.callout.callout-style-simple.no-icon::before,.reveal div.callout.callout-style-default.no-icon::before{display:none !important}.reveal div.callout.callout-style-simple{padding:0em .5em;display:flex}.reveal div.callout.callout-style-simple.callout-titled .callout-body{margin-top:.2em}.reveal div.callout.callout-style-simple.callout-titled:not(.no-icon) .callout-content{padding-left:1.6em}.reveal div.callout.callout-style-simple.callout-titled .callout-content p{margin-top:0}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body{display:flex}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-icon::before{margin-top:var(--r-block-margin);padding-right:.5em}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body>.callout-content>div.sourceCode:last-child{margin-bottom:1rem}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body>.callout-content>:first-child{margin-top:var(--r-block-margin)}.reveal div.callout.callout-style-simple .callout-icon::before{display:inline-block;content:"";background-repeat:no-repeat}.reveal div.callout.callout-style-simple div.callout-title{opacity:75%}.reveal div.callout.callout-style-simple div.callout-body{font-weight:400}.reveal div.callout.callout-style-default.callout-titled .callout-content p{margin-top:.7em}.reveal div.callout.callout-style-default .callout-icon::before{display:inline-block;content:"";background-repeat:no-repeat}.reveal div.callout.callout-style-default div.callout-body{font-weight:400}.reveal div.callout.callout-style-default div.callout-title{opacity:85%;padding-left:.5em;padding-right:.5em}.reveal div.callout.callout-style-default div.callout-content{padding-left:.5em;padding-right:.5em}.reveal div.callout .callout-body-container{flex-grow:1}.reveal div.callout.callout-note{border-left-color:#0d6efd}.reveal div.callout.callout-note.callout-style-default .callout-title{background-color:rgb(230.8,240.5,254.8)}.reveal div.callout.callout-note .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-tip{border-left-color:#198754}.reveal div.callout.callout-tip.callout-style-default .callout-title{background-color:rgb(232,243,237.9)}.reveal div.callout.callout-tip .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-warning{border-left-color:#ffc107}.reveal div.callout.callout-warning.callout-style-default .callout-title{background-color:rgb(255,248.8,230.2)}.reveal div.callout.callout-warning .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-caution{border-left-color:#fd7e14}.reveal div.callout.callout-caution.callout-style-default .callout-title{background-color:rgb(254.8,242.1,231.5)}.reveal div.callout.callout-caution .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-important{border-left-color:#dc3545}.reveal div.callout.callout-important.callout-style-default .callout-title{background-color:rgb(251.5,234.8,236.4)}.reveal div.callout.callout-important .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal .quarto-title-block .quarto-title-authors{display:flex;justify-content:center}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author{padding-left:.5em;padding-right:.5em}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:hover,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:visited,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:active{color:inherit;text-decoration:none}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-name{margin-bottom:.1rem}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-email{margin-top:0px;margin-bottom:.4em;font-size:.6em}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-orcid img{margin-bottom:4px}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-affiliation{font-size:.7em;margin-top:0px;margin-bottom:8px}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-affiliation:first{margin-top:12px}:root{--quarto-scss-export-link-color-bg: transparent;--quarto-scss-export-body-bg: #fff;--quarto-scss-export-body-color: #222;--quarto-scss-export-text-muted: rgb(110.5, 110.5, 110.5);--quarto-scss-export-gray-200: #e9ecef;--quarto-scss-export-gray-100: #f8f9fa;--quarto-scss-export-gray-900: #212529;--quarto-scss-export-primary: #2a76dd;--quarto-scss-export-link-color: #2a76dd;--quarto-scss-export-link-color-hover: rgb(85.979757085, 146.2874493927, 228.020242915);--quarto-scss-export-selection-bg: rgb(151.9493927126, 188.7186234818, 238.5506072874);--quarto-scss-export-selection-color: #fff;--quarto-scss-export-border-color: rgb(110.5, 110.5, 110.5);--quarto-scss-export-presentation-heading-color: #222;--quarto-scss-export-presentation-list-bullet-color: #222;--quarto-scss-export-code-block-bg: #fff;--quarto-scss-export-code-block-border-color: #bbbbbb;--quarto-scss-export-code-block-color: #222;--quarto-scss-export-code-bg: transparent;--quarto-scss-export-tabset-border-color: #bbbbbb;--quarto-scss-export-table-border-color: #bbbbbb;--quarto-scss-export-input-panel-border-color: #bbbbbb;--quarto-scss-export-input-panel-bg: rgb(248, 249, 250);--quarto-scss-export-callout-color-note: #0d6efd;--quarto-scss-export-callout-color-tip: #198754;--quarto-scss-export-callout-color-important: #dc3545;--quarto-scss-export-callout-color-caution: #fd7e14;--quarto-scss-export-callout-color-warning: #ffc107;--quarto-scss-export-light-bg-text-color: #222;--quarto-scss-export-dark-bg-text-color: #fff;--quarto-scss-export-light-bg-link-color: #2a76dd;--quarto-scss-export-dark-bg-link-color: #42affa;--quarto-scss-export-light-bg-code-color: #4758ab;--quarto-scss-export-dark-bg-code-color: #ffa07a;--quarto-scss-export-kbd-color: #222;--quarto-scss-export-kbd-bg: #f8f9fa;--quarto-scss-export-revealjs-heading-color: #222;--quarto-scss-export-revealjs-list-bullet-color: #222;--quarto-scss-export-backgroundColor: #fff;--quarto-scss-export-mainColor: #222;--quarto-scss-export-headingColor: #222;--quarto-scss-export-linkColor: #2a76dd;--quarto-scss-export-linkColorHover: rgb(85.979757085, 146.2874493927, 228.020242915);--quarto-scss-export-selectionBackgroundColor: rgb(151.9493927126, 188.7186234818, 238.5506072874);--quarto-scss-export-selectionColor: #fff;--quarto-scss-export-btn-code-copy-color: rgb(110.5, 110.5, 110.5);--quarto-scss-export-btn-code-copy-color-active: #2a76dd;--quarto-scss-export-secondary: #999;--quarto-scss-export-mermaid-bg-color: #fff;--quarto-scss-export-mermaid-edge-color: #999;--quarto-scss-export-mermaid-node-fg-color: #222;--quarto-scss-export-mermaid-fg-color: #222;--quarto-scss-export-mermaid-fg-color--lighter: rgb(59.5, 59.5, 59.5);--quarto-scss-export-mermaid-fg-color--lightest: #555555;--quarto-scss-export-mermaid-label-bg-color: #fff;--quarto-scss-export-mermaid-label-fg-color: #2a76dd;--quarto-scss-export-mermaid-node-bg-color: rgba(42, 118, 221, 0.1)} \ No newline at end of file diff --git a/docs/site_libs/revealjs/dist/theme/quarto-f563837468303362081e247dddd440d0.css b/docs/site_libs/revealjs/dist/theme/quarto-f563837468303362081e247dddd440d0.css new file mode 100644 index 0000000..7c88e48 --- /dev/null +++ b/docs/site_libs/revealjs/dist/theme/quarto-f563837468303362081e247dddd440d0.css @@ -0,0 +1,8 @@ +@import"./fonts/source-sans-pro/source-sans-pro.css";:root{--r-background-color: #fff;--r-main-font: Source Sans Pro, Helvetica, sans-serif;--r-main-font-size: 40px;--r-main-color: #222;--r-block-margin: 12px;--r-heading-margin: 0 0 12px 0;--r-heading-font: Source Sans Pro, Helvetica, sans-serif;--r-heading-color: #222;--r-heading-line-height: 1.2;--r-heading-letter-spacing: normal;--r-heading-text-transform: none;--r-heading-text-shadow: none;--r-heading-font-weight: 600;--r-heading1-text-shadow: none;--r-heading1-size: 2.5em;--r-heading2-size: 1.6em;--r-heading3-size: 1.3em;--r-heading4-size: 1em;--r-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-link-color: #2a76dd;--r-link-color-dark: rgb(25.6720647773, 83.0566801619, 160.8279352227);--r-link-color-hover: rgb(85.979757085, 146.2874493927, 228.020242915);--r-selection-background-color: rgb(151.9493927126, 188.7186234818, 238.5506072874);--r-selection-color: #fff;--r-overlay-element-bg-color: 240, 240, 240;--r-overlay-element-fg-color: 0, 0, 0}.reveal-viewport{background:#fff;background-color:var(--r-background-color)}.reveal{font-family:var(--r-main-font);font-size:var(--r-main-font-size);font-weight:normal;color:var(--r-main-color)}.reveal ::selection{color:var(--r-selection-color);background:var(--r-selection-background-color);text-shadow:none}.reveal ::-moz-selection{color:var(--r-selection-color);background:var(--r-selection-background-color);text-shadow:none}.reveal .slides section,.reveal .slides section>section{line-height:1.3;font-weight:inherit}.reveal h1,.reveal h2,.reveal h3,.reveal h4,.reveal h5,.reveal h6{margin:var(--r-heading-margin);color:var(--r-heading-color);font-family:var(--r-heading-font);font-weight:var(--r-heading-font-weight);line-height:var(--r-heading-line-height);letter-spacing:var(--r-heading-letter-spacing);text-transform:var(--r-heading-text-transform);text-shadow:var(--r-heading-text-shadow);word-wrap:break-word}.reveal h1{font-size:var(--r-heading1-size)}.reveal h2{font-size:var(--r-heading2-size)}.reveal h3{font-size:var(--r-heading3-size)}.reveal h4{font-size:var(--r-heading4-size)}.reveal h1{text-shadow:var(--r-heading1-text-shadow)}.reveal p{margin:var(--r-block-margin) 0;line-height:1.3}.reveal h1:last-child,.reveal h2:last-child,.reveal h3:last-child,.reveal h4:last-child,.reveal h5:last-child,.reveal h6:last-child{margin-bottom:0}.reveal img,.reveal video,.reveal iframe{max-width:95%;max-height:95%}.reveal strong,.reveal b{font-weight:bold}.reveal em{font-style:italic}.reveal ol,.reveal dl,.reveal ul{display:inline-block;text-align:left;margin:0 0 0 1em}.reveal ol{list-style-type:decimal}.reveal ul{list-style-type:disc}.reveal ul ul{list-style-type:square}.reveal ul ul ul{list-style-type:circle}.reveal ul ul,.reveal ul ol,.reveal ol ol,.reveal ol ul{display:block;margin-left:40px}.reveal dt{font-weight:bold}.reveal dd{margin-left:40px}.reveal blockquote{display:block;position:relative;width:70%;margin:var(--r-block-margin) auto;padding:5px;font-style:italic;background:hsla(0,0%,100%,.05);box-shadow:0px 0px 2px rgba(0,0,0,.2)}.reveal blockquote p:first-child,.reveal blockquote p:last-child{display:inline-block}.reveal q{font-style:italic}.reveal pre{display:block;position:relative;width:90%;margin:var(--r-block-margin) auto;text-align:left;font-size:.55em;font-family:var(--r-code-font);line-height:1.2em;word-wrap:break-word;box-shadow:0px 5px 15px rgba(0,0,0,.15)}.reveal code{font-family:var(--r-code-font);text-transform:none;tab-size:2}.reveal pre code{display:block;padding:5px;overflow:auto;max-height:400px;word-wrap:normal}.reveal .code-wrapper{white-space:normal}.reveal .code-wrapper code{white-space:pre}.reveal table{margin:auto;border-collapse:collapse;border-spacing:0}.reveal table th{font-weight:bold}.reveal table th,.reveal table td{text-align:left;padding:.2em .5em .2em .5em;border-bottom:1px solid}.reveal table th[align=center],.reveal table td[align=center]{text-align:center}.reveal table th[align=right],.reveal table td[align=right]{text-align:right}.reveal table tbody tr:last-child th,.reveal table tbody tr:last-child td{border-bottom:none}.reveal sup{vertical-align:super;font-size:smaller}.reveal sub{vertical-align:sub;font-size:smaller}.reveal small{display:inline-block;font-size:.6em;line-height:1.2em;vertical-align:top}.reveal small *{vertical-align:top}.reveal img{margin:var(--r-block-margin) 0}.reveal a{color:var(--r-link-color);text-decoration:none;transition:color .15s ease}.reveal a:hover{color:var(--r-link-color-hover);text-shadow:none;border:none}.reveal .roll span:after{color:#fff;background:var(--r-link-color-dark)}.reveal .r-frame{border:4px solid var(--r-main-color);box-shadow:0 0 10px rgba(0,0,0,.15)}.reveal a .r-frame{transition:all .15s linear}.reveal a:hover .r-frame{border-color:var(--r-link-color);box-shadow:0 0 20px rgba(0,0,0,.55)}.reveal .controls{color:var(--r-link-color)}.reveal .progress{background:rgba(0,0,0,.2);color:var(--r-link-color)}@media print{.backgrounds{background-color:var(--r-background-color)}}.top-right{position:absolute;top:1em;right:1em}.visually-hidden{border:0;clip:rect(0 0 0 0);height:auto;margin:0;overflow:hidden;padding:0;position:absolute;width:1px;white-space:nowrap}.hidden{display:none !important}.zindex-bottom{z-index:-1 !important}figure.figure{display:block}.quarto-layout-panel{margin-bottom:1em}.quarto-layout-panel>figure{width:100%}.quarto-layout-panel>figure>figcaption,.quarto-layout-panel>.panel-caption{margin-top:10pt}.quarto-layout-panel>.table-caption{margin-top:0px}.table-caption p{margin-bottom:.5em}.quarto-layout-row{display:flex;flex-direction:row;align-items:flex-start}.quarto-layout-valign-top{align-items:flex-start}.quarto-layout-valign-bottom{align-items:flex-end}.quarto-layout-valign-center{align-items:center}.quarto-layout-cell{position:relative;margin-right:20px}.quarto-layout-cell:last-child{margin-right:0}.quarto-layout-cell figure,.quarto-layout-cell>p{margin:.2em}.quarto-layout-cell img{max-width:100%}.quarto-layout-cell .html-widget{width:100% !important}.quarto-layout-cell div figure p{margin:0}.quarto-layout-cell figure{display:block;margin-inline-start:0;margin-inline-end:0}.quarto-layout-cell table{display:inline-table}.quarto-layout-cell-subref figcaption,figure .quarto-layout-row figure figcaption{text-align:center;font-style:italic}.quarto-figure{position:relative;margin-bottom:1em}.quarto-figure>figure{width:100%;margin-bottom:0}.quarto-figure-left>figure>p,.quarto-figure-left>figure>div{text-align:left}.quarto-figure-center>figure>p,.quarto-figure-center>figure>div{text-align:center}.quarto-figure-right>figure>p,.quarto-figure-right>figure>div{text-align:right}.quarto-figure>figure>div.cell-annotation,.quarto-figure>figure>div code{text-align:left}figure>p:empty{display:none}figure>p:first-child{margin-top:0;margin-bottom:0}figure>figcaption.quarto-float-caption-bottom{margin-bottom:.5em}figure>figcaption.quarto-float-caption-top{margin-top:.5em}div[id^=tbl-]{position:relative}.quarto-figure>.anchorjs-link{position:absolute;top:.6em;right:.5em}div[id^=tbl-]>.anchorjs-link{position:absolute;top:.7em;right:.3em}.quarto-figure:hover>.anchorjs-link,div[id^=tbl-]:hover>.anchorjs-link,h2:hover>.anchorjs-link,h3:hover>.anchorjs-link,h4:hover>.anchorjs-link,h5:hover>.anchorjs-link,h6:hover>.anchorjs-link,.reveal-anchorjs-link>.anchorjs-link{opacity:1}#title-block-header{margin-block-end:1rem;position:relative;margin-top:-1px}#title-block-header .abstract{margin-block-start:1rem}#title-block-header .abstract .abstract-title{font-weight:600}#title-block-header a{text-decoration:none}#title-block-header .author,#title-block-header .date,#title-block-header .doi{margin-block-end:.2rem}#title-block-header .quarto-title-block>div{display:flex}#title-block-header .quarto-title-block>div>h1{flex-grow:1}#title-block-header .quarto-title-block>div>button{flex-shrink:0;height:2.25rem;margin-top:0}tr.header>th>p:last-of-type{margin-bottom:0px}table,table.table{margin-top:.5rem;margin-bottom:.5rem}caption,.table-caption{padding-top:.5rem;padding-bottom:.5rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-top{margin-top:.5rem;margin-bottom:.25rem;text-align:center}figure.quarto-float-tbl figcaption.quarto-float-caption-bottom{padding-top:.25rem;margin-bottom:.5rem;text-align:center}.utterances{max-width:none;margin-left:-8px}iframe{margin-bottom:1em}details{margin-bottom:1em}details[show]{margin-bottom:0}details>summary{color:rgb(110.5,110.5,110.5)}details>summary>p:only-child{display:inline}pre.sourceCode,code.sourceCode{position:relative}dd code:not(.sourceCode),p code:not(.sourceCode){white-space:pre-wrap}code{white-space:pre}@media print{code{white-space:pre-wrap}}pre>code{display:block}pre>code.sourceCode{white-space:pre}pre>code.sourceCode>span>a:first-child::before{text-decoration:none}pre.code-overflow-wrap>code.sourceCode{white-space:pre-wrap}pre.code-overflow-scroll>code.sourceCode{white-space:pre}code a:any-link{color:inherit;text-decoration:none}code a:hover{color:inherit;text-decoration:underline}ul.task-list{padding-left:1em}[data-tippy-root]{display:inline-block}.tippy-content .footnote-back{display:none}.footnote-back{margin-left:.2em}.tippy-content{overflow-x:auto}.quarto-embedded-source-code{display:none}.quarto-unresolved-ref{font-weight:600}.quarto-cover-image{max-width:35%;float:right;margin-left:30px}.cell-output-display .widget-subarea{margin-bottom:1em}.cell-output-display:not(.no-overflow-x),.knitsql-table:not(.no-overflow-x){overflow-x:auto}.panel-input{margin-bottom:1em}.panel-input>div,.panel-input>div>div{display:inline-block;vertical-align:top;padding-right:12px}.panel-input>p:last-child{margin-bottom:0}.layout-sidebar{margin-bottom:1em}.layout-sidebar .tab-content{border:none}.tab-content>.page-columns.active{display:grid}div.sourceCode>iframe{width:100%;height:300px;margin-bottom:-0.5em}a{text-underline-offset:3px}.callout pre.sourceCode{padding-left:0}div.ansi-escaped-output{font-family:monospace;display:block}/*! +* +* ansi colors from IPython notebook's +* +* we also add `bright-[color]-` synonyms for the `-[color]-intense` classes since +* that seems to be what ansi_up emits +* +*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-black,.ansi-bright-black-fg{color:#282c36}.ansi-black-intense-black,.ansi-bright-black-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-red,.ansi-bright-red-fg{color:#b22b31}.ansi-red-intense-red,.ansi-bright-red-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-green,.ansi-bright-green-fg{color:#007427}.ansi-green-intense-green,.ansi-bright-green-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-yellow,.ansi-bright-yellow-fg{color:#b27d12}.ansi-yellow-intense-yellow,.ansi-bright-yellow-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-blue,.ansi-bright-blue-fg{color:#0065ca}.ansi-blue-intense-blue,.ansi-bright-blue-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-magenta,.ansi-bright-magenta-fg{color:#a03196}.ansi-magenta-intense-magenta,.ansi-bright-magenta-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-cyan,.ansi-bright-cyan-fg{color:#258f8f}.ansi-cyan-intense-cyan,.ansi-bright-cyan-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-white,.ansi-bright-white-fg{color:#a1a6b2}.ansi-white-intense-white,.ansi-bright-white-bg{background-color:#a1a6b2}.ansi-default-inverse-fg{color:#fff}.ansi-default-inverse-bg{background-color:#000}.ansi-bold{font-weight:bold}.ansi-underline{text-decoration:underline}:root{--quarto-body-bg: #fff;--quarto-body-color: #222;--quarto-text-muted: rgb(110.5, 110.5, 110.5);--quarto-border-color: #bbbbbb;--quarto-border-width: 1px;--quarto-border-radius: 4px}table.gt_table{color:var(--quarto-body-color);font-size:1em;width:100%;background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_column_spanner_outer{color:var(--quarto-body-color);background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_col_heading{color:var(--quarto-body-color);font-weight:bold;background-color:rgba(0,0,0,0)}table.gt_table thead.gt_col_headings{border-bottom:1px solid currentColor;border-top-width:inherit;border-top-color:var(--quarto-border-color)}table.gt_table thead.gt_col_headings:not(:first-child){border-top-width:1px;border-top-color:var(--quarto-border-color)}table.gt_table td.gt_row{border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-width:0px}table.gt_table tbody.gt_table_body{border-top-width:1px;border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-color:currentColor}div.columns{display:initial;gap:initial}div.column{display:inline-block;overflow-x:initial;vertical-align:top;width:50%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:relative;float:right;background-color:rgba(0,0,0,0)}input[type=checkbox]{margin-right:.5ch}:root{--mermaid-bg-color: #fff;--mermaid-edge-color: #999;--mermaid-node-fg-color: #222;--mermaid-fg-color: #222;--mermaid-fg-color--lighter: rgb(59.5, 59.5, 59.5);--mermaid-fg-color--lightest: #555555;--mermaid-font-family: Source Sans Pro, Helvetica, sans-serif;--mermaid-label-bg-color: #fff;--mermaid-label-fg-color: #2a76dd;--mermaid-node-bg-color: rgba(42, 118, 221, 0.1);--mermaid-node-fg-color: #222}@media print{:root{font-size:11pt}#quarto-sidebar,#TOC,.nav-page{display:none}.page-columns .content{grid-column-start:page-start}.fixed-top{position:relative}.panel-caption,.figure-caption,figcaption{color:#666}}.code-copy-button{position:absolute;top:0;right:0;border:0;margin-top:5px;margin-right:5px;background-color:rgba(0,0,0,0);z-index:3}.code-copy-button-tooltip{font-size:.75em}pre.sourceCode:hover>.code-copy-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}pre.sourceCode:hover>.code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}pre.sourceCode:hover>.code-copy-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}pre.sourceCode:hover>.code-copy-button-checked:hover>.bi::before{background-image:url('data:image/svg+xml,')}.panel-tabset [role=tablist]{border-bottom:1px solid #bbb;list-style:none;margin:0;padding:0;width:100%}.panel-tabset [role=tablist] *{-webkit-box-sizing:border-box;box-sizing:border-box}@media(min-width: 30em){.panel-tabset [role=tablist] li{display:inline-block}}.panel-tabset [role=tab]{border:1px solid rgba(0,0,0,0);border-top-color:#bbb;display:block;padding:.5em 1em;text-decoration:none}@media(min-width: 30em){.panel-tabset [role=tab]{border-top-color:rgba(0,0,0,0);display:inline-block;margin-bottom:-1px}}.panel-tabset [role=tab][aria-selected=true]{background-color:#bbb}@media(min-width: 30em){.panel-tabset [role=tab][aria-selected=true]{background-color:rgba(0,0,0,0);border:1px solid #bbb;border-bottom-color:#fff}}@media(min-width: 30em){.panel-tabset [role=tab]:hover:not([aria-selected=true]){border:1px solid #bbb}}.code-with-filename .code-with-filename-file{margin-bottom:0;padding-bottom:2px;padding-top:2px;padding-left:.7em;border:var(--quarto-border-width) solid var(--quarto-border-color);border-radius:var(--quarto-border-radius);border-bottom:0;border-bottom-left-radius:0%;border-bottom-right-radius:0%}.code-with-filename div.sourceCode,.reveal .code-with-filename div.sourceCode{margin-top:0;border-top-left-radius:0%;border-top-right-radius:0%}.code-with-filename .code-with-filename-file pre{margin-bottom:0}.code-with-filename .code-with-filename-file{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}.reveal.center .slide aside,.reveal.center .slide div.aside{position:initial}section.has-light-background,section.has-light-background h1,section.has-light-background h2,section.has-light-background h3,section.has-light-background h4,section.has-light-background h5,section.has-light-background h6{color:#222}section.has-light-background a,section.has-light-background a:hover{color:#2a76dd}section.has-light-background code{color:#4758ab}section.has-dark-background,section.has-dark-background h1,section.has-dark-background h2,section.has-dark-background h3,section.has-dark-background h4,section.has-dark-background h5,section.has-dark-background h6{color:#fff}section.has-dark-background a,section.has-dark-background a:hover{color:#42affa}section.has-dark-background code{color:#ffa07a}#title-slide,div.reveal div.slides section.quarto-title-block{text-align:center}#title-slide .subtitle,div.reveal div.slides section.quarto-title-block .subtitle{margin-bottom:2.5rem}.reveal .slides{text-align:left}.reveal .title-slide h1{font-size:1.6em}.reveal[data-navigation-mode=linear] .title-slide h1{font-size:2.5em}.reveal div.sourceCode{border:1px solid #bbb;border-radius:4px}.reveal pre{width:100%;box-shadow:none;background-color:#fff;border:none;margin:0;font-size:.55em;line-height:1.3;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.reveal pre code{background-color:#fff;font-size:inherit;color:#222;font-family:inherit}.reveal pre.sourceCode code{color:#222;font-size:inherit;background-color:inherit;white-space:pre;font-family:inherit;padding:6px 9px;max-height:500px}.reveal .code-with-filename .code-with-filename-file pre{background-color:unset}.reveal code{color:var(--quarto-hl-fu-color);font-size:.875em;background-color:rgba(0,0,0,0);white-space:pre-wrap;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.reveal .column-output-location{display:flex;align-items:stretch}.reveal .column-output-location .column:first-of-type div.sourceCode{height:100%;background-color:#fff}.reveal blockquote{display:block;position:relative;color:rgb(110.5,110.5,110.5);width:unset;margin:var(--r-block-margin) auto;padding:.625rem 1.75rem;border-left:.25rem solid rgb(110.5,110.5,110.5);font-style:normal;background:none;box-shadow:none}.reveal blockquote p:first-child,.reveal blockquote p:last-child{display:block}.reveal .slide aside,.reveal .slide div.aside{position:absolute;bottom:20px;font-size:0.7em;color:rgb(110.5,110.5,110.5)}.reveal .slide sup{font-size:0.7em}.reveal .slide.scrollable aside,.reveal .slide.scrollable div.aside{position:relative;margin-top:1em}.reveal .slide aside .aside-footnotes{margin-bottom:0}.reveal .slide aside .aside-footnotes li:first-of-type{margin-top:0}.reveal .layout-sidebar{display:flex;width:100%;margin-top:.8em}.reveal .layout-sidebar .panel-sidebar{width:270px}.reveal .layout-sidebar-left .panel-sidebar{margin-right:calc(0.5em*2)}.reveal .layout-sidebar-right .panel-sidebar{margin-left:calc(0.5em*2)}.reveal .layout-sidebar .panel-fill,.reveal .layout-sidebar .panel-center,.reveal .layout-sidebar .panel-tabset{flex:1}.reveal .panel-input,.reveal .panel-sidebar{font-size:.5em;padding:.5em;border-style:solid;border-color:#bbb;border-width:1px;border-radius:4px;background-color:#f8f9fa}.reveal .panel-sidebar :first-child,.reveal .panel-fill :first-child{margin-top:0}.reveal .panel-sidebar :last-child,.reveal .panel-fill :last-child{margin-bottom:0}.panel-input>div,.panel-input>div>div{vertical-align:middle;padding-right:1em}.reveal p,.reveal .slides section,.reveal .slides section>section{line-height:1.3}.reveal.smaller .slides section{font-size:0.7em}.reveal.smaller .slides section section{font-size:inherit}.reveal.smaller .slides h1{font-size:calc(2.5em/0.7)}.reveal.smaller .slides h2{font-size:calc(1.6em/0.7)}.reveal.smaller .slides h3{font-size:calc(1.3em/0.7)}.reveal .slides section.smaller{font-size:0.7em}.reveal .slides section.smaller h1{font-size:calc(2.5em/0.7)}.reveal .slides section.smaller h2{font-size:calc(1.6em/0.7)}.reveal .slides section.smaller h3{font-size:calc(1.3em/0.7)}.reveal .slides section div.callout{font-size:0.7em}.reveal .slides section div.callout h1{font-size:calc(2.5em/0.7)}.reveal .slides section div.callout h2{font-size:calc(1.6em/0.7)}.reveal .slides section div.callout h3{font-size:calc(1.3em/0.7)}.reveal .columns>.column>:not(ul,ol){margin-left:.25rem;margin-right:.25rem}.reveal .columns>.column:first-child>:not(ul,ol){margin-right:.5rem;margin-left:0}.reveal .columns>.column:last-child>:not(ul,ol){margin-right:0;margin-left:.5rem}.reveal .slide-number{color:rgb(85.979757085,146.2874493927,228.020242915);background-color:#fff}.reveal .footer{color:rgb(110.5,110.5,110.5)}.reveal .footer a{color:#2a76dd}.reveal .footer.has-dark-background{color:#fff}.reveal .footer.has-dark-background a{color:rgb(122.7,198.1114130435,250)}.reveal .footer.has-light-background{color:hsl(0,0%,31.2169312169%)}.reveal .footer.has-light-background a{color:rgb(105.9,154.769273743,221)}.reveal .slide-number{color:rgb(110.5,110.5,110.5)}.reveal .slide-number.has-dark-background{color:#fff}.reveal .slide-number.has-light-background{color:hsl(0,0%,31.2169312169%)}.reveal .slide figure>figcaption,.reveal .slide img.stretch+p.caption,.reveal .slide img.r-stretch+p.caption{font-size:0.7em}@media screen and (min-width: 500px){.reveal .controls[data-controls-layout=edges] .navigate-left{left:.2em}.reveal .controls[data-controls-layout=edges] .navigate-right{right:.2em}.reveal .controls[data-controls-layout=edges] .navigate-up{top:.4em}.reveal .controls[data-controls-layout=edges] .navigate-down{bottom:2.3em}}.tippy-box[data-theme~=light-border]{background-color:#fff;color:#222;border-radius:4px;border:solid 1px rgb(110.5,110.5,110.5);font-size:.6em}.tippy-box[data-theme~=light-border] .tippy-arrow{color:rgb(110.5,110.5,110.5)}.tippy-box[data-placement^=bottom]>.tippy-content{padding:7px 10px;z-index:1}.reveal .panel-tabset [role=tab]{padding:.25em .7em}.reveal .slide-menu-button .fa-bars::before{background-image:url('data:image/svg+xml,')}.reveal .slide-chalkboard-buttons .fa-easel2::before{background-image:url('data:image/svg+xml,')}.reveal .slide-chalkboard-buttons .fa-brush::before{background-image:url('data:image/svg+xml,')}/*! light */.reveal ol[type=a]{list-style-type:lower-alpha}.reveal ol[type=a s]{list-style-type:lower-alpha}.reveal ol[type=A s]{list-style-type:upper-alpha}.reveal ol[type=i]{list-style-type:lower-roman}.reveal ol[type=i s]{list-style-type:lower-roman}.reveal ol[type=I s]{list-style-type:upper-roman}.reveal ol[type="1"]{list-style-type:decimal}.reveal ul.task-list{list-style:none}.reveal ul.task-list li input[type=checkbox]{width:2em;height:2em;margin:0 1em .5em -1.6em;vertical-align:middle}div.cell-output-display div.pagedtable-wrapper table.table{font-size:.6em}.reveal .code-annotation-container-hidden{display:none}.reveal code.sourceCode button.code-annotation-anchor,.reveal code.sourceCode .code-annotation-anchor{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;font-size:.7em;line-height:1.2em;margin-top:2px;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none}.reveal code.sourceCode button.code-annotation-anchor{cursor:pointer}.reveal code.sourceCode a.code-annotation-anchor{text-align:center;vertical-align:middle;text-decoration:none;cursor:default;height:1.2em;width:1.2em}.reveal code.sourceCode.fragment a.code-annotation-anchor{left:auto}.reveal #code-annotation-line-highlight-gutter{width:100%;border-top:solid var(--quarto-hl-co-color) 1px;border-bottom:solid var(--quarto-hl-co-color) 1px;z-index:2}.reveal #code-annotation-line-highlight{margin-left:-8em;width:calc(100% + 4em);border-top:solid var(--quarto-hl-co-color) 1px;border-bottom:solid var(--quarto-hl-co-color) 1px;z-index:2;margin-bottom:-2px}.reveal code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#fff;font-weight:bolder}.reveal pre.code-annotation-code{padding-top:0;padding-bottom:0}.reveal pre.code-annotation-code code{z-index:3;padding-left:0px}.reveal dl.code-annotation-container-grid{margin-left:.1em}.reveal dl.code-annotation-container-grid dt{margin-top:.65rem;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;border:solid #222 1px;border-radius:50%;height:1.3em;width:1.3em;line-height:1.3em;font-size:.5em;text-align:center;vertical-align:middle;text-decoration:none}.reveal dl.code-annotation-container-grid dd{margin-left:.25em}.reveal .scrollable ol li:first-child:nth-last-child(n+10),.reveal .scrollable ol li:first-child:nth-last-child(n+10)~li{margin-left:1em}kbd{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:40px;color:#222;background-color:#f8f9fa;border:1px solid;border-color:#bbb;border-radius:5px;padding:.4rem .4rem}:root{--r-inline-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-block-code-font: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;--r-inline-code-font-size: 0.875em;--r-block-code-font-size: 0.55em}.reveal a{font-weight:400;background-color:rgba(0,0,0,0);text-decoration:inherit}.reveal div.callout{margin-top:1rem;margin-bottom:1rem;border-radius:4px;overflow-wrap:break-word}.reveal div.callout.callout-style-simple,.reveal div.callout.callout-style-default{border-left:.3rem solid #acacac;border-right:solid 1px #bbb;border-top:solid 1px #bbb;border-bottom:solid 1px #bbb}.reveal div.callout.callout-style-simple div.callout-body,.reveal div.callout.callout-style-simple div.callout-title,.reveal div.callout.callout-style-default div.callout-body,.reveal div.callout.callout-style-default div.callout-title{font-size:inherit;border-bottom:none;font-weight:600}.reveal div.callout.callout-style-simple div.callout-title,.reveal div.callout.callout-style-default div.callout-title{display:flex;align-items:center}.reveal div.callout.callout-style-simple div.callout-title p,.reveal div.callout.callout-style-default div.callout-title p{margin-top:.5em;margin-bottom:.5em;color:var(--r-main-color)}.reveal div.callout.callout-style-simple .callout-icon::before,.reveal div.callout.callout-style-default .callout-icon::before{height:1.25em;width:1.25em;background-size:1.25em 1.25em}.reveal div.callout.callout-style-simple.callout-titled .callout-body>.callout-content>:last-child,.reveal div.callout.callout-style-default.callout-titled .callout-body>.callout-content>:last-child{margin-bottom:var(--r-block-margin)}.reveal div.callout.callout-style-simple.callout-titled .callout-body>.callout-content>:last-child:not(div.sourceCode),.reveal div.callout.callout-style-default.callout-titled .callout-body>.callout-content>:last-child:not(div.sourceCode){padding-bottom:.5rem;margin-bottom:0}.reveal div.callout.callout-style-simple.callout-titled .callout-icon::before,.reveal div.callout.callout-style-default.callout-titled .callout-icon::before{margin-top:.25em;padding-right:.25em}.reveal div.callout.callout-style-simple.no-icon::before,.reveal div.callout.callout-style-default.no-icon::before{display:none !important}.reveal div.callout.callout-style-simple{padding:0em .5em;display:flex}.reveal div.callout.callout-style-simple.callout-titled .callout-body{margin-top:.2em}.reveal div.callout.callout-style-simple.callout-titled:not(.no-icon) .callout-content{padding-left:1.6em}.reveal div.callout.callout-style-simple.callout-titled .callout-content p{margin-top:0}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body{display:flex}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-icon::before{margin-top:var(--r-block-margin);padding-right:.5em}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body>.callout-content>div.sourceCode:last-child{margin-bottom:1rem}.reveal div.callout.callout-style-simple:not(.callout-titled) .callout-body>.callout-content>:first-child{margin-top:var(--r-block-margin)}.reveal div.callout.callout-style-simple .callout-icon::before{display:inline-block;content:"";background-repeat:no-repeat}.reveal div.callout.callout-style-simple div.callout-title{opacity:75%}.reveal div.callout.callout-style-simple div.callout-body{font-weight:400}.reveal div.callout.callout-style-default.callout-titled .callout-content p{margin-top:.7em}.reveal div.callout.callout-style-default .callout-icon::before{display:inline-block;content:"";background-repeat:no-repeat}.reveal div.callout.callout-style-default div.callout-body{font-weight:400}.reveal div.callout.callout-style-default div.callout-title{opacity:85%;padding-left:.5em;padding-right:.5em}.reveal div.callout.callout-style-default div.callout-content{padding-left:.5em;padding-right:.5em}.reveal div.callout .callout-body-container{flex-grow:1}.reveal div.callout.callout-note{border-left-color:#0d6efd}.reveal div.callout.callout-note.callout-style-default .callout-title{background-color:rgb(230.8,240.5,254.8)}.reveal div.callout.callout-note .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-tip{border-left-color:#198754}.reveal div.callout.callout-tip.callout-style-default .callout-title{background-color:rgb(232,243,237.9)}.reveal div.callout.callout-tip .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-warning{border-left-color:#ffc107}.reveal div.callout.callout-warning.callout-style-default .callout-title{background-color:rgb(255,248.8,230.2)}.reveal div.callout.callout-warning .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-caution{border-left-color:#fd7e14}.reveal div.callout.callout-caution.callout-style-default .callout-title{background-color:rgb(254.8,242.1,231.5)}.reveal div.callout.callout-caution .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal div.callout.callout-important{border-left-color:#dc3545}.reveal div.callout.callout-important.callout-style-default .callout-title{background-color:rgb(251.5,234.8,236.4)}.reveal div.callout.callout-important .callout-icon::before{background-image:url('data:image/svg+xml,');}.reveal .quarto-title-block .quarto-title-authors{display:flex;justify-content:center}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author{padding-left:.5em;padding-right:.5em}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:hover,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:visited,.reveal .quarto-title-block .quarto-title-authors .quarto-title-author a:active{color:inherit;text-decoration:none}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-name{margin-bottom:.1rem}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-email{margin-top:0px;margin-bottom:.4em;font-size:.6em}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-author-orcid img{margin-bottom:4px}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-affiliation{font-size:.7em;margin-top:0px;margin-bottom:8px}.reveal .quarto-title-block .quarto-title-authors .quarto-title-author .quarto-title-affiliation:first{margin-top:12px}:root{--quarto-scss-export-link-color-bg: transparent;--quarto-scss-export-body-bg: #fff;--quarto-scss-export-body-color: #222;--quarto-scss-export-text-muted: rgb(110.5, 110.5, 110.5);--quarto-scss-export-gray-200: #e9ecef;--quarto-scss-export-gray-100: #f8f9fa;--quarto-scss-export-gray-900: #212529;--quarto-scss-export-primary: #2a76dd;--quarto-scss-export-link-color: #2a76dd;--quarto-scss-export-link-color-hover: rgb(85.979757085, 146.2874493927, 228.020242915);--quarto-scss-export-selection-bg: rgb(151.9493927126, 188.7186234818, 238.5506072874);--quarto-scss-export-selection-color: #fff;--quarto-scss-export-border-color: rgb(110.5, 110.5, 110.5);--quarto-scss-export-presentation-heading-color: #222;--quarto-scss-export-presentation-list-bullet-color: #222;--quarto-scss-export-code-block-bg: #fff;--quarto-scss-export-code-block-border-color: #bbbbbb;--quarto-scss-export-code-block-color: #222;--quarto-scss-export-code-bg: transparent;--quarto-scss-export-tabset-border-color: #bbbbbb;--quarto-scss-export-table-border-color: #bbbbbb;--quarto-scss-export-input-panel-border-color: #bbbbbb;--quarto-scss-export-input-panel-bg: rgb(248, 249, 250);--quarto-scss-export-callout-color-note: #0d6efd;--quarto-scss-export-callout-color-tip: #198754;--quarto-scss-export-callout-color-important: #dc3545;--quarto-scss-export-callout-color-caution: #fd7e14;--quarto-scss-export-callout-color-warning: #ffc107;--quarto-scss-export-light-bg-text-color: #222;--quarto-scss-export-dark-bg-text-color: #fff;--quarto-scss-export-light-bg-link-color: #2a76dd;--quarto-scss-export-dark-bg-link-color: #42affa;--quarto-scss-export-light-bg-code-color: #4758ab;--quarto-scss-export-dark-bg-code-color: #ffa07a;--quarto-scss-export-kbd-color: #222;--quarto-scss-export-kbd-bg: #f8f9fa;--quarto-scss-export-revealjs-heading-color: #222;--quarto-scss-export-revealjs-list-bullet-color: #222;--quarto-scss-export-backgroundColor: #fff;--quarto-scss-export-mainColor: #222;--quarto-scss-export-headingColor: #222;--quarto-scss-export-linkColor: #2a76dd;--quarto-scss-export-linkColorHover: rgb(85.979757085, 146.2874493927, 228.020242915);--quarto-scss-export-selectionBackgroundColor: rgb(151.9493927126, 188.7186234818, 238.5506072874);--quarto-scss-export-selectionColor: #fff;--quarto-scss-export-btn-code-copy-color: rgb(110.5, 110.5, 110.5);--quarto-scss-export-btn-code-copy-color-active: #2a76dd;--quarto-scss-export-secondary: #999;--quarto-scss-export-mermaid-bg-color: #fff;--quarto-scss-export-mermaid-edge-color: #999;--quarto-scss-export-mermaid-node-fg-color: #222;--quarto-scss-export-mermaid-fg-color: #222;--quarto-scss-export-mermaid-fg-color--lighter: rgb(59.5, 59.5, 59.5);--quarto-scss-export-mermaid-fg-color--lightest: #555555;--quarto-scss-export-mermaid-label-bg-color: #fff;--quarto-scss-export-mermaid-label-fg-color: #2a76dd;--quarto-scss-export-mermaid-node-bg-color: rgba(42, 118, 221, 0.1)} \ No newline at end of file diff --git a/pages/Bloque4/forms.html b/pages/Bloque4/forms.html new file mode 100644 index 0000000..f233a80 --- /dev/null +++ b/pages/Bloque4/forms.html @@ -0,0 +1,32 @@ + + + + + Prueba + + + +
+
+ + Nombre
+ Apellido + + + +
+ + +

Google

+ +
+ + + + +
+ +
+ + + \ No newline at end of file diff --git a/pages/Bloque4/rando.csv b/pages/Bloque4/rando.csv new file mode 100644 index 0000000..ed0b272 --- /dev/null +++ b/pages/Bloque4/rando.csv @@ -0,0 +1,31 @@ +company,S01,S02,S03,S04,S05,S06,S07,S08 +C01,0,0,1,0,0,0,0,0 +C02,1,0,0,0,0,0,0,0 +C03,1,0,1,1,0,0,1,1 +C04,1,0,0,1,0,0,1,0 +C05,1,1,1,0,1,0,1,0 +C06,1,1,0,0,0,0,1,0 +C07,0,0,1,1,1,1,0,1 +C08,1,0,1,0,0,0,0,1 +C09,1,1,0,1,0,0,0,1 +C10,1,1,0,0,1,0,1,1 +C11,1,0,1,0,0,1,1,1 +C12,1,0,0,1,0,0,0,0 +C13,0,1,0,1,1,1,1,0 +C14,0,1,1,0,1,1,0,1 +C15,0,1,0,0,1,0,0,1 +C16,0,0,1,1,0,0,1,1 +C17,0,0,0,0,0,1,0,0 +C18,0,1,0,1,1,0,0,1 +C19,1,1,1,1,0,0,0,0 +C20,1,0,0,0,0,0,0,0 +C21,0,0,1,0,0,1,1,0 +C22,0,1,0,1,1,0,0,0 +C23,1,1,1,1,1,0,0,0 +C24,1,1,0,0,0,0,1,0 +C25,1,0,1,1,0,0,1,0 +C26,0,0,0,0,0,1,1,1 +C27,0,1,0,0,1,0,1,1 +C28,1,1,1,1,0,0,1,0 +C29,0,0,1,0,0,1,0,0 +C30,0,0,0,0,0,1,0,0 diff --git a/pages/backend-linux.zip b/pages/backend-linux.zip new file mode 100644 index 0000000..783b53e Binary files /dev/null and b/pages/backend-linux.zip differ diff --git a/pages/backend-windows.zip b/pages/backend-windows.zip new file mode 100644 index 0000000..c55c4ce Binary files /dev/null and b/pages/backend-windows.zip differ diff --git a/pages/final.qmd b/pages/final.qmd new file mode 100644 index 0000000..d9dfe4e --- /dev/null +++ b/pages/final.qmd @@ -0,0 +1,537 @@ +--- +title: "Proyecto Final TC SW" +author: "Javier Ribal del Río" +date: "2026-03-27" +date-modified: today +affiliation: "Hyperloop UPV" +toc: true +toc-title: "Tabla de contenidos" +--- + +{{< pagebreak >}} + +## Contextualización de la tarea + +El Hyperloop es una tecnología que se basa en la eficiencia energética; para ello se introduce lo que a día de hoy conocemos como *booster*, un módulo de infraestructura que confiere energía al pod para su propulsión. Al externalizar la potencia de la cápsula al carril, se logra un sistema más verde y sostenible, reduciendo el peso del vehículo y optimizando el consumo eléctrico mediante una gestión inteligente de la energía. + +A lo largo de los años en *Hyperloop-UPV* hemos tratado de desarrollar nuestro propio *booster*, uno de los problemas que identificamos a la hora de desarrollarlo es que para poder hacer pruebas era necesario disponer del POD completo. Por ello, este año hemos diseñado una bancada de pruebas^[Sistema estructural diseñado para el montaje rígido de prototipos o componentes, equipado con sensores para la medición de parámetros operativos y la simulación de condiciones de carga controladas.] que nos permite validar el funcionamiento del *booster* de forma sencilla. + +### Fundamentos físicos + +#### Componentes + +Podemos dividir la bancada de pruebas en 4 componentes: + +- **Carro *booster*** (o Carro): módulo de tracción móvil compuesto por un bastidor de cuatro ruedas y un EMS^[Un imán.] que es propulsado por la sección +- ***Track***: vía de 50 metros por la que circula el Carro *booster* + - **Sección *booster***: sección del *track* por la que el al pasar Carro *booster* lo impulsa. +- **Armario *booster***: lugar donde se almacenan los supercondensadores y las resistencias que generan el campo magnético que impulsa al Carro *booster* en la Sección *booster* + +#### Variables + +Trabajaremos como todas la variables asumiendo que son escalares. + +##### Carro *Booster* + +- Posición: ubicación física del Carro en el *Track*. $s$ medida en $\mathrm{m}$ y $s \in[0,50]$. + - Asumiremos que la Sección *booster* se encuentra ubicada entre $s=2$ y $s =4$ +- Velocidad: cambio de la posición del Carro respecto al tiempo. $v$ medida en $\mathrm{km/h}$ +- Aceleración: cambio de la velocidad del Carro respecto al tiempo. $a$ medida en $\mathrm{m/s^2}$ +- Masa: masa total del Carro *booster*. $m$ medida en $\mathrm{kg}$ +- Fuerza: fuerza neta aplicada sobre el Carro, resultado del empuje del *booster* y de las fuerzas de frenado u oposición al movimiento. $F$ medida en $\mathrm{N}$. Por convenio, $F>0$ si actúa en el sentido de avance (dirección creciente de $s$) y $F<0$ en caso contrario. + +##### Armario *Booster* + +- Tensión: diferencia de potencial eléctrico suministrada por el sistema de almacenamiento de energía. $V$ medida en $\mathrm{V}$ +- Intensidad: corriente eléctrica que circula por el sistema para generar el campo magnético del *booster*. $I$ medida en $\mathrm{A}$ + + +##### Otras consideraciones + +Asumiremos que: + +- El movimiento únicamente es horizontal +$$ +\begin{aligned} +g &= 9.81\,\mathrm{m/s^2} \\ +F &= m \cdot a \\ +|\vec{N}| &= |\vec{P}| = m g \\ +F_\text{rozamiento} &= \mu|\vec{N}| \\ +\mu &= +\begin{cases} +0, & \text{si el Carro no está frenando} \\ +0.5, & \text{si el Carro está frenando} +\end{cases} +\end{aligned} +$$ + +#### Funcionamiento + +- Partiremos de un estado inicial de espera `IDLE` donde $s=0; a = 0; v = 0; V= 0; I = 0$; +- Se dará la orden de precarga `PRECHARGE` donde todas las variables se mantendrán a 0 menos $V$ que empezará a aumentar hasta alcanzar $V =400$ +- Cuando $V=400$ entramos al estado de `READY`, donde la bancada está lista para operar +- Cuando se inicie la prueba, el carro adquirirá una $v_\text{base} = 4$ que mantendrá en todo momento hasta que frene. `RUNNING` $a = 0; V= 400$ +- Al entrar en la Sección *Booster* la velocidad y aceleración del Carro se verán incrementadas. `BOOSTING`. Durante el funcionamiento del *Booster* la Intensidad se modificará +- Una vez haya salido de la Sección *Booster* ($s > 4$) mantendrá una velocidad constante de $v = 25$ hasta que el Carro llegue al final del *track* ($s=50$) o bien sea frenado. `RUNNING` +- Frenado: al lanzar la orden de Frenado `BRAKE` el Carro comenzará a disminuir su velocidad hasta $v = 0$ o llegar al final del *track*. + - Si el Carro llega a detenerse antes de alcanzar el final del *track* ($s < 50$), se informará mediante un mensaje + - En caso de que el Carro llegue al final del *track* con $v > 0$, será detenido por el *mechanical stopper*^[Plancha de metal que asegura que el Carro *booster* no salga del *track*.], también se informará con un mensaje. +- Una vez el Carro reste detenido `STOPPED` todas las variables de mantendran a 0 salvo la posición. + + +{{< pagebreak >}} + +## Objetivos de aprendizaje + +El objetivo principal de la tarea es poner en práctica el contenido aprendido durante el *training center* de Software, el diseño de UI dinámicas para el control de telemetría del vehículo. Asimismo, el proyecto se realizará en colaboración con otros subsistemas, para potenciar el trabajo en equipo y la comunicación entre los subsistemas, simulando así un *miniequipo* de Hyperloop. + + +## Descripción general de la tarea + +El objetivo del proyecto final consiste en desarrollar una bancada de pruebas para el *booster* (de ahora en adelante bancada *booster*). Los alumnos de los diferentes subsistemas del Training Center tienen asignadas diferentes tareas interrelacionadas entre sí, donde será necesario que todos cooperen de manera coordinada para cumplir con los requisitos técnicos y alcanzar el objetivo final de la tarea. + +### Descripción de la tarea de Software + +Se deberá implementar un emulador de la bancada *booster*, el emulador estará compuesto por dos partes: + +- Frontend: se conecta al backend y muestra datos sobre la bancada +- Backend: emulación de la bancada *booster*. (**Se permite hacer uso del backend de ejemplo**) + +### Evaluación de la tarea + +Los requesitos mínimos para evaluar la tarea es realizar un frontend incluyendo las 5 funcionalidades detalladas en el apartado anterior. + +Aquellos que deseen aspirar a nota extra podrán entregar también su propio backend que cumpla con las especificaciones de la tarea. + + + +{{< pagebreak >}} + +## Frontend + +La *Graphical User Interface* (GUI) o frontend, debe de conectarse al backend utilizando los protocolos `http` y `websockets`. + +### Especificacciones de la GUI + +1. Una sección con gráficas que nos permita visualizar las siguientes varibles + +- $V$ +- $a$ +- $v$ +- $F$ (Habrá que calcularla en el Frontend. Usar 2ª Ley de Newton) +- $I$ + +- Además, de una cronograma con los estados establecidos. + +`IDLE`, `PRECHARGE`, `READY`, `RUNNING`, `BOOSTING`, `BRAKING` y `STOPPED` + + +2. Una sección que nos permita mediante botones enviar ordenes al simulador + +- `PRECHARGE`: inicia la precarga +- `START`: inicia el movimiento del Carro, se debe de especificar la masa +- `BRAKE`: frena el Carro +- `RESET`: reinicia el simulador + +3. Una sección que nos permita ver los mensajes que envía el simulador al *Front*, los mensajes harán referencia al estado del simulador, por ejemplo : *V = 400V precharge completed sucessfully* + +4. Una sección que nos permita calcular la distancia óptima para presionar el freno mediante una llamada a API + +5. Inclusión de un modelo 3D del Carro *Booster* + +### Implementación + +#### Notas Generales + +- Se debe desarollar una aplicación Web utilizando React +- Se debe utilizar preferiblemente Typescript o en su defecto JavaScript +- Se recomienda el uso de librerías de estilo como *tailwind* o *bootsrap*, así mismo se recomienda el uso de librerías de componentes, en especial *shadcn* +- El frontend deberá validar los inputs antes de enviarlos + + + +#### Gráficas (1) y Mensajes (3) + +El Backend expondrá en el puerto `5001` un protocolo `websockets` con ruta `backend/stream` que se corresponderá con el flujo de datos envidados del cliente a servidor; es decir utilizaremos `websockets` únicamente para leer datos. De la información transmitida por `ws` obtendremos tanto los datos que habrá que mostrar en la gráfica, como los mensajes que se debén de mostrar en el área de mensajes. + +WebSocket endpoint: `ws://localhost:5001/backend/stream` + +La comunicación por websockets estará, codificada como JSON, del cual distinguiremos 2 formatos, uno para recepción de los datos y otro para los mensajes del servidor. + + + +**Formato datos:** + +```json +{ + "topic": "data", + "payload": { + "timestamp": "2026-03-27T10:15:32.125Z", + "state": "RUNNING", + "position_m": 3.42, + "velocity_kmh": 18.7, + "acceleration_ms2": 2.15, + "mass_kg": 40, + "voltage_v": 400, + "current_a": 125.4 + } +} +``` + +**Formato mensajes:** + +```json +{ + "topic": "message", + "payload": { + "type": "info", + "content": "Precharge started" + } +} +``` + +Los tipos de mensajes pueden ser:`info`, `critical`, `error` y `success` + + +En el caso de formato de datos los paquetes llegarán con una frecuencia de 4 Hz, debido a la gran afluencia de paquetes se recomienda mantener un histórico limitado (por ejemplo, los últimos 5–10 segundos de datos) para la representación gráfica, eliminando los valores más antiguos para evitar problemas de rendimiento. + + +#### Botones (2) + +Al ser presionados mandarán una orden utilizando una petición `HTTP-POST` al backend utilizando el formato especificado. El botón de `START` debe de estar acompañado de un `input` numérico que permita especificar la massa del carro. + + +La orden se dirigirá al siguiente enpoint `/api/command` la cual estará en el puerto `8001`. El servidor tras recibir la orden devolvera el código 200 indicando que todo ha sido correcto y se enviará un mensaje via `websockets`. + +El servidor responderá con: + +- `200 OK` si el comando se ha aceptado correctamente +- `400 Bad Request` si el comando es inválido + +El resultado de la acción se notificará mediante un mensaje vía `websockets`. + +**Formato petición HTTP:** +``` +POST /api/command +Content-Type: application/json + +{ + "command": "PRECHARGE" +} +``` + + +(en el caso de la orden `START`) + +``` +POST /api/command +Content-Type: application/json + +{ + "command": "START", + "payload": { + "mass": 40 + } +} +``` + +En caso de error (`400 Bad Request`), el frontend deberá mostrar un mensaje informativo al usuario indicando que la operación no se ha podido realizar. + + +#### *Cálculo* de la posición óptima para presionar el freno (4) + +Sección con un formulario que permitirá indicar el peso del vehículo y a cuánta distancia se desea acabar del fin de la vía. Al presionar el botón de *submit* el frontend realizará una petición tipo `GET - HTTP` a `/api/calculate` con los *query parameters* `m` y `d`, que se corresponden con la masa del Carro y la distancia deseada, respectivamente. + +El servidor devolverá una respuesta en formato JSON con la posición óptima en la que se debe iniciar el frenado. El frontend deberá procesar dicha respuesta y mostrar el resultado de forma clara al usuario. + +**Formato de respuesta esperado:** + +```json +{ + "braking_position_m": 37.5 +} +``` + + +#### Inclusión del modelo 3D (5) + +Se debe incluir un modelo 3D del Carro *booster* diseñado por el subsistema de *Mechanics*, debe de ser interáctivo, no debe de tratarse de una fotografía. ^[No se incluye una descripción más extensa porque el desafio de este apartado es ver, vuestras habilidades para seleccionar la forma óptima.] + + +> **Nota sobre la velocidad del backend de ejemplo** +> +> El backend de ejemplo no corre en tiempo real: la simulación transcurre **5× más lenta** que el tiempo real (`SIM_SPEED = 0.2`). La frecuencia de emisión por WebSocket se mantiene en 4 Hz, pero los valores de posición, velocidad, etc. avanzan más despacio de lo que cabría esperar físicamente. Esto es intencionado para facilitar las pruebas interactivas — el frontend no necesita hacer ningún ajuste al respecto. + +### Recomendaciones + +- El objetivo de la tarea es diseñar un panel de control, por lo que sería interesante que haya una única vista, donde no se pueda hacer *scroll* que lo concentre todo +- Se recomienda hacer uso de los estilos como herramienta para resaltar los eventos que suceden en el simulador, por ejemplo si el Carro, se estrella con el *mechanical stopper*, podría ponerse el fondo en rojo para indicar que ha colisionado. + + +{{< pagebreak >}} + +## Backend **(VOLUNTARIA REALIZACIÓN)** + +El backend será el encargado de emular el comportamiento de la bancada *booster*, gestionando tanto la lógica de estados como la generación de datos de telemetría y la comunicación con el frontend. + +El backend deberá implementarse preferiblemente en **Rust** o **Go**, o en su defecto en **Java**. Queda a criterio del alumno la elección del lenguaje dentro de estas opciones. + +El backend deberá exponer dos tipos de comunicación: + +- `WebSockets`: envío continuo de datos y mensajes al frontend +- `HTTP`: recepción de órdenes y cálculo de la posición óptima de frenado + + +### Especificaciones del Backend + +#### Arquitectura general + +El backend deberá estar compuesto por: + +- Un servidor `WebSocket` en el puerto `5001` para el envío de datos en tiempo real +- Un servidor `HTTP` en el puerto `8001` para la recepción de comandos y cálculo + +El sistema deberá funcionar como una **máquina de estados**, donde las transiciones estarán controladas por las órdenes recibidas. + +--- + +### Máquina de estados + +El backend deberá implementar los siguientes estados: + +`IDLE`, `PRECHARGE`, `READY`, `RUNNING`, `BOOSTING`, `BRAKING` y `STOPPED` + +#### Transiciones + +- `IDLE → PRECHARGE` mediante comando `PRECHARGE` +- `PRECHARGE → READY` cuando $V = 400$ +- `READY → RUNNING` mediante comando `START` +- `RUNNING → BOOSTING` cuando $2 \le s \le 4$ +- `BOOSTING → RUNNING` cuando $s > 4$ +- `RUNNING → BRAKING` mediante comando `BRAKE` +- `BOOSTING → BRAKING` mediante comando `BRAKE` +- `BRAKING → STOPPED` cuando $v = 0$ +- `Cualquier estado → IDLE` mediante comando `RESET`: todas las variables vuelven a su valor inicial ($s=0$, $v=0$, $a=0$, $V=0$, $I=0$, $m=0$) + +Los comandos únicamente podrán ser enviados por el frontend + +No se permitirán transiciones no definidas. + +--- + +### Generación de datos + +El backend deberá generar y enviar datos de forma continua mediante `WebSockets`. + +- Frecuencia: **4 Hz** +- Cada mensaje representará el estado instantáneo del sistema + +#### Variables a generar + +- `position_m` +- `velocity_kmh` +- `acceleration_ms2` +- `mass_kg` +- `voltage_v` +- `current_a` +- `state` +- `timestamp` + +El campo `timestamp` deberá estar en formato **ISO 8601 (UTC)**. + +--- + +### Lógica física simplificada + +Se deberá respetar el siguiente comportamiento: + +- En `PRECHARGE`: incremento progresivo de $V$ hasta 400 (Incremento de $25\,\mathrm{V}$ por tick, es decir, cada $250\,\mathrm{ms}$; la precarga completa dura 16 ticks = 4 segundos) +- En `RUNNING`: velocidad constante $v = 4\,\mathrm{km/h}$ (antes del booster) +- En `BOOSTING`: la aceleración se recalcula en cada tick para garantizar que el Carro alcance exactamente $v_f = 25\,\mathrm{km/h}$ al llegar a $s = 4\,\mathrm{m}$: +$$ +a = \frac{v_f^2 - v^2}{2\,(4 - s)} +$$ +donde $v$ y $s$ son la velocidad (en $\mathrm{m/s}$) y posición actuales. La fuerza es $F = m \cdot a$. **Cuando $s \ge 4$, no calcular $a$ — fijar directamente $v = 25\,\mathrm{km/h}$ y transicionar a `RUNNING`** para evitar división por cero. La intensidad sigue el perfil: +$$ +I(s) = I_{\max} \cdot \sin\!\left(\frac{\pi\,(s - 2)}{2}\right), \quad I_{\max} = 200\,\mathrm{A} +$$ +- En `RUNNING` (post-booster): velocidad constante $v = 25\,\mathrm{km/h}$, $a = 0$, $I = 0$ +- En `BRAKING`: $F_{\text{brake}} = 196\,\mathrm{N}$, $a = -F_{\text{brake}}/m$. En cada tick ($\Delta t = 0.25\,\mathrm{s}$) se actualiza: +$$ +v \leftarrow v + a\,\Delta t \qquad s \leftarrow s + v\,\Delta t +$$ +Si $v \le 0$ → transición a `STOPPED`. Si $s \ge 50$ → *mechanical stopper*. +- En `STOPPED`: todas las variables a 0 salvo posición + +No es necesario implementar un modelo físico complejo, pero sí coherente con las reglas anteriores. + +> **Velocidad de simulación** +> +> El backend no corre en tiempo real. El paso de tiempo físico se escala con un factor fijo `SIM_SPEED = 0.2`, de modo que la simulación transcurre **5× más lenta** que el tiempo real. La frecuencia de emisión por WebSocket se mantiene en 4 Hz. +> +> $$ +> \Delta t_{\text{físico}} = \Delta t \times 0{,}2 = 0{,}05\,\mathrm{s/tick} +> $$ +> +> Esto proporciona aproximadamente 33 segundos de margen en el tramo post-booster para enviar la orden de frenado, facilitando las pruebas interactivas. + +--- + +### Comunicación WebSocket + +El servidor deberá emitir mensajes en formato JSON siguiendo los dos tipos definidos: + +#### Datos + +```json +{ + "topic": "data", + "payload": { + "timestamp": "2026-03-27T10:15:32.125Z", + "state": "RUNNING", + "position_m": 3.42, + "velocity_kmh": 18.7, + "acceleration_ms2": 2.15, + "mass_kg": 40, + "voltage_v": 400, + "current_a": 125.4 + } +} +``` + +#### Mensajes + +```json +{ + "topic": "message", + "payload": { + "type": "info", + "content": "Precharge started" + } +} +``` + +Los tipos posibles son `info`, `success`, `error` y `critical`. El backend deberá emitir un mensaje en cada transición de estado: + +| Evento | `type` | `content` (ejemplo) | +|--------|--------|----------------------| +| Comando `PRECHARGE` recibido | `info` | `"Precharge started"` | +| $V = 400$ alcanzado | `success` | `"V = 400V precharge completed successfully"` | +| Comando `START` recibido | `info` | `"Booster test started. Mass: 40 kg"` | +| Carro entra en Sección *Booster* | `info` | `"Booster section entered"` | +| Carro sale de Sección *Booster* | `success` | `"Boost completed. Velocity: 25 km/h"` | +| Comando `BRAKE` recibido | `info` | `"Braking initiated"` | +| Carro detenido ($v = 0$, $s < 50$) | `success` | `"Cart stopped at s = 12.3 m"` | +| Carro llega al *mechanical stopper* | `critical` | `"Cart reached mechanical stopper at s = 50 m"` | +| Comando no válido para el estado actual | `error` | `"Command BRAKE not allowed in state IDLE"` | +| Comando `RESET` recibido | `info` | `"System reset"` | + +--- + +### Comunicación HTTP + +El servidor HTTP escuchará en el puerto `8001` y expondrá dos endpoints. + +#### `POST /api/command` + +Recibe un comando del frontend. Si el comando no es válido para el estado actual, responde `400 Bad Request`. En caso contrario responde `200 OK` y emite el mensaje correspondiente por WebSocket. + +Comandos aceptados: + +| Comando | Estado requerido | Payload adicional | +|---------|-----------------|-------------------| +| `PRECHARGE` | `IDLE` | — | +| `START` | `READY` | `{ "mass": }` | +| `BRAKE` | `RUNNING` o `BOOSTING` | — | +| `RESET` | Cualquiera | — | + +#### `GET /api/calculate` + +Calcula la posición óptima para iniciar el frenado. Recibe dos *query parameters*: + +- `m`: masa del Carro en $\mathrm{kg}$ +- `d`: distancia deseada al final del *track* en $\mathrm{m}$ + +$$ +d_{\text{brake}} = \frac{v_0^2 \cdot m}{2\,F_{\text{brake}}} \qquad s_{\text{brake}} = (50 - d) - d_{\text{brake}} +$$ + +con $v_0 = 25\,\mathrm{km/h}$ (convertido a m/s) y $F_{\text{brake}} = 196\,\mathrm{N}$. + +**Respuesta:** + +```json +{ + "braking_position_m": 37.5 +} +``` + +Si los parámetros son inválidos (negativos, ausentes, o $s_{\text{brake}} < 0$), responder `400 Bad Request`. + +{{< pagebreak >}} + +## Uso del backend de ejemplo + +Para los alumnos que realicen únicamente el frontend, se proporciona un backend de ejemplo ya compilado y listo para ejecutar, sin necesidad de instalar Python ni ninguna dependencia. + +### Descarga + +| Sistema operativo | Enlace | +|-------------------|--------| +| Windows | [Descargar backend.exe](backend-windows.zip) | +| Linux | [Descargar backend](backend-linux.zip) | + +### Ejecución + +:::::: {.panel-tabset} + +#### Windows + +Haz doble clic sobre `backend.exe` o ejecútalo desde una terminal: + +```bat +backend.exe +``` + +#### Linux + +Otorga permisos de ejecución y lánzalo: + +```bash +chmod +x backend +./backend +``` + +:::::: + +### Puertos + +Una vez en marcha, el backend expone dos servicios de forma simultánea: + +| Servicio | URL | +|----------|-----| +| WebSocket (telemetría) | `ws://localhost:5001/backend/stream` | +| HTTP (comandos y cálculo) | `http://localhost:8001` | + +El frontend debe estar lanzado **después** de que el backend esté corriendo. Asegúrate de que los puertos `5001` y `8001` no están ocupados por otro proceso. + +--- + +{{< pagebreak >}} + +## Entrega + +La entrega del proyecto se realizará a través de un **repositorio de GitHub** que deberá cumplir los siguientes requisitos: + +- El repositorio debe contener **únicamente el proyecto** — sin archivos innecesarios, sin el binario del backend de ejemplo descargado, y sin carpetas de dependencias (`node_modules`, entornos virtuales, etc.). Se recomienda incluir un `.gitignore` adecuado. +- El repositorio debe incluir un `README.md` con instrucciones claras para instalar y ejecutar el proyecto. +- Se enviará el enlace al repositorio al responsable del subsistema de Software. + +### Plazos + +| Hito | Fecha | +|------|-------| +| Plazo para añadir cambios al repositorio | **12 de mayo de 2026** | +| Exposición del proyecto | **14 de mayo de 2026** | + +> A partir del **12 de mayo** no se tendrán en cuenta los cambios realizados en el repositorio para la evaluación. La exposición consistirá en una demostración en vivo del proyecto y una breve explicación de las decisiones de implementación tomadas. \ No newline at end of file