diff options
39 files changed, 4117 insertions, 0 deletions
diff --git a/.github/workflows/hugo.yml b/.github/workflows/hugo.yml new file mode 100644 index 0000000..0966c4d --- /dev/null +++ b/.github/workflows/hugo.yml @@ -0,0 +1,74 @@ +# Sample workflow for building and deploying a Hugo site to GitHub Pages +name: Deploy Hugo site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +# Default to bash +defaults: + run: + shell: bash + +jobs: + # Build job + build: + runs-on: ubuntu-latest + env: + HUGO_VERSION: 0.128.0 + steps: + - name: Install Hugo CLI + run: | + wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ + && sudo dpkg -i ${{ runner.temp }}/hugo.deb + - name: Install Dart Sass + run: sudo snap install dart-sass + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Setup Pages + id: pages + uses: actions/configure-pages@v5 + - name: Install Node.js dependencies + run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true" + - name: Build with Hugo + env: + HUGO_CACHEDIR: ${{ runner.temp }}/hugo_cache + HUGO_ENVIRONMENT: production + run: | + hugo \ + --minify \ + --baseURL "${{ steps.pages.outputs.base_url }}/" + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./public + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17181ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.hugo_build.lock +/public diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..92d51fe --- /dev/null +++ b/config.toml @@ -0,0 +1,11 @@ +baseURL = "https://dir.sivaldodavi.com/" +languageCode = "pt-br" +title = "Direito - Sivaldo" + +[params] + description = "Estudos e materiais de Direito" + +[markup] + [markup.goldmark] + [markup.goldmark.renderer] + unsafe = true diff --git a/content/quizzes/sociologia-antropologia-juridica/divisao-trabalho.md b/content/quizzes/sociologia-antropologia-juridica/divisao-trabalho.md new file mode 100644 index 0000000..1321586 --- /dev/null +++ b/content/quizzes/sociologia-antropologia-juridica/divisao-trabalho.md @@ -0,0 +1,129 @@ +--- +title: Exercício de aprendizado +--- + +? **Estudo de caso:** Durante o século XIX, o "Factory Act" na Inglaterra restringiu o trabalho infantil. O texto de Marx relata que, diante dessa proibição, os capitalistas ingleses, que antes consideravam o trabalho de crianças "seminuas" condizente com sua moral, passaram a investir em máquinas de quebrar pedras que já haviam sido inventadas pelos americanos. De acordo com o texto de Marx, o que determina a *introdução da maquinaria* no processo produtivo? +- A preocupação ética do capitalista com o bem-estar e a saúde das mulheres e crianças trabalhadoras. +- A necessidade de aumentar o tempo de lazer do operário, permitindo que ele se dedique a atividades culturais e religiosas. +- A pressão das Instituições de Controle Social (ICS) morais, como a Igreja, que exigiam a modernização tecnológica das minas. +- A escassez absoluta de trabalhadores, visto que o capital prefere sempre a máquina mesmo quando a mão de obra é gratuita. += O momento em que o custo da força de trabalho humana se torna superior ao custo de produção e manutenção da máquina. +> Para Marx, a introdução da maquinaria no capitalismo não se dá por razões humanitárias, mas sim por uma lógica estrita de valor e lucro. A máquina substitui o trabalhador quando o seu uso barateia a produção. +--- + +? **Estudo de caso:** Um sindicato de operários organiza uma greve reivindicando que o valor excedente produzido pela jornada extra de trabalho seja convertido em salários. O Estado intervém enviando forças policiais para desocupar a fábrica e garantir a "ordem e o direito à propriedade". Relacionando a *Mais-Valia* com os conceitos de *ARE (Aparelho Repressivo de Estado)*, avalie as asserções: I. A mais-valia representa o trabalho não pago que é apropriado pelo capitalista, sendo a base da acumulação de capital. II. O Estado atua como um ARE (Aparelho Repressivo) quando utiliza a força física (Polícia) para proteger a estrutura que permite a extração da mais-valia. III. O conflito entre a busca pelo lucro (capital) e a sobrevivência do trabalhador (trabalho) exemplifica a Luta de Classes mencionada por Marx. É correto o que se afirma em: +- I, apenas. +- II, apenas. +- I e II, apenas. +- III, apenas. += I, II e III. +> Todas as asserções estão corretas. A mais-valia é a exploração, o Estado age via ARE para garantir a ordem burguesa, e essa tensão evidencia a Luta de Classes. +--- + +? **Situação-problema:** Um designer gráfico trabalha em uma agência onde produz apenas logotipos para empresas que ele não conhece e cujos valores ele não compartilha. Ao final do dia, ele sente que seu trabalho não lhe pertence e que ele é apenas uma ferramenta para o enriquecimento do dono da agência. Este cenário ilustra a *Alienação* e a deturpação da Importância do Trabalho. Assinale a alternativa que descreve essa relação: +- A alienação é um processo positivo, pois permite que o trabalhador não se preocupe com as consequências éticas do que produz. +- A importância do trabalho no capitalismo reside na capacidade do operário de se tornar dono dos meios de produção através da economia do seu salário. +- O designer não é alienado, pois utiliza um computador (maquinaria), o que, segundo Marx, devolve ao homem o controle total sobre a criação. +- A alienação ocorre porque o trabalhador se recusa a cooperar com seus colegas, preferindo o isolamento da produção artesanal. += O trabalho, que deveria ser a forma do homem se realizar e transformar o mundo, torna-se um fardo onde o trabalhador se sente um "miserável" (wretch). +> A alienação ocorre quando o trabalhador não se reconhece no produto, não controla o processo e vê sua atividade como mera obrigação para sobrevivência. +--- + +? **Estudo de caso:** Imagine um canteiro de obras onde 20 homens precisam levantar uma viga pesada. Se trabalharem isoladamente, nenhum conseguirá movê-la. Se trabalharem juntos, o esforço é realizado em segundos. Marx utiliza exemplos similares para explicar a "potência de massa". Sobre a *cooperação simples* descrita por Marx, assinale a alternativa correta: +- A cooperação não altera a produtividade, apenas facilita o convívio social entre os trabalhadores. +- A cooperação é uma invenção da era da tecnologia digital, inexistente no período da manufatura. +- Na cooperação simples, cada operário é dono de sua própria ferramenta e decide o ritmo de sua produção. +- A reunião de operários em um mesmo local aumenta os custos de produção, pois exige mais espaço físico. += O esforço coletivo gera uma força superior à soma das forças individuais isoladas, reduzindo o tempo de trabalho necessário. +> O conceito de "potência de massa" explica que a cooperação não é só a soma de indivíduos, mas a criação de uma força coletiva mais eficiente. +--- + +? **Estudo de caso:** Uma grande montadora de veículos em Minas Gerais organiza a sua produção de forma que cada operário aperte apenas um parafuso específico durante toda a jornada. Simultaneamente, essa fábrica faz parte de uma rede global que depende de mineradoras de ferro e empresas de logística. Marx diferencia esses dois níveis de especialização para explicar a estrutura do capital. Sobre a *Divisão do Trabalho*, assinale a alternativa correta: +- A divisão técnica ocorre entre os diferentes ramos da indústria (como agricultura e indústria), enquanto a social ocorre dentro da oficina. +- Na divisão técnica da manufatura, o operário mantém a sua autonomia e o domínio completo sobre o produto final da sua atividade. +- Marx defende que a divisão técnica do trabalho é uma ferramenta de libertação, pois reduz o esforço intelectual exigido do trabalhador. +- O Estado, como ICS soberana, proíbe a divisão técnica do trabalho para garantir que todos os cidadãos possuam múltiplas habilidades profissionais. += A divisão social do trabalho fragmenta a sociedade em profissões, enquanto a divisão técnica fragmenta o próprio operário em uma peça parcial da máquina. +> A divisão social separa a sociedade em ramos; a técnica ocorre 'da porta para dentro' da fábrica, fragmentando o processo produtivo. +--- + +? **Situação-problema:** Em uma metrópole, observamos uma clara separação entre os bairros onde residem os engenheiros, juízes e médicos, e as periferias onde moram os operários da construção civil e auxiliares de limpeza. Marx explica que essa organização não é apenas funcional, mas reflete a estrutura de classes e a distribuição desigual da riqueza produzida. Sobre a *Divisão Social do Trabalho*, assinale a alternativa correta: +- Ela garante que todos os indivíduos, independentemente da profissão, recebam exatamente o mesmo valor pela sua contribuição social. +- É uma escolha individual baseada puramente no talento, sem qualquer relação com o acesso desigual à educação ou aos meios de produção. +- O Estado-Jurídico atua para eliminar a divisão social do trabalho, transformando todos os cidadãos em "operários parciais" vitalícios. +- Na manufatura, a divisão social é o que permite que o operário fabrique uma mercadoria completa sem precisar de outros colegas. += Diferencia-se da divisão técnica pois organiza os diversos ramos da produção na sociedade, consolidando a hierarquia entre quem detém o capital e quem vende a força de trabalho. +> A divisão social no capitalismo reflete a estrutura de classes, distribuindo de forma desigual a riqueza e os espaços sociais. +--- + +? **Estudo de caso:** Na manufatura, o processo de produção é decomposto em várias operações parciais. O operário, antes um artesão que conhecia todo o processo, passa a realizar apenas um movimento repetitivo, tornando-se um "operário parcial" vinculado a uma função vitalícia. Este processo de *alienação no ambiente de trabalho* resulta em qual consequência para o trabalhador, segundo a lógica marxista? +- O aumento da criatividade e da liberdade, pois o operário tem menos responsabilidades sobre o produto final. +- A valorização do saber técnico do artesão, que passa a comandar as máquinas com total autonomia e poder de decisão. +- A eliminação da hierarquia na fábrica, visto que todos os operários parciais possuem o mesmo nível de conhecimento total. +- O fortalecimento do vínculo emocional entre o operário e a mercadoria, já que ele a produz inteiramente com suas mãos. += A transformação do operário em uma peça de um mecanismo vivo, cuja habilidade se torna um detalhe automático e limitado. +> Este é o clímax da degradação na manufatura: o operário perde autonomia e vira engrenagem viva de um processo que não domina. +--- + +? **Situação-problema:** Durante a tramitação de uma nova lei ambiental, grandes grupos empresariais do setor industrial (a burguesia industrial) financiam institutos de pesquisa e contratam escritórios de advocacia para pressionar parlamentares. O objetivo é garantir que a norma não encareça o custo de produção, mesmo que isso aumente a poluição. De acordo com a *Crítica de Marx ao Estado e o papel dos Lobbies*, avalie as asserções: I. O Estado moderno tende a funcionar como um comitê que administra os negócios comuns da burguesia. II. O Direito é visto como uma superestrutura que reflete as relações de poder econômico. III. O lobby burguês é uma forma de "Ação Indireta" que garante que o Estado-Jurídico mantenha a lucratividade. É correto o que se afirma em: +- I e II, apenas. +- II e III, apenas. +- I, apenas. +- III, apenas. += I, II e III. +> Todas corretas. O Estado atua como gestor dos negócios burgueses, o Direito reflete a infraestrutura econômica e os lobbies são ferramentas práticas desse poder. +--- + +? **Estudo de caso:** Marx observa que a produção capitalista não nasce de uma mudança tecnológica súbita, mas de uma mudança na organização do trabalho. Antes das máquinas, o que definia o início do capitalismo era a reunião de muitos trabalhadores em uma grande oficina, sob o comando de um único patrão, realizando as mesmas tarefas que antes faziam de forma isolada. De acordo com o texto, qual é o *ponto de partida histórico e formal* da produção capitalista? +- A invenção da máquina a vapor e a substituição completa do trabalho manual por motores. +- A distribuição de lucros igualitária entre o mestre artesão e seus aprendizes na oficina corporativa. +- A proibição do trabalho manual em favor de um sistema baseado exclusivamente no comércio de importação. +- A redução quantitativa de operários para garantir que cada um produza uma mercadoria completa do início ao fim. += O momento em que o capitalista ocupa simultaneamente um grande número de operários no mesmo campo de trabalho. +> O capitalismo nasce formalmente com a cooperação simples: a reunião de muitos trabalhadores artesanais no mesmo galpão sob o comando do capital. +--- + +? **Situação-problema:** Um escritório de advocacia em expansão decide alterar sua forma de trabalho. Antes, cada advogado cuidava de um processo do início ao fim. Agora, o titular reuniu 20 advogados em um mesmo salão e determinou que cinco farão apenas pesquisas jurisprudenciais, dez redigirão apenas petições e cinco cuidarão exclusivamente de prazos processuais. De acordo com o texto de Marx sobre a *Manufatura*, essa mudança organizacional caracteriza: +- A manutenção do sistema de artesanato corporativo, onde a habilidade individual permanece sendo o fator determinante da qualidade jurídica. +- Uma revolução tecnológica, pois a reunião de pessoas no mesmo local é o que Marx define como a invenção da maquinaria pesada. +- A eliminação da autoridade do capitalista, visto que o trabalho coletivo permite que os advogados decidam autonomamente. +- O fim da divisão social do trabalho, uma vez que todos os ramos da sociedade passam a ser representados dentro daquela única oficina. += A criação de um "operário parcial", onde o advogado perde a visão do conjunto do processo para ganhar agilidade técnica em uma tarefa específica e repetitiva. +> O estudo de caso aplica a divisão técnica à advocacia: o profissional deixa de conhecer o todo e repete apenas uma etapa processual específica. +--- + +? **Situação-problema:** Em um mutirão de assistência judiciária gratuita, 50 advogados trabalham simultaneamente em um ginásio para atender uma comunidade. O coordenador do evento observa que o grupo consegue atender dez vezes mais pessoas do que se cada advogado trabalhasse isoladamente. Sobre o conceito de *Cooperação* em Marx, como se explica esse fenômeno? +- A cooperação reduz a produtividade, pois o "operário médio" tende a trabalhar mais devagar quando está cercado de colegas. +- O aumento da produção deve-se exclusivamente ao fato de que, em grupo, os advogados deixam de ser "miseráveis" (wretches) e tornam-se capitalistas. +- A reunião de muitos trabalhadores no mesmo lugar elimina a diferença quantitativa entre a oficina do mestre e a grande indústria. +- Marx defende que a cooperação só ocorre quando há o uso de cavalos ou mulheres puxando barcos, não sendo aplicável ao trabalho intelectual. += O trabalho coletivo cria uma força produtiva nova (potência de massa), que é essencialmente gratuita para quem coordena e detém os meios de produção. +> A força extra gerada pela coletividade (potência de massa) não é remunerada individualmente; ela é apropriada de forma gratuita pelo coordenador/capitalista. +--- + +? **Situação-problema:** Uma plataforma de tecnologia aumenta a taxa cobrada dos motoristas parceiros, alegando necessidade de lucro para os acionistas. Os motoristas organizam uma paralisação nacional exigindo melhores condições e direitos trabalhistas. O conflito descrito acima é uma manifestação contemporânea da *Luta de Classes*. Como Marx define a base desse conflito? +- É uma disputa estética entre diferentes grupos de consumidores por marcas de luxo. +- Trata-se de um mal-entendido técnico que pode ser resolvido com uma melhor "gestão de planilhas" pela tecnocracia estatal. +- A luta de classes só existe quando há violência armada física, não se aplicando a greves ou reivindicações jurídicas pacíficas. +- O conflito ocorre porque os motoristas desejam ser ARE (Aparelhos Repressivos) e o Estado não permite. += É o choque irreconciliável entre as Relações de Produção (quem possui o capital/app) e as forças produtivas que detêm apenas a força de trabalho. +> A greve de aplicativos ilustra o antagonismo estrutural: os detentores do capital focam no lucro, enquanto as forças produtivas buscam sobrevivência. +--- + +? **Estudo de caso:** Em um escritório de contabilidade tecnocrático, os funcionários são monitorados por softwares que registram cada segundo de inatividade. O trabalhador sente-se como um estranho em relação ao relatório final que produz, não compreendendo a finalidade do seu esforço para a sociedade, sentindo-se apenas uma engrenagem na "Máquina Fria" do Estado-Burocrático. O cenário acima descreve o encontro dos conceitos de *Alienação e Microfísica do Poder*. Assinale a alternativa que explica essa relação: +- A alienação é eliminada pela microfísica do poder, pois a vigilância constante faz o trabalhador sentir-se parte importante da empresa. +- O poder, segundo Foucault, reside apenas no topo da hierarquia, o que impede que o trabalhador se sinta alienado nas bases. +- A burocracia estatal brasileira resolve o problema da alienação ao transformar a gestão de pessoas em um processo puramente afetivo. +- A consciência de classe surge automaticamente quando o trabalhador é submetido à vigilância técnica dos softwares de produtividade. += O estranhamento do trabalhador (alienação) é reforçado por mecanismos invisíveis de controle e vigilância (microfísica) que operam no cotidiano. +> A união entre Marx (estranhamento do produto) e Foucault (vigilância tecnológica dos corpos) descreve o controle tecnocrático moderno. +--- + +? Assinale a alternativa que descreve a *transição da manufatura para a grande indústria*, conforme discutido no texto: +- A grande indústria elimina a necessidade de capital, tornando o trabalhador dono dos meios de produção. +- A manufatura é mais eficiente que a grande indústria, pois utiliza ferramentas mais simples e baratas. +- A transição para a indústria ocorreu sem conflitos, pois os operários preferiam a monotonia das máquinas. +- O uso de máquinas na Inglaterra começou exclusivamente para proteger mulheres e crianças de trabalhos pesados. += A manufatura baseia-se na habilidade manual do operário, enquanto a grande indústria baseia-se no sistema de máquinas que executa as tarefas antes manuais. +> Na manufatura o trabalho depende da destreza manual. Na maquinofatura, a máquina é a força motriz e o trabalhador apenas a serve. +--- diff --git a/content/quizzes/sociologia-antropologia-juridica/poder-dominacao.md b/content/quizzes/sociologia-antropologia-juridica/poder-dominacao.md new file mode 100644 index 0000000..c4ea725 --- /dev/null +++ b/content/quizzes/sociologia-antropologia-juridica/poder-dominacao.md @@ -0,0 +1,169 @@ +--- +title: Exercício de aprendizado +--- + +? No cenário brasileiro, críticas recorrentes apontam que a gestão pública é frequentemente entregue a técnicos que priorizam métricas orçamentárias em detrimento das necessidades humanas. Essa "gestão de planilhas" gera um distanciamento entre a administração e os anseios democráticos da população. Sobre o conceito de *Tecnocracia e Burocracia*, assinale a alternativa que apresenta o problema central descrito no material: +- A tecnocracia fortalece a democracia ao permitir que o poder econômico assuma o controle político direto diariamente. +- A burocracia estatal é uma invenção das sociedades pré-capitalistas que visava humanizar as relações de poder. +- No Brasil, o poder político e o poder econômico são polos que nunca se encontram, o que elimina qualquer risco de autoritarismo. +- A tecnocracia elimina as formas de resistência, pois oferece soluções científicas que satisfazem todos os estratos sociais. += O domínio dos tecnocratas resulta em uma "máquina fria", onde a tensão entre a gestão técnica e a anomia social se torna permanente. +> A tecnocracia transforma o Estado em uma 'máquina fria' governada por especialistas técnicos e métricas, gerando tensão com as necessidades humanas reais (anomia social) e afastando-se do ideal democrático participativo. +--- + +? Em uma análise sobre a sociabilidade brasileira, um pesquisador observa que o Estado atua como o ápice soberano de um mecanismo ideológico que abrange toda a sociedade. Ele nota que, mesmo em instituições privadas como a família e a igreja, as diretrizes de comportamento são indiretamente moldadas pelo aparato estatal. Considerando o conceito de *Estado como ICS Soberana*, assinale a alternativa correta: +- Nas sociedades modernas, o Estado atua de forma isolada, sem interpenetração nas demais Instituições de Controle Social (ICS). +- A sociabilização primária, realizada pela família, é um processo puramente acidental e livre de qualquer influência das ICS. +- O controle social pelo Direito é exercido apenas através da força física, sendo a moral e a ética domínios exclusivos da Igreja. +- O Estado abdica de sua soberania nas sociedades complexas, transferindo o controle social integralmente para as corporações privadas. += O Estado é o aparelho técnico-ideológico supremo, capaz de coagir organismos sociais tanto por ação direta quanto por infiltração invisível. +> O Estado moderno não atua apenas de forma coercitiva direta, mas também permeia as instituições privadas (família, igreja) ditando regras morais, jurídicas e ideológicas, agindo como a ICS soberana coordenadora. +--- + +? Em uma determinada comunidade urbana, observa-se que as regras de convivência e a resolução de conflitos não derivam apenas do Código Civil, mas de normas morais religiosas e diretrizes pedagógicas das escolas locais. Contudo, em situações de crise, o Estado intervém através de decretos que regulam desde o currículo escolar até a estrutura familiar permitida para benefícios sociais. Considerando a *Arquitetura da Sociabilização* e a atuação do Estado como ICS Soberana, avalie as asserções a seguir: I. O Estado atua de forma direta por meio do braço forte (Polícia e Judiciário) e de forma indireta ao ditar regras que interpenetram instituições privadas como a família e a escola. II. Nas sociedades complexas, a família perdeu sua função de base primária de sociabilização, sendo integralmente substituída pelo aparelho técnico-ideológico estatal. III. O sistema Judiciário e Penal é o ponto de convergência absoluto, pois acumula funções de coerção ideológica (pelo pensamento) e repressiva (pela força física). É correto o que se afirma em: +- I e II, apenas. +- II e III, apenas. +- I, II e III. +- I, apenas. += I e III, apenas. +> A assertiva II é falsa porque a família não foi integralmente substituída; ela continua sendo a base primária, embora sob forte influência estatal. As assertivas I e III refletem perfeitamente as vias de ação do Estado e a dupla função do sistema penal. +--- + +? A sociabilização de um indivíduo e as suas estratégias de sobrevivência dentro de uma comunidade não ocorrem de forma aleatória. Elas são coordenadas por um conjunto de instituições que tecem um mecanismo técnico-científico e ideológico abrangendo toda a estrutura social. Com base no conceito de *Instituições de Controle Social (ICS)*, assinale a alternativa correta: +- As ICS atuam de forma isolada e independente, sem formar um bloco de controle sobre o comportamento do indivíduo. +- As instituições de controle social possuem uma atuação meramente biológica, sem qualquer carga ideológica ou científica. +- A escola atua como uma ICS focada exclusivamente no controle moral, deixando a disciplina do saber para a igreja. +- O controle social exercido pelas ICS é opcional, permitindo que o indivíduo ignore as pressões das instituições básicas. += A família é considerada a base primária das ICS, enquanto o Estado representa o ápice soberano desse mecanismo. +> Na sociologia do controle, a Família representa o núcleo/base da sociabilização, enquanto o Estado-Jurídico opera no topo da pirâmide como a instituição que rege, centraliza e coordena todo o sistema de controle. +--- + +? A centralidade sociológica das instituições mudou ao longo da história para atender às demandas de cada época. O que antes era baseado no sangue e na fé, passou a focar na produção industrial e, posteriormente, na burocracia do saber e da lei formal. Considerando a *Evolução da Centralidade Sociológica*, relacione os períodos históricos com seus respectivos binômios: +- No pré-capitalismo, a centralidade residia no binômio Escola-Jurídico, fundamentada na burocracia técnica. +- Na pós-modernidade, o binômio Família-Igreja retorna como o eixo central de sociabilização e controle formal. +- O binômio Escola-Jurídico caracteriza o período pré-capitalista, onde a lei formal era a única unidade de controle. +- A centralidade baseada na fé e no sangue (Família-Igreja) é o modelo predominante na sociedade pós-moderna atual. += O capitalismo industrial estabeleceu o binômio Família-Escola para preparar corpos e mentes para a produção em massa. +> A Revolução Industrial demandou um novo perfil de trabalhador. O binômio Família-Escola substituiu a hegemonia da Igreja ao assumir a função de disciplinar e capacitar técnica e fisicamente os indivíduos para o trabalho fabril. +--- + +? Dois teóricos analisam como o Estado-Jurídico se relaciona com a base social (Família, Escola, Sindicatos). Um deles defende que o Estado atua de cima para baixo, determinando a base; o outro sugere que as instituições se sobrepõem no espaço social, gerando lacunas de autonomia. Comparando os modelos de *Bourdieu e Althusser* presentes no material, assinale a alternativa correta: +- Bourdieu defende que o Estado-Jurídico atua de forma vertical e absoluta, eliminando qualquer lacuna para a autonomia. +- Para Althusser, as instituições se sobrepõem de modo que sempre surgem espaços de resistência onde as esferas não se tocam. +- Ambos os autores concordam que a determinação do Estado sobre a base social é elástica e sempre permite a contestação. +- O modelo de Althusser foca na interpenetração espacial, enquanto Bourdieu foca na determinação hierárquica do Estado-Jurídico. += O modelo de interpenetração espacial de Bourdieu prevê que, onde as circunferências não se tocam, surgem espaços de resistência. +> Enquanto Althusser foca em uma visão estrutural top-down (determinação vertical pelo Estado), a leitura de Bourdieu foca nos campos (esferas) que se sobrepõem imperfeitamente, criando espaços sociais livres ou de resistência. +--- + +? Hannah Arendt defende que a verdadeira força política não nasce da violência bruta, mas da capacidade de agir em conjunto através da palavra e do convencimento. Em contrapartida, sistemas penais modernos debatem-se entre o castigo puramente vingativo e a restauração dos laços sociais. Avalie as alternativas sobre os *Paradigmas de Punição e a Potência da Não Violência*: +- O Direito Repressivo foca na reintegração do infrator, enquanto a Justiça Restaurativa foca no suplício corporal. +- A não violência, como demonstrado por Gandhi, é uma ferramenta passiva que aceita o poder mecânico do Estado sem resistência. +- A apatia do cidadão no espaço público é vista por Arendt como uma forma saudável de preservar a individualidade contra o Estado. +- A responsabilidade coletiva, para Arendt, significa que o cidadão não é cúmplice das atrocidades produzidas pelo Estado em democracias. += O Direito Repressivo foca na vingança e exclusão social, enquanto a Justiça Restaurativa foca nas causas do crime e na reativação social. +> O modelo punitivo (Direito Repressivo) baseia-se na retribuição, dor e isolamento do infrator. Já a Justiça Restaurativa propõe reatar o tecido social, tratando as origens sociológicas do conflito. +--- + +? Hannah Arendt alerta que a maior ameaça à liberdade não é necessariamente o golpe de força, mas a apatia do cidadão que permite a ascensão de poderes despóticos através de meios legais e democráticos, como ocorreu em regimes totalitários do século XX. Sobre a *Microfísica do Poder e a Potência da Não Violência*, analise as alternativas: +- O poder, segundo Foucault, reside exclusivamente em edifícios governamentais centrais, não se estendendo a hospitais ou escolas. +- A desobediência pacífica e a ocupação ética dos espaços públicos são ferramentas inúteis contra o poder mecânico e unidimensional do Estado. +- A justiça restaurativa foca na punição sumária e no suplício corporal como forma de reativar o tecido social e prevenir o crime. +- A impunidade do Estado oficial é a única causa da violência moderna, visto que os microaparelhos não possuem poder punitivo autônomo. += O poder despótico e o totalitarismo podem emergir utilizando mecanismos democráticos e o apoio das massas, alimentados pela inércia no espaço público. +> Arendt demonstra que o mal, muitas vezes, é 'banal' e burocrático. O totalitarismo triunfa quando os cidadãos perdem a potência de agir em conjunto (espaço público), permitindo que tiranos usem as leis e o sistema a seu favor. +--- + +? Um projeto de lei propõe que, em vez de apenas encarcerar indivíduos, o sistema penal deve focar na reparação do dano à vítima e na reintegração do infrator, buscando entender as causas originárias do crime para evitar a reincidência. Considerando o comparativo entre o *Direito Repressivo e a Justiça Restaurativa*, avalie as asserções: +I. O Direito Repressivo possui foco central na vítima, utilizando muitas vezes a mediação para curar o suplício corporificado. +II. A Justiça Restaurativa busca a reativação social por meio da prevenção e da reintegração, focando nas causas do conflito. +III. A violência moderna é causada exclusivamente pela impunidade estatal, já que o poder punitivo não existe fora do Estado oficial. É correto o que se afirma em: +- I, apenas. +- I e II, apenas. +- II e III, apenas. +- I, II e III. += II, apenas. +> A assertiva I está incorreta porque o Direito Repressivo clássico 'rouba' o conflito da vítima (teoria de Nils Christie) focando exclusivamente na punição estatal sobre o ofensor. A III é incorreta porque a microfísica de Foucault prova que o poder punitivo existe além do Estado. +--- + +? A análise histórica de regimes como o de Hitler e Stalin revela que o poder totalitário não necessita apenas de armas, mas também da arregimentação de massas e, principalmente, do silêncio daqueles que se abstêm da vida política. Com base nas reflexões de *Hannah Arendt* apresentadas no material, é correto afirmar que: +- A maior ameaça à liberdade é a força física do ditador, sendo a participação cidadã irrelevante para conter o despotismo. +- O poder despótico surge exclusivamente de golpes militares violentos, nunca utilizando mecanismos democráticos ou votos populares. +- A independência da Índia liderada por Gandhi exemplifica como a força bruta é o único caminho para desmontar um império. +- O espaço público mantém seu sentido original mesmo quando os indivíduos deixam de participar ou de dar significado a ele. += A inércia no espaço público e a apatia do cidadão eliminam o contrapeso necessário ao poder pelo poder do Estado. +> Para Arendt, o poder pertence ao grupo e só existe enquanto o grupo atua conjuntamente. A apatia e o recolhimento ao espaço puramente privado destroem a potência política que freia os abusos do Estado. +--- + +? Durante uma reforma no sistema de segurança pública, o governo decide investir simultaneamente em novas viaturas e armamentos para a Polícia Militar e em campanhas publicitárias que exaltam os "valores da família e do civismo" nas escolas públicas. Relacione as ações do governo com os conceitos de *AIE (Aparelhos Ideológicos de Estado)* e *ARE (Aparelhos Repressivos de Estado)*: +- O investimento em armamento policial exemplifica um AIE, pois foca na persuasão e na ética do cidadão. +- As campanhas sobre civismo nas escolas são exemplos de ARE, pois utilizam a hostilidade para garantir a ordem. +- O sistema judiciário atua exclusivamente como um ARE, sendo incapaz de exercer qualquer função ideológica na sociedade. +- Segundo Weber, a "coação jurídica" é um mecanismo exclusivo dos aparelhos repressivos, não possuindo carga ideológica. += A Polícia e o Exército operam a coercitividade pela violência física (ARE), enquanto a Escola e a Família operam pela ideologia e moral (AIE). +> Na teoria althusseriana, os AREs baseiam-se primordialmente na força e repressão física (polícia), enquanto os AIEs funcionam moldando a subjetividade através da moral, conhecimento e ideologia (escolas, mídia). +--- + +? Em uma sociedade complexa, o Estado exerce sua centralidade através de um aparelho técnico-ideológico supremo. Para manter a ordem, ele pode utilizar tanto operações ostensivas de segurança quanto a criação de regulamentações que interferem no funcionamento de entidades privadas. Sobre as formas de *Ação Direta e Indireta* do Estado como ICS, é correto afirmar que: +- A ação direta ocorre quando o Estado se infiltra de forma invisível nas famílias para ditar regras de convivência interna. +- A ação indireta caracteriza-se pelo uso do "Braço Forte", como o emprego do Exército e da Polícia em operações de rua. +- A interpenetração nas ICS privadas é uma forma de ação direta, pois o Estado utiliza a força física para coagir famílias. +- Nas sociedades modernas, o Estado abandonou a ação direta, limitando-se a influenciar o pensamento através da mídia. += O Estado exerce ação direta via Judiciário e órgãos públicos, e ação indireta ao ditar regras para escolas e corporações. +> A ação direta se dá pelas próprias 'mãos' do Estado (seus tribunais, sua polícia, seu fisco). A ação indireta ocorre por meio de leis e normas que forçam instituições não estatais a se adaptarem às vontades do Estado. +--- + +? Ao analisar a relação entre Estado, Escola e Família sob a ótica de Bourdieu e Althusser, observa-se que, embora o Estado tente determinar a base social de cima para baixo, existem lacunas onde a norma estatal não alcança plenamente a conduta dos indivíduos. Sobre os *Modelos de Interpenetração das ICS*, é correto afirmar que: +- A teoria de Althusser prevê que as instituições se sobrepõem de forma elástica, permitindo sempre a autonomia total do indivíduo. +- Os "Espaços de Resistência" surgem precisamente nos pontos de interpenetração espacial onde as circunferências das instituições se sobrepõem perfeitamente. +- A determinação do Estado-Jurídico sobre a base (família e sindicatos) elimina qualquer possibilidade de conduta contestatória ou autonomia. +- No modelo de Bourdieu, o Estado é a única instituição capaz de gerar sociabilização, sendo as demais meros apêndices sem interpenetração. += Onde as esferas de controle das instituições não se tocam, abrem-se fronteiras para a autonomia e o surgimento de espaços de resistência. +> Na metáfora dos espaços sociais, como as instituições (família, religião, estado) não conseguem estar em todos os lugares ao mesmo tempo, suas imperfeições de contato geram vácuos sociológicos onde floresce a autonomia e a resistência. +--- + +? Apesar da dominância do Estado e das instituições de controle, os teóricos observam que o sistema não é onipresente. Existem "bolsões de autonomia" que sobrevivem às pressões institucionais, abrindo fronteiras para condutas contestatórias. Sobre o surgimento desses *Espaços de Resistência*, assinale a alternativa correta: +- Eles surgem exclusivamente através da violência física contra os aparelhos repressivos de Estado (ARE). +- A resistência é impossível em sociedades pós-modernas devido à total eficiência da burocracia do saber. +- A microfísica do poder demonstra que a resistência só existe se for autorizada pelas instituições de controle moral. +- A autonomia do indivíduo é determinada pelo Estado-Jurídico, que define onde e como a resistência pode ocorrer. += Os espaços de resistência emergem nas lacunas deixadas pela falha na interpenetração absoluta das instituições. +> O controle social não é hermético. Onde o maquinário repressivo e ideológico falha em preencher a vida do sujeito, abrem-se os 'bolsões' onde novas práticas, contestações e ressignificações podem ocorrer. +--- + +? A Sociologia do Controle observa que a centralidade das Instituições de Controle Social (ICS) não é estática, deslocando-se para acompanhar as transformações tecnológicas e burocráticas da sociedade. Se o capitalismo industrial foi marcado pelo binômio Família-Escola, a contemporaneidade é definida pela "Máquina Fria" da tecnocracia e pela onipresença dos fluxos informacionais. Nesse contexto, a unidade predominante de controle deixa de ser apenas a disciplina do saber e passa a integrar a gestão da visibilidade e do discurso. Com base na *Evolução da Centralidade Sociológica*, qual binômio melhor define a configuração atual das estratégias de poder e controle social? +- Família e Igreja, baseando-se no eixo central do sangue e da fé para a manutenção da ordem. +- Família e Escola, preparando mentes e corpos para a produção em massa e o trabalho industrial. +- Escola e Sindicatos, focando na organização política da base trabalhadora contra o ápice soberano. +- Igreja e Comunicação, utilizando o controle moral para resistir à interpenetração do Estado-Jurídico. += Estado/Jurídico e Comunicação, consolidando a burocracia da lei formal e o controle dos fluxos de informação. +> Na pós-modernidade, a força coordenadora migra pesadamente para o aparato burocrático-legal do Estado aliado ao controle das massas pela mídia e plataformas tecnológicas (Comunicação). +--- + +? No século XXI, um cidadão percebe que a maioria de seus conflitos cotidianos não é resolvida por conselhos familiares ou autoridades religiosas, mas por burocracias digitais, tribunais e normas técnicas geridas por especialistas que ele nunca conheceu. Considerando a *Evolução da Centralidade do poder* em diversos períodos da história humana e a *Tecnocracia*, assinale a alternativa correta: +- O capitalismo industrial manteve a fé e o sangue (binômio Família-Igreja) como os eixos centrais de preparação de mentes para a produção. +- A tecnocracia estatal aproxima a gestão pública dos valores humanos e da democracia participativa, eliminando o risco da anomia. +- A separação entre o poder econômico e o aparelho de Estado em regimes autoritários é o que garante a liberdade e os espaços de contestação. +- A "Máquina Fria" da burocracia estatal impede o surgimento de novas formas de resistência, pois transforma a gestão de pessoas em gestão de planilhas. += Na pós-modernidade, o binômio Escola-Jurídico torna-se a unidade predominante de controle, consolidando a lei formal e a burocracia do saber. +> No mundo contemporâneo, a autoridade não deriva mais prioritariamente do sagrado (Igreja), mas sim do conhecimento técnico especializado, das certificações (Escola/Saber) e da normalização das leis (Jurídico). +--- + +? Um sociólogo analisa o funcionamento de uma unidade de internação de menores. Ele percebe que, ao mesmo tempo que os jovens são submetidos a uma disciplina rígida e vigilância armada (coação física), eles participam de oficinas de ética e moralidade que visam moldar seu comportamento futuro (coação jurídica/ideológica). Com base nos conceitos de *Aparelhos Ideológicos (AIE) e Aparelhos Repressivos de Estado (ARE)*, assinale a alternativa correta: +- A escola e a igreja são exemplos típicos de ARE, pois utilizam a violência física para garantir a manutenção da ordem social. +- A coação jurídica, conforme o conceito de Weber, baseia-se exclusivamente na hostilidade física exercida pelo Exército. +- Os AIEs são operados unicamente pelo braço forte do Estado, sem qualquer interpenetração em instituições da esfera privada. +- A microfísica do poder demonstra que a repressão física é a única ferramenta eficaz de controle em sociedades pós-modernas. += O Estado exerce sua soberania de forma bipolar, alternando entre a persuasão ideológica e a repressão física, sendo o sistema penal o local onde ambas se fundem. +> O sistema penal/prisional é o maior exemplo prático de convergência no pensamento estruturalista de Althusser. Ele atua duramente como ARE (muros, força letal), mas possui um fortíssimo elemento de AIE (tentativa disciplinar e ideológica de 'reeducar' o interno). +--- + +? Michel Foucault propõe que o poder não deve ser visto apenas como uma estrutura centralizada (como um palácio ou sede de governo), mas como algo que se manifesta nos hospitais, nas fábricas e nos escritórios locais, muitas vezes à revelia da lei estatal. Com base na *Microfísica do Poder*, é correto afirmar que: +- O poder é uma propriedade que pertence exclusivamente ao Estado e que é distribuída de cima para baixo de forma linear. +- O poder de Foucault reside no acordo tácito onde o Estado abre mão de vigiar as classes mais pobres e excluídas. +- A vigilância especializada exercida por subórgãos locais é sempre menos eficaz que o ordenamento jurídico formal. +- A resistência ao poder só é possível quando se ataca o edifício central do Estado, ignorando as instituições locais. += O poder é imanente e descentralizado, o que significa que cada micro-organização inventa sua própria repressão e ideologia. +> Foucault quebra o paradigma de que o poder só emana do Estado (visão macro). Ele argumenta que o poder é uma malha capilar (micro) presente no panoptismo dos hospitais, manicômios e indústrias, operando uma vigilância disciplinar constante. +--- + diff --git a/content/txts/ciencia-politica-teoria-estado/_index.md b/content/txts/ciencia-politica-teoria-estado/_index.md new file mode 100644 index 0000000..ab0b6bb --- /dev/null +++ b/content/txts/ciencia-politica-teoria-estado/_index.md @@ -0,0 +1,22 @@ +--- +title: "Ciência Política e Teoria do Estado" +files: + - name: "Teoria Geral do Estado e Ciência Política" + url: "https://drive.google.com/file/d/1_mkWxoowmvMUq6yZKzfs-pbV1l9cM7Jl/view?usp=drive_link" + - name: "Natureza Humana" + url: "https://drive.google.com/file/d/1vLFlqSL4PjBBmZvuBM2HOH8--q8cUKt6/view?usp=drive_link" + - name: "Sociedade" + url: "https://drive.google.com/file/d/1S1aRtnCBWjrVqGNB-lIJU-n0PnsZRNhw/view?usp=drive_link" + - name: "Estado" + url: "https://drive.google.com/file/d/1Q5fGUrHiPfo9wN4llh_bwggBLrr2W6vQ/view?usp=drive_link" + - name: "Formas de Governo" + url: "https://drive.google.com/file/d/1UEQGWODoYjjHt-3Dae3-iczadLkfOFks/view?usp=drive_link" + - name: "Democracia" + url: "https://drive.google.com/file/d/1L6vlkRtrau2-Z0TtWA3uVvbrPZ2BDiK8/view?usp=drive_link" + - name: "Sistemas de Governo" + url: "https://drive.google.com/file/d/1FaJBl6GVfuARcjOGKzQuyyRdR7CzREcu/view?usp=drive_link" + - name: "O Estado Brasileiro" + url: "https://drive.google.com/file/d/1kf1oAGpA0YK3gXjGplPlANoXg8kxaHxD/view?usp=drive_link" + - name: "Corrupção" + url: "https://drive.google.com/file/d/1koIUYVzYTn6tySnNjim-F6eKOf8xojo7/view?usp=drive_link" +--- diff --git a/content/txts/direito-constitucional/_index.md b/content/txts/direito-constitucional/_index.md new file mode 100644 index 0000000..2d7f57f --- /dev/null +++ b/content/txts/direito-constitucional/_index.md @@ -0,0 +1,14 @@ +--- +title: "Direito constitucional" +guide: | + ### Referências recomendadas + - Flávios Martins, por possuir linguagem mais simples; + - Marcelino Novelino, mais detalhado; + - Obras dos ministros (do STF) Barroso, Alexandre de Moraes e Gilmar Mendes (a linguagem deles é, geralmente, acessível, sem muito juridiquês). + + Nos clássicos incluiu-se: + - Pendro Lenza (o esquematizado sobre Direito Constitucional); + - José Afonso da Silva (bem clássicos); + - Uadi Lammêgo Bulos; + - José Gomes Canotilho (teoria constitucional). +--- diff --git a/content/txts/linguagem-juridica/_index.md b/content/txts/linguagem-juridica/_index.md new file mode 100644 index 0000000..c4599ea --- /dev/null +++ b/content/txts/linguagem-juridica/_index.md @@ -0,0 +1,6 @@ +--- +title: "Linguagem e argumentação jurídica" +files: + - name: "Texto-base" + url: "https://drive.google.com/file/d/1X9rF9MQbdfP10fDTJbGFnK-AeGGCO2gL/view?usp=drive_link" +--- diff --git a/content/txts/metodologia/_index.md b/content/txts/metodologia/_index.md new file mode 100644 index 0000000..228d2bc --- /dev/null +++ b/content/txts/metodologia/_index.md @@ -0,0 +1,6 @@ +--- +title: "Metodologia do trabalho científico" +files: + - name: "Texto-base" + url: "https://drive.google.com/file/d/1lrtG3kkYaV-hCprSVuEacwiDytpOY3FO/view?usp=drive_link" +--- diff --git a/content/txts/relacoes-etnico-raciais-direitos-humanos/_index.md b/content/txts/relacoes-etnico-raciais-direitos-humanos/_index.md new file mode 100644 index 0000000..cd0a046 --- /dev/null +++ b/content/txts/relacoes-etnico-raciais-direitos-humanos/_index.md @@ -0,0 +1,6 @@ +--- +title: "Relações étnico-raciais e Direito humanos" +guide: | + ### Referências recomendadas + - Direito humanos: André Ramos Tavares +--- diff --git a/content/txts/sociologia-antropologia-juridica/_index.md b/content/txts/sociologia-antropologia-juridica/_index.md new file mode 100644 index 0000000..8a8fcc2 --- /dev/null +++ b/content/txts/sociologia-antropologia-juridica/_index.md @@ -0,0 +1,6 @@ +--- +title: "Sociologia e Antropologia Jurídica" +files: + - name: "Texto-base" + url: "https://drive.google.com/file/d/1hcbHhitTgH3qMkM-nPwBm9LkanzIlglj/view?usp=drive_link" +--- diff --git a/content/txts/sociologia-antropologia-juridica/divisao-trabalho.md b/content/txts/sociologia-antropologia-juridica/divisao-trabalho.md new file mode 100644 index 0000000..461c59a --- /dev/null +++ b/content/txts/sociologia-antropologia-juridica/divisao-trabalho.md @@ -0,0 +1,3 @@ +--- +title: Divisão técnica e social do trabalho +--- diff --git a/content/txts/sociologia-antropologia-juridica/poder-dominacao.md b/content/txts/sociologia-antropologia-juridica/poder-dominacao.md new file mode 100644 index 0000000..a93cc9d --- /dev/null +++ b/content/txts/sociologia-antropologia-juridica/poder-dominacao.md @@ -0,0 +1,3 @@ +--- +title: Poder e dominação +--- diff --git a/content/txts/teoria-historia-direito/_index.md b/content/txts/teoria-historia-direito/_index.md new file mode 100644 index 0000000..c779573 --- /dev/null +++ b/content/txts/teoria-historia-direito/_index.md @@ -0,0 +1,8 @@ +--- +title: "Teoria e história do Direito" +guide: | + ### Referências recomendadas + - Eduardo Bittar — principal da disciplina + - Flávio Lages — complementar (fora da biblioteca) + - Wolkmir — evite, está desatualizada +--- diff --git a/content/txts/teoria-historia-direito/constituicoes.md b/content/txts/teoria-historia-direito/constituicoes.md new file mode 100644 index 0000000..667d099 --- /dev/null +++ b/content/txts/teoria-historia-direito/constituicoes.md @@ -0,0 +1,30 @@ +--- +title: Comparação de constituições +--- + +| Ano | Forma de Estado | Forma de Governo | Separação de Poderes | Direitos e Deveres [^5] [^6] | +| --- | --- | --- | --- | --- | +| **1824** [^7] | Unitário | Monarquia [^8] | Quadripartite [^9] | *Não especificado* | +| **1891** [^1] [^10] | Federativo | República [^11] | Tripartite [^12] | Individuais e políticos [^13] | +| **1934** | Federativo | República | Tripartite | Individuais, políticos [^14] e sociais [^15] | +| **1937** [^1] | Federativo | República | Tripartite | Retrocesso | +| **1946** | Federativo | República | Tripartite | Restauração | +| **1969** [^1] | Federativo | República | Tripartite | Retrocesso | +| **1988** | Federativo | República | Tripartite | Individuais/coletivos, sociais, nacionalidade e políticos [^16] | + +[^1]: **Tipo de outorga:** 1891, 1937 e 1969 foram todos 'outorgados' (impostos) e não 'promulgados'. +[^2]: **Forma de Estado:** Como o poder é determinado no território. +[^3]: **Forma de Governo:** Como o poder é exercido. +[^4]: **Separação de Poderes:** É um modelo político. +[^5]: **Direitos:** É o "bem jurídico tutelado" (exigível/exercível). +[^6]: **Deveres:** Todo 'direito' tem mecanismo de proteções ('remédios constitucionais'). +[^7]: **Contexto (1824):** A elite era a nobreza, já os marginalizados eram os escravizados, no geral. +[^8]: **Monarquia:** Hereditariedade, vitaliciedade e irresponsabilidade (não-responder pessoalmente). +[^9]: **Quadripartite:** Adiciona-se o "poder moderador". +[^10]: **Contexto (1891):** A elite eram os militares e coronéis; já os marginalizados eram os imigrantes e ex-escravizados. +[^11]: **República:** Efetividade, temporalidade e responsabilidade. +[^12]: **Tripartite (1891):** Nominal, pois o executivo manda nos demais. +[^13]: **Direitos (1891):** Porém desigual (sem voto de mulheres ou de analfabetos). +[^14]: **Políticos (1934):** Adição de voto feminino; constitucionalizando o já previsto em 1932 (decreto nº 21.076). +[^15]: **Sociais (1934):** Previsão de direitos trabalhistas. +[^16]: **Políticos (1988):** Facilitasse e promovesse a filiação partidária (partidos políticos). diff --git a/content/txts/teoria-historia-direito/direito-colonial.md b/content/txts/teoria-historia-direito/direito-colonial.md new file mode 100644 index 0000000..a879409 --- /dev/null +++ b/content/txts/teoria-historia-direito/direito-colonial.md @@ -0,0 +1,8 @@ +--- +title: Direito colonial +--- + +O Direito brasileiro colonial não foi apenas um conjunto de regras importadas, mas sim uma complexa montagem de um aparelho jurídico e administrativo voltado para a manutenção do projeto colonial lusitano. Estruturado sobre os pilares político, jurídico e administrativo, o sistema evoluiu da fragmentação das capitanias hereditárias para a centralização do Governo-Geral, enquanto as Câmaras Municipais serviam como o elo vital para a gestão dos interesses locais e das elites regionais. Esse ordenamento operava dentro de uma lógica econômica mercantilista e era sustentado pelo regime escravagista, o que resultou em uma sociedade profundamente desigual, hierarquizada de forma vertical e rígida, onde o Direito servia para institucionalizar a exclusão. + +Juridicamente, o sistema era marcado por um caráter heterogêneo e confessional, fundindo o Direito português com o Direito Canônico (da Igreja) e as sucessivas Ordenações do Reino. Como a realidade da Colônia frequentemente ultrapassava as normas fixas, utilizavam-se leis extravagantes para atualizar as práticas, recorrendo-se ao Direito Romano para integrar as lacunas do sistema. O grande marco legislativo desse período foram as Ordenações Filipinas, que organizavam a vida social e institucional em cinco volumes distintos: o primeiro tratava da administração e organização judiciária; o segundo regulava a forte relação com a Igreja; o terceiro focava nos ritos processuais; o quarto nas relações civis e comerciais, como contratos; e o quinto definia o rigoroso sistema de crimes e penas. +A finalidade última desse aparato não era a justiça social, mas a perpetuação do poder da Coroa e das elites, garantindo que a lei funcionasse como um instrumento de controle e estabilidade de um sistema pautado pela exploração e pela desigualdade. diff --git a/content/txts/teoria-historia-direito/direito-contemporaneo.md b/content/txts/teoria-historia-direito/direito-contemporaneo.md new file mode 100644 index 0000000..0a07667 --- /dev/null +++ b/content/txts/teoria-historia-direito/direito-contemporaneo.md @@ -0,0 +1,5 @@ +--- +title: Direito contemporâneo +--- + +O Direito contemporâneo consolidou-se como o estágio atual da evolução jurídica, manifestando-se plenamente no conceito de Estado Democrático de Direito, que fundamenta o sistema legal na soberania popular, garantindo o direito de votar e ser votado, e na estrita submissão de todos os poderes à lei. Este modelo surge como uma evolução necessária após a crise do Estado Social, sistema que buscava a igualdade material através da ampla intervenção estatal, mas que acabou declinando devido à falta de recursos financeiros para sustentar suas crescentes demandas e garantias sociais. Dessa forma, o Estado Democrático de Direito contemporâneo atua como uma síntese integradora que busca equilibrar a justiça social com a racionalidade econômica, reconhecendo que a viabilidade das normas e direitos depende da saúde financeira do Estado para que as promessas legais não se tornem vazias. Nesse contexto, o Direito é compreendido como uma produção cultural e um reflexo das exigências da sociedade, onde o ordenamento jurídico contemporâneo protege as liberdades individuais e promove a participação política, mantendo-se atento às limitações econômicas para garantir a estabilidade das instituições e a dignidade humana de forma clara e objetiva. diff --git a/content/txts/teoria-historia-direito/direito-moderno.md b/content/txts/teoria-historia-direito/direito-moderno.md new file mode 100644 index 0000000..16a0e0f --- /dev/null +++ b/content/txts/teoria-historia-direito/direito-moderno.md @@ -0,0 +1,9 @@ +--- +title: "Direito moderno" +--- + +O Direito moderno surgiu da convergência entre o Humanismo, o Renascimento e a recepção técnica do Direito Romano, que serviu de base para a estruturação jurídica do Estado Moderno. Inicialmente, essa transição manifestou-se no Estado Absolutista, cujo auge na França de Luís XIV centralizou o poder nas mãos do monarca, criando um sistema sem direitos ou garantias individuais para os súditos, onde a vontade real prevalecia sobre qualquer norma. + +A ruptura com esse modelo ocorreu por meio das críticas do Iluminismo, com pensadores como Cesare Beccaria defendendo a reforma da justiça e a proporcionalidade das penas contra o arbítrio soberano. Esse movimento culminou no Estado Liberal, impulsionado pela Independência dos Estados Unidos e pela Revolução Francesa, que instituiu a Igualdade Formal e a proteção das liberdades individuais por meio de documentos como a Declaração dos Direitos do Homem e do Cidadão. Um marco fundamental dessa era foi o Código Civil Napoleônico de 1804, que sistematizou as normas civis sob uma ótica liberal e burguesa, utilizando a tradição técnica romana para garantir a segurança jurídica. + +Com o tempo, a percepção de que a igualdade perante a lei não corrigia as desigualdades sociais levou ao surgimento do Estado Social, que buscou a igualdade material através da intervenção estatal na economia e na proteção de direitos sociais. Contudo, esse modelo enfrentou crises profundas devido à falta de recursos para sustentar suas demandas. A evolução para o Direito Contemporâneo consolidou o Estado Democrático, fundamentado na soberania popular e no direito de votar e ser votado, culminando no conceito de Estado de Direito, que une os ideais liberais e sociais sob a estrita submissão de todos os poderes à lei. diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html new file mode 100644 index 0000000..4fca1f9 --- /dev/null +++ b/layouts/_default/baseof.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<html lang="pt-BR"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta name="description" content="{{ .Site.Params.description }}"> + <meta name="theme-color" content="#c9a961"> + <link rel="manifest" href="{{ "/manifest.json" | relURL }}"> + <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect fill='%230f0f13' width='100' height='100'/><text x='50' y='75' font-size='60' font-weight='bold' text-anchor='middle' fill='%23c9a961' font-family='monospace'>D</text></svg>"> + <link rel="preconnect" href="https://fonts.googleapis.com"> + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;1,400&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> + <link rel="stylesheet" href="{{ "/main.css" | relURL }}"> + <title>{{ block "title" . }}{{ .Site.Title }}{{ end }}</title> +</head> +<body> + <header> + {{ block "header" . }} + <a class="site-name" href="{{ "/" | relURL }}">direito</a> + {{ end }} + </header> + <main> + {{ block "main" . }}{{ end }} + </main> + + <script src="{{ "/quiz-parser.js" | relURL }}"></script> + <script src="{{ "/quiz-engine.js" | relURL }}"></script> + <script src="{{ "/quiz-runtime.js" | relURL }}"></script> + <script src="{{ "/footnotes.js" | relURL }}"></script> + <script src="{{ "/home-filter.js" | relURL }}"></script> + <script src="{{ "/toc.js" | relURL }}"></script> + <script src="{{ "/dark-mode.js" | relURL }}"></script> + <script src="{{ "/keyboard-shortcuts.js" | relURL }}"></script> + <script src="{{ "/dashboard.js" | relURL }}"></script> + + {{ block "scripts" . }}{{ end }} + + <script> + if ('serviceWorker' in navigator) { + window.addEventListener('load', () => { + navigator.serviceWorker.register('/sw.js').catch(() => {}); + }); + } + </script> +</body> +</html> diff --git a/layouts/_default/list.html b/layouts/_default/list.html new file mode 100644 index 0000000..09804c9 --- /dev/null +++ b/layouts/_default/list.html @@ -0,0 +1,146 @@ +{{ define "title" }} + {{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} · {{ .Site.Title }}{{ end }} +{{ end }} + +{{ define "header" }} + <a class="site-name" href="{{ "/" | relURL }}">direito</a> + {{ if not .IsHome }} + <span class="header-sep">/</span> + <span class="breadcrumb">{{ .Title }}</span> + {{ end }} +{{ end }} + +{{ define "main" }} +{{ if .IsHome }} + + {{/* Mapas por slug de disciplina */}} + {{ $simMap := dict }} + {{ $quizMap := dict }} + {{ $discsQz := slice }} + {{ range .Site.RegularPages }} + {{ if eq .Section "quizzes" }} + {{ $slug := .File.Dir | strings.TrimSuffix "/" | path.Base }} + {{ if not (in $discsQz $slug) }}{{ $discsQz = $discsQz | append $slug }}{{ end }} + {{ $isSim := or (.Params.simulado) (in (.File.BaseFileName | lower) "simulado") }} + {{ if $isSim }} + {{ $simMap = merge $simMap (dict $slug (add (index $simMap $slug | default 0) 1)) }} + {{ else }} + {{ $quizMap = merge $quizMap (dict $slug (add (index $quizMap $slug | default 0) 1)) }} + {{ end }} + {{ end }} + {{ end }} + + <div class="disc-grid"> + {{ $txtsSlugs := slice }} + {{ range .Site.Sections }} + {{ if eq .Section "txts" }} + {{ range .Sections }} + {{ $slug := .RelPermalink | strings.TrimSuffix "/" | path.Base }} + {{ $txtsSlugs = $txtsSlugs | append $slug }} + {{ $sims := index $simMap $slug | default 0 }} + {{ $qz := index $quizMap $slug | default 0 }} + <a class="disc-card fade" href="{{ .RelPermalink }}" data-filterable> + <div class="disc-card-name">{{ .Title }}</div> + <div class="disc-card-meta"> + {{ $n := len .RegularPages }} + {{ if $n }}{{ $n }} assunto{{ if gt $n 1 }}s{{ end }}{{ else }}sem textos{{ end }} + {{ if $sims }} · simulado{{ end }} + {{ if $qz }} · {{ $qz }} exercício{{ if gt $qz 1 }}s{{ end }}{{ end }} + </div> + </a> + {{ end }} + {{ end }} + {{ end }} + + {{/* Disciplinas que só existem em quizzes (sem txts) */}} + {{ range $slug := $discsQz }} + {{ if not (in $txtsSlugs $slug) }} + {{ $sims := index $simMap $slug | default 0 }} + {{ $qz := index $quizMap $slug | default 0 }} + <a class="disc-card fade" href="/{{ $slug }}/" data-filterable> + <div class="disc-card-name">{{ humanize (replace $slug "-" " ") }}</div> + <div class="disc-card-meta"> + {{ if $sims }}simulado · {{ end }}{{ $qz }} exercício{{ if gt $qz 1 }}s{{ end }} + </div> + </a> + {{ end }} + {{ end }} + </div> + +{{ else }} + {{/* Página de disciplina */}} + {{ $discSlug := .RelPermalink | strings.TrimSuffix "/" | path.Base }} + <div class="discipline fade"> + <div class="disc-back"><a href="{{ "/" | relURL }}">← disciplinas</a></div> + <div class="discipline-name">{{ .Title }}</div> + + {{ if .Content }} + <div class="disc-intro"> + <article class="prose disc-intro-prose">{{ .Content }}</article> + </div> + {{ end }} + + <div class="disc-extras-wrap"> + {{ partial "disc-extras.html" . }} + </div> + + {{/* Simulados desta disciplina */}} + {{ $simulados := slice }} + {{ range $.Site.RegularPages }} + {{ if eq .Section "quizzes" }} + {{ $parent := .File.Dir | strings.TrimSuffix "/" | path.Base }} + {{ if eq $parent $discSlug }} + {{ if or (.Params.simulado) (in (.File.BaseFileName | lower) "simulado") }} + {{ $simulados = $simulados | append . }} + {{ end }} + {{ end }} + {{ end }} + {{ end }} + {{ with $simulados }} + <div class="subject-group" style="margin-bottom:1.5rem;"> + <div class="sq-label" style="margin-bottom:.65rem;">simulados</div> + <div class="sq-list"> + {{ range . }} + <a class="sq-item" href="{{ .RelPermalink }}"> + <span class="sq-name" style="color:var(--amber);">{{ .Title }}</span> + <span class="sq-meta">simulado</span> + </a> + {{ end }} + </div> + </div> + {{ end }} + + {{/* Assuntos com vínculo aos exercícios */}} + {{ $exercises := slice }} + {{ range $.Site.RegularPages }} + {{ if eq .Section "quizzes" }} + {{ $parent := .File.Dir | strings.TrimSuffix "/" | path.Base }} + {{ if eq $parent $discSlug }} + {{ if not (or (.Params.simulado) (in (.File.BaseFileName | lower) "simulado")) }} + {{ $exercises = $exercises | append . }} + {{ end }} + {{ end }} + {{ end }} + {{ end }} + + <div class="subject-list"> + {{ range .RegularPages }} + {{ $assuntoSlug := .File.BaseFileName }} + {{ $linkedQuiz := false }} + {{ range $exercises }} + {{ if eq .File.BaseFileName $assuntoSlug }} + {{ $linkedQuiz = . }} + {{ end }} + {{ end }} + <a class="subject-row" href="{{ .RelPermalink }}"> + <span class="subject-row-name">{{ .Title }}</span> + <span class="subject-row-meta"> + texto{{ if $linkedQuiz }} · exercícios{{ end }} + </span> + </a> + {{ end }} + {{ if not .RegularPages }}<p class="empty-note">sem assuntos ainda</p>{{ end }} + </div> + </div> +{{ end }} +{{ end }} diff --git a/layouts/partials/disc-extras.html b/layouts/partials/disc-extras.html new file mode 100644 index 0000000..adff907 --- /dev/null +++ b/layouts/partials/disc-extras.html @@ -0,0 +1,72 @@ +{{/* disc-extras.html — recebe . (a página, seja _index ou single) + Renderiza colapsáveis: Guia de Estudo, Materiais (com subdivisões: Textos, Vídeos, Áudios) */}} + +{{/* ── Guia de estudo (guide) ── */}} +{{ with .Params.guide }} +<details class="disc-extra"> + <summary class="disc-extra-summary"> + <span class="disc-extra-label">guia de estudo</span> + <span class="disc-extra-chevron">↓</span> + </summary> + <div class="disc-extra-body disc-extra-prose"> + {{ . | markdownify }} + </div> +</details> +{{ end }} + +{{/* ── Materiais unificados (files, videos, audios) ── */}} +{{ $files := .Params.files }} +{{ $videos := .Params.videos }} +{{ $audios := .Params.audios }} +{{ if or $files (or $videos $audios) }} +<details class="disc-extra"> + <summary class="disc-extra-summary"> + <span class="disc-extra-label">materiais</span> + <span class="disc-extra-count"> + {{ $n := 0 }} + {{ with $files }}{{ $n = add $n (len .) }}{{ end }} + {{ with $videos }}{{ $n = add $n (len .) }}{{ end }} + {{ with $audios }}{{ $n = add $n (len .) }}{{ end }} + {{ $n }} item{{ if gt $n 1 }}s{{ end }} + </span> + <span class="disc-extra-chevron">↓</span> + </summary> + <div class="disc-extra-body"> + {{/* Textos (arquivos/links) */}} + {{ with $files }} + <div class="disc-extra-kind-label">textos</div> + {{ range . }} + <a class="disc-extra-row" href="{{ .url }}" target="_blank" rel="noopener"> + <span class="disc-extra-icon">↗</span> + <span class="disc-extra-name">{{ .name }}</span> + <span class="disc-extra-meta">link</span> + </a> + {{ end }} + {{ end }} + + {{/* Vídeos */}} + {{ with $videos }} + <div class="disc-extra-kind-label">vídeos</div> + {{ range . }} + <a class="disc-extra-row" href="{{ .url }}" target="_blank" rel="noopener"> + <span class="disc-extra-icon">▶</span> + <span class="disc-extra-name">{{ .name }}</span> + <span class="disc-extra-meta">youtube</span> + </a> + {{ end }} + {{ end }} + + {{/* Áudios */}} + {{ with $audios }} + <div class="disc-extra-kind-label">áudios</div> + {{ range . }} + <a class="disc-extra-row" href="{{ .url }}" target="_blank" rel="noopener"> + <span class="disc-extra-icon">♪</span> + <span class="disc-extra-name">{{ .name }}</span> + <span class="disc-extra-meta">youtube</span> + </a> + {{ end }} + {{ end }} + </div> +</details> +{{ end }} diff --git a/layouts/partials/quiz-runtime.html b/layouts/partials/quiz-runtime.html new file mode 100644 index 0000000..9ce5aab --- /dev/null +++ b/layouts/partials/quiz-runtime.html @@ -0,0 +1,447 @@ +<script> +/* ── ESTADO DA APLICAÇÃO ── */ +let currentQuiz = null; +/* questions, qIndex, acertos, erros, answered → usam _qState (quiz-engine.html) */ + +const BASE_TITLE = document.title; + +let activeFloater = null; +let currentTextComments = {}; +let lastSubjectRoute = null; + +/* ── UTILITÁRIOS ── */ +/* shuffle, esc, md → quiz-parser.html | _qState, _renderQuestion, etc. → quiz-engine.html */ + +function slugifyText(s) { + return String(s || '') + .normalize('NFD').replace(/[\u0300-\u036f]/g, '') + .toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''); +} + +function show(id) { + ['page-home','page-quiz','page-result','page-text'].forEach(p => { + const el = document.getElementById(p); + if (el) el.classList.toggle('active', p === id); + }); +} + +/* ── ROTEAMENTO ── */ +function encodeHash(type, a, b, c) { + if (type === 'home') return ''; + if (type === 'disc') return '#disc/' + encodeURIComponent(a); + if (type === 'assunto') return '#assunto/' + [a, b].map(encodeURIComponent).join('/'); + if (type === 'quiz') return '#quiz/' + [a, b, c].map(encodeURIComponent).join('/'); + if (type === 'simulado') return '#simulado/' + [a, b].map(encodeURIComponent).join('/'); + return ''; +} + +function decodeHash(hash) { + if (!hash || hash === '#') return { type: 'home' }; + const parts = (hash.startsWith('#') ? hash.slice(1) : hash) + .split('/').map(s => { try { return decodeURIComponent(s); } catch(e) { return s; } }); + + const type = parts[0]; + if (type === 'disc' && parts.length >= 2) + return { type: 'disc', disc: parts[1] }; + if (type === 'assunto' && parts.length >= 3) + return { type: 'assunto', discFolder: parts[1], slug: parts[2] }; + if (type === 'quiz' && parts.length >= 4) + return { type: 'quiz', discFolder: parts[1], slug: parts[2], quizId: parts[3] }; + if (type === 'simulado' && parts.length >= 3) + return { type: 'simulado', discFolder: parts[1], quizId: parts[2] }; + + return { type: 'home' }; +} + +function navigate(hash, replace) { + const url = window.location.pathname + window.location.search + (hash || ''); + if (replace) history.replaceState({ hash }, '', url || window.location.pathname); + else history.pushState({ hash }, '', url || window.location.pathname); +} + +function renderRoute(route) { + if (route.type === 'disc') _showDisc(route.disc); + else if (route.type === 'assunto') _openSubject(route.discFolder, route.slug); + else if (route.type === 'quiz') _startQuiz(route.discFolder, route.slug, route.quizId); + else if (route.type === 'simulado')_startSimulado(route.discFolder, route.quizId); + else _showHome(); +} + +window.addEventListener('popstate', () => renderRoute(decodeHash(window.location.hash))); + +/* ── INTERFACE: HOME ── */ +function showHome() { navigate('', false); _showHome(); } + +function _showHome() { + closeFloater(); + buildHome(); + show('page-home'); + document.getElementById('breadcrumb').textContent = ''; + document.title = BASE_TITLE; +} + +function buildHome() { + const el = document.getElementById('page-home'); + el.innerHTML = ` + <div class="disc-grid"> + ${DATA.map(disc => { + const n = disc.subjects.length; + const nq = disc.subjects.reduce((acc, s) => acc + s.quizzes.length, 0) + (disc.quizzes ? disc.quizzes.length : 0); + const meta = [ + n ? `${n} assunto${n > 1 ? 's' : ''}` : null, + nq ? `${nq} quiz${nq > 1 ? 'zes' : ''}` : null, + ].filter(Boolean).join(' · ') || 'sem conteúdo'; + return ` + <a class="disc-card fade" href="${encodeHash('disc', disc.discipline)}" + onclick="openDisc('${esc(disc.discipline)}');return false;"> + <div class="disc-card-name">${esc(disc.discipline)}</div> + <div class="disc-card-meta">${meta}</div> + </a>`; + }).join('')} + </div>`; +} + +/* ── INTERFACE: DISCIPLINA ── */ +function openDisc(discName) { navigate(encodeHash('disc', discName), false); _showDisc(discName); } + +function _showDisc(discName) { + closeFloater(); + const disc = DATA.find(d => d.discipline === discName); + if (!disc) { _showHome(); return; } + + const introHtml = disc.intro && disc.intro.content + ? `<div class="disc-intro"> + <article class="prose disc-intro-prose">${disc.intro.content}</article> + ${disc.intro.references && disc.intro.references.length + ? `<div class="references disc-intro-refs"> + <div class="ref-head">referências</div> + <div class="ref-list">${disc.intro.references.map(r => `<p>${esc(r)}</p>`).join('')}</div> + </div>` + : ''} + </div>` + : ''; + + const simuladosHtml = disc.quizzes && disc.quizzes.length ? ` + <div class="subject-group"> + <div class="sq-label" style="margin-bottom:10px;">Simulados da Disciplina</div> + ${disc.quizzes.map(qz => ` + <a class="sq-item" href="${encodeHash('simulado', disc.folderKey, qz.id)}" + onclick="startSimulado('${esc(disc.folderKey)}','${esc(qz.id)}');return false;" + style="border-bottom:none; background:var(--surface); padding: 10px; border-radius: 4px; margin-bottom: 5px;"> + <span class="sq-name" style="color:var(--amber); font-weight:bold;">${esc(qz.title)}</span> + <span class="sq-meta">${qz.questions.length} questões</span> + </a> + `).join('')} + </div> + ` : ''; + + const subjectsHtml = disc.subjects.map(s => { + const bits = []; + if (s.text) bits.push('texto'); + if (s.quizzes.length) bits.push(`${s.quizzes.length} quiz${s.quizzes.length > 1 ? 'zes' : ''}`); + const meta = bits.join(' · ') || 'vazio'; + return ` + <a class="subject-row" href="${encodeHash('assunto', disc.folderKey, s.slug)}" + onclick="openSubject('${esc(disc.folderKey)}','${esc(s.slug)}');return false;"> + <span class="subject-row-name">${esc(s.name)}</span> + <span class="subject-row-meta">${meta}</span> + </a>`; + }).join(''); + + const el = document.getElementById('page-home'); + el.innerHTML = ` + <div class="discipline fade"> + <div class="disc-back"><a href="#" onclick="showHome();return false;">← disciplinas</a></div> + <div class="discipline-name">${esc(disc.discipline)}</div> + ${introHtml} + ${simuladosHtml} + <div class="subject-list">${subjectsHtml || '<p class="empty-note">sem assuntos ainda</p>'}</div> + </div>`; + + show('page-home'); + document.getElementById('breadcrumb').textContent = disc.discipline; + document.title = disc.discipline + ' · ' + BASE_TITLE; +} + +/* ── INTERFACE: ASSUNTO ── */ +function openSubject(discFolder, slug) { + navigate(encodeHash('assunto', discFolder, slug), false); + _openSubject(discFolder, slug); +} + +function _openSubject(discFolder, slug) { + closeFloater(); + const disc = DATA.find(d => d.folderKey === discFolder); + if (!disc) { _showHome(); return; } + const sub = disc.subjects.find(s => s.slug === slug); + if (!sub) { _showDisc(disc.discipline); return; } + + lastSubjectRoute = { discFolder, slug, discName: disc.discipline, subjectName: sub.name }; + + document.getElementById('text-discipline').textContent = disc.discipline; + document.getElementById('text-title').textContent = sub.name; + + const qzWrap = document.getElementById('text-quizzes'); + const qzList = document.getElementById('text-quizzes-list'); + + if (sub.quizzes.length) { + qzWrap.style.display = ''; + qzList.innerHTML = sub.quizzes.map(qz => { + const mc = qz.questions.filter(q => q.type === 'mc').length; + const open = qz.questions.filter(q => q.type === 'open').length; + const meta = [ + mc ? `${mc} mc` : null, + open ? `${open} abertas` : null, + ].filter(Boolean).join(' · ') || `${qz.questions.length} questões`; + return ` + <a class="sq-item" href="${encodeHash('quiz', discFolder, slug, qz.id)}" + onclick="startQuiz('${esc(discFolder)}','${esc(slug)}','${esc(qz.id)}');return false;"> + <span class="sq-name">${esc(qz.title)}</span> + <span class="sq-meta">${meta}</span> + </a>`; + }).join(''); + } else { + qzWrap.style.display = 'none'; + qzList.innerHTML = ''; + } + + const prose = document.getElementById('prose'); + if (sub.text && sub.text.content) { + prose.innerHTML = sub.text.content; + currentTextComments = {}; + buildToc(prose); + } else { + prose.innerHTML = '<p class="empty-note">Texto ainda não disponível para este assunto.</p>'; + document.getElementById('text-toc').style.display = 'none'; + } + + const refContainer = document.getElementById('text-references-container'); + const refList = document.getElementById('text-references-list'); + const refs = sub.text ? (sub.text.references || []) : []; + + if (refs.length) { + refContainer.style.display = 'block'; + refList.innerHTML = refs.map(r => `<p>${esc(r)}</p>`).join(''); + } else { + refContainer.style.display = 'none'; + } + + document.getElementById('breadcrumb').textContent = `${disc.discipline} · ${sub.name}`; + document.title = sub.name + ' · ' + BASE_TITLE; + show('page-text'); + window.scrollTo(0, 0); +} + +/* ── INTERFACE: SUMÁRIO (TOC) ── */ +function buildToc(proseEl) { + const tocWrap = document.getElementById('text-toc'); + const tocList = document.getElementById('text-toc-list'); + tocList.innerHTML = ''; + + const headings = proseEl.querySelectorAll('h2, h3'); + if (!headings.length) { tocWrap.style.display = 'none'; return; } + + const used = new Set(); + headings.forEach(h => { + if (!h.id) { + let base = slugifyText(h.textContent) || 'sec'; + let id = base, i = 2; + while (used.has(id) || document.getElementById(id)) { id = base + '-' + i++; } + h.id = id; + } + used.add(h.id); + }); + + headings.forEach(h => { + const li = document.createElement('li'); + li.className = 'toc-' + h.tagName.toLowerCase(); + const a = document.createElement('a'); + a.href = '#' + h.id; + a.textContent = h.textContent; + a.addEventListener('click', e => { + e.preventDefault(); + h.scrollIntoView({ behavior: 'smooth', block: 'start' }); + history.replaceState(history.state, '', '#' + h.id); + }); + li.appendChild(a); + tocList.appendChild(li); + }); + + tocWrap.style.display = ''; +} + +/* ── INTERFACE: COMPONENTES FLUTUANTES ── */ +document.addEventListener('click', e => { + const footnoteLink = e.target.closest('a[href^="#fn:"]'); + const closeBtn = e.target.closest('.f-close'); + + if (closeBtn) { closeFloater(); return; } + + if (footnoteLink) { + e.preventDefault(); e.stopPropagation(); closeFloater(); + const targetId = footnoteLink.getAttribute('href').substring(1); + const footnoteElement = document.getElementById(targetId); + if (footnoteElement) { + const contentClone = footnoteElement.cloneNode(true); + const backLink = contentClone.querySelector('.footnote-backref'); + if (backLink) backLink.remove(); + const parentParagraph = footnoteLink.closest('p, li'); + const quoteText = parentParagraph ? parentParagraph.innerText.substring(0, 40) + '…' : 'Nota'; + openFloater(footnoteLink, quoteText, contentClone.innerHTML); + footnoteLink.classList.add('active'); + } + } else if (activeFloater && !e.target.closest('.floater')) { + closeFloater(); + } +}); + +function openFloater(target, quote, innerHTMLContent) { + const f = document.createElement('div'); + f.className = 'floater'; + f.innerHTML = ` + <div class="f-label"><span>comentário</span><span class="f-close">×</span></div> + <div class="f-quote">${quote}</div> + <div>${innerHTMLContent}</div>`; + document.body.appendChild(f); + + const r = target.getBoundingClientRect(); + const top = window.scrollY + r.bottom + 8; + const padding = 20; + let left = window.scrollX + r.left; + const maxLeft = window.innerWidth - f.offsetWidth - padding; + + if (left > maxLeft) left = Math.max(padding, maxLeft); + + f.style.top = top + 'px'; + f.style.left = left + 'px'; + activeFloater = f; +} + +function closeFloater() { + if (activeFloater) { activeFloater.remove(); activeFloater = null; } + document.querySelectorAll('a[href^="#fn:"]').forEach(a => a.classList.remove('active')); +} + +document.addEventListener('keydown', e => { if (e.key === 'Escape') closeFloater(); }); + +/* ── MOTOR: QUIZ E SIMULADO ── */ +function startQuiz(discFolder, slug, quizId) { + navigate(encodeHash('quiz', discFolder, slug, quizId), false); + _startQuiz(discFolder, slug, quizId); +} + +function _startQuiz(discFolder, slug, quizId) { + closeFloater(); + const disc = DATA.find(d => d.folderKey === discFolder); + if (!disc) { _showHome(); return; } + const sub = disc.subjects.find(s => s.slug === slug); + if (!sub) { _showDisc(disc.discipline); return; } + const quiz = sub.quizzes.find(q => q.id === quizId); + if (!quiz) { _openSubject(discFolder, slug); return; } + + lastSubjectRoute = { discFolder, slug, discName: disc.discipline, subjectName: sub.name }; + currentQuiz = { + isSimulado: false, discFolder, slug, quizId, + subjectName: sub.name, quizTitle: quiz.title + }; + + _qState.questions = prepareQuizQuestions(quiz.questions); + _qState.qIndex = _qState.acertos = _qState.erros = 0; + _clearProgress('spa_' + discFolder + '_' + slug + '_' + quizId); + + document.getElementById('breadcrumb').textContent = `${disc.discipline} · ${sub.name} · ${quiz.title}`; + document.title = `${quiz.title} · ${sub.name} · ${BASE_TITLE}`; + + show('page-quiz'); + renderQuestion(); +} + +function startSimulado(discFolder, quizId) { + navigate(encodeHash('simulado', discFolder, quizId), false); + _startSimulado(discFolder, quizId); +} + +function _startSimulado(discFolder, quizId) { + closeFloater(); + const disc = DATA.find(d => d.folderKey === discFolder); + if (!disc) { _showHome(); return; } + const quiz = disc.quizzes.find(q => q.id === quizId); + if (!quiz) { _showDisc(disc.discipline); return; } + + lastSubjectRoute = null; + currentQuiz = { + isSimulado: true, discFolder, quizId, + subjectName: disc.discipline, quizTitle: quiz.title + }; + + _qState.questions = prepareQuizQuestions(quiz.questions); + _qState.qIndex = _qState.acertos = _qState.erros = 0; + _clearProgress('spa_sim_' + discFolder + '_' + quizId); + + document.getElementById('breadcrumb').textContent = `${disc.discipline} · ${quiz.title}`; + document.title = `${quiz.title} · ${disc.discipline} · ${BASE_TITLE}`; + + show('page-quiz'); + renderQuestion(); +} + +function restartQuiz() { + if (currentQuiz.isSimulado) { + _clearProgress('spa_sim_' + currentQuiz.discFolder + '_' + currentQuiz.quizId); + _startSimulado(currentQuiz.discFolder, currentQuiz.quizId); + } else { + _clearProgress('spa_' + currentQuiz.discFolder + '_' + currentQuiz.slug + '_' + currentQuiz.quizId); + _startQuiz(currentQuiz.discFolder, currentQuiz.slug, currentQuiz.quizId); + } +} + +function backToSubject() { + if (currentQuiz && currentQuiz.isSimulado) { + openDisc(currentQuiz.subjectName); + } else if (lastSubjectRoute) { + navigate(encodeHash('assunto', lastSubjectRoute.discFolder, lastSubjectRoute.slug), false); + _openSubject(lastSubjectRoute.discFolder, lastSubjectRoute.slug); + } else { + showHome(); + } +} + +function renderQuestion() { + _renderQuestion(_qState.questions[_qState.qIndex]); + const progKey = currentQuiz + ? 'spa_' + (currentQuiz.isSimulado + ? 'sim_' + currentQuiz.discFolder + '_' + currentQuiz.quizId + : currentQuiz.discFolder + '_' + currentQuiz.slug + '_' + currentQuiz.quizId) + : null; + if (progKey) _saveProgress(progKey); +} + +/* selectOpt, showRef, selfJudge, setFeedback → definidos em quiz-parser.html */ + +function nextQuestion() { + _qState.qIndex++; + if (_qState.qIndex >= _qState.questions.length) { showResult(); return; } + const body = document.getElementById('quiz-body'); + body.classList.add('fade-out'); + setTimeout(() => { body.classList.remove('fade-out'); renderQuestion(); }, 100); +} + +/* ── TELA DE RESULTADOS ── */ +function showResult() { + show('page-result'); + _buildResult(); + document.getElementById('breadcrumb').textContent = currentQuiz.subjectName + ' · ' + currentQuiz.quizTitle; +} + +/* ── INICIALIZAÇÃO DE ROTA ── */ +(function init() { + const route = decodeHash(window.location.hash); + if (route.type !== 'home') { + history.replaceState({ hash: window.location.hash }, '', window.location.href); + renderRoute(route); + } else { + history.replaceState({ hash: '' }, '', window.location.pathname + window.location.search); + _showHome(); + } +})(); +</script> diff --git a/layouts/quizzes/list.html b/layouts/quizzes/list.html new file mode 100644 index 0000000..c37a486 --- /dev/null +++ b/layouts/quizzes/list.html @@ -0,0 +1,57 @@ +{{ define "title" }}{{ .Title }} · {{ .Site.Title }}{{ end }} + +{{ define "header" }} + <a class="site-name" href="{{ "/" | relURL }}">direito</a> + <span class="header-sep">/</span> + <span class="breadcrumb">{{ .Title }}</span> +{{ end }} + +{{ define "main" }} +{{ $discSlug := .RelPermalink | strings.TrimSuffix "/" | path.Base }} +<div class="discipline fade"> + <div class="disc-back"><a href="{{ "/" | relURL }}">← disciplinas</a></div> + <div class="discipline-name">{{ .Title }}</div> + + {{/* Separa simulados (frontmatter `simulado: true` OU nome contendo "simulado") dos quizzes + desta disciplina, usando .File.Dir já que as subpastas de quizzes/ não são sections. */}} + {{ $simulados := slice }} + {{ $quizzes := slice }} + {{ range .RegularPages }} + {{ if or (.Params.simulado) (in (.File.BaseFileName | lower) "simulado") }} + {{ $simulados = $simulados | append . }} + {{ else }} + {{ $quizzes = $quizzes | append . }} + {{ end }} + {{ end }} + + {{ with $simulados }} + <div class="subject-group" style="margin-bottom:1.5rem;"> + <div class="sq-label" style="margin-bottom:.65rem;">simulados</div> + <div class="sq-list"> + {{ range . }} + <a class="sq-item" href="{{ .RelPermalink }}"> + <span class="sq-name" style="color:var(--amber);">{{ .Title }}</span> + <span class="sq-meta">simulado</span> + </a> + {{ end }} + </div> + </div> + {{ end }} + + {{ with $quizzes }} + <div class="subject-list"> + <div class="sq-label" style="margin-bottom:.65rem;">exercícios</div> + {{ range . }} + <a class="subject-row" href="{{ .RelPermalink }}"> + <span class="subject-row-name">{{ .Title }}</span> + <span class="subject-row-meta">quiz</span> + </a> + {{ end }} + </div> + {{ end }} + + {{ if and (not $simulados) (not $quizzes) }} + <p class="empty-note">sem conteúdo ainda</p> + {{ end }} +</div> +{{ end }} diff --git a/layouts/quizzes/single.html b/layouts/quizzes/single.html new file mode 100644 index 0000000..0a51a9c --- /dev/null +++ b/layouts/quizzes/single.html @@ -0,0 +1,60 @@ +{{ define "title" }}{{ .Title }} · {{ .Site.Title }}{{ end }} +{{ define "header" }} + {{ $discSlug := .File.Dir | path.Dir | path.Base }} + <a class="site-name" href="{{ "/" | relURL }}">direito</a> + <span class="header-sep">/</span> + {{ $discPage := $.Site.GetPage (printf "/txts/%s" $discSlug) }} + {{ if $discPage }} + <a class="breadcrumb-link" href="{{ $discPage.RelPermalink }}">{{ $discPage.Title }}</a> + <span class="header-sep">/</span> + {{ end }} + <span class="breadcrumb">{{ .Title }}</span> +{{ end }} +{{ define "main" }} +{{ $discSlug := .File.Dir | path.Dir | path.Base }} +{{ $discPage := $.Site.GetPage (printf "/txts/%s" $discSlug) }} +<div class="fade"> + {{ if $discPage }} + <div class="disc-back"><a href="{{ $discPage.RelPermalink }}">← {{ $discPage.Title }}</a></div> + {{ else }} + <div class="disc-back"><a href="{{ "/" | relURL }}">← disciplinas</a></div> + {{ end }} + <div class="discipline-name" style="margin-bottom:1.5rem;">{{ .Title }}</div> + <div id="quiz-wrap"> + <div class="progress-wrap"><div class="progress-fill" id="prog"></div></div> + <div class="q-meta" id="q-counter"></div> + <div id="quiz-body"></div> + <div class="next-row"> + <button class="btn" id="btn-next" onclick="nextQuestion()" style="display:none">próxima →</button> + </div> + </div> + <div id="quiz-result" style="display:none;"> + <div class="res-score" id="res-pct">0%</div> + <div class="res-subtitle"><span id="res-a">0</span> de <span id="res-t">0</span> acertos</div> + <div class="res-divider"></div> + <div class="res-stats"> + <div class="rs-item"><div class="rs-num g" id="res-a2">0</div><div class="rs-label">acertos</div></div> + <div class="rs-item"><div class="rs-num r" id="res-e">0</div><div class="rs-label">erros</div></div> + </div> + <div class="res-msg"> + <div class="res-msg-label">diagnóstico</div> + <div id="res-msg-body"></div> + </div> + <div class="res-btns"> + <button class="btn" onclick="restartQuiz()">refazer</button> + {{ if $discPage }} + <a class="btn" href="{{ $discPage.RelPermalink }}">voltar</a> + {{ end }} + </div> + </div> +</div> + +<script> + // Definir a chave única para este quiz + window.QUIZ_KEY = '{{ $discSlug }}_{{ .File.BaseFileName }}'; + // Conteúdo bruto do quiz em JSON (sem notas de rodapé Markdown) + const RAW_CONTENT = {{ .RawContent | jsonify | safeJS }}; + // Remover notas de rodapé Markdown ([^1]: ...) + const RAW_QUIZ = RAW_CONTENT.replace(/\n\[\^\d+\]:[^\n]*/g, ''); +</script> +{{ end }} diff --git a/layouts/txts/single.html b/layouts/txts/single.html new file mode 100644 index 0000000..d8f7eb1 --- /dev/null +++ b/layouts/txts/single.html @@ -0,0 +1,224 @@ +{{ define "title" }}{{ .Title }} · {{ .Site.Title }}{{ end }} + +{{ define "header" }} + {{ $discSlug := .RelPermalink | strings.TrimSuffix "/" | path.Dir | path.Base }} + <a class="site-name" href="{{ "/" | relURL }}">direito</a> + {{ range $.Site.Sections }} + {{ if and (eq .Section "txts") (eq (.RelPermalink | strings.TrimSuffix "/" | path.Base) $discSlug) }} + <span class="header-sep">/</span> + <a class="breadcrumb-link" href="{{ .RelPermalink }}">{{ .Title }}</a> + {{ end }} + {{ end }} + <span class="header-sep">/</span> + <span class="breadcrumb">{{ .Title }}</span> +{{ end }} + +{{ define "main" }} +{{ $slug := .File.BaseFileName }} +{{ $discSlug := .RelPermalink | strings.TrimSuffix "/" | path.Dir | path.Base }} + +{{/* Encontra quiz do assunto (exercício, não simulado) */}} +{{ $quizPage := false }} +{{ range $.Site.RegularPages }} + {{ if eq .Section "quizzes" }} + {{ $parent := .File.Dir | strings.TrimSuffix "/" | path.Base }} + {{ if and (eq $parent $discSlug) (eq .File.BaseFileName $slug) }} + {{ if not (or (.Params.simulado) (in (.File.BaseFileName | lower) "simulado")) }} + {{ $quizPage = . }} + {{ end }} + {{ end }} + {{ end }} +{{ end }} + +<div class="page-text fade"> + <div class="text-meta"> + {{ range $.Site.Sections }} + {{ if and (eq .Section "txts") (eq (.RelPermalink | strings.TrimSuffix "/" | path.Base) $discSlug) }} + <span>{{ .Title }}</span><span class="dot"></span> + {{ end }} + {{ end }} + <span>assunto</span> + </div> + <h1 class="text-title">{{ .Title }}</h1> + + {{/* Colapsáveis: guia de estudo e materiais do assunto */}} + {{ $hasExtras := or .Params.guide (or .Params.files (or .Params.videos .Params.audios)) }} + {{ if $hasExtras }} + <div class="disc-extras-wrap"> + {{ partial "disc-extras.html" . }} + </div> + {{ end }} + + <nav class="toc" id="toc-nav" style="display:none;" aria-label="Sumário"> + <div class="toc-label">sumário</div> + <ol id="toc-list"></ol> + </nav> + + <article class="prose" id="prose">{{ .Content }}</article> + + {{/* Notas de rodapé — renderizadas pelo Hugo automaticamente no .Content; + o floater (footnotes.js) intercepta os links #fn: e mostra tooltip */}} + + {{/* Questões do assunto — quiz inline embutido */}} + {{ if $quizPage }} + <div class="inline-quiz-wrap" id="inline-quiz-section"> + <div class="inline-quiz-head"> + <span class="sq-label">exercícios deste assunto</span> + <a class="inline-quiz-link" href="{{ $quizPage.RelPermalink }}">modo completo →</a> + </div> + <div id="inline-quiz-body"> + <button class="btn btn-start-quiz" id="btn-start-inline" onclick="startInlineQuiz()"> + Iniciar exercícios + </button> + </div> + <div id="inline-quiz-player" style="display:none;"> + <div class="progress-wrap" style="margin-bottom:.75rem;"><div class="progress-fill" id="iprog"></div></div> + <div class="q-meta" id="iq-counter"></div> + <div id="iq-body"></div> + <div class="next-row"> + <button class="btn" id="ibtn-next" onclick="inlineNextQuestion()" style="display:none">próxima →</button> + </div> + </div> + <div id="inline-quiz-result" style="display:none;"> + <div class="res-score" id="ires-pct">0%</div> + <div class="res-subtitle"><span id="ires-a">0</span> de <span id="ires-t">0</span> acertos</div> + <div class="res-msg" style="margin-top:1rem;"> + <div class="res-msg-label">diagnóstico</div> + <div id="ires-msg"></div> + </div> + <div class="res-btns" style="margin-top:1.2rem;"> + <button class="btn" onclick="restartInlineQuiz()">refazer</button> + <a class="btn" href="{{ $quizPage.RelPermalink }}">modo completo</a> + </div> + </div> + </div> + <script> + (function() { + const RAW = {{ $quizPage.RawContent | jsonify | safeJS }}; + // Remove frontmatter YAML e notas de rodapé + const body = RAW.replace(/^---[\s\S]*?---\n/, '').replace(/\n\[\^\d+\]:[^\n]*/g, ''); + window._IQ = { raw: body, questions: [], idx: 0, acertos: 0, erros: 0, answered: false, flipped: false }; + })(); + + function startInlineQuiz() { + document.getElementById('btn-start-inline').style.display = 'none'; + const qs = prepareQuizQuestions(parseQuizBody(window._IQ.raw)); + window._IQ.questions = qs; + window._IQ.idx = 0; window._IQ.acertos = 0; window._IQ.erros = 0; + document.getElementById('inline-quiz-player').style.display = ''; + _iqRender(); + } + + function _iqRender() { + const q = window._IQ.questions[window._IQ.idx]; + if (!q) return; + window._IQ.answered = false; window._IQ.flipped = false; + const total = window._IQ.questions.length; + document.getElementById('iq-counter').textContent = (window._IQ.idx + 1) + ' de ' + total; + document.getElementById('iprog').style.width = ((window._IQ.idx + 1) / total * 100) + '%'; + document.getElementById('ibtn-next').style.display = 'none'; + const body = document.getElementById('iq-body'); + if (q.type === 'mc') { + body.innerHTML = '<div class="mc-area fade"><p class="q-text">' + md(q.text) + '</p><div class="options">' + + q.options.map((o, i) => '<button class="opt" data-opt="' + esc(o) + '" data-answer="' + esc(q.answer) + '" onclick="iqSelectOpt(this)" aria-label="Opção ' + LETRAS[i] + ': ' + o + '"><span class="opt-letter">' + LETRAS[i] + '</span><span>' + md(o) + '</span></button>').join('') + + '</div><div class="feedback-line" id="iq-feedback" aria-live="polite"><span class="feedback-dot"></span><span id="iq-feedback-msg"></span></div>' + + (q.comment ? '<div class="comment-block" id="iq-comment"><div class="comment-label">comentário</div><div>' + md(q.comment) + '</div></div>' : '') + + '</div>'; + } else { + body.innerHTML = '<div class="flashcard-area fade"><p class="q-text">' + md(q.text) + '</p>' + + '<div class="flashcard-simple" id="iq-flashcard"><button class="btn btn-reveal" onclick="iqFlip()">Revelar Resposta</button></div>' + + '<div class="feedback-line" id="iq-feedback" aria-live="polite"><span class="feedback-dot"></span><span id="iq-feedback-msg"></span></div>' + + (q.comment ? '<div class="comment-block" id="iq-comment"><div class="comment-label">comentário</div><div>' + md(q.comment) + '</div></div>' : '') + + '<div class="flashcard-btns" id="iq-fc-btns"><button class="btn btn-ok" onclick="iqMarkOk()">✓ Acertei</button><button class="btn btn-bad" onclick="iqMarkBad()">✗ Errei</button></div>' + + '</div>'; + } + } + + function iqSelectOpt(btn) { + if (window._IQ.answered) return; + window._IQ.answered = true; + const chosen = btn.dataset.opt, correct = btn.dataset.answer; + btn.closest('.options').querySelectorAll('.opt').forEach(b => { + b.disabled = true; + if (b.dataset.opt === correct) b.classList.add('correct'); + else if (b === btn) b.classList.add('wrong'); + else b.classList.add('revealed'); + }); + const ok = chosen === correct; + ok ? window._IQ.acertos++ : window._IQ.erros++; + _iqFeedback(ok, ok ? 'Correto' : 'Incorreto — a correta está destacada'); + document.getElementById('iq-comment')?.classList.add('show'); + document.getElementById('ibtn-next').style.display = ''; + } + + function iqFlip() { + const q = window._IQ.questions[window._IQ.idx]; + const card = document.getElementById('iq-flashcard'); + if (card) card.innerHTML = '<div class="ref-label">resposta de referência</div><div class="ref-content">' + md(q.reference) + '</div>'; + document.getElementById('iq-fc-btns')?.style && (document.getElementById('iq-fc-btns').style.display = ''); + } + + function iqMarkOk() { _iqMark(true); } + function iqMarkBad() { _iqMark(false); } + function _iqMark(ok) { + if (window._IQ.answered) return; + window._IQ.answered = true; + ok ? window._IQ.acertos++ : window._IQ.erros++; + _iqFeedback(ok, ok ? 'Marcado como acertado' : 'Marcado como errado'); + document.getElementById('iq-comment')?.classList.add('show'); + const btns = document.getElementById('iq-fc-btns'); + if (btns) btns.innerHTML = ''; + document.getElementById('ibtn-next').style.display = ''; + } + + function _iqFeedback(ok, msg) { + const fb = document.getElementById('iq-feedback'); + if (fb) { fb.className = 'feedback-line show ' + (ok ? 'ok' : 'bad'); } + const fm = document.getElementById('iq-feedback-msg'); + if (fm) fm.textContent = msg; + } + + function inlineNextQuestion() { + window._IQ.idx++; + if (window._IQ.idx >= window._IQ.questions.length) { + _iqShowResult(); + } else { + _iqRender(); + } + } + + function _iqShowResult() { + document.getElementById('inline-quiz-player').style.display = 'none'; + document.getElementById('inline-quiz-result').style.display = ''; + const { acertos, erros, questions } = window._IQ; + const total = questions.length; + const pct = total ? Math.round(acertos / total * 100) : 0; + document.getElementById('ires-pct').textContent = pct + '%'; + document.getElementById('ires-a').textContent = acertos; + document.getElementById('ires-t').textContent = total; + let nivel = pct >= 90 ? 'Excelente' : pct >= 70 ? 'Bom' : pct >= 50 ? 'Regular' : 'Insuficiente'; + let det = pct >= 90 ? 'Domínio sólido deste assunto.' : pct >= 70 ? 'Boa base, revise os erros.' : pct >= 50 ? 'Lacunas importantes, releia o texto.' : 'Retome o conteúdo antes de prosseguir.'; + document.getElementById('ires-msg').innerHTML = '<strong>' + nivel + '</strong> — ' + acertos + ' de ' + total + ' (' + pct + '%). ' + det; + } + + function restartInlineQuiz() { + document.getElementById('inline-quiz-result').style.display = 'none'; + window._IQ.idx = 0; window._IQ.acertos = 0; window._IQ.erros = 0; + window._IQ.questions = prepareQuizQuestions(parseQuizBody(window._IQ.raw)); + document.getElementById('inline-quiz-player').style.display = ''; + _iqRender(); + } + </script> + {{ end }} + + {{ if .Params.references }} + <div class="references"> + <div class="ref-head">referências</div> + <div class="ref-list"> + {{ range .Params.references }}<p>{{ . }}</p>{{ end }} + </div> + </div> + {{ end }} +</div> + +{{ end }} diff --git a/static/CNAME b/static/CNAME new file mode 100644 index 0000000..488dfd6 --- /dev/null +++ b/static/CNAME @@ -0,0 +1 @@ +dir.sivaldodavi.com diff --git a/static/dark-mode.js b/static/dark-mode.js new file mode 100644 index 0000000..8e2d761 --- /dev/null +++ b/static/dark-mode.js @@ -0,0 +1,59 @@ +/** + * Dark Mode Toggle + * Respeita a preferência do sistema e permite alternar manualmente + * Salva a preferência no localStorage + */ + +const DARK_MODE_KEY = 'caderno_dark_mode'; + +function initDarkMode() { + // Verificar preferência salva + const saved = localStorage.getItem(DARK_MODE_KEY); + let isDark; + + if (saved !== null) { + isDark = saved === 'true'; + } else { + // Respeitar preferência do sistema + isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + } + + applyDarkMode(isDark); +} + +function applyDarkMode(isDark) { + const html = document.documentElement; + const toggle = document.getElementById('dark-mode-toggle'); + + if (isDark) { + html.setAttribute('data-theme', 'dark'); + if (toggle) toggle.textContent = '☀️'; + } else { + html.removeAttribute('data-theme'); + if (toggle) toggle.textContent = '🌙'; + } + + localStorage.setItem(DARK_MODE_KEY, isDark); +} + +function toggleDarkMode() { + const html = document.documentElement; + const isDark = html.getAttribute('data-theme') === 'dark'; + applyDarkMode(!isDark); +} + +// Inicializar quando o DOM estiver pronto +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initDarkMode); +} else { + initDarkMode(); +} + +// Detectar mudanças na preferência do sistema +window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { + const saved = localStorage.getItem(DARK_MODE_KEY); + if (saved === null) { + // Se o usuário não escolheu manualmente, seguir o sistema + applyDarkMode(e.matches); + } +}); diff --git a/static/dashboard.js b/static/dashboard.js new file mode 100644 index 0000000..c14369f --- /dev/null +++ b/static/dashboard.js @@ -0,0 +1,130 @@ +/** + * Dashboard de Progresso + * Lê dados do localStorage e mostra estatísticas de estudo + */ + +var PROGRESS_PREFIX = window.PROGRESS_PREFIX || 'quiz_progress_'; + +function initDashboard() { + const dashboard = document.getElementById('dashboard-stats'); + if (!dashboard) return; + + const stats = calculateStats(); + renderDashboard(stats); +} + +function calculateStats() { + const allKeys = Object.keys(localStorage); + const progressKeys = allKeys.filter((k) => k.startsWith(PROGRESS_PREFIX)); + + const stats = { + totalQuizzes: progressKeys.length, + totalAcertos: 0, + totalErros: 0, + disciplinas: {}, + }; + + progressKeys.forEach((key) => { + try { + const data = JSON.parse(localStorage.getItem(key)); + if (!data || !data.questions) return; + + const disciplina = key.replace(PROGRESS_PREFIX, '').split('_')[0]; + if (!stats.disciplinas[disciplina]) { + stats.disciplinas[disciplina] = { + acertos: 0, + erros: 0, + total: 0, + }; + } + + stats.disciplinas[disciplina].acertos += data.acertos || 0; + stats.disciplinas[disciplina].erros += data.erros || 0; + stats.disciplinas[disciplina].total += data.questions.length; + + stats.totalAcertos += data.acertos || 0; + stats.totalErros += data.erros || 0; + } catch (e) {} + }); + + return stats; +} + +function renderDashboard(stats) { + const dashboard = document.getElementById('dashboard-stats'); + if (!dashboard) return; + + const totalQuestoes = stats.totalAcertos + stats.totalErros; + const pctGeral = totalQuestoes > 0 ? Math.round((stats.totalAcertos / totalQuestoes) * 100) : 0; + + let html = ` + <div class="dashboard-container"> + <h2>📊 Seu Progresso de Estudo</h2> + + <div class="dashboard-overview"> + <div class="stat-card"> + <div class="stat-label">Quizzes Iniciados</div> + <div class="stat-value">${stats.totalQuizzes}</div> + </div> + <div class="stat-card"> + <div class="stat-label">Taxa de Acerto Geral</div> + <div class="stat-value">${pctGeral}%</div> + </div> + <div class="stat-card"> + <div class="stat-label">Questões Respondidas</div> + <div class="stat-value">${totalQuestoes}</div> + </div> + </div> + + <div class="dashboard-disciplines"> + <h3>Por Disciplina</h3> + `; + + Object.entries(stats.disciplinas).forEach(([disc, data]) => { + const pct = data.total > 0 ? Math.round((data.acertos / data.total) * 100) : 0; + const nivel = pct >= 80 ? '🟢 Dominado' : pct >= 60 ? '🟡 Bom' : '🔴 Revisar'; + + html += ` + <div class="disc-stat"> + <div class="disc-name">${disc}</div> + <div class="disc-bar"> + <div class="disc-progress" style="width: ${pct}%"></div> + </div> + <div class="disc-info"> + <span>${data.acertos}/${data.total}</span> + <span>${pct}%</span> + <span>${nivel}</span> + </div> + </div> + `; + }); + + html += ` + </div> + <div class="dashboard-actions"> + <button class="btn" onclick="clearAllProgress()">Limpar Histórico</button> + </div> + </div> + `; + + dashboard.innerHTML = html; +} + +function clearAllProgress() { + if (confirm('Tem certeza que quer limpar todo o histórico de progresso?')) { + const allKeys = Object.keys(localStorage); + allKeys.forEach((key) => { + if (key.startsWith(PROGRESS_PREFIX)) { + localStorage.removeItem(key); + } + }); + initDashboard(); + } +} + +// Inicializar quando o DOM estiver pronto +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initDashboard); +} else { + initDashboard(); +} diff --git a/static/footnotes.js b/static/footnotes.js new file mode 100644 index 0000000..1d2700a --- /dev/null +++ b/static/footnotes.js @@ -0,0 +1,77 @@ +/** + * Footnotes - Sistema de notas flutuantes (floater) + */ + +let activeFloater = null; + +document.addEventListener('click', (e) => { + const fn = e.target.closest('a[href^="#fn:"]'); + const close = e.target.closest('.f-close'); + + if (close) { + closeFloater(); + return; + } + + if (fn) { + e.preventDefault(); + e.stopPropagation(); + closeFloater(); + + const el = document.getElementById(fn.getAttribute('href').slice(1)); + if (!el) return; + + const clone = el.cloneNode(true); + clone.querySelector('.footnote-backref')?.remove(); + + const parent = fn.closest('p, li'); + const quote = parent ? parent.innerText.slice(0, 40) + '…' : 'Nota'; + + openFloater(fn, quote, clone.innerHTML); + fn.classList.add('active'); + } else if (activeFloater && !e.target.closest('.floater')) { + closeFloater(); + } +}); + +function openFloater(target, quote, html) { + const f = document.createElement('div'); + f.className = 'floater'; + f.innerHTML = `<div class="f-label"><span>comentário</span><span class="f-close" role="button" tabindex="0" aria-label="Fechar nota">×</span></div><div class="f-quote">${quote}</div><div>${html}</div>`; + + document.body.appendChild(f); + + const r = target.getBoundingClientRect(); + const top = window.scrollY + r.bottom + 8; + const padding = 20; + let left = window.scrollX + r.left; + const maxLeft = window.innerWidth - f.offsetWidth - padding; + + if (left > maxLeft) left = Math.max(padding, maxLeft); + + f.style.top = top + 'px'; + f.style.left = left + 'px'; + + activeFloater = f; + + // Permitir fechar com Enter ou Space + f.querySelector('.f-close').addEventListener('keydown', (e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + closeFloater(); + } + }); +} + +function closeFloater() { + if (activeFloater) { + activeFloater.remove(); + activeFloater = null; + } + document.querySelectorAll('a[href^="#fn:"]').forEach((a) => a.classList.remove('active')); +} + +// Fechar com Escape +document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') closeFloater(); +}); diff --git a/static/home-filter.js b/static/home-filter.js new file mode 100644 index 0000000..b2d1a47 --- /dev/null +++ b/static/home-filter.js @@ -0,0 +1,72 @@ +/** + * Home Filter — filtra disciplinas conforme você digita + * Só funciona quando há itens [data-filterable] na página + */ +document.addEventListener('DOMContentLoaded', () => { + const items = document.querySelectorAll('[data-filterable]'); + if (!items.length) return; + + // Cria campo de busca + const wrap = document.createElement('div'); + wrap.className = 'filter-container'; + wrap.innerHTML = ` + <input + type="search" + id="home-filter" + class="filter-input" + placeholder="Buscar disciplina…" + aria-label="Filtrar disciplinas" + autocomplete="off" + spellcheck="false" + > + <span class="filter-count" id="filter-count" aria-live="polite"></span> + `; + + // Insere antes do disc-grid dentro do <main> + const grid = document.querySelector('.disc-grid'); + if (grid) { + grid.parentNode.insertBefore(wrap, grid); + } else { + const main = document.querySelector('main'); + if (main) main.insertBefore(wrap, main.firstChild); + } + + const input = document.getElementById('home-filter'); + const count = document.getElementById('filter-count'); + + // Normaliza texto para comparação (remove acentos, maiúsculas) + function normalize(str) { + return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(); + } + + input.addEventListener('input', () => { + const q = normalize(input.value.trim()); + let visible = 0; + + items.forEach(item => { + const text = normalize(item.textContent); + const match = !q || text.includes(q); + item.style.display = match ? '' : 'none'; + if (match) visible++; + }); + + count.textContent = q ? `${visible} resultado${visible !== 1 ? 's' : ''}` : ''; + }); + + // Esc limpa e desfoca + input.addEventListener('keydown', e => { + if (e.key === 'Escape') { + input.value = ''; + input.dispatchEvent(new Event('input')); + input.blur(); + } + }); + + // Foco automático na home com / ou Ctrl+K + document.addEventListener('keydown', e => { + if ((e.key === '/' || (e.ctrlKey && e.key === 'k')) && document.activeElement !== input) { + e.preventDefault(); + input.focus(); + } + }); +}); diff --git a/static/keyboard-shortcuts.js b/static/keyboard-shortcuts.js new file mode 100644 index 0000000..53cfe44 --- /dev/null +++ b/static/keyboard-shortcuts.js @@ -0,0 +1,73 @@ +/** + * Keyboard Shortcuts + * Atalhos para acelerar a navegação e resposta de quizzes + * + * Atalhos: + * 1-5: Selecionar opção A-E + * Espaço: Próxima questão + * Esc: Fechar notas/floaters + * ?: Mostrar ajuda + */ + +const SHORTCUTS = { + '1': () => selectOptionByLetter(0), + '2': () => selectOptionByLetter(1), + '3': () => selectOptionByLetter(2), + '4': () => selectOptionByLetter(3), + '5': () => selectOptionByLetter(4), + ' ': (e) => { + e.preventDefault(); + const nextBtn = document.getElementById('btn-next'); + if (nextBtn && nextBtn.style.display !== 'none') { + nextBtn.click(); + } + }, + 'Escape': () => { + // Fechar floaters se existir + if (typeof closeFloater === 'function') { + closeFloater(); + } + }, + '?': (e) => { + e.preventDefault(); + showKeyboardHelp(); + }, +}; + +function selectOptionByLetter(index) { + const buttons = document.querySelectorAll('.opt'); + if (buttons[index]) { + buttons[index].click(); + } +} + +function showKeyboardHelp() { + const help = ` +⌨️ Atalhos de Teclado + +Quizzes: + 1-5 Selecionar opção A-E + SPC Próxima questão + ESC Fechar notas + +Geral: + ESC Fechar floaters + ? Mostrar esta ajuda + `; + + alert(help); +} + +document.addEventListener('keydown', (e) => { + // Ignorar se está digitando em um input/textarea + if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') { + return; + } + + const key = e.key; + const handler = SHORTCUTS[key]; + + if (handler) { + handler(e); + } +}); diff --git a/static/main.css b/static/main.css new file mode 100644 index 0000000..d4257a4 --- /dev/null +++ b/static/main.css @@ -0,0 +1,1560 @@ +/* ── VARIÁVEIS E RESET ── */ +:root { + --serif: 'Lora', serif; + --mono: 'JetBrains Mono', monospace; + --text: #dedad4; + --text-2: #a8a8a8; + --muted: #6a6a75; + --surface: #1a1a1f; + --border: #2a2a30; + --border-2: #3a3a40; + --amber: #c9a961; + --t: 150ms ease; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + background: #0f0f13; + color: var(--text); + font-family: var(--serif); + font-size: 16px; + line-height: 1.6; +} + +body { + background: #0f0f13; + color: var(--text); + font-family: var(--serif); + display: flex; + flex-direction: column; + min-height: 100vh; +} + +/* ── HEADER ── */ +header { + padding: 1.2rem 2rem; + border-bottom: 1px solid var(--border); + background: rgba(15, 15, 19, 0.95); + backdrop-filter: blur(4px); + position: sticky; + top: 0; + z-index: 100; +} + +.site-name { + font-family: var(--mono); + font-size: 0.75rem; + letter-spacing: 0.15em; + text-transform: uppercase; + color: var(--text); + text-decoration: none; + transition: color var(--t); +} + +.site-name:hover { + color: var(--amber); +} + +.header-sep { + color: var(--border-2); + margin: 0 0.5rem; +} + +.breadcrumb { + color: var(--text-2); + font-size: 0.9rem; +} + +.breadcrumb-link { + color: var(--text-2); + text-decoration: none; + transition: color var(--t); +} + +.breadcrumb-link:hover { + color: var(--text); +} + +/* ── MAIN ── */ +main { + flex: 1; + padding: 2rem; + max-width: 900px; + margin: 0 auto; + width: 100%; +} + +/* ── FADE ANIMATION ── */ +.fade { + animation: fadeIn 200ms ease; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +.fade-out { + animation: fadeOut 100ms ease; +} + +@keyframes fadeOut { + from { opacity: 1; } + to { opacity: 0; } +} + +/* ── GRID DE DISCIPLINAS ── */ +.disc-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 1.2rem; + margin-bottom: 2rem; +} + +.disc-card { + display: flex; + flex-direction: column; + padding: 1.2rem; + border: 1px dashed var(--border); + border-radius: 4px; + text-decoration: none; + color: var(--text); + transition: all var(--t); + cursor: pointer; +} + +.disc-card:hover { + border-color: var(--amber); + background: color-mix(in srgb, var(--amber) 3%, transparent); + transform: translateY(-2px); +} + +.disc-card-name { + font-size: 1rem; + font-weight: 600; + margin-bottom: 0.5rem; + line-height: 1.3; +} + +.disc-card-meta { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.08em; + text-transform: uppercase; +} + +/* ── BOTÃO VOLTAR ── */ +.disc-back { + margin-bottom: 1.2rem; +} + +.disc-back a { + font-family: var(--mono); + font-size: 0.68rem; + color: var(--muted); + text-decoration: none; + letter-spacing: 0.06em; + text-transform: uppercase; + transition: color var(--t); +} + +.disc-back a:hover { + color: var(--text-2); +} + +/* ── DISCIPLINA ── */ +.discipline { + margin-bottom: 2.5rem; +} + +.discipline-name { + font-family: var(--mono); + font-size: 0.68rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; + margin-bottom: 0.65rem; + padding-bottom: 0.45rem; + border-bottom: 1px solid var(--border); +} + +/* ── INTRO/NOTAS DA DISCIPLINA ── */ +.disc-intro { + margin: 1.2rem 0 1.8rem; + padding: 1rem 1.2rem; + border-left: 2px solid var(--amber); + background: color-mix(in srgb, var(--amber) 5%, transparent); + border-radius: 0 4px 4px 0; +} + +.disc-intro-prose { + font-size: 0.9rem; + color: var(--text-2); + margin: 0; +} + +.disc-intro-prose p:first-child { + margin-top: 0; +} + +.disc-intro-prose p:last-child { + margin-bottom: 0; +} + +.disc-intro-refs { + margin-top: 0.75rem; + padding-top: 0.5rem; + border-top: 1px solid var(--border); +} + +/* ── TÓPICOS (SUBPASTAS) ── */ +.topic-section { + margin-bottom: 0.5rem; +} + +.topic-head { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.12em; + text-transform: uppercase; + margin: 1.4rem 0 0.3rem; + padding-bottom: 0.3rem; + border-bottom: 1px dashed var(--border); +} + +.subject-group { + padding: 0.85rem 0; + border-bottom: 1px solid var(--border); +} + +.subject-group:last-child { + border-bottom: none; +} + +.subject-head { + font-size: 0.95rem; + color: var(--text); + margin-bottom: 0.5rem; +} + +.item-row { + display: flex; + justify-content: space-between; + align-items: baseline; + padding: 0.35rem 0; + cursor: pointer; + text-decoration: none; + color: var(--text-2); + transition: opacity var(--t); + gap: 1rem; +} + +.item-row:hover { + opacity: 0.55; + color: var(--text); +} + +.item-kind { + font-family: var(--mono); + font-size: 0.62rem; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--muted); + min-width: 3.2rem; + flex-shrink: 0; +} + +.item-kind.txt { + color: var(--amber); +} + +.item-kind.qz { + color: var(--text-2); +} + +.item-title { + flex: 1; + font-size: 0.9rem; +} + +.item-meta { + font-family: var(--mono); + font-size: 0.66rem; + color: var(--muted); + white-space: nowrap; + flex-shrink: 0; +} + +/* ── LEITOR DE TEXTOS & PROSE ── */ +.text-meta { + font-family: var(--mono); + font-size: 0.68rem; + color: var(--muted); + margin-bottom: 1.4rem; + letter-spacing: 0.02em; + display: flex; + gap: 0.6rem; + align-items: center; + flex-wrap: wrap; +} + +.text-meta .dot { + width: 3px; + height: 3px; + border-radius: 50%; + background: var(--border-2); +} + +.text-title { + font-size: 1.4rem; + line-height: 1.35; + margin-bottom: 1.6rem; + color: var(--text); + font-weight: 600; +} + +.prose { + font-size: 1rem; + line-height: 1.8; + color: var(--text); +} + +.prose p { + margin-bottom: 1.1rem; +} + +.prose h2 { + font-family: var(--serif); + font-size: 1.1rem; + font-weight: 600; + margin: 1.8rem 0 0.8rem; + color: var(--text); +} + +.prose h3 { + font-family: var(--mono); + font-size: 0.72rem; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--muted); + margin: 1.6rem 0 0.6rem; +} + +.prose blockquote { + border-left: 2px solid var(--border-2); + padding: 0.2rem 0 0.2rem 1rem; + margin: 1rem 0; + color: var(--text-2); + font-style: italic; +} + +.prose em { + color: var(--text-2); +} + +.prose strong { + color: var(--text); + font-weight: 600; +} + +.prose code { + font-family: var(--mono); + font-size: 0.85em; + background: var(--surface); + padding: 0.05em 0.35em; + border-radius: 2px; + color: var(--text-2); +} + +/* ── TABELAS ── */ +.prose table { + width: 100%; + border-collapse: collapse; + margin: 1.8rem 0; + font-size: 0.9rem; + color: var(--text-2); +} + +.prose th { + font-family: var(--mono); + font-size: 0.68rem; + letter-spacing: 0.05em; + text-transform: uppercase; + color: var(--muted); + border-bottom: 1px solid var(--border-2); + padding: 0.5rem 0.6rem; + text-align: left; +} + +.prose td { + padding: 0.6rem; + border-bottom: 1px solid var(--border); + vertical-align: top; +} + +.prose tr:hover td { + color: var(--text); +} + +@media (max-width: 480px) { + .prose table { + overflow-x: auto; + display: block; + } +} + +/* ── NOTAS DE RODAPÉ ── */ +a[href^="#fn:"] { + text-decoration: none; + color: var(--amber); + cursor: pointer; + transition: opacity var(--t); +} + +a[href^="#fn:"]:hover { + opacity: 0.7; +} + +a[href^="#fn:"].active { + font-weight: 600; +} + +.footnotes { + margin-top: 2rem; + padding-top: 1rem; + border-top: 1px solid var(--border); + font-size: 0.9rem; +} + +.footnote-backref { + text-decoration: none; + color: var(--amber); +} + +/* ── FLOATER (POPUP DE NOTAS) ── */ +.floater { + position: fixed; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 4px; + padding: 1rem; + max-width: 320px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); + z-index: 1000; + animation: slideUp 150ms ease; +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.f-label { + display: flex; + justify-content: space-between; + align-items: center; + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; + margin-bottom: 0.5rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--border); +} + +.f-close { + cursor: pointer; + font-size: 1.2rem; + color: var(--text-2); + transition: color var(--t); +} + +.f-close:hover { + color: var(--text); +} + +.f-quote { + font-size: 0.85rem; + color: var(--text-2); + font-style: italic; + margin-bottom: 0.5rem; +} + +/* ── SUMÁRIO (TOC) ── */ +.toc { + margin: 1.5rem 0 2rem; + padding: 1rem; + background: color-mix(in srgb, var(--amber) 3%, transparent); + border: 1px solid var(--border); + border-radius: 4px; +} + +.toc-label { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; + margin-bottom: 0.75rem; +} + +#toc-list { + list-style: none; + margin: 0; + padding: 0; +} + +.toc-h2 { + margin-bottom: 0.5rem; +} + +.toc-h2 a { + font-size: 0.95rem; + color: var(--text); + text-decoration: none; + transition: color var(--t); +} + +.toc-h2 a:hover { + color: var(--amber); +} + +.toc-h3 { + margin-left: 1.2rem; + margin-bottom: 0.3rem; +} + +.toc-h3 a { + font-size: 0.85rem; + color: var(--text-2); + text-decoration: none; + transition: color var(--t); +} + +.toc-h3 a:hover { + color: var(--text); +} + +/* ── REFERÊNCIAS ── */ +.references { + margin-top: 2rem; + padding-top: 1.5rem; + border-top: 1px solid var(--border); +} + +.ref-head { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; + margin-bottom: 1rem; +} + +.ref-list { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.ref-list p { + font-size: 0.9rem; + color: var(--text-2); + margin: 0; + line-height: 1.5; +} + +/* ── QUIZ: LAYOUT GERAL ── */ +.progress-wrap { + width: 100%; + height: 3px; + background: var(--border); + border-radius: 2px; + overflow: hidden; + margin-bottom: 1.5rem; +} + +.progress-fill { + height: 100%; + background: var(--amber); + width: 0%; + transition: width 300ms ease; +} + +.q-meta { + font-family: var(--mono); + font-size: 0.68rem; + color: var(--muted); + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 1.2rem; +} + +#quiz-body { + margin-bottom: 1.5rem; +} + +/* ── QUIZ: MÚLTIPLA ESCOLHA ── */ +.q-text { + font-size: 1rem; + line-height: 1.8; + color: var(--text); + margin-bottom: 1.5rem; +} + +.options { + display: flex; + flex-direction: column; + gap: 0.75rem; + margin-bottom: 1.5rem; +} + +.opt { + display: flex; + align-items: flex-start; + gap: 0.75rem; + padding: 0.75rem 1rem; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 4px; + cursor: pointer; + transition: all var(--t); + font-family: var(--serif); + font-size: 0.95rem; + color: var(--text); + text-align: left; + min-height: 3rem; +} + +.opt:hover:not(:disabled) { + border-color: var(--amber); + background: color-mix(in srgb, var(--amber) 5%, transparent); +} + +.opt:disabled { + cursor: not-allowed; + opacity: 0.7; +} + +.opt.correct { + border-color: #4ade80; + background: color-mix(in srgb, #4ade80 15%, transparent); +} + +.opt.wrong { + border-color: #ef4444; + background: color-mix(in srgb, #ef4444 15%, transparent); +} + +.opt.revealed { + opacity: 0.5; +} + +.opt-letter { + font-family: var(--mono); + font-size: 0.75rem; + font-weight: 600; + color: var(--muted); + min-width: 1.5rem; + flex-shrink: 0; +} + +/* ── QUIZ: RESPOSTA ABERTA ── */ +.open-area { + display: flex; + flex-direction: column; + gap: 1rem; +} + +textarea { + padding: 1rem; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 4px; + color: var(--text); + font-family: var(--serif); + font-size: 0.95rem; + line-height: 1.6; + resize: vertical; + min-height: 120px; + transition: border-color var(--t); +} + +textarea:focus { + outline: none; + border-color: var(--amber); +} + +textarea::placeholder { + color: var(--muted); +} + +/* ── QUIZ: FEEDBACK ── */ +.feedback-line { + display: flex; + align-items: center; + gap: 0.5rem; + font-family: var(--mono); + font-size: 0.75rem; + letter-spacing: 0.05em; + text-transform: uppercase; + color: var(--text-2); + opacity: 0; + transition: opacity var(--t); +} + +.feedback-line.show { + opacity: 1; +} + +.feedback-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--text-2); +} + +.feedback-line.ok .feedback-dot { + background: #4ade80; +} + +.feedback-line.bad .feedback-dot { + background: #ef4444; +} + +/* ── QUIZ: COMENTÁRIO ── */ +.comment-block { + padding: 1rem; + background: color-mix(in srgb, var(--amber) 5%, transparent); + border-left: 2px solid var(--amber); + border-radius: 0 4px 4px 0; + max-height: 0; + overflow: hidden; + transition: max-height var(--t); +} + +.comment-block.show { + max-height: 500px; +} + +.comment-label { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; + margin-bottom: 0.5rem; +} + +.comment-block > div:last-child { + font-size: 0.9rem; + color: var(--text-2); + line-height: 1.6; +} + +/* ── QUIZ: REFERÊNCIA (RESPOSTA ABERTA) ── */ +.ref-block { + padding: 1rem; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 4px; + max-height: 0; + overflow: hidden; + transition: max-height var(--t)); +} + +.ref-block.show { + max-height: 500px; +} + +.ref-label { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; + margin-bottom: 0.5rem; +} + +.ref-block > div:last-child { + font-size: 0.9rem; + color: var(--text-2); + line-height: 1.6; +} + +/* ── QUIZ: BOTÕES ── */ +.next-row { + display: flex; + justify-content: flex-end; + margin-top: 1.5rem; +} + +.open-btns { + display: flex; + gap: 0.75rem; + justify-content: flex-end; +} + +.btn { + padding: 0.6rem 1.2rem; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 4px; + color: var(--text); + font-family: var(--serif); + font-size: 0.9rem; + cursor: pointer; + transition: all var(--t); + text-decoration: none; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.btn:hover:not(:disabled) { + border-color: var(--amber); + background: color-mix(in srgb, var(--amber) 10%, transparent); +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn-ok { + border-color: #4ade80; + color: #4ade80; +} + +.btn-ok:hover:not(:disabled) { + background: color-mix(in srgb, #4ade80 15%, transparent); +} + +.btn-bad { + border-color: #ef4444; + color: #ef4444; +} + +.btn-bad:hover:not(:disabled) { + background: color-mix(in srgb, #ef4444 15%, transparent); +} + +/* ── QUIZ: RESULTADO ── */ +.res-score { + font-size: 3rem; + font-weight: 600; + color: var(--amber); + text-align: center; + margin-bottom: 0.5rem; +} + +.res-subtitle { + font-size: 1rem; + color: var(--text-2); + text-align: center; + margin-bottom: 1.5rem; +} + +.res-divider { + height: 1px; + background: var(--border); + margin: 1.5rem 0; +} + +.res-stats { + display: flex; + justify-content: center; + gap: 3rem; + margin-bottom: 1.5rem; +} + +.rs-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; +} + +.rs-num { + font-size: 1.8rem; + font-weight: 600; +} + +.rs-num.g { + color: #4ade80; +} + +.rs-num.r { + color: #ef4444; +} + +.rs-label { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.res-msg { + padding: 1rem; + background: color-mix(in srgb, var(--amber) 5%, transparent); + border-left: 2px solid var(--amber); + border-radius: 0 4px 4px 0; + margin-bottom: 1.5rem; +} + +.res-msg-label { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; + margin-bottom: 0.5rem; +} + +.res-msg-body { + font-size: 0.95rem; + color: var(--text-2); + line-height: 1.6; +} + +.res-btns { + display: flex; + gap: 0.75rem; + justify-content: center; + flex-wrap: wrap; +} + +/* ── EXTRAS DE DISCIPLINA (COLAPSÁVEIS) ── */ +.disc-extras-wrap { + margin: 1.5rem 0 2rem; +} + +.disc-extra { + margin-bottom: 0.75rem; + border: 1px solid var(--border); + border-radius: 4px; + overflow: hidden; +} + +.disc-extra-summary { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.75rem 1rem; + background: var(--surface); + cursor: pointer; + user-select: none; + transition: background var(--t); +} + +.disc-extra-summary:hover { + background: color-mix(in srgb, var(--amber) 5%, transparent); +} + +.disc-extra-label { + font-family: var(--mono); + font-size: 0.68rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.disc-extra-count { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--text-2); + letter-spacing: 0.05em; +} + +.disc-extra-chevron { + color: var(--text-2); + transition: transform var(--t); +} + +.disc-extra[open] .disc-extra-chevron { + transform: rotate(180deg); +} + +.disc-extra-body { + padding: 1rem; + border-top: 1px solid var(--border); +} + +.disc-extra-row { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.5rem 0; + text-decoration: none; + color: var(--text-2); + transition: color var(--t); +} + +.disc-extra-row:hover { + color: var(--text); +} + +.disc-extra-icon { + font-size: 0.8rem; + color: var(--muted); + min-width: 1.2rem; +} + +.disc-extra-name { + flex: 1; + font-size: 0.9rem; +} + +.disc-extra-meta { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.disc-extra-prose { + font-size: 0.9rem; + color: var(--text-2); +} + +.disc-extra-prose p { + margin-bottom: 0.75rem; +} + +.disc-extra-prose p:last-child { + margin-bottom: 0; +} + +.disc-extra-kind-label { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; + margin: 1rem 0 0.5rem; + padding-bottom: 0.5rem; + border-bottom: 1px dashed var(--border); +} + +.disc-extra-kind-label:first-child { + margin-top: 0; +} + +/* ── SUBJECT QUIZZES (LINKS PARA EXERCÍCIOS) ── */ +.subject-quizzes { + margin: 1.5rem 0 2rem; + padding: 1rem; + background: color-mix(in srgb, var(--amber) 3%, transparent); + border: 1px solid var(--border); + border-radius: 4px; +} + +.sq-label { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; + margin-bottom: 0.75rem; +} + +.sq-list { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.sq-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.6rem 0.75rem; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 3px; + text-decoration: none; + color: var(--text); + transition: all var(--t); +} + +.sq-item:hover { + border-color: var(--amber); + background: color-mix(in srgb, var(--amber) 8%, transparent); +} + +.sq-name { + font-size: 0.9rem; + flex: 1; +} + +.sq-meta { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.05em; + text-transform: uppercase; + flex-shrink: 0; +} + +/* ── SUBJECT LIST ── */ +.subject-list { + display: flex; + flex-direction: column; + gap: 0; +} + +.subject-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem 0; + border-bottom: 1px solid var(--border); + text-decoration: none; + color: var(--text); + transition: all var(--t); +} + +.subject-row:last-child { + border-bottom: none; +} + +.subject-row:hover { + color: var(--amber); + padding-left: 0.5rem; +} + +.subject-row-name { + font-size: 0.95rem; + flex: 1; +} + +.subject-row-meta { + font-family: var(--mono); + font-size: 0.62rem; + color: var(--muted); + letter-spacing: 0.1em; + text-transform: uppercase; + flex-shrink: 0; +} + +.empty-note { + font-size: 0.9rem; + color: var(--muted); + font-style: italic; + padding: 1rem 0; +} + +/* ── RESPONSIVIDADE ── */ +@media (max-width: 768px) { + main { + padding: 1.5rem; + } + + .disc-grid { + grid-template-columns: 1fr; + } + + .res-stats { + gap: 2rem; + } + + header { + padding: 1rem 1.5rem; + } +} + +@media (max-width: 480px) { + main { + padding: 1rem; + } + + .text-title { + font-size: 1.2rem; + } + + .prose { + font-size: 0.95rem; + } + + .options { + gap: 0.5rem; + } + + .opt { + padding: 0.6rem 0.75rem; + min-height: 2.5rem; + } + + .floater { + max-width: 280px; + } + + .res-score { + font-size: 2.5rem; + } + + .res-stats { + gap: 1.5rem; + } +} + +/* ── FLASHCARD ── */ +.flashcard-area { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.flashcard { + perspective: 1000px; + cursor: pointer; + height: 300px; + position: relative; +} + +.flashcard-front, +.flashcard-back { + position: absolute; + width: 100%; + height: 100%; + backface-visibility: hidden; + display: flex; + align-items: center; + justify-content: center; + border: 2px solid var(--amber); + border-radius: 4px; + padding: 2rem; + text-align: center; + transition: transform 0.6s; +} + +.flashcard-front { + background: var(--surface); + color: var(--text); + transform: rotateY(0deg); +} + +.flashcard-back { + background: var(--amber); + color: #0f0f13; + transform: rotateY(180deg); +} + +.flashcard.flipped .flashcard-front { + transform: rotateY(180deg); +} + +.flashcard.flipped .flashcard-back { + transform: rotateY(0deg); +} + +.flashcard-hint { + font-size: 1.2rem; + opacity: 0.7; +} + +.flashcard-btns { + display: flex; + gap: 0.75rem; + justify-content: center; +} + +/* ── FILTRO NA HOME ── */ +.filter-container { + margin-bottom: 2rem; + display: flex; + gap: 1rem; + align-items: center; +} + +.filter-input { + flex: 1; + padding: 0.75rem 1rem; + background: var(--surface); + color: var(--text); + border: 1px solid var(--border); + border-radius: 4px; + font-size: 1rem; + font-family: var(--serif); +} + +.filter-input:focus { + outline: none; + border-color: var(--amber); +} + +.filter-count { + color: var(--amber); + font-size: 0.9rem; + white-space: nowrap; +} + +/* ── DASHBOARD ── */ +.dashboard-container { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 4px; + padding: 2rem; + margin-bottom: 2rem; +} + +.dashboard-overview { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; + margin: 1.5rem 0; +} + +.stat-card { + background: #0f0f13; + border: 1px solid var(--border); + border-radius: 4px; + padding: 1.5rem; + text-align: center; +} + +.stat-label { + font-size: 0.9rem; + color: var(--muted); + margin-bottom: 0.5rem; +} + +.stat-value { + font-size: 2rem; + font-weight: 600; + color: var(--amber); +} + +.dashboard-disciplines { + margin-top: 2rem; +} + +.disc-stat { + margin-bottom: 1.5rem; + padding-bottom: 1rem; + border-bottom: 1px solid var(--border); +} + +.disc-name { + font-weight: 600; + margin-bottom: 0.5rem; +} + +.disc-bar { + height: 8px; + background: #0f0f13; + border-radius: 4px; + overflow: hidden; + margin-bottom: 0.5rem; +} + +.disc-progress { + height: 100%; + background: #4ade80; + transition: width 0.3s; +} + +.disc-info { + display: flex; + justify-content: space-between; + font-size: 0.85rem; + color: var(--muted); +} + +.dashboard-actions { + margin-top: 2rem; + text-align: center; +} + +/* ── MODO ESCURO TOGGLE ── */ +#dark-mode-toggle { + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer; + margin-left: auto; + padding: 0.5rem; + border-radius: 4px; + transition: background-color var(--t); +} + +#dark-mode-toggle:hover { + background-color: var(--border); +} + +/* ── PRINT-FRIENDLY ── */ +@media print { + header, + .filter-container, + .dashboard-container, + .btn, + .next-row, + #dark-mode-toggle, + .flashcard-btns, + .open-btns { + display: none; + } + + body { + background: white; + color: black; + padding: 0; + } + + main { + max-width: 100%; + padding: 0; + } + + .q-text, + .flashcard-back { + page-break-inside: avoid; + } + + .flashcard { + border: 1px solid black; + background: white; + color: black; + } + + .flashcard-front, + .flashcard-back { + position: static; + transform: none; + border: none; + background: white; + color: black; + } +} + +/* ── RESPONSIVIDADE MOBILE ── */ +@media (max-width: 768px) { + .dashboard-overview { + grid-template-columns: 1fr; + } + + .flashcard { + height: 250px; + } +} + +@media (max-width: 480px) { + .flashcard { + height: 200px; + } + + .filter-container { + flex-direction: column; + } + + .filter-input { + width: 100%; + } +} + +/* ── QUIZ INLINE (dentro do assunto) ── */ +.inline-quiz-wrap { + margin: 2.5rem 0 1.5rem; + border: 1px solid var(--border); + border-radius: 4px; + overflow: hidden; +} + +.inline-quiz-head { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.9rem 1.2rem; + background: var(--surface); + border-bottom: 1px solid var(--border); +} + +.inline-quiz-head .sq-label { + margin: 0; + padding: 0; + border: none; +} + +.inline-quiz-link { + font-family: var(--mono); + font-size: 0.65rem; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--muted); + text-decoration: none; + transition: color var(--t); +} + +.inline-quiz-link:hover { + color: var(--amber); +} + +#inline-quiz-body, +#inline-quiz-player, +#inline-quiz-result { + padding: 1.2rem; +} + +.btn-start-quiz { + width: 100%; + padding: 0.85rem; + justify-content: center; + background: color-mix(in srgb, var(--amber) 8%, transparent); + border-color: var(--amber); + color: var(--amber); +} + +.btn-start-quiz:hover { + background: color-mix(in srgb, var(--amber) 15%, transparent); +} + +/* ── TABELA NA PROSE ── */ +.prose table { + width: 100%; + border-collapse: collapse; + font-size: 0.88rem; + margin: 1.5rem 0; +} + +.prose th { + font-family: var(--mono); + font-size: 0.7rem; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--muted); + padding: 0.55rem 0.75rem; + border-bottom: 1px solid var(--border-2); + text-align: left; +} + +.prose td { + padding: 0.55rem 0.75rem; + border-bottom: 1px solid var(--border); + color: var(--text-2); + vertical-align: top; +} + +.prose tr:last-child td { + border-bottom: none; +} + +.prose tr:hover td { + background: color-mix(in srgb, var(--amber) 3%, transparent); +} diff --git a/static/manifest.json b/static/manifest.json new file mode 100644 index 0000000..6460cbe --- /dev/null +++ b/static/manifest.json @@ -0,0 +1,34 @@ +{ + "name": "Caderno de Estudos - Direito", + "short_name": "Caderno", + "description": "Caderno interativo de estudos com quizzes e simulados de Direito", + "start_url": "/", + "scope": "/", + "display": "standalone", + "background_color": "#0f0f13", + "theme_color": "#c9a961", + "orientation": "portrait-primary", + "icons": [ + { + "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192'><rect fill='%230f0f13' width='192' height='192'/><text x='96' y='120' font-size='80' font-weight='bold' text-anchor='middle' fill='%23c9a961' font-family='monospace'>D</text></svg>", + "sizes": "192x192", + "type": "image/svg+xml", + "purpose": "any" + }, + { + "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192'><rect fill='%230f0f13' width='192' height='192'/><text x='96' y='120' font-size='80' font-weight='bold' text-anchor='middle' fill='%23c9a961' font-family='monospace'>D</text></svg>", + "sizes": "512x512", + "type": "image/svg+xml", + "purpose": "maskable" + } + ], + "categories": ["education", "productivity"], + "screenshots": [ + { + "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 540 720'><rect fill='%230f0f13' width='540' height='720'/><text x='270' y='360' font-size='60' font-weight='bold' text-anchor='middle' fill='%23c9a961' font-family='monospace'>Caderno</text></svg>", + "sizes": "540x720", + "type": "image/svg+xml", + "form_factor": "narrow" + } + ] +} diff --git a/static/offline.html b/static/offline.html new file mode 100644 index 0000000..0cb1bdc --- /dev/null +++ b/static/offline.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html lang="pt-BR"> +<head> +<meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<title>Sem conexão · Caderno</title> +<style> + body { background:#0f0f13; color:#dedad4; font-family:'Georgia',serif; display:flex; + align-items:center; justify-content:center; min-height:100vh; margin:0; text-align:center; padding:2rem; } + h1 { font-size:1.2rem; margin-bottom:.75rem; } + p { font-size:.9rem; color:#6a6a75; } + a { color:#c9a961; } +</style> +</head> +<body> + <div> + <h1>Você está offline</h1> + <p>Páginas já visitadas estão disponíveis em cache.<br> + <a href="/">Voltar ao início</a></p> + </div> +</body> +</html> diff --git a/static/quiz-engine.js b/static/quiz-engine.js new file mode 100644 index 0000000..03e50a5 --- /dev/null +++ b/static/quiz-engine.js @@ -0,0 +1,170 @@ +/** + * Quiz Engine - Renderiza questões na página + */ + +function _renderQuestion(q) { + if (!q) return; + const body = document.getElementById('quiz-body'); + if (!body) return; + + const counter = document.getElementById('q-counter'); + if (counter) { + counter.textContent = `${_qState.qIndex + 1} de ${_qState.questions.length}`; + } + + const prog = document.getElementById('prog'); + if (prog) { + const pct = ((_qState.qIndex + 1) / _qState.questions.length) * 100; + prog.style.width = pct + '%'; + } + + if (q.type === 'mc') { + body.innerHTML = `<div class="mc-area fade"> + <p class="q-text">${md(q.text)}</p> + <div class="options"> + ${q.options + .map( + (o, i) => `<button class="opt" data-opt="${esc(o)}" data-answer="${esc(q.answer)}" onclick="selectOpt(this)" aria-label="Opção ${LETRAS[i]}: ${o}"> + <span class="opt-letter">${LETRAS[i]}</span> + <span>${md(o)}</span> + </button>` + ) + .join('')} + </div> + <div class="feedback-line" id="feedback" aria-live="polite" aria-atomic="true"><span class="feedback-dot"></span><span id="feedback-msg"></span></div> + ${q.comment ? `<div class="comment-block" id="comment"><div class="comment-label">comentário</div><div>${md(q.comment)}</div></div>` : ''} + </div>`; + } else { + // Modo Flashcard para questões abertas + const isRevealed = _qState.flipped || false; + body.innerHTML = `<div class="flashcard-area fade"> + <p class="q-text">${md(q.text)}</p> + <div class="flashcard-simple" id="flashcard"> + ${!isRevealed ? `<button class="btn btn-reveal" onclick="flipFlashcard()">Revelar Resposta</button>` : `<div class="ref-label">resposta de referência</div><div class="ref-content">${md(q.reference)}</div>`} + </div> + <div class="feedback-line" id="feedback" aria-live="polite" aria-atomic="true"><span class="feedback-dot"></span><span id="feedback-msg"></span></div> + ${q.comment ? `<div class="comment-block" id="comment"><div class="comment-label">comentário</div><div>${md(q.comment)}</div></div>` : ''} + <div class="flashcard-btns" id="flashcard-btns"> + <button class="btn btn-ok" onclick="markFlashcardOk()">✓ Acertei</button> + <button class="btn btn-bad" onclick="markFlashcardBad()">✗ Errei</button> + </div> + </div>`; + } +} + +function selectOpt(btn) { + if (_qState.answered) return; + _qState.answered = true; + const chosen = btn.dataset.opt; + const correct = btn.dataset.answer; + btn.closest('.options').querySelectorAll('.opt').forEach((b) => { + b.disabled = true; + if (b.dataset.opt === correct) b.classList.add('correct'); + else if (b === btn) b.classList.add('wrong'); + else b.classList.add('revealed'); + }); + if (chosen === correct) { + _qState.acertos++; + setFeedback(true, 'Correto'); + } else { + _qState.erros++; + setFeedback(false, 'Incorreto — a correta está destacada'); + } + document.getElementById('comment')?.classList.add('show'); + document.getElementById('btn-next').style.display = ''; +} + +function flipFlashcard() { + const card = document.getElementById('flashcard'); + if (card) { + _qState.flipped = true; + _renderQuestion(_qState.questions[_qState.qIndex]); + } +} + +function markFlashcardOk() { + _qState.answered = true; + _qState.acertos++; + setFeedback(true, 'Marcado como acertado'); + document.getElementById('comment')?.classList.add('show'); + document.getElementById('flashcard-btns').innerHTML = ''; + document.getElementById('btn-next').style.display = ''; +} + +function markFlashcardBad() { + _qState.answered = true; + _qState.erros++; + setFeedback(false, 'Marcado como errado'); + document.getElementById('comment')?.classList.add('show'); + document.getElementById('flashcard-btns').innerHTML = ''; + document.getElementById('btn-next').style.display = ''; +} + +function setFeedback(ok, msg) { + const fb = document.getElementById('feedback'); + fb.className = 'feedback-line show ' + (ok ? 'ok' : 'bad'); + document.getElementById('feedback-msg').textContent = msg; +} + +function _buildResult() { + const { acertos, erros, questions } = _qState; + const total = questions.length; + const pct = total ? Math.round((acertos / total) * 100) : 0; + let nivel, detalhe; + if (pct >= 90) { + nivel = 'Excelente'; + detalhe = 'Domínio consistente do conteúdo. Mantenha revisão periódica.'; + } else if (pct >= 70) { + nivel = 'Bom'; + detalhe = 'Base sólida com pontos a refinar. Revise os erros e releia os comentários.'; + } else if (pct >= 50) { + nivel = 'Regular'; + detalhe = 'Compreensão parcial, com lacunas importantes. Retome o material teórico.'; + } else { + nivel = 'Insuficiente'; + detalhe = 'Os conceitos centrais ainda não estão consolidados. Estude o tema e refaça.'; + } + document.getElementById('res-pct').textContent = pct + '%'; + document.getElementById('res-a').textContent = acertos; + document.getElementById('res-a2').textContent = acertos; + document.getElementById('res-e').textContent = erros; + document.getElementById('res-t').textContent = total; + document.getElementById('res-msg-body').innerHTML = `<strong>${nivel}</strong> — ${acertos} de ${total} (${pct}%). ${detalhe}`; +} + +function _saveProgress(key) { + try { + localStorage.setItem(PROGRESS_PREFIX + key, JSON.stringify({ + questions: _qState.questions, + qIndex: _qState.qIndex, + acertos: _qState.acertos, + erros: _qState.erros, + flipped: _qState.flipped, + })); + } catch (e) {} +} + +function _loadProgress(key) { + try { + const s = localStorage.getItem(PROGRESS_PREFIX + key); + if (!s) return false; + const d = JSON.parse(s); + if (!d.questions || !d.questions.length) return false; + Object.assign(_qState, { + questions: d.questions, + qIndex: d.qIndex || 0, + acertos: d.acertos || 0, + erros: d.erros || 0, + flipped: d.flipped || false, + }); + return true; + } catch (e) { + return false; + } +} + +function _clearProgress(key) { + try { + localStorage.removeItem(PROGRESS_PREFIX + key); + } catch (e) {} +} diff --git a/static/quiz-parser.js b/static/quiz-parser.js new file mode 100644 index 0000000..c62beb5 --- /dev/null +++ b/static/quiz-parser.js @@ -0,0 +1,144 @@ +/** + * Quiz Parser - Converte sintaxe customizada em JSON estruturado + * Sintaxe suportada: + * ? Enunciado da questão + * - Opção A / - Opção B + * = Opção correta (incluída nas opções automaticamente se ausente) + * > Comentário (pode repetir; linha vazia separa parágrafos) + * + * ? Questão aberta + * [ Resposta de referência (pode repetir) + * > Comentário + * + * --- ou linha em branco separam questões + * # linha ignorada + */ + +function parseQuizBody(raw) { + if (!raw || !raw.trim()) return []; + const lines = raw.replace(/\r\n/g, '\n').split('\n'); + const result = []; + let enunciado = '', tipo = '', opcoes = [], gabarito = '', referencia = '', comentario = ''; + + function flush() { + if (!enunciado.trim()) return; + const q = { type: tipo || 'mc', text: enunciado.trim() }; + if (tipo === 'mc') { + q.options = opcoes; + q.answer = gabarito; + } else { + q.reference = referencia.trim(); + } + if (comentario.trim()) q.comment = comentario.trim(); + result.push(q); + enunciado = ''; + tipo = ''; + opcoes = []; + gabarito = ''; + referencia = ''; + comentario = ''; + } + + for (const rawLine of lines) { + const line = rawLine.replace(/\r$/, '').trimEnd(); + if (/^---+\s*$/.test(line)) { + flush(); + continue; + } + if (line.trim() === '') continue; + const mark = line[0]; + const content = line.length > 1 ? line.slice(2) : ''; + switch (mark) { + case '#': + break; + case '?': + flush(); + enunciado = content; + break; + case '-': + opcoes.push(content); + tipo = 'mc'; + break; + case '=': + gabarito = content; + tipo = 'mc'; + if (content && !opcoes.includes(content)) opcoes.push(content); + break; + case '[': + referencia += (referencia ? '\n' : '') + content; + tipo = 'open'; + break; + case '>': + comentario += (comentario ? '\n' : '') + content; + break; + default: + if (enunciado && !tipo) enunciado += '\n' + line; + else if (tipo === 'open' && referencia) referencia += '\n' + line; + } + } + flush(); + return result; +} + +/* ── UTILITÁRIOS BASE ── */ +const LETRAS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + +function shuffle(arr) { + const a = [...arr]; + for (let i = a.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [a[i], a[j]] = [a[j], a[i]]; + } + return a; +} + +function prepareQuizQuestions(questions) { + const letterCounts = []; + let previousCorrectIndex = -1; + return questions.map((q) => { + if (!q || q.type !== 'mc' || !Array.isArray(q.options) || q.options.length < 2) { + return q; + } + const answerIndex = q.options.findIndex((o) => o === q.answer); + if (answerIndex < 0) { + return { ...q, options: shuffle(q.options) }; + } + const optionCount = q.options.length; + const availableIndexes = Array.from({ length: optionCount }, (_, i) => i); + const lowestCount = Math.min(...availableIndexes.map((i) => letterCounts[i] || 0)); + let candidates = availableIndexes.filter((i) => (letterCounts[i] || 0) === lowestCount); + if (optionCount > 1 && candidates.length > 1) { + candidates = candidates.filter((i) => i !== previousCorrectIndex); + } + const correctIndex = shuffle(candidates)[0] ?? 0; + const otherOptions = shuffle(q.options.filter((_, i) => i !== answerIndex)); + const arrangedOptions = [...otherOptions]; + arrangedOptions.splice(correctIndex, 0, q.options[answerIndex]); + letterCounts[correctIndex] = (letterCounts[correctIndex] || 0) + 1; + previousCorrectIndex = correctIndex; + return { ...q, options: arrangedOptions }; + }); +} + +/* Para uso em atributos HTML (onclick="...") */ +function esc(s) { + return String(s ?? '') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +/* Para innerHTML: escapa HTML e renderiza Markdown inline. + Suporta: **negrito**, *itálico*, `código`, \n→<br> */ +function md(s) { + return String(s ?? '') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>') + .replace(/\*(.+?)\*/g, '<em>$1</em>') + .replace(/`(.+?)`/g, '<code>$1</code>') + .replace(/\n/g, '<br>'); +} diff --git a/static/quiz-runtime.js b/static/quiz-runtime.js new file mode 100644 index 0000000..d6e824d --- /dev/null +++ b/static/quiz-runtime.js @@ -0,0 +1,92 @@ +/** + * Quiz Runtime — controla o fluxo de quiz/simulado na página dedicada + */ + +const PROGRESS_PREFIX = window.PROGRESS_PREFIX = 'quiz_progress_'; + +// Estado global do quiz (página de quiz dedicada) +let _qState = { + questions: [], + qIndex: 0, + acertos: 0, + erros: 0, + answered: false, + flipped: false, +}; + +function nextQuestion() { + _qState.qIndex++; + _qState.answered = false; + _qState.flipped = false; + if (_qState.qIndex >= _qState.questions.length) { + showResult(); + return; + } + const body = document.getElementById('quiz-body'); + if (!body) return; + body.classList.add('fade-out'); + setTimeout(() => { + body.classList.remove('fade-out'); + _renderQuestion(_qState.questions[_qState.qIndex]); + _saveProgress(window.QUIZ_KEY); + }, 100); +} + +function showResult() { + const wrap = document.getElementById('quiz-wrap'); + const result = document.getElementById('quiz-result'); + if (wrap) wrap.style.display = 'none'; + if (result) result.style.display = ''; + _buildResult(); + _clearProgress(window.QUIZ_KEY); + window.scrollTo({ top: 0, behavior: 'smooth' }); +} + +function restartQuiz() { + _clearProgress(window.QUIZ_KEY); + _qState.questions = prepareQuizQuestions(parseQuizBody( + typeof RAW_QUIZ !== 'undefined' ? RAW_QUIZ : '' + )); + _qState.qIndex = 0; + _qState.acertos = 0; + _qState.erros = 0; + _qState.answered = false; + _qState.flipped = false; + const wrap = document.getElementById('quiz-wrap'); + const result = document.getElementById('quiz-result'); + if (result) result.style.display = 'none'; + if (wrap) wrap.style.display = ''; + _renderQuestion(_qState.questions[0]); +} + +function initQuiz() { + // Só inicializa na página de quiz (onde existe #quiz-body) + if (!document.getElementById('quiz-body')) return; + + const raw = typeof RAW_QUIZ !== 'undefined' ? RAW_QUIZ : ''; + const key = window.QUIZ_KEY; + + if (!_loadProgress(key)) { + _qState.questions = prepareQuizQuestions(parseQuizBody(raw)); + _qState.qIndex = 0; + _qState.acertos = 0; + _qState.erros = 0; + } + + _qState.answered = false; + _qState.flipped = false; + + const wrap = document.getElementById('quiz-wrap'); + const result = document.getElementById('quiz-result'); + if (result) result.style.display = 'none'; + if (wrap) wrap.style.display = ''; + + if (_qState.qIndex >= _qState.questions.length) { + showResult(); + return; + } + + _renderQuestion(_qState.questions[_qState.qIndex]); +} + +document.addEventListener('DOMContentLoaded', initQuiz); diff --git a/static/sw.js b/static/sw.js new file mode 100644 index 0000000..71f0e5f --- /dev/null +++ b/static/sw.js @@ -0,0 +1,68 @@ +/* Service Worker — caderno de estudos + Estratégia: cache-first para assets estáticos, network-first para HTML + Versão: 3 (com suporte a stale-while-revalidate) */ +const CACHE = 'caderno-v3'; +const STATIC = [ + '/', + '/offline.html' +]; + +self.addEventListener('install', e => { + e.waitUntil( + caches.open(CACHE).then(c => c.addAll(STATIC)).then(() => self.skipWaiting()) + ); +}); + +self.addEventListener('activate', e => { + e.waitUntil( + caches.keys().then(keys => + Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k))) + ).then(() => self.clients.claim()) + ); +}); + +self.addEventListener('fetch', e => { + const url = new URL(e.request.url); + + // Ignora requisições externas (fontes Google, etc.) + if (url.origin !== location.origin) return; + + if (e.request.mode === 'navigate') { + // HTML: network-first, fallback para offline.html + e.respondWith( + fetch(e.request) + .then(res => { + const clone = res.clone(); + caches.open(CACHE).then(c => c.put(e.request, clone)); + return res; + }) + .catch(() => caches.match(e.request).then(r => r || caches.match('/offline.html'))) + ); + } else { + // Assets (CSS, JS, imagens): stale-while-revalidate + e.respondWith( + caches.match(e.request).then(cached => { + // Se estiver em cache, retorna imediatamente e atualiza em background + if (cached) { + // Atualizar em background + fetch(e.request).then(res => { + if (res && res.status === 200) { + const clone = res.clone(); + caches.open(CACHE).then(c => c.put(e.request, clone)); + } + }); + return cached; + } + + // Se não estiver em cache, busca da rede + return fetch(e.request).then(res => { + if (res && res.status === 200) { + const clone = res.clone(); + caches.open(CACHE).then(c => c.put(e.request, clone)); + } + return res; + }); + }) + ); + } +}); diff --git a/static/toc.js b/static/toc.js new file mode 100644 index 0000000..950533e --- /dev/null +++ b/static/toc.js @@ -0,0 +1,52 @@ +/** + * Table of Contents - Gera sumário automático de headings + */ + +document.addEventListener('DOMContentLoaded', () => { + const prose = document.getElementById('prose'); + const tocNav = document.getElementById('toc-nav'); + const tocList = document.getElementById('toc-list'); + + if (!prose || !tocNav || !tocList) return; + + const headings = prose.querySelectorAll('h2, h3'); + if (!headings.length) return; + + const used = new Set(); + + headings.forEach((h) => { + // Gerar ID único baseado no texto + let base = h.textContent + .trim() + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); + + let id = base || 'sec'; + let i = 2; + while (used.has(id)) id = base + '-' + i++; + + h.id = id; + used.add(id); + + // Criar item no sumário + const li = document.createElement('li'); + li.className = 'toc-' + h.tagName.toLowerCase(); + + const a = document.createElement('a'); + a.href = '#' + id; + a.textContent = h.textContent; + a.addEventListener('click', (e) => { + e.preventDefault(); + h.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }); + + li.appendChild(a); + tocList.appendChild(li); + }); + + // Mostrar sumário se houver conteúdo + tocNav.style.display = ''; +}); |
