starling-devex 0.1.2

Starling: a local dev orchestrator with a central daemon, shared named-URL proxy, and a k9s-style TUI (a Rust port of Tilt + portless)
import React, { useEffect } from "react"
import { useStorageState } from "react-storage-hooks"
import styled from "styled-components"
import ClearLogs from "./ClearLogs"
import CopyLogs from "./CopyLogs"
import { InstrumentedButton } from "./instrumentedComponents"
import { FilterSet } from "./logfilters"
import {
  AnimDuration,
  Color,
  FontSize,
  mixinResetButtonStyle,
} from "./style-helpers"

export const LogFontSizeScaleLocalStorageKey = "tilt.global.log-font-scale"
export const LogFontSizeScaleCSSProperty = "--log-font-scale"
export const LogFontSizeScaleMinimumPercentage = 10

const LogActionsGroup = styled.div`
  margin-left: auto;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
`

const FontSizeControls = styled.div`
  color: ${Color.gray60};
  vertical-align: middle;
  display: flex;
  flex-wrap: nowrap;
`

const FontSizeDivider = styled.div`
  color: ${Color.gray60};
  font-size: ${FontSize.default};
  user-select: none;
`

const LogActionsDivider = styled.div`
  color: ${Color.gray60};
  font-size: ${FontSize.default};
  user-select: none;
  padding: 0 6px;
`

const FontSizeButton = styled(InstrumentedButton)`
  ${mixinResetButtonStyle};
  color: ${Color.gray60};
  transition: color ${AnimDuration.default} ease;
  padding: 0 4px;
  user-select: none;

  &:hover {
    color: ${Color.blue};
  }
`

export const FontSizeDecreaseButton = styled(FontSizeButton)`
  font-size: ${FontSize.smallest};
`

export const FontSizeIncreaseButton = styled(FontSizeButton)`
  font-size: ${FontSize.default};
`

export const LogsFontSize: React.FC = () => {
  // this uses `useStorageState` directly vs `usePersistentState` wrapper because it's a global setting
  // (i.e. log zoom applies across all Tiltfiles)
  const [logFontScale, setLogFontSize] = useStorageState<string>(
    localStorage,
    LogFontSizeScaleLocalStorageKey,
    () =>
      document.documentElement.style.getPropertyValue(
        LogFontSizeScaleCSSProperty
      )
  )
  useEffect(() => {
    if (!logFontScale?.endsWith("%")) {
      // somehow an invalid value ended up in local storage - reset to 100% and let effect run again
      setLogFontSize("100%")
      return
    }
    document.documentElement.style.setProperty(
      LogFontSizeScaleCSSProperty,
      logFontScale
    )
  }, [logFontScale])

  const adjustLogFontScale = (step: number) => {
    const val = Math.max(
      parseFloat(logFontScale) + step,
      LogFontSizeScaleMinimumPercentage
    )
    setLogFontSize(`${val}%`)
  }

  const zoomStep = 5
  return (
    <FontSizeControls>
      <FontSizeDecreaseButton
        aria-label="Decrease log font size"
        onClick={() => adjustLogFontScale(-zoomStep)}
      >
        A
      </FontSizeDecreaseButton>
      <FontSizeDivider aria-hidden={true}>|</FontSizeDivider>
      <FontSizeIncreaseButton
        aria-label="Increase log font size"
        onClick={() => adjustLogFontScale(zoomStep)}
      >
        A
      </FontSizeIncreaseButton>
    </FontSizeControls>
  )
}

export interface LogActionsProps {
  resourceName: string
  isSnapshot: boolean
  filterSet: FilterSet
}

const LogActions: React.FC<LogActionsProps> = ({
  resourceName,
  isSnapshot,
  filterSet,
}) => {
  return (
    <LogActionsGroup>
      <LogsFontSize />
      {isSnapshot || (
        <>
          <LogActionsDivider aria-hidden={true}>|</LogActionsDivider>
          <CopyLogs resourceName={resourceName} filterSet={filterSet} />
          <LogActionsDivider aria-hidden={true}>|</LogActionsDivider>
          <ClearLogs resourceName={resourceName} />
        </>
      )}
    </LogActionsGroup>
  )
}

export default LogActions