|
@ -83,6 +83,9 @@ zulip.kdev4
|
||||||
# Core dump files
|
# Core dump files
|
||||||
core
|
core
|
||||||
|
|
||||||
|
# Static generated files for landing page.
|
||||||
|
/static/images/landing-page/hello/generated
|
||||||
|
|
||||||
## Miscellaneous
|
## Miscellaneous
|
||||||
# (Ideally this section is empty.)
|
# (Ideally this section is empty.)
|
||||||
.transifexrc
|
.transifexrc
|
||||||
|
|
After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 503 KiB |
After Width: | Height: | Size: 360 KiB |
After Width: | Height: | Size: 370 KiB |
After Width: | Height: | Size: 478 KiB |
After Width: | Height: | Size: 485 KiB |
After Width: | Height: | Size: 260 KiB |
After Width: | Height: | Size: 274 KiB |
After Width: | Height: | Size: 481 KiB |
After Width: | Height: | Size: 170 KiB |
After Width: | Height: | Size: 172 KiB |
After Width: | Height: | Size: 420 KiB |
After Width: | Height: | Size: 448 KiB |
After Width: | Height: | Size: 2.1 MiB |
After Width: | Height: | Size: 723 KiB |
After Width: | Height: | Size: 2.1 KiB |
|
@ -1,562 +1,350 @@
|
||||||
{% extends "zerver/portico.html" %}
|
{% extends "zerver/base.html" %}
|
||||||
{% set entrypoint = "landing-page" %}
|
{% set entrypoint = "landing-page-hello" %}
|
||||||
|
|
||||||
{% set PAGE_TITLE = "Zulip: Open-source team chat with topic-based threading" %}
|
{% set PAGE_TITLE = "Zulip — organized team chat" %}
|
||||||
|
|
||||||
{% set PAGE_DESCRIPTION = "Zulip is the only modern team chat app that is
|
{% set PAGE_DESCRIPTION = "Zulip is the only modern team chat app that is designed for both live and asynchronous conversations. Organized to help you collaborate productively and efficiently." %}
|
||||||
designed for both live and asynchronous conversations. Organized to help you
|
|
||||||
collaborate productively and efficiently." %}
|
|
||||||
|
|
||||||
{% block customhead %}
|
{% block customhead %}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
<style>
|
|
||||||
.portico-page {
|
|
||||||
padding-bottom: 0px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block portico_content %}
|
{% block content %}
|
||||||
|
{% include 'zerver/landing_nav.html' %}
|
||||||
{% include 'zerver/landing_nav.html' %}
|
<div class="portico-hello-page">
|
||||||
|
<div class='body-bg'>
|
||||||
<div class="gradients">
|
<div class='body-bg__layer'></div>
|
||||||
<div class="gradient sunburst"></div>
|
</div>
|
||||||
<div class="gradient dark-blue"></div>
|
<div class="screen-1">
|
||||||
<div class="gradient green"></div>
|
<h1>Organized team chat</h1>
|
||||||
<div class="gradient blue"></div>
|
<div class='h1-subheader'>
|
||||||
<div class="gradient white-fade"></div>
|
The calmer, more efficient way to work
|
||||||
</div>
|
</div>
|
||||||
|
<div class='appshot-1'>
|
||||||
<div class="portico-landing hello show">
|
<picture>
|
||||||
<div class="hero">
|
<!-- dark theme is only with webp images, since webp support was at the same time or earlier than prefers-color-scheme https://caniuse.com/?search=prefers-color-scheme https://caniuse.com/webp -->
|
||||||
<div class="content">
|
<source class='appshot-1__img'
|
||||||
<header>
|
type="image/webp"
|
||||||
<h1>Chat for distributed teams.</h1>
|
srcset="{{ static('images/landing-page/hello/generated/screen-1-dark-1x.webp') }},
|
||||||
<p>
|
{{ static('images/landing-page/hello/generated/screen-1-dark-2x.webp') }} 2x,
|
||||||
Zulip combines the immediacy of real-time chat with an email
|
{{ static('images/landing-page/hello/generated/screen-1-dark-3x.webp') }} 3x"
|
||||||
threading model. <br class="line-break-desktop" />With Zulip, you can catch
|
media="(min-width: 941px) and (prefers-color-scheme: dark)"/>
|
||||||
up on important conversations while ignoring
|
<source class='appshot-1__img'
|
||||||
irrelevant ones.
|
type="image/webp"
|
||||||
</p>
|
srcset="{{ static('images/landing-page/hello/generated/screen-1-mobile-dark-1x.webp') }},
|
||||||
</header>
|
{{ static('images/landing-page/hello/generated/screen-1-mobile-dark-2x.webp') }} 2x,
|
||||||
<div class="tour">
|
{{ static('images/landing-page/hello/generated/screen-1-mobile-dark-3x.webp') }} 3x"
|
||||||
<div id="tour-carousel" class="carousel slide carousel-fade">
|
media="(max-width: 940px) and (prefers-color-scheme: dark)"/>
|
||||||
<!-- Carousel items -->
|
<source class='appshot-1__img'
|
||||||
<div class="carousel-inner">
|
type="image/webp"
|
||||||
<div class="item-container">
|
srcset="{{ static('images/landing-page/hello/generated/screen-1-1x.webp') }},
|
||||||
<div class="item active">
|
{{ static('images/landing-page/hello/generated/screen-1-2x.webp') }} 2x,
|
||||||
<div class="item-inner">
|
{{ static('images/landing-page/hello/generated/screen-1-3x.webp') }} 3x"
|
||||||
|
media="(min-width: 941px)"/>
|
||||||
<button data-target="#tour-carousel" data-slide="next" type="button" name="button" class="start-button">Take the tour</button>
|
<source class='appshot-1__img'
|
||||||
<img src="{{ static('images/story-tutorial/zulip-topic-blurred.png') }}" alt="" class="start-image" />
|
type="image/webp"
|
||||||
</div>
|
srcset="{{ static('images/landing-page/hello/generated/screen-1-mobile-1x.webp') }},
|
||||||
</div>
|
{{ static('images/landing-page/hello/generated/screen-1-mobile-2x.webp') }} 2x,
|
||||||
<div class="item">
|
{{ static('images/landing-page/hello/generated/screen-1-mobile-3x.webp') }} 3x"
|
||||||
<div class="item-inner">
|
media="(max-width: 940px)"/>
|
||||||
<p class="tour-item-header">In Zulip, you subscribe to <b>streams.</b> Streams are like channels in Slack or IRC.</p>
|
<source class='appshot-1__img'
|
||||||
<div class="zulip-slack-comparison">
|
srcset="{{ static('images/landing-page/hello/generated/screen-1-mobile-1x.jpg') }},
|
||||||
<div class="comparison-zulip">
|
{{ static('images/landing-page/hello/generated/screen-1-mobile-2x.jpg') }} 2x,
|
||||||
<div class="caption">Zulip</div>
|
{{ static('images/landing-page/hello/generated/screen-1-mobile-3x.jpg') }} 3x"
|
||||||
<img src="{{ static('images/story-tutorial/zulip-streams.png') }}" class="zulip-streams" alt="{{ _('Streams in Zulip') }}" />
|
media="(max-width: 940px)"/>
|
||||||
</div>
|
<img alt="" class='appshot-1__img' src="{{ static('images/landing-page/hello/generated/screen-1-1x.jpg') }}"
|
||||||
<div class="comparison-slack">
|
srcset="{{ static('images/landing-page/hello/generated/screen-1-1x.jpg') }},
|
||||||
<div class="caption">Other team chat</div>
|
{{ static('images/landing-page/hello/generated/screen-1-2x.jpg') }} 2x,
|
||||||
<img src="{{ static('images/story-tutorial/slack-streams.png') }}" class="slack-streams" alt="{{ _('Streams in Slack') }}" />
|
{{ static('images/landing-page/hello/generated/screen-1-3x.jpg') }} 3x"/>
|
||||||
</div>
|
</picture>
|
||||||
</div>
|
<div class="cta-buttons">
|
||||||
</div>
|
<a href="/try-zulip/">
|
||||||
</div>
|
<i class="background-activity-icon"></i>
|
||||||
<div class="item">
|
Try Zulip now
|
||||||
<div class="item-inner">
|
</a>
|
||||||
<p class="tour-item-header">Each stream message also has a <b>topic.</b> Topics are unique to Zulip.</p>
|
<a href="/communities/">
|
||||||
<div class="zulip-slack-comparison">
|
<i class="visibility-icon"></i>
|
||||||
<div class="comparison-zulip">
|
See it in use
|
||||||
<div class="caption">Zulip</div>
|
</a>
|
||||||
<img src="{{ static('images/story-tutorial/zulip-streams-selected.png') }}" class="zulip-streams-selected" alt="{{ _('Topics in Zulip') }}" />
|
<a href="/new/">
|
||||||
</div>
|
<i class="add-box-icon"></i>
|
||||||
<div class="comparison-slack">
|
Create organization
|
||||||
<div class="caption">Other team chat</div>
|
</a>
|
||||||
<img src="{{ static('images/story-tutorial/slack-streams-selected.png') }}" class="slack-streams-selected" alt="{{ _('Streams in Slack') }}" />
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="client-logos">
|
||||||
</div>
|
<div class='client-logos__logo_akamai'></div>
|
||||||
</div>
|
<div class='client-logos__logo_tum'></div>
|
||||||
<div class="item">
|
<div class='client-logos__logo_wikimedia'></div>
|
||||||
<div class="item-inner">
|
<div class='client-logos__logo_rust'></div>
|
||||||
<p class="tour-item-header">Topics make it easy to catch up after a day of meetings.</p>
|
<div class='client-logos__logo_dr_on_demand'></div>
|
||||||
<div class="zulip-slack-comparison">
|
<div class='client-logos__logo_maria'></div>
|
||||||
<div class="comparison-zulip">
|
</div>
|
||||||
<div class="caption comparison-2-caption-zulip">Zulip</div>
|
</div>
|
||||||
<img src="{{ static('images/story-tutorial/zulip-streams-unreads-arrows.png') }}" alt="{{ _('Stream topics in Zulip') }}" class="zulip-unreads-arrows" />
|
<div class="screen-2">
|
||||||
</div>
|
<div class="screen-2__container">
|
||||||
<div class="comparison-slack">
|
<div class="screen-2__content">
|
||||||
<div class="caption comparison-2-caption-slack">Other team chat</div>
|
<h2 class="screen-2__header">Efficient communication</h2>
|
||||||
<img src="{{ static('images/story-tutorial/slack-streams-unreads.png') }}" class="slack-stream-unreads" alt="{{ _('Streams in Slack') }}" />
|
<div class="screen-2__desc">
|
||||||
</div>
|
Unlike other chat apps, Zulip lets you read and respond to every message <em>in context</em>, no matter when it was sent. Maintain your <em>focus</em> and catch up on your own time <em>without stress</em>, reading just the topics <em>you care about</em>.
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="item-inner">
|
|
||||||
<p class="tour-item-header">Let’s click on “Tuesday night catering.”</p>
|
|
||||||
<img src="{{ static('images/story-tutorial/zulip-topic.png') }}" alt="{{ _('The Tuesday night catering topic in Zulip') }}" class="zulip-topic mobile-hide" />
|
|
||||||
<img src="{{ static('images/story-tutorial/zulip-topic-crop.png') }}" alt="{{ _('The Tuesday night catering topic in Zulip') }}" class="centered-image zulip-topic mobile-show" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="item-inner">
|
|
||||||
<p class="tour-item-header">Messages in Zulip retain their context even if they’re sent hours after the conversation started:</p>
|
|
||||||
<img src="{{ static('images/story-tutorial/zulip-streams-easy.png') }}" alt="{{ _('The Tuesday night catering topic in Zulip') }}" class="zulip-easy mobile-hide" />
|
|
||||||
<div class="mobile-show">
|
|
||||||
<img src="{{ static('images/story-tutorial/zulip-streams-easy-mobile-top.png') }}" class="centered-image" alt="{{ _('The Tuesday night catering topic in Zulip') }}" />
|
|
||||||
<p class="tour-explanation">Messages sent hours apart are linked in the same topic.</p>
|
|
||||||
<img src="{{ static('images/story-tutorial/zulip-streams-easy-mobile-bottom.png') }}" class="centered-image" alt="{{ _('The Tuesday night catering topic in Zulip - compose box') }}" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="item-inner">
|
|
||||||
<p class="tour-item-header">Without topics, it’s hard to catch up efficiently, and hard to participate in conversations that started while you were away.</p>
|
|
||||||
<img src="{{ static('images/story-tutorial/slack-overwhelming.png') }}" alt="{{ _('The Tuesday night catering topic in Slack') }}" class="slack-overwhelming mobile-hide" />
|
|
||||||
<div class="mobile-show">
|
|
||||||
<img src="{{ static('images/story-tutorial/slack-overwhelming-mobile.png') }}" class="centered-image" alt="{{ _('The Tuesday night catering topic in Slack') }}" />
|
|
||||||
<p class="tour-explanation">The last message about Tuesday night catering is hidden 56 messages ago. Meanwhile, you just see a mix of unrelated messages.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="item-inner">
|
|
||||||
<p class="tour-item-header tour-item-header-top-push tour-item-header-centered">Zulip Free is free for an unlimited number of users.</p>
|
|
||||||
<a href="/plans/" class="call-to-action">
|
|
||||||
{{ _('See plans and pricing') }}
|
|
||||||
</a>
|
|
||||||
<div class="other-resources">
|
|
||||||
<div class="other-resources-section">
|
|
||||||
<a href="/why-zulip/"><img src="{{ static('images/landing-page/hello/organised.svg') }}" alt="" /></a>
|
|
||||||
<p><a href="/why-zulip/">Zulip vs Slack →</a></p>
|
|
||||||
</div>
|
|
||||||
<div class="other-resources-section">
|
|
||||||
<a href="/features/"><img src="{{ static('images/landing-page/hello/featured.svg') }}" alt="" /></a>
|
|
||||||
<p><a href="/features/">See all features →</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="quote">
|
||||||
<a class="carousel-control left visibility-control hide"
|
<div class="quote__text">
|
||||||
href="#tour-carousel" data-slide="prev"
|
Zulip’s <strong>threading model</strong> makes it so much easier to <strong>manage my team</strong>... As a leader, <strong>in just a few minutes</strong> I can get an overview over what’s going on and see where my attention is needed.
|
||||||
aria-label="{{ _('Previous') }}" title="{{ _('Previous') }}">
|
<div class="quote__text-tail"></div>
|
||||||
<i class="fa fa-chevron-left"></i>
|
</div>
|
||||||
</a>
|
<div class="quote__source">
|
||||||
<a class="carousel-control right visibility-control"
|
<a href="/case-studies/idrift/">Case study with
|
||||||
href="#tour-carousel" data-slide="next"
|
<strong>Gaute Lund</strong></a>,
|
||||||
aria-label="{{ _('Next') }}" title="{{ _('Next') }}">
|
<i>co-founder of iDrift AS</i>
|
||||||
<i class="fa fa-chevron-right"></i>
|
|
||||||
</a>
|
|
||||||
<ol class="carousel-indicators">
|
|
||||||
<li data-target="#tour-carousel" data-slide-to="0" class="active"></li>
|
|
||||||
<li data-target="#tour-carousel" data-slide-to="1"></li>
|
|
||||||
<li data-target="#tour-carousel" data-slide-to="2"></li>
|
|
||||||
<li data-target="#tour-carousel" data-slide-to="3"></li>
|
|
||||||
<li data-target="#tour-carousel" data-slide-to="4"></li>
|
|
||||||
<li data-target="#tour-carousel" data-slide-to="5"></li>
|
|
||||||
<li data-target="#tour-carousel" data-slide-to="6"></li>
|
|
||||||
<li data-target="#tour-carousel" data-slide-to="7"></li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="apps">
|
|
||||||
<div class="triangle"></div>
|
|
||||||
<div class="left-side">
|
|
||||||
<div class="content">
|
|
||||||
<header>
|
|
||||||
<h1>Apps for every platform.</h1>
|
|
||||||
</header>
|
|
||||||
<p>
|
|
||||||
Zulip has modern apps for every major platform,
|
|
||||||
powered by Electron and React Native.
|
|
||||||
</p>
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="platform-icons">
|
|
||||||
<div class="group">
|
|
||||||
<h2>Web</h2>
|
|
||||||
<a href="{{ apps_page_web }}">
|
|
||||||
<i class="fa fa-desktop platform-icon" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="group">
|
|
||||||
<h2>Desktop</h2>
|
|
||||||
<a href="{{ apps_page_url }}mac">
|
|
||||||
<i class="fa fa-apple platform-icon" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
<a href="{{ apps_page_url }}windows">
|
|
||||||
<i class="fa fa-windows platform-icon" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
<a href="{{ apps_page_url }}linux">
|
|
||||||
<i class="fa fa-linux platform-icon" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="group">
|
|
||||||
<h2>Mobile</h2>
|
|
||||||
<a href="{{ apps_page_url }}ios">
|
|
||||||
<i class="fa fa-mobile-phone platform-icon" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
<a href="{{ apps_page_url }}android">
|
|
||||||
<i class="fa fa-android platform-icon" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="right-side">
|
|
||||||
<div class="screen ios">
|
|
||||||
<div class="main-page">
|
|
||||||
<div class="navbar"></div>
|
|
||||||
<div class="center-page">
|
|
||||||
<div class="message-feed">
|
|
||||||
<div class="stream">
|
|
||||||
<div class="line micro red"></div>
|
|
||||||
<div class="line nano"></div>
|
|
||||||
|
|
||||||
<div class="message">
|
|
||||||
<div class="top">
|
|
||||||
<div class="avatar"></div>
|
|
||||||
<div class="line top-line"></div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="line"></div>
|
|
||||||
<div class="line"></div>
|
|
||||||
<div class="line med"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="message">
|
|
||||||
<div class="top">
|
|
||||||
<div class="avatar"></div>
|
|
||||||
<div class="line top-line"></div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="line med"></div>
|
|
||||||
<div class="line"></div>
|
|
||||||
<div class="line small"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stream">
|
|
||||||
<div class="line micro blue"></div>
|
|
||||||
<div class="line nano"></div>
|
|
||||||
|
|
||||||
<div class="message">
|
|
||||||
<div class="top">
|
|
||||||
<div class="avatar"></div>
|
|
||||||
<div class="line top-line"></div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="line med"></div>
|
|
||||||
<div class="line med"></div>
|
|
||||||
<div class="line small"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="message">
|
|
||||||
<div class="top">
|
|
||||||
<div class="avatar"></div>
|
|
||||||
<div class="line top-line"></div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="line"></div>
|
|
||||||
<div class="line"></div>
|
|
||||||
<div class="line small"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="appshot-2">
|
||||||
|
<picture>
|
||||||
|
<!-- dark theme is only with webp images, since webp support was at the same time or earlier than prefers-color-scheme https://caniuse.com/?search=prefers-color-scheme https://caniuse.com/webp -->
|
||||||
|
<source class='appshot-2__img'
|
||||||
|
type="image/webp"
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-2-dark-1x.webp') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-dark-2x.webp') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-dark-3x.webp') }} 3x"
|
||||||
|
media="(min-width: 941px) and (prefers-color-scheme: dark)"/>
|
||||||
|
<source class='appshot-2__img'
|
||||||
|
type="image/webp"
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-2-mobile-dark-1x.webp') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-mobile-dark-2x.webp') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-mobile-dark-3x.webp') }} 3x"
|
||||||
|
media="(max-width: 940px) and (prefers-color-scheme: dark)"/>
|
||||||
|
<source class='appshot-2__img'
|
||||||
|
type="image/webp"
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-2-1x.webp') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-2x.webp') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-3x.webp') }} 3x"
|
||||||
|
media="(min-width: 941px)"/>
|
||||||
|
<source class='appshot-2__img'
|
||||||
|
type="image/webp"
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-2-mobile-1x.webp') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-mobile-2x.webp') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-mobile-3x.webp') }} 3x"
|
||||||
|
media="(max-width: 940px)"/>
|
||||||
|
<source class='appshot-2__img'
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-2-mobile-1x.jpg') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-mobile-2x.jpg') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-mobile-3x.jpg') }} 3x"
|
||||||
|
media="(max-width: 940px)"/>
|
||||||
|
<img alt="" class='appshot-2__img'
|
||||||
|
src="{{ static('images/landing-page/hello/generated/screen-2-1x.jpg') }}"
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-2-1x.jpg') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-2x.jpg') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-2-3x.jpg') }} 3x"/>
|
||||||
|
</picture>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="screen android">
|
<div class="screen-3">
|
||||||
<div class="main-page">
|
<div class="screen-3__container">
|
||||||
<div class="navbar"></div>
|
<div class="screen-3__content">
|
||||||
<div class="center-page">
|
<h2 class="screen-3__header">Collaboration at scale</h2>
|
||||||
<div class="message-feed">
|
<div class="screen-3__desc">
|
||||||
<div class="stream">
|
Zulip helps teams of all sizes be <em>more productive</em> together, from a few friends hacking on a new idea, to <em>globally distributed</em> organizations with hundreds of people tackling the world’s hardest problems.
|
||||||
<div class="line micro red"></div>
|
</div>
|
||||||
<div class="line nano"></div>
|
<div class="quote">
|
||||||
|
<div class="quote__text">
|
||||||
<div class="message">
|
Zulip has the <strong>best user experience</strong> of all the chat apps I’ve tried... It is the only app that makes hundreds of conversations <strong>manageable</strong>.
|
||||||
<div class="top">
|
<div class="quote__text-tail"></div>
|
||||||
<div class="avatar"></div>
|
</div>
|
||||||
<div class="line top-line"></div>
|
<div class="quote__source">
|
||||||
</div>
|
<a href="/case-studies/tum/">Case study with
|
||||||
<div class="content">
|
<strong>Tobias Lasser</strong></a>,
|
||||||
<div class="line"></div>
|
<i>Technical University of
|
||||||
<div class="line"></div>
|
Munich</i>
|
||||||
<div class="line med"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="message">
|
|
||||||
<div class="top">
|
|
||||||
<div class="avatar"></div>
|
|
||||||
<div class="line top-line"></div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="line med"></div>
|
|
||||||
<div class="line"></div>
|
|
||||||
<div class="line small"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stream">
|
|
||||||
<div class="line micro blue"></div>
|
|
||||||
<div class="line nano"></div>
|
|
||||||
|
|
||||||
<div class="message">
|
|
||||||
<div class="top">
|
|
||||||
<div class="avatar"></div>
|
|
||||||
<div class="line top-line"></div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="line med"></div>
|
|
||||||
<div class="line med"></div>
|
|
||||||
<div class="line small"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="message">
|
|
||||||
<div class="top">
|
|
||||||
<div class="avatar"></div>
|
|
||||||
<div class="line top-line"></div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="line"></div>
|
|
||||||
<div class="line"></div>
|
|
||||||
<div class="line small"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="appshot-3">
|
||||||
|
<picture>
|
||||||
|
<!-- dark theme -->
|
||||||
|
<source class='appshot-3-1__img'
|
||||||
|
type="image/webp"
|
||||||
|
media='(prefers-color-scheme: dark)'
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-3-1-dark-1x.webp') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-1-dark-2x.webp') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-1-dark-3x.webp') }} 3x"/>
|
||||||
|
<source class='appshot-3-1__img'
|
||||||
|
type="image/webp"
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-3-1-1x.webp') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-1-2x.webp') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-1-3x.webp') }} 3x"/>
|
||||||
|
<img alt="" class='appshot-3-1__img'
|
||||||
|
src="{{ static('images/landing-page/hello/generated/screen-3-1-1x.jpg') }}"
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-3-1-1x.jpg') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-1-2x.jpg') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-1-3x.jpg') }} 3x"/>
|
||||||
|
</picture>
|
||||||
|
<picture>
|
||||||
|
<!-- dark theme -->
|
||||||
|
<source class='appshot-3-2__img'
|
||||||
|
type="image/webp"
|
||||||
|
media='(prefers-color-scheme: dark)'
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-3-2-dark-1x.webp') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-2-dark-2x.webp') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-2-dark-3x.webp') }} 3x"/>
|
||||||
|
<source class='appshot-3-2__img'
|
||||||
|
type="image/webp"
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-3-2-1x.webp') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-2-2x.webp') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-2-3x.webp') }} 3x"/>
|
||||||
|
<img alt="" class='appshot-3-2__img'
|
||||||
|
src="{{ static('images/landing-page/hello/generated/screen-3-2-1x.jpg') }}"
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-3-2-1x.jpg') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-2-2x.jpg') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-3-2-3x.jpg') }} 3x"/>
|
||||||
|
</picture>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="screen-4">
|
||||||
|
<div class="screen-4__container">
|
||||||
<div class="open-source">
|
<div class="screen-4__content">
|
||||||
<div class="flex">
|
<h2 class="screen-4__header">Enterprise ready</h2>
|
||||||
<img src="{{ static('images/landing-page/hello/opensource.svg') }}" alt=""/>
|
<div class="screen-4__desc">
|
||||||
<div class="il-block">
|
<p>
|
||||||
<h1>Open source.</h1>
|
Take charge of your mission-critical communications with Zulip’s reliable <a href="/for/open-source/">100% free and open-source software</a>, with no vendor lock-in. You can count on our industry-leading <a href="/security/">security practices</a> to keep your data safe.
|
||||||
<p>
|
</p>
|
||||||
Zulip is <a href="https://github.com/zulip/zulip">100% open source software</a>,
|
<p>
|
||||||
built by a vibrant community of over 1000 developers
|
When you <a href="/self-hosting/">self-host Zulip</a>, you get all the features of our cloud offering, plus ultimate control and compliance.
|
||||||
from all around the world. With 160,000 words of
|
</p>
|
||||||
<a href="https://zulip.readthedocs.io/">developer documentation</a>,
|
</div>
|
||||||
a high quality code base, and a
|
<div class="screen-4__tags">
|
||||||
<a href="/development-community/">welcoming community</a>,
|
<a href="https://zulip.readthedocs.io/en/stable/production/authentication-methods.html">SAML authentication</a>
|
||||||
it’s easy to extend or tweak Zulip.
|
<a href="https://zulip.readthedocs.io/en/stable/production/authentication-methods.html#synchronizing-data">LDAP sync</a>
|
||||||
</p>
|
<a href="/help/export-your-organization">Import & export tools</a>
|
||||||
<p>
|
<a href="/help/roles-and-permissions">Fine-grained permissions</a>
|
||||||
Zulip has a significantly
|
<a href="https://zulip.readthedocs.io/en/stable/production/settings.html">Extensive configuration</a>
|
||||||
<a href="https://github.com/zulip/zulip/graphs/contributors">larger and more active</a>
|
<a href="/integrations/">100s of integrations</a>
|
||||||
development community than other modern
|
<a href="/integrations/doc/slack_incoming">Slack-compatible webhooks</a>
|
||||||
open source group chat solutions like
|
<a href="/help/gdpr-compliance">GDPR compliance</a>
|
||||||
<a href="https://github.com/mattermost/mattermost-server/graphs/contributors">Mattermost</a>,
|
<a href="/help/message-retention-policy">Retention policies</a>
|
||||||
<a href="https://github.com/RocketChat/Rocket.Chat/graphs/contributors">Rocket.Chat</a>,
|
</div>
|
||||||
and <a href="https://github.com/matrix-org/synapse/graphs/contributors">matrix.org</a>.
|
</div>
|
||||||
</p>
|
<div class="appshot-4">
|
||||||
<p>
|
<picture>
|
||||||
Learn about <a href="/self-hosting/">self-hosting Zulip</a>
|
<!-- dark theme -->
|
||||||
or <a href="{{ latest_release_announcement }}">read the Zulip {{ latest_major_version }} release announcement</a>.
|
<source class='appshot-4__img'
|
||||||
</p>
|
type="image/webp"
|
||||||
|
media='(prefers-color-scheme: dark)'
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-4-dark-1x.webp') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-4-dark-2x.webp') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-4-dark-3x.webp') }} 3x"/>
|
||||||
|
<source class='appshot-4__img'
|
||||||
|
type="image/webp"
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-4-1x.webp') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-4-2x.webp') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-4-3x.webp') }} 3x"/>
|
||||||
|
<img alt="" class='appshot-4__img'
|
||||||
|
src="{{ static('images/landing-page/hello/generated/screen-4-1x.jpg') }}"
|
||||||
|
srcset="{{ static('images/landing-page/hello/generated/screen-4-1x.jpg') }},
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-4-2x.jpg') }} 2x,
|
||||||
|
{{ static('images/landing-page/hello/generated/screen-4-3x.jpg') }} 3x"/>
|
||||||
|
</picture>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="screen-5">
|
||||||
|
<div class="screen-5__container">
|
||||||
<div class="integrations">
|
<h2 class="screen-5__header">
|
||||||
<div class="content">
|
Learn how Zulip can help your organization!
|
||||||
<header>
|
</h2>
|
||||||
<h1>Seamless integrations with everything you use.</h1>
|
<div class='screen-5__quotes'>
|
||||||
</header>
|
<div class="quote">
|
||||||
<p>
|
<div class="quote__text">
|
||||||
Zulip has more than 90 native integrations. Several hundred more
|
<div class="quote__text-industry">
|
||||||
are available through
|
<a href="/for/business/">Business</a>
|
||||||
<a href="/integrations/doc/zapier">Zapier</a>
|
<span class="quote__text-industry-icon quote-business-icon"></span>
|
||||||
and
|
</div>
|
||||||
<a href="/integrations/doc/ifttt">IFTTT</a>.
|
Zulip is everything Slack is, but it's <strong>smarter</strong> and more powerful... There's even support for getting messages from Slack and IRC
|
||||||
</p>
|
<div class="quote__text-tail"></div>
|
||||||
<p><a href="/integrations/">See all available integrations.</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="integration-icons">
|
|
||||||
<a href="/integrations/doc/travis">
|
|
||||||
<div class="group">
|
|
||||||
<img class="integration-logo" src="{{ static('images/integrations/logos/travis.svg') }}" alt="{{ _('Travis logo') }}" />
|
|
||||||
<h3 class="integration-name">Travis CI</h3>
|
|
||||||
<p class="integration-description">See build results immediately</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a href="/integrations/doc/github">
|
|
||||||
<div class="group">
|
|
||||||
<img class="integration-logo" src="{{ static('images/integrations/logos/github.svg') }}" alt="{{ _('GitHub logo') }}" />
|
|
||||||
<h3 class="integration-name">GitHub</h3>
|
|
||||||
<p class="integration-description">Track issues and pull requests</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a href="/integrations/doc/heroku">
|
|
||||||
<div class="group">
|
|
||||||
<img class="integration-logo" src="{{ static('images/integrations/logos/heroku.svg') }}" alt="{{ _('Heroku logo') }}" />
|
|
||||||
<h3 class="integration-name">Heroku</h3>
|
|
||||||
<p class="integration-description">Keep up with deployments</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a href="/integrations/doc/zendesk">
|
|
||||||
<div class="group">
|
|
||||||
<img class="integration-logo" src="{{ static('images/integrations/logos/zendesk.svg') }}" alt="{{ _('Zendesk logo') }}" />
|
|
||||||
<h3 class="integration-name">Zendesk</h3>
|
|
||||||
<p class="integration-description">Receive support tickets and updates</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a href="/integrations/doc/jira">
|
|
||||||
<div class="group">
|
|
||||||
<img class="integration-logo" src="{{ static('images/integrations/logos/jira.svg') }}" alt="{{ _('Jira logo') }}" />
|
|
||||||
<h3 class="integration-name">Jira</h3>
|
|
||||||
<p class="integration-description">Monitor project bugs and issues</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a href="/integrations/doc/sentry">
|
|
||||||
<div class="group">
|
|
||||||
<img class="integration-logo" src="{{ static('images/integrations/logos/sentry.svg') }}" alt="{{ _('Sentry logo') }}" />
|
|
||||||
<h3 class="integration-name">Sentry</h3>
|
|
||||||
<p class="integration-description">See real-time error tracking</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a href="/integrations/doc/pagerduty" class="hide-1">
|
|
||||||
<div class="group">
|
|
||||||
<img class="integration-logo" src="{{ static('images/integrations/logos/pagerduty.svg') }}" alt="{{ _('Pagerduty logo') }}" />
|
|
||||||
<h3 class="integration-name">Pagerduty</h3>
|
|
||||||
<p class="integration-description">Connect to your monitoring systems</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<p>Or build your own integrations with <a href="/api/integrations-overview">Zulip’s powerful API</a>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="testimonials">
|
|
||||||
<div class="padded-content">
|
|
||||||
<div id="quote-carousel" class="carousel slide" data-ride="carousel">
|
|
||||||
<div class="carousel-quotes">
|
|
||||||
<div class="carousel-inner">
|
|
||||||
<div class="item active quote-container">
|
|
||||||
<blockquote>
|
|
||||||
Zulip’s unique threading saves me well
|
|
||||||
over an hour a day in working with our
|
|
||||||
distributed team of engineers and PMs
|
|
||||||
across 7+ time zones. We tried Slack,
|
|
||||||
Mattermost, and other team chat
|
|
||||||
products that claim to support
|
|
||||||
threading, and nothing handles
|
|
||||||
synchronous and asynchronous
|
|
||||||
communication so intuitively.
|
|
||||||
</blockquote>
|
|
||||||
<cite>Jacinda Shelly, CTO, Doctor on Demand</cite>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="item quote-container">
|
<div class="quote__source">
|
||||||
<blockquote>
|
<a href="https://www.theregister.com/2021/07/28/zulip_open_source_chat_collaboration_software/">Review in <strong>The Register</strong></a>
|
||||||
Akamai’s Zulip Enterprise deployment
|
|
||||||
connects over 2000 users around the planet.
|
|
||||||
The threading model of conversations
|
|
||||||
provides a large number of participants the
|
|
||||||
ability to engage in real, ongoing, and
|
|
||||||
substantive discussions, without the
|
|
||||||
overwhelming experience of many other chat
|
|
||||||
systems. This coordination across far-flung
|
|
||||||
teams has had a significant, positive impact
|
|
||||||
on the happiness and productivity of our
|
|
||||||
personnel, regardless of location or
|
|
||||||
seniority.
|
|
||||||
</blockquote>
|
|
||||||
<cite>Andy Ellis, Chief Security Officer, Akamai</cite>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="item quote-container">
|
</div>
|
||||||
<blockquote>
|
<div class="quote">
|
||||||
Choosing Zulip over Slack as our group chat is one
|
<div class="quote__text">
|
||||||
of the best decisions we’ve ever made. Zulip makes
|
<div class="quote__text-industry">
|
||||||
it easy for our community of 1000 Recursers around
|
<a href="/for/open-source/">Open source</a>
|
||||||
the world to stay involved, even years after their
|
<span class="quote__text-industry-icon quote-open-source-icon"></span>
|
||||||
batches finish. No other tool has a user
|
</div>
|
||||||
experience that scales to a community of our
|
Rust development would not be <strong>moving at the pace</strong> that it has been without Zulip...
|
||||||
size.
|
<div class="quote__text-tail"></div>
|
||||||
</blockquote>
|
</div>
|
||||||
<cite>Nick Bergson-Shilcock, founder and CEO, Recurse Center</cite>
|
<div class="quote__source">
|
||||||
|
<a href="https://www.rust-lang.org/governance/teams/lang"><strong>Josh Triplett</strong></a>,<i class="quote__source-title">Rust Language team co-lead</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="quote">
|
||||||
|
<div class="quote__text">
|
||||||
|
<div class="quote__text-industry">
|
||||||
|
<a href="/for/education/">Education</a>
|
||||||
|
<span class="quote__text-industry-icon quote-education-icon"></span>
|
||||||
|
</div>
|
||||||
|
Zulip’s topics have made it much easier for me to keep things <strong>coherent</strong>...
|
||||||
|
<div class="quote__text-tail">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="quote__source">
|
||||||
|
<a href="/case-studies/ucsd/"><strong>Kiran S. Kedlaya</strong></a>,<i class="quote__source-title">Professor of Mathematics at UCSD</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="quote">
|
||||||
|
<div class="quote__text">
|
||||||
|
<div class="quote__text-industry">
|
||||||
|
<a href="/for/research/">Research</a>
|
||||||
|
<span class="quote__text-industry-icon quote-research-icon"></span>
|
||||||
|
</div>
|
||||||
|
The excellent LaTeX rendering and clever threading make it far <strong>superior to email</strong> and Slack
|
||||||
|
<div class="quote__text-tail">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="quote__source">
|
||||||
|
<a href="https://twitter.com/TomGur/status/1294322062274842624"><strong>Tom Gur</strong></a>,<i class="quote__source-title">Associate Professor at Warwick</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="quote">
|
||||||
|
<div class="quote__text">
|
||||||
|
<div class="quote__text-industry">
|
||||||
|
<a href="/for/events/">Events</a>
|
||||||
|
<span class="quote__text-industry-icon quote-events-icon"></span>
|
||||||
|
</div>
|
||||||
|
With Zulip, questions or ideas were <strong>spread openly</strong>...
|
||||||
|
<div class="quote__text-tail"></div>
|
||||||
|
</div>
|
||||||
|
<div class="quote__source">
|
||||||
|
<a href="https://perso.univ-rennes1.fr/christophe.ritzenthaler/"><strong>Christophe Ritzenthaler</strong></a>,<i class="quote__source-title">Executive Director of CIMPA</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="quote">
|
||||||
|
<div class="quote__text">
|
||||||
|
<div class="quote__text-industry">
|
||||||
|
<a href="/for/communities/">Communities</a>
|
||||||
|
<span class="quote__text-industry-icon quote-communities-icon"></span>
|
||||||
|
</div>
|
||||||
|
No other tool has a user experience that <strong>scales</strong> to a community of our size...
|
||||||
|
<div class="quote__text-tail">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="quote__source">
|
||||||
|
<a href="/case-studies/recurse-center/"><strong>Nick Bergson-Shilcock</strong></a>,<i class="quote__source-title">CEO of Recurse Center</i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="screen-5__badges">
|
||||||
<div class="left visibility-control hide">
|
<a class="screen-5__badge-capterra" href="https://www.capterra.com/p/197945/Zulip/">
|
||||||
<a class="fa fa-chevron-left" aria-hidden="true" href="#quote-carousel" data-slide="prev"></a>
|
<img alt="" src="{{ static('images/landing-page/hello/capterra-2023.png') }}"/>
|
||||||
</div>
|
</a>
|
||||||
<div class="right visibility-control">
|
|
||||||
<a class="fa fa-chevron-right" aria-hidden="true" href="#quote-carousel" data-slide="next"></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<div class="company-container">
|
|
||||||
<header>
|
|
||||||
<h2 class="float left">Trusted by thousands of teams.</h2>
|
|
||||||
<div class="float clear"></div>
|
|
||||||
</header>
|
|
||||||
<div class="company-box">
|
|
||||||
<div class="company akamai"></div>
|
|
||||||
<div class="company wikimedia-outreach"></div>
|
|
||||||
<div class="company doctorondemand"></div>
|
|
||||||
<div class="company mariadb"></div>
|
|
||||||
<div class="company levelup"></div>
|
|
||||||
<div class="company recurse"></div>
|
|
||||||
<div class="company cmt"></div>
|
|
||||||
<div class="company layershift"></div>
|
|
||||||
<div class="company panjiva"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% include 'zerver/footer.html' %}
|
||||||
<div class="padded-content call-to-action-bottom">
|
|
||||||
<h1>Learn how Zulip can help your organization!</h1>
|
|
||||||
<div class="register-buttons">
|
|
||||||
<a href="/for/business/" class="register-now button">Business</a>
|
|
||||||
<a href="/for/open-source/" class="register-now button">Open source</a>
|
|
||||||
<a href="/for/education/" class="register-now button">Education</a>
|
|
||||||
<a href="/for/events/" class="register-now button">Events and Conferences</a>
|
|
||||||
<a href="/for/research/" class="register-now button">Research</a>
|
|
||||||
<a href="/for/communities/" class="register-now button">Communities</a>
|
|
||||||
</div>
|
|
||||||
{% if register_link_disabled %}
|
|
||||||
{% elif only_sso %}
|
|
||||||
<a href="{{ url('login-sso') }}" class="styled-button button green">
|
|
||||||
{{ _('Log in now') }}
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
<a href="/plans/" class="styled-button button green">
|
|
||||||
{{ _('See plans and pricing') }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
<div class="zulip-octopus"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -65,6 +65,12 @@ def build_timezones_data_paths() -> List[str]:
|
||||||
return paths
|
return paths
|
||||||
|
|
||||||
|
|
||||||
|
def build_landing_page_images_paths() -> List[str]:
|
||||||
|
paths = ["tools/setup/generate_landing_page_images.py"]
|
||||||
|
paths += glob.glob("static/images/landing-page/hello/original/*")
|
||||||
|
return paths
|
||||||
|
|
||||||
|
|
||||||
def compilemessages_paths() -> List[str]:
|
def compilemessages_paths() -> List[str]:
|
||||||
paths = ["zerver/management/commands/compilemessages.py"]
|
paths = ["zerver/management/commands/compilemessages.py"]
|
||||||
paths += glob.glob("locale/*/LC_MESSAGES/*.po")
|
paths += glob.glob("locale/*/LC_MESSAGES/*.po")
|
||||||
|
@ -159,6 +165,16 @@ def need_to_run_build_timezone_data() -> bool:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def need_to_regenerate_landing_page_images() -> bool:
|
||||||
|
if not os.path.exists("static/images/landing-page/hello/generated"):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return is_digest_obsolete(
|
||||||
|
"landing_page_images_hash",
|
||||||
|
build_landing_page_images_paths(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def need_to_run_compilemessages() -> bool:
|
def need_to_run_compilemessages() -> bool:
|
||||||
if not os.path.exists("locale/language_name_map.json"):
|
if not os.path.exists("locale/language_name_map.json"):
|
||||||
# User may have cleaned their Git checkout.
|
# User may have cleaned their Git checkout.
|
||||||
|
@ -227,6 +243,13 @@ def main(options: argparse.Namespace) -> int:
|
||||||
else:
|
else:
|
||||||
print("No need to run `tools/setup/build_timezone_values`.")
|
print("No need to run `tools/setup/build_timezone_values`.")
|
||||||
|
|
||||||
|
if options.is_force or need_to_regenerate_landing_page_images():
|
||||||
|
run(["tools/setup/generate_landing_page_images.py"])
|
||||||
|
write_new_digest(
|
||||||
|
"landing_page_images_hash",
|
||||||
|
build_landing_page_images_paths(),
|
||||||
|
)
|
||||||
|
|
||||||
if not options.is_build_release_tarball_only:
|
if not options.is_build_release_tarball_only:
|
||||||
# The following block is skipped when we just need the development
|
# The following block is skipped when we just need the development
|
||||||
# environment to build a release tarball.
|
# environment to build a release tarball.
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generates versions of landing page images to be served in different conditions."""
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
ZULIP_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
if ZULIP_PATH not in sys.path:
|
||||||
|
sys.path.append(ZULIP_PATH)
|
||||||
|
|
||||||
|
LANDING_IMAGES_DIR = os.path.join(ZULIP_PATH, "static", "images", "landing-page", "hello")
|
||||||
|
ORIGINAL_IMAGES_DIR = os.path.join(LANDING_IMAGES_DIR, "original")
|
||||||
|
GENERATED_IMAGES_DIR = os.path.join(LANDING_IMAGES_DIR, "generated")
|
||||||
|
|
||||||
|
|
||||||
|
def get_x_size(size: Tuple[float, float], x: int) -> Tuple[int, int]:
|
||||||
|
return int(x / 3 * size[0]), int(x / 3 * size[1])
|
||||||
|
|
||||||
|
|
||||||
|
def generate_landing_page_images() -> None:
|
||||||
|
if not os.path.exists(GENERATED_IMAGES_DIR):
|
||||||
|
os.mkdir(GENERATED_IMAGES_DIR)
|
||||||
|
|
||||||
|
for image_file_path in glob.glob(f"{ORIGINAL_IMAGES_DIR}/*"):
|
||||||
|
file_name = Path(image_file_path).stem
|
||||||
|
with Image.open(image_file_path) as image:
|
||||||
|
size_2x = get_x_size(image.size, 2)
|
||||||
|
size_1x = get_x_size(image.size, 1)
|
||||||
|
|
||||||
|
## Generate WEBP images.
|
||||||
|
image.save(f"{GENERATED_IMAGES_DIR}/{file_name}-3x.webp", quality=50)
|
||||||
|
image_2x = image.resize(size_2x)
|
||||||
|
image_2x.save(f"{GENERATED_IMAGES_DIR}/{file_name}-2x.webp", quality=50)
|
||||||
|
image_1x = image.resize(size_1x)
|
||||||
|
image_1x.save(f"{GENERATED_IMAGES_DIR}/{file_name}-1x.webp", quality=70)
|
||||||
|
|
||||||
|
## Generate JPG images.
|
||||||
|
# Convert from RGBA to RGB since jpg doesn't support transparency.
|
||||||
|
rgb_image = image.convert("RGB")
|
||||||
|
rgb_image.save(f"{GENERATED_IMAGES_DIR}/{file_name}-3x.jpg", quality=19, optimize=True)
|
||||||
|
rgb_image_2x = rgb_image.resize(size_2x)
|
||||||
|
rgb_image_2x.save(
|
||||||
|
f"{GENERATED_IMAGES_DIR}/{file_name}-2x.jpg", quality=50, optimize=True
|
||||||
|
)
|
||||||
|
rgb_image_1x = rgb_image.resize(size_1x)
|
||||||
|
rgb_image_1x.save(
|
||||||
|
f"{GENERATED_IMAGES_DIR}/{file_name}-1x.jpg", quality=70, optimize=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_landing_page_images()
|
|
@ -12,6 +12,7 @@ import "font-awesome/css/font-awesome.css";
|
||||||
import "../../images/icons/zulip-icons.font";
|
import "../../images/icons/zulip-icons.font";
|
||||||
import "source-sans/source-sans-3VF.css";
|
import "source-sans/source-sans-3VF.css";
|
||||||
import "source-code-pro/source-code-pro.css";
|
import "source-code-pro/source-code-pro.css";
|
||||||
|
import "@fontsource-variable/open-sans";
|
||||||
import "../../styles/alerts.css";
|
import "../../styles/alerts.css";
|
||||||
import "../../styles/modal.css";
|
import "../../styles/modal.css";
|
||||||
import "../../styles/progress_bar.css";
|
import "../../styles/progress_bar.css";
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
// TODO: Use `common.ts` directly in hello page when `boostrap` is
|
||||||
|
// removed from it.
|
||||||
|
|
||||||
|
import "source-sans/source-sans-3VF.css";
|
||||||
|
import "source-code-pro/source-code-pro.css";
|
||||||
|
import "@fontsource-variable/open-sans";
|
|
@ -14,6 +14,59 @@ export function path_parts() {
|
||||||
return window.location.pathname.split("/").filter((chunk) => chunk !== "");
|
return window.location.pathname.split("/").filter((chunk) => chunk !== "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hello_events = function () {
|
||||||
|
function get_new_rand(oldRand, max) {
|
||||||
|
const newRand = Math.floor(Math.random() * max);
|
||||||
|
return newRand === oldRand ? get_new_rand(newRand, max) : newRand;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_random_item_from_array(array) {
|
||||||
|
return array[Math.floor(Math.random() * array.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const current_clint_logo_class_names = new Set([
|
||||||
|
"client-logos__logo_akamai",
|
||||||
|
"client-logos__logo_tum",
|
||||||
|
"client-logos__logo_wikimedia",
|
||||||
|
"client-logos__logo_rust",
|
||||||
|
"client-logos__logo_dr_on_demand",
|
||||||
|
"client-logos__logo_maria",
|
||||||
|
]);
|
||||||
|
const future_logo_class_names = new Set([
|
||||||
|
"client-logos__logo_pilot",
|
||||||
|
"client-logos__logo_recurse",
|
||||||
|
"client-logos__logo_level_up",
|
||||||
|
|
||||||
|
"client-logos__logo_layershift",
|
||||||
|
"client-logos__logo_julia",
|
||||||
|
"client-logos__logo_ucsd",
|
||||||
|
"client-logos__logo_lean",
|
||||||
|
"client-logos__logo_asciidoc",
|
||||||
|
]);
|
||||||
|
let current_clint_logo_class_namesIndex = 0;
|
||||||
|
function update_client_logo() {
|
||||||
|
if (document.hidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const logos = [...document.querySelectorAll("[class^='client-logos__']")];
|
||||||
|
current_clint_logo_class_namesIndex = get_new_rand(
|
||||||
|
current_clint_logo_class_namesIndex,
|
||||||
|
logos.length,
|
||||||
|
);
|
||||||
|
const el = logos[current_clint_logo_class_namesIndex];
|
||||||
|
|
||||||
|
const oldClass = el.className;
|
||||||
|
el.className = "";
|
||||||
|
current_clint_logo_class_names.delete(oldClass);
|
||||||
|
const newClass = get_random_item_from_array([...future_logo_class_names.values()]);
|
||||||
|
future_logo_class_names.delete(newClass);
|
||||||
|
el.className = newClass;
|
||||||
|
current_clint_logo_class_names.add(newClass);
|
||||||
|
future_logo_class_names.add(oldClass);
|
||||||
|
}
|
||||||
|
setInterval(update_client_logo, 2500);
|
||||||
|
};
|
||||||
|
|
||||||
const apps_events = function () {
|
const apps_events = function () {
|
||||||
const info = {
|
const info = {
|
||||||
windows: {
|
windows: {
|
||||||
|
@ -121,78 +174,16 @@ const apps_events = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
const events = function () {
|
const events = function () {
|
||||||
// get the location url like `zulip.com/features/`, cut off the trailing
|
|
||||||
// `/` and then split by `/` to get ["zulip.com", "features"], then
|
|
||||||
// pop the last element to get the current section (eg. `features`).
|
|
||||||
const location = window.location.pathname.replace(/\/$/, "").split(/\//).pop();
|
|
||||||
|
|
||||||
$(`[data-on-page='${CSS.escape(location)}']`).addClass("active");
|
|
||||||
|
|
||||||
$("body").on("click", (e) => {
|
|
||||||
const $e = $(e.target);
|
|
||||||
|
|
||||||
if ($e.is("nav ul .exit")) {
|
|
||||||
$("nav ul").css("transform", "translate(-350px, 0)");
|
|
||||||
// See https://ishadeed.com/article/layout-flickering/ for
|
|
||||||
// more context as to why the following timeout is important.
|
|
||||||
setTimeout(() => {
|
|
||||||
$("nav ul").removeClass("show");
|
|
||||||
$("nav ul").css("transform", "");
|
|
||||||
$("body").removeClass("noscroll");
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($("nav ul.show") && !$e.closest("nav ul.show").length && !$e.is("nav ul.show")) {
|
|
||||||
$("nav ul").removeClass("show");
|
|
||||||
$("body").removeClass("noscroll");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".hamburger").on("click", (e) => {
|
|
||||||
$("nav ul").addClass("show");
|
|
||||||
$("body").addClass("noscroll");
|
|
||||||
e.stopPropagation();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (path_parts().includes("apps")) {
|
if (path_parts().includes("apps")) {
|
||||||
apps_events();
|
apps_events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (path_parts().includes("hello")) {
|
||||||
|
hello_events();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
// Initiate the bootstrap carousel logic
|
|
||||||
$(".carousel").carousel({
|
|
||||||
interval: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Move to the next slide on clicking inside the carousel container
|
|
||||||
$(".carousel-inner .item-container").on("click", function (e) {
|
|
||||||
const get_tag_name = e.target.tagName.toLowerCase();
|
|
||||||
const is_button = get_tag_name === "button";
|
|
||||||
const is_link = get_tag_name === "a";
|
|
||||||
const is_last_slide = $("#tour-carousel .carousel-inner .item:last-child").hasClass(
|
|
||||||
"active",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Do not trigger this event if user clicks on a button, link
|
|
||||||
// or if it's the last slide
|
|
||||||
const move_slide_forward = !is_button && !is_link && !is_last_slide;
|
|
||||||
|
|
||||||
if (move_slide_forward) {
|
|
||||||
$(this).closest(".carousel").carousel("next");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".carousel").on("slid", function () {
|
|
||||||
const $this = $(this);
|
|
||||||
$this.find(".visibility-control").show();
|
|
||||||
if ($this.find(".carousel-inner .item").first().hasClass("active")) {
|
|
||||||
$this.find(".left.visibility-control").hide();
|
|
||||||
} else if ($this.find(".carousel-inner .item").last().hasClass("active")) {
|
|
||||||
$this.find(".right.visibility-control").hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set up events / categories / search
|
// Set up events / categories / search
|
||||||
events();
|
events();
|
||||||
|
|
||||||
|
@ -206,14 +197,14 @@ $(() => {
|
||||||
// Resize tweet to avoid overlapping with image. Since tweet uses an iframe which doesn't adjust with
|
// Resize tweet to avoid overlapping with image. Since tweet uses an iframe which doesn't adjust with
|
||||||
// screen resize, we need to manually adjust its width.
|
// screen resize, we need to manually adjust its width.
|
||||||
|
|
||||||
function resizeIFrameToFitContent(iFrame) {
|
function resize_iframe_to_fit_content(iFrame) {
|
||||||
$(iFrame).width("38vw");
|
$(iFrame).width("38vw");
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("resize", () => {
|
window.addEventListener("resize", () => {
|
||||||
const iframes = document.querySelectorAll(".twitter-tweet iframe");
|
const iframes = document.querySelectorAll(".twitter-tweet iframe");
|
||||||
for (const iframe of iframes) {
|
for (const iframe of iframes) {
|
||||||
resizeIFrameToFitContent(iframe);
|
resize_iframe_to_fit_content(iframe);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,6 +51,15 @@
|
||||||
"./src/portico/landing-page",
|
"./src/portico/landing-page",
|
||||||
"./styles/portico/landing_page.css"
|
"./styles/portico/landing_page.css"
|
||||||
],
|
],
|
||||||
|
"landing-page-hello": [
|
||||||
|
"./src/bundles/hello",
|
||||||
|
"./src/portico/landing-page",
|
||||||
|
"./src/portico/header",
|
||||||
|
"./styles/portico/svg_icons.css",
|
||||||
|
"./styles/portico/hello.css",
|
||||||
|
"./styles/portico/navbar.css",
|
||||||
|
"./styles/portico/footer.css"
|
||||||
|
],
|
||||||
"integrations": [
|
"integrations": [
|
||||||
"./src/bundles/portico",
|
"./src/bundles/portico",
|
||||||
"./src/portico/integrations",
|
"./src/portico/integrations",
|
||||||
|
|
|
@ -253,7 +253,7 @@ class DocPageTest(ZulipTestCase):
|
||||||
# Test the i18n version of one of these pages.
|
# Test the i18n version of one of these pages.
|
||||||
self._test("/en/history/", ["Zulip released as open source!"])
|
self._test("/en/history/", ["Zulip released as open source!"])
|
||||||
self._test("/values/", ["designed our company"])
|
self._test("/values/", ["designed our company"])
|
||||||
self._test("/hello/", ["Chat for distributed teams"])
|
self._test("/hello/", ["your mission-critical communications with Zulip"])
|
||||||
self._test("/communities/", ["Open communities directory"])
|
self._test("/communities/", ["Open communities directory"])
|
||||||
self._test("/development-community/", ["Zulip development community"])
|
self._test("/development-community/", ["Zulip development community"])
|
||||||
self._test("/features/", ["Beautiful messaging"])
|
self._test("/features/", ["Beautiful messaging"])
|
||||||
|
|
|
@ -544,7 +544,7 @@ class HomeTest(ZulipTestCase):
|
||||||
result = self.client_post("/accounts/accept_terms/")
|
result = self.client_post("/accounts/accept_terms/")
|
||||||
self.assertEqual(result.status_code, 200)
|
self.assertEqual(result.status_code, 200)
|
||||||
self.assert_in_response("I agree to the", result)
|
self.assert_in_response("I agree to the", result)
|
||||||
self.assert_in_response("Chat for distributed teams", result)
|
self.assert_in_response("your mission-critical communications with Zulip", result)
|
||||||
|
|
||||||
def test_accept_terms_of_service(self) -> None:
|
def test_accept_terms_of_service(self) -> None:
|
||||||
self.login("hamlet")
|
self.login("hamlet")
|
||||||
|
@ -1045,7 +1045,7 @@ class HomeTest(ZulipTestCase):
|
||||||
with patch("zerver.views.home.get_subdomain", return_value=""):
|
with patch("zerver.views.home.get_subdomain", return_value=""):
|
||||||
result = self._get_home_page()
|
result = self._get_home_page()
|
||||||
self.assertEqual(result.status_code, 200)
|
self.assertEqual(result.status_code, 200)
|
||||||
self.assert_in_response("Chat for distributed teams", result)
|
self.assert_in_response("your mission-critical communications with Zulip", result)
|
||||||
|
|
||||||
with patch("zerver.views.home.get_subdomain", return_value="subdomain"):
|
with patch("zerver.views.home.get_subdomain", return_value="subdomain"):
|
||||||
result = self._get_home_page()
|
result = self._get_home_page()
|
||||||
|
|